[core, ios, macos, android, node] Heatmap layer (#11046)

Co-Authored-By: Konstantin Käfer <mail@kkaefer.com>
Co-Authored-By: Anand Thakker <anandthakker@users.noreply.github.com>
Co-Authored-By: Minh Nguyễn <1ec5@users.noreply.github.com>
This commit is contained in:
Vladimir Agafonkin
2018-02-15 17:38:23 +02:00
committed by GitHub
parent 1fdec7a5c1
commit 82de856c94
90 changed files with 4570 additions and 61 deletions

View File

@@ -146,6 +146,10 @@ set(MBGL_CORE_FILES
src/mbgl/programs/fill_extrusion_program.hpp
src/mbgl/programs/fill_program.cpp
src/mbgl/programs/fill_program.hpp
src/mbgl/programs/heatmap_program.cpp
src/mbgl/programs/heatmap_program.hpp
src/mbgl/programs/heatmap_texture_program.cpp
src/mbgl/programs/heatmap_texture_program.hpp
src/mbgl/programs/hillshade_prepare_program.cpp
src/mbgl/programs/hillshade_prepare_program.hpp
src/mbgl/programs/hillshade_program.cpp
@@ -225,6 +229,8 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/buckets/fill_bucket.hpp
src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
src/mbgl/renderer/buckets/heatmap_bucket.cpp
src/mbgl/renderer/buckets/heatmap_bucket.hpp
src/mbgl/renderer/buckets/hillshade_bucket.cpp
src/mbgl/renderer/buckets/hillshade_bucket.hpp
src/mbgl/renderer/buckets/line_bucket.cpp
@@ -245,6 +251,8 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
src/mbgl/renderer/layers/render_fill_layer.cpp
src/mbgl/renderer/layers/render_fill_layer.hpp
src/mbgl/renderer/layers/render_heatmap_layer.cpp
src/mbgl/renderer/layers/render_heatmap_layer.hpp
src/mbgl/renderer/layers/render_hillshade_layer.cpp
src/mbgl/renderer/layers/render_hillshade_layer.hpp
src/mbgl/renderer/layers/render_line_layer.cpp
@@ -297,6 +305,10 @@ set(MBGL_CORE_FILES
src/mbgl/shaders/fill_outline_pattern.hpp
src/mbgl/shaders/fill_pattern.cpp
src/mbgl/shaders/fill_pattern.hpp
src/mbgl/shaders/heatmap.cpp
src/mbgl/shaders/heatmap.hpp
src/mbgl/shaders/heatmap_texture.cpp
src/mbgl/shaders/heatmap_texture.hpp
src/mbgl/shaders/hillshade.cpp
src/mbgl/shaders/hillshade.hpp
src/mbgl/shaders/hillshade_prepare.cpp
@@ -349,6 +361,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/data_driven_property_value.hpp
include/mbgl/style/filter.hpp
include/mbgl/style/filter_evaluator.hpp
include/mbgl/style/heatmap_color_property_value.hpp
include/mbgl/style/image.hpp
include/mbgl/style/layer.hpp
include/mbgl/style/layer_type.hpp
@@ -401,6 +414,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/conversion/geojson.hpp
include/mbgl/style/conversion/geojson_options.hpp
include/mbgl/style/conversion/get_json_type.hpp
include/mbgl/style/conversion/heatmap_color_property_value.hpp
include/mbgl/style/conversion/layer.hpp
include/mbgl/style/conversion/light.hpp
include/mbgl/style/conversion/position.hpp
@@ -495,6 +509,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/layers/custom_layer.hpp
include/mbgl/style/layers/fill_extrusion_layer.hpp
include/mbgl/style/layers/fill_layer.hpp
include/mbgl/style/layers/heatmap_layer.hpp
include/mbgl/style/layers/hillshade_layer.hpp
include/mbgl/style/layers/line_layer.hpp
include/mbgl/style/layers/raster_layer.hpp
@@ -522,6 +537,11 @@ set(MBGL_CORE_FILES
src/mbgl/style/layers/fill_layer_impl.hpp
src/mbgl/style/layers/fill_layer_properties.cpp
src/mbgl/style/layers/fill_layer_properties.hpp
src/mbgl/style/layers/heatmap_layer.cpp
src/mbgl/style/layers/heatmap_layer_impl.cpp
src/mbgl/style/layers/heatmap_layer_impl.hpp
src/mbgl/style/layers/heatmap_layer_properties.cpp
src/mbgl/style/layers/heatmap_layer_properties.hpp
src/mbgl/style/layers/hillshade_layer.cpp
src/mbgl/style/layers/hillshade_layer_impl.cpp
src/mbgl/style/layers/hillshade_layer_impl.hpp

View File

@@ -0,0 +1,46 @@
#pragma once
#include <mbgl/style/heatmap_color_property_value.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
#include <mbgl/style/conversion/expression.hpp>
#include <mbgl/style/expression/value.hpp>
#include <mbgl/style/expression/is_constant.hpp>
#include <mbgl/style/expression/is_expression.hpp>
#include <mbgl/style/expression/find_zoom_curve.hpp>
namespace mbgl {
namespace style {
namespace conversion {
template <>
struct Converter<HeatmapColorPropertyValue> {
optional<HeatmapColorPropertyValue> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return HeatmapColorPropertyValue();
} else if (isExpression(value)) {
optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, expression::type::Color);
if (!expression) {
return {};
}
assert(*expression);
if (!isFeatureConstant(**expression)) {
error = { "property expressions not supported" };
return {};
}
if (!isZoomConstant(**expression)) {
error = { "zoom expressions not supported" };
return {};
}
return {HeatmapColorPropertyValue(std::move(*expression))};
} else {
error = { "heatmap-color must be an expression" };
return {};
}
}
};
} // namespace conversion
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,49 @@
#pragma once
#include <mbgl/util/variant.hpp>
#include <mbgl/style/undefined.hpp>
#include <mbgl/style/function/camera_function.hpp>
namespace mbgl {
namespace style {
/*
* Special-case implementation of (a subset of) the PropertyValue<T> interface
* used for building the HeatmapColor paint property traits class.
*/
class HeatmapColorPropertyValue {
private:
std::shared_ptr<expression::Expression> value;
friend bool operator==(const HeatmapColorPropertyValue& lhs, const HeatmapColorPropertyValue& rhs) {
return (lhs.isUndefined() && rhs.isUndefined()) || (lhs.value && rhs.value && *(lhs.value) == *(rhs.value));
}
friend bool operator!=(const HeatmapColorPropertyValue& lhs, const HeatmapColorPropertyValue& rhs) {
return !(lhs == rhs);
}
public:
HeatmapColorPropertyValue() : value(nullptr) {}
HeatmapColorPropertyValue(std::shared_ptr<expression::Expression> value_) : value(std::move(value_)) {}
bool isUndefined() const { return value.get() == nullptr; }
// noop, needed for batch evaluation of paint property values to compile
template <typename Evaluator>
Color evaluate(const Evaluator&, TimePoint = {}) const { return {}; }
Color evaluate(double heatmapDensity) const {
const auto result = value->evaluate(expression::EvaluationContext({}, nullptr, {heatmapDensity}));
return *expression::fromExpressionValue<Color>(*result);
}
bool isDataDriven() const { return false; }
bool hasDataDrivenPropertyDifference(const HeatmapColorPropertyValue&) const { return false; }
const expression::Expression& getExpression() const { return *value; }
};
} // namespace style
} // namespace mbgl

View File

@@ -23,6 +23,7 @@ class HillshadeLayer;
class BackgroundLayer;
class CustomLayer;
class FillExtrusionLayer;
class HeatmapLayer;
class LayerObserver;
/**
@@ -93,6 +94,8 @@ public:
return std::forward<V>(visitor)(*as<CustomLayer>());
case LayerType::FillExtrusion:
return std::forward<V>(visitor)(*as<FillExtrusionLayer>());
case LayerType::Heatmap:
return std::forward<V>(visitor)(*as<HeatmapLayer>());
}

View File

@@ -13,6 +13,7 @@ enum class LayerType {
Background,
Custom,
FillExtrusion,
Heatmap,
};
} // namespace style

View File

@@ -0,0 +1,86 @@
// This file is generated. Do not edit.
#pragma once
#include <mbgl/style/layer.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
#include <mbgl/style/heatmap_color_property_value.hpp>
#include <mbgl/util/color.hpp>
namespace mbgl {
namespace style {
class TransitionOptions;
class HeatmapLayer : public Layer {
public:
HeatmapLayer(const std::string& layerID, const std::string& sourceID);
~HeatmapLayer() final;
// Source
const std::string& getSourceID() const;
const std::string& getSourceLayer() const;
void setSourceLayer(const std::string& sourceLayer);
void setFilter(const Filter&);
const Filter& getFilter() const;
// Visibility
void setVisibility(VisibilityType) final;
// Zoom range
void setMinZoom(float) final;
void setMaxZoom(float) final;
// Paint properties
static DataDrivenPropertyValue<float> getDefaultHeatmapRadius();
DataDrivenPropertyValue<float> getHeatmapRadius() const;
void setHeatmapRadius(DataDrivenPropertyValue<float>);
void setHeatmapRadiusTransition(const TransitionOptions&);
TransitionOptions getHeatmapRadiusTransition() const;
static DataDrivenPropertyValue<float> getDefaultHeatmapWeight();
DataDrivenPropertyValue<float> getHeatmapWeight() const;
void setHeatmapWeight(DataDrivenPropertyValue<float>);
void setHeatmapWeightTransition(const TransitionOptions&);
TransitionOptions getHeatmapWeightTransition() const;
static PropertyValue<float> getDefaultHeatmapIntensity();
PropertyValue<float> getHeatmapIntensity() const;
void setHeatmapIntensity(PropertyValue<float>);
void setHeatmapIntensityTransition(const TransitionOptions&);
TransitionOptions getHeatmapIntensityTransition() const;
static HeatmapColorPropertyValue getDefaultHeatmapColor();
HeatmapColorPropertyValue getHeatmapColor() const;
void setHeatmapColor(HeatmapColorPropertyValue);
void setHeatmapColorTransition(const TransitionOptions&);
TransitionOptions getHeatmapColorTransition() const;
static PropertyValue<float> getDefaultHeatmapOpacity();
PropertyValue<float> getHeatmapOpacity() const;
void setHeatmapOpacity(PropertyValue<float>);
void setHeatmapOpacityTransition(const TransitionOptions&);
TransitionOptions getHeatmapOpacityTransition() const;
// Private implementation
class Impl;
const Impl& impl() const;
Mutable<Impl> mutableImpl() const;
HeatmapLayer(Immutable<Impl>);
std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<HeatmapLayer>() const {
return getType() == LayerType::Heatmap;
}
} // namespace style
} // namespace mbgl

View File

@@ -11,6 +11,9 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
<% if (type === 'heatmap') { -%>
#include <mbgl/style/heatmap_color_property_value.hpp>
<% } -%>
#include <mbgl/util/color.hpp>

View File

@@ -2,6 +2,10 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
## master
- HeatmapLayer [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
## 6.0.0-beta.2 - February 13, 2018
- Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185)
- Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186)

View File

@@ -0,0 +1,221 @@
// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
* A heatmap.
*
* @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-heatmap">The online documentation</a>
*/
@UiThread
public class HeatmapLayer extends Layer {
/**
* Creates a HeatmapLayer.
*
* @param nativePtr pointer used by core
*/
public HeatmapLayer(long nativePtr) {
super(nativePtr);
}
/**
* Creates a HeatmapLayer.
*
* @param layerId the id of the layer
* @param sourceId the id of the source
*/
public HeatmapLayer(String layerId, String sourceId) {
initialize(layerId, sourceId);
}
protected native void initialize(String layerId, String sourceId);
/**
* Set the source layer.
*
* @param sourceLayer the source layer to set
*/
public void setSourceLayer(String sourceLayer) {
nativeSetSourceLayer(sourceLayer);
}
/**
* Set the source Layer.
*
* @param sourceLayer the source layer to set
* @return This
*/
public HeatmapLayer withSourceLayer(String sourceLayer) {
setSourceLayer(sourceLayer);
return this;
}
/**
* Get the source layer.
*
* @return sourceLayer the source layer to get
*/
public String getSourceLayer() {
return nativeGetSourceLayer();
}
/**
* Set a single filter.
*
* @param filter the filter to set
*/
public void setFilter(Filter.Statement filter) {
nativeSetFilter(filter.toArray());
}
/**
* Set a single filter.
*
* @param filter the filter to set
* @return This
*/
public HeatmapLayer withFilter(Filter.Statement filter) {
setFilter(filter);
return this;
}
/**
* Set a property or properties.
*
* @param properties the var-args properties
* @return This
*/
public HeatmapLayer withProperties(@NonNull PropertyValue<?>... properties) {
setProperties(properties);
return this;
}
// Property getters
/**
* Get the HeatmapRadius property
*
* @return property wrapper value around Float
*/
@SuppressWarnings("unchecked")
public PropertyValue<Float> getHeatmapRadius() {
return (PropertyValue<Float>) new PropertyValue("heatmap-radius", nativeGetHeatmapRadius());
}
/**
* Get the HeatmapRadius property transition options
*
* @return transition options for Float
*/
public TransitionOptions getHeatmapRadiusTransition() {
return nativeGetHeatmapRadiusTransition();
}
/**
* Set the HeatmapRadius property transition options
*
* @param options transition options for Float
*/
public void setHeatmapRadiusTransition(TransitionOptions options) {
nativeSetHeatmapRadiusTransition(options.getDuration(), options.getDelay());
}
/**
* Get the HeatmapWeight property
*
* @return property wrapper value around Float
*/
@SuppressWarnings("unchecked")
public PropertyValue<Float> getHeatmapWeight() {
return (PropertyValue<Float>) new PropertyValue("heatmap-weight", nativeGetHeatmapWeight());
}
/**
* Get the HeatmapIntensity property
*
* @return property wrapper value around Float
*/
@SuppressWarnings("unchecked")
public PropertyValue<Float> getHeatmapIntensity() {
return (PropertyValue<Float>) new PropertyValue("heatmap-intensity", nativeGetHeatmapIntensity());
}
/**
* Get the HeatmapIntensity property transition options
*
* @return transition options for Float
*/
public TransitionOptions getHeatmapIntensityTransition() {
return nativeGetHeatmapIntensityTransition();
}
/**
* Set the HeatmapIntensity property transition options
*
* @param options transition options for Float
*/
public void setHeatmapIntensityTransition(TransitionOptions options) {
nativeSetHeatmapIntensityTransition(options.getDuration(), options.getDelay());
}
/**
* Get the HeatmapOpacity property
*
* @return property wrapper value around Float
*/
@SuppressWarnings("unchecked")
public PropertyValue<Float> getHeatmapOpacity() {
return (PropertyValue<Float>) new PropertyValue("heatmap-opacity", nativeGetHeatmapOpacity());
}
/**
* Get the HeatmapOpacity property transition options
*
* @return transition options for Float
*/
public TransitionOptions getHeatmapOpacityTransition() {
return nativeGetHeatmapOpacityTransition();
}
/**
* Set the HeatmapOpacity property transition options
*
* @param options transition options for Float
*/
public void setHeatmapOpacityTransition(TransitionOptions options) {
nativeSetHeatmapOpacityTransition(options.getDuration(), options.getDelay());
}
private native Object nativeGetHeatmapRadius();
private native TransitionOptions nativeGetHeatmapRadiusTransition();
private native void nativeSetHeatmapRadiusTransition(long duration, long delay);
private native Object nativeGetHeatmapWeight();
private native Object nativeGetHeatmapIntensity();
private native TransitionOptions nativeGetHeatmapIntensityTransition();
private native void nativeSetHeatmapIntensityTransition(long duration, long delay);
private native Object nativeGetHeatmapOpacity();
private native TransitionOptions nativeGetHeatmapOpacityTransition();
private native void nativeSetHeatmapOpacityTransition(long duration, long delay);
@Override
protected native void finalize() throws Throwable;
}

View File

@@ -1515,6 +1515,138 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-opacity", function);
}
/**
* Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
*
* @param value a Float value
* @return property wrapper around Float
*/
public static PropertyValue<Float> heatmapRadius(Float value) {
return new PaintPropertyValue<>("heatmap-radius", value);
}
/**
* Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
*
* @param expression an expression statement
* @return property wrapper around an expression statement
*/
public static PropertyValue<Expression> heatmapRadius(Expression expression) {
return new PaintPropertyValue<>("heatmap-radius", expression);
}
/**
* Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
*
* @param <T> the function input type
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
@Deprecated
public static <T> PropertyValue<Function<T, Float>> heatmapRadius(Function<T, Float> function) {
return new PaintPropertyValue<>("heatmap-radius", function);
}
/**
* A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
*
* @param value a Float value
* @return property wrapper around Float
*/
public static PropertyValue<Float> heatmapWeight(Float value) {
return new PaintPropertyValue<>("heatmap-weight", value);
}
/**
* A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
*
* @param expression an expression statement
* @return property wrapper around an expression statement
*/
public static PropertyValue<Expression> heatmapWeight(Expression expression) {
return new PaintPropertyValue<>("heatmap-weight", expression);
}
/**
* A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
*
* @param <T> the function input type
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
@Deprecated
public static <T> PropertyValue<Function<T, Float>> heatmapWeight(Function<T, Float> function) {
return new PaintPropertyValue<>("heatmap-weight", function);
}
/**
* Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
*
* @param value a Float value
* @return property wrapper around Float
*/
public static PropertyValue<Float> heatmapIntensity(Float value) {
return new PaintPropertyValue<>("heatmap-intensity", value);
}
/**
* Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
*
* @param expression an expression statement
* @return property wrapper around an expression statement
*/
public static PropertyValue<Expression> heatmapIntensity(Expression expression) {
return new PaintPropertyValue<>("heatmap-intensity", expression);
}
/**
* Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
@Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> heatmapIntensity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("heatmap-intensity", function);
}
/**
* The global opacity at which the heatmap layer will be drawn.
*
* @param value a Float value
* @return property wrapper around Float
*/
public static PropertyValue<Float> heatmapOpacity(Float value) {
return new PaintPropertyValue<>("heatmap-opacity", value);
}
/**
* The global opacity at which the heatmap layer will be drawn.
*
* @param expression an expression statement
* @return property wrapper around an expression statement
*/
public static PropertyValue<Expression> heatmapOpacity(Expression expression) {
return new PaintPropertyValue<>("heatmap-opacity", expression);
}
/**
* The global opacity at which the heatmap layer will be drawn.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
@Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> heatmapOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("heatmap-opacity", function);
}
/**
* The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
*

View File

@@ -0,0 +1,626 @@
// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
import android.support.test.espresso.UiController;
import android.support.test.runner.AndroidJUnit4;
import timber.log.Timber;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
import com.mapbox.mapboxsdk.style.functions.SourceFunction;
import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
import com.mapbox.mapboxsdk.style.functions.stops.Stop;
import com.mapbox.mapboxsdk.style.functions.stops.Stops;
import com.mapbox.mapboxsdk.style.layers.HeatmapLayer;
import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.mapbox.mapboxsdk.style.functions.Function.*;
import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
/**
* Basic smoke tests for HeatmapLayer
*/
@RunWith(AndroidJUnit4.class)
public class HeatmapLayerTest extends BaseActivityTest {
private HeatmapLayer layer;
@Override
protected Class getActivityClass() {
return EspressoTestActivity.class;
}
private void setupLayer() {
Timber.i("Retrieving layer");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
Timber.i("Adding layer");
layer = new HeatmapLayer("my-layer", "composite");
layer.setSourceLayer("composite");
mapboxMap.addLayer(layer);
// Layer reference is now stale, get new reference
layer = mapboxMap.getLayerAs("my-layer");
}
}
});
}
@Test
public void testSetVisibility() {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
});
}
@Test
public void testSourceLayer() {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
});
}
@Test
public void testHeatmapRadiusTransition() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radiusTransitionOptions");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHeatmapRadiusTransition(options);
assertEquals(layer.getHeatmapRadiusTransition(), options);
}
});
}
@Test
public void testHeatmapRadiusAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radius");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
layer.setProperties(heatmapRadius(0.3f));
assertEquals((Float) layer.getHeatmapRadius().getValue(), (Float) 0.3f);
}
});
}
@Test
public void testHeatmapRadiusAsCameraFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radius");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapRadius(
zoom(
exponential(
stop(2, heatmapRadius(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getHeatmapRadius());
assertNotNull(layer.getHeatmapRadius().getFunction());
assertEquals(CameraFunction.class, layer.getHeatmapRadius().getFunction().getClass());
assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).getBase(), 0.001);
assertEquals(1, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).size());
}
});
}
@Test
public void testHeatmapRadiusAsIdentitySourceFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radius");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapRadius(property("FeaturePropertyA", Stops.<Float>identity()))
);
// Verify
assertNotNull(layer.getHeatmapRadius());
assertNotNull(layer.getHeatmapRadius().getFunction());
assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty());
assertEquals(IdentityStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
}
});
}
@Test
public void testHeatmapRadiusAsExponentialSourceFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radius");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapRadius(
property(
"FeaturePropertyA",
exponential(
stop(0.3f, heatmapRadius(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getHeatmapRadius());
assertNotNull(layer.getHeatmapRadius().getFunction());
assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
}
});
}
@Test
public void testHeatmapRadiusAsCategoricalSourceFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radius");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapRadius(
property(
"FeaturePropertyA",
categorical(
stop(1.0f, heatmapRadius(0.3f))
)
).withDefaultValue(heatmapRadius(0.3f))
)
);
// Verify
assertNotNull(layer.getHeatmapRadius());
assertNotNull(layer.getHeatmapRadius().getFunction());
assertEquals(SourceFunction.class, layer.getHeatmapRadius().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapRadius().getFunction()).getProperty());
assertEquals(CategoricalStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
assertNotNull(((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue());
assertNotNull(((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue().getValue());
assertEquals(0.3f, ((SourceFunction) layer.getHeatmapRadius().getFunction()).getDefaultValue().getValue());
}
});
}
@Test
public void testHeatmapRadiusAsCompositeFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-radius");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapRadius(
composite(
"FeaturePropertyA",
exponential(
stop(0, 0.3f, heatmapRadius(0.9f))
).withBase(0.5f)
).withDefaultValue(heatmapRadius(0.3f))
)
);
// Verify
assertNotNull(layer.getHeatmapRadius());
assertNotNull(layer.getHeatmapRadius().getFunction());
assertEquals(CompositeFunction.class, layer.getHeatmapRadius().getFunction().getClass());
assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getHeatmapRadius().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getHeatmapRadius().getFunction().getStops().getClass());
assertEquals(1, ((ExponentialStops) layer.getHeatmapRadius().getFunction().getStops()).size());
ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
(ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getHeatmapRadius().getFunction().getStops();
Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
assertEquals(0f, stop.in.zoom, 0.001);
assertEquals(0.3f, stop.in.value, 0.001f);
assertEquals(0.9f, stop.out, 0.001f);
}
});
}
@Test
public void testHeatmapWeightAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-weight");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
layer.setProperties(heatmapWeight(0.3f));
assertEquals((Float) layer.getHeatmapWeight().getValue(), (Float) 0.3f);
}
});
}
@Test
public void testHeatmapWeightAsCameraFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-weight");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapWeight(
zoom(
exponential(
stop(2, heatmapWeight(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getHeatmapWeight());
assertNotNull(layer.getHeatmapWeight().getFunction());
assertEquals(CameraFunction.class, layer.getHeatmapWeight().getFunction().getClass());
assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).getBase(), 0.001);
assertEquals(1, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).size());
}
});
}
@Test
public void testHeatmapWeightAsIdentitySourceFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-weight");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapWeight(property("FeaturePropertyA", Stops.<Float>identity()))
);
// Verify
assertNotNull(layer.getHeatmapWeight());
assertNotNull(layer.getHeatmapWeight().getFunction());
assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty());
assertEquals(IdentityStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
}
});
}
@Test
public void testHeatmapWeightAsExponentialSourceFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-weight");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapWeight(
property(
"FeaturePropertyA",
exponential(
stop(0.3f, heatmapWeight(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getHeatmapWeight());
assertNotNull(layer.getHeatmapWeight().getFunction());
assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
}
});
}
@Test
public void testHeatmapWeightAsCategoricalSourceFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-weight");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapWeight(
property(
"FeaturePropertyA",
categorical(
stop(1.0f, heatmapWeight(0.3f))
)
).withDefaultValue(heatmapWeight(0.3f))
)
);
// Verify
assertNotNull(layer.getHeatmapWeight());
assertNotNull(layer.getHeatmapWeight().getFunction());
assertEquals(SourceFunction.class, layer.getHeatmapWeight().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getHeatmapWeight().getFunction()).getProperty());
assertEquals(CategoricalStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
assertNotNull(((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue());
assertNotNull(((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue().getValue());
assertEquals(0.3f, ((SourceFunction) layer.getHeatmapWeight().getFunction()).getDefaultValue().getValue());
}
});
}
@Test
public void testHeatmapWeightAsCompositeFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-weight");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapWeight(
composite(
"FeaturePropertyA",
exponential(
stop(0, 0.3f, heatmapWeight(0.9f))
).withBase(0.5f)
).withDefaultValue(heatmapWeight(0.3f))
)
);
// Verify
assertNotNull(layer.getHeatmapWeight());
assertNotNull(layer.getHeatmapWeight().getFunction());
assertEquals(CompositeFunction.class, layer.getHeatmapWeight().getFunction().getClass());
assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getHeatmapWeight().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getHeatmapWeight().getFunction().getStops().getClass());
assertEquals(1, ((ExponentialStops) layer.getHeatmapWeight().getFunction().getStops()).size());
ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
(ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getHeatmapWeight().getFunction().getStops();
Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
assertEquals(0f, stop.in.zoom, 0.001);
assertEquals(0.3f, stop.in.value, 0.001f);
assertEquals(0.9f, stop.out, 0.001f);
}
});
}
@Test
public void testHeatmapIntensityTransition() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-intensityTransitionOptions");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHeatmapIntensityTransition(options);
assertEquals(layer.getHeatmapIntensityTransition(), options);
}
});
}
@Test
public void testHeatmapIntensityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-intensity");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
layer.setProperties(heatmapIntensity(0.3f));
assertEquals((Float) layer.getHeatmapIntensity().getValue(), (Float) 0.3f);
}
});
}
@Test
public void testHeatmapIntensityAsCameraFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-intensity");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapIntensity(
zoom(
exponential(
stop(2, heatmapIntensity(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getHeatmapIntensity());
assertNotNull(layer.getHeatmapIntensity().getFunction());
assertEquals(CameraFunction.class, layer.getHeatmapIntensity().getFunction().getClass());
assertEquals(ExponentialStops.class, layer.getHeatmapIntensity().getFunction().getStops().getClass());
assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapIntensity().getFunction().getStops()).getBase(), 0.001);
assertEquals(1, ((ExponentialStops) layer.getHeatmapIntensity().getFunction().getStops()).size());
}
});
}
@Test
public void testHeatmapOpacityTransition() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-opacityTransitionOptions");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHeatmapOpacityTransition(options);
assertEquals(layer.getHeatmapOpacityTransition(), options);
}
});
}
@Test
public void testHeatmapOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-opacity");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set and Get
layer.setProperties(heatmapOpacity(0.3f));
assertEquals((Float) layer.getHeatmapOpacity().getValue(), (Float) 0.3f);
}
});
}
@Test
public void testHeatmapOpacityAsCameraFunction() {
validateTestSetup();
setupLayer();
Timber.i("heatmap-opacity");
invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
@Override
public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
assertNotNull(layer);
// Set
layer.setProperties(
heatmapOpacity(
zoom(
exponential(
stop(2, heatmapOpacity(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getHeatmapOpacity());
assertNotNull(layer.getHeatmapOpacity().getFunction());
assertEquals(CameraFunction.class, layer.getHeatmapOpacity().getFunction().getClass());
assertEquals(ExponentialStops.class, layer.getHeatmapOpacity().getFunction().getStops().getClass());
assertEquals(0.5f, ((ExponentialStops) layer.getHeatmapOpacity().getFunction().getStops()).getBase(), 0.001);
assertEquals(1, ((ExponentialStops) layer.getHeatmapOpacity().getFunction().getStops()).size());
}
});
}
}

View File

@@ -789,6 +789,17 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
android:name=".activity.style.HeatmapLayerActivity"
android:description="@string/description_heatmaplayer"
android:label="@string/activity_heatmaplayer">
<meta-data
android:name="@string/category"
android:value="@string/category_style"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<!-- For Instrumentation tests -->
<activity

View File

@@ -0,0 +1,216 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.HeatmapLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
import java.net.MalformedURLException;
import java.net.URL;
import timber.log.Timber;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeWidth;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapIntensity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapOpacity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapRadius;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapWeight;
/**
* Test activity showcasing the heatmap layer api.
*/
public class HeatmapLayerActivity extends AppCompatActivity {
private static final String EARTHQUAKE_SOURCE_URL = "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson";
private static final String EARTHQUAKE_SOURCE_ID = "earthquakes";
private static final String HEATMAP_LAYER_ID = "earthquakes-heat";
private static final String HEATMAP_LAYER_SOURCE = "earthquakes";
private static final String CIRCLE_LAYER_ID = "earthquakes-circle";
private MapView mapView;
private MapboxMap mapboxMap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_heatmaplayer);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(map -> {
mapboxMap = map;
addEarthquakeSource();
addHeatmapLayer();
addCircleLayer();
});
}
private void addEarthquakeSource() {
try {
mapboxMap.addSource(new GeoJsonSource(EARTHQUAKE_SOURCE_ID, new URL(EARTHQUAKE_SOURCE_URL)));
} catch (MalformedURLException malformedUrlException) {
Timber.e(malformedUrlException, "That's not an url... ");
}
}
private void addHeatmapLayer() {
HeatmapLayer layer = new HeatmapLayer(HEATMAP_LAYER_ID, EARTHQUAKE_SOURCE_ID);
layer.setMaxZoom(9);
layer.setSourceLayer(HEATMAP_LAYER_SOURCE);
layer.setProperties(
// TODO add heatmap color https://github.com/mapbox/mapbox-gl-native/issues/11172
// Color ramp for heatmap. Domain is 0 (low) to 1 (high).
// Begin color ramp at 0-stop with a 0-transparancy color
// to create a blur-like effect.
//heatmapColor(),
// Increase the heatmap weight based on frequency and property magnitude
heatmapWeight(
interpolate(
linear(), get("mag"),
stop(0, 0),
stop(6, 1)
)
),
// Increase the heatmap color weight weight by zoom level
// heatmap-intensity is a multiplier on top of heatmap-weight
heatmapIntensity(
interpolate(
linear(), zoom(),
stop(0, 1),
stop(9, 3)
)
),
// Adjust the heatmap radius by zoom level
heatmapRadius(
interpolate(
linear(), zoom(),
stop(0, 2),
stop(9, 20)
)
),
// Transition from heatmap to circle layer by zoom level
heatmapOpacity(
interpolate(
linear(), zoom(),
stop(7, 1),
stop(9, 0)
)
)
);
mapboxMap.addLayerAbove(layer, "waterway-label");
}
private void addCircleLayer() {
CircleLayer circleLayer = new CircleLayer(CIRCLE_LAYER_ID, EARTHQUAKE_SOURCE_ID);
circleLayer.setProperties(
// Size circle radius by earthquake magnitude and zoom level
circleRadius(
interpolate(
linear(), zoom(),
literal(7), interpolate(
linear(), get("mag"),
stop(1, 1),
stop(6, 4)
),
literal(16), interpolate(
linear(), get("mag"),
stop(1, 5),
stop(6, 50)
)
)
),
// Color circle by earthquake magnitude
circleColor(
interpolate(
linear(), get("mag"),
literal(1), rgba(33, 102, 172, 0),
literal(2), rgb(103, 169, 207),
literal(3), rgb(209, 229, 240),
literal(4), rgb(253, 219, 199),
literal(5), rgb(239, 138, 98),
literal(6), rgb(178, 24, 43)
)
),
// Transition from heatmap to circle layer by zoom level
circleOpacity(
interpolate(
linear(), zoom(),
stop(7, 0),
stop(8, 1)
)
),
circleStrokeColor("white"),
circleStrokeWidth(1.0f)
);
mapboxMap.addLayerBelow(circleLayer, HEATMAP_LAYER_ID);
}
@Override
protected void onStart() {
super.onStart();
mapView.onStart();
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mapbox_styleUrl="@string/mapbox_style_dark"/>
</RelativeLayout>

View File

@@ -70,4 +70,5 @@
<string name="description_grid_source">Example Custom Geometry Source</string>
<string name="description_local_glyph">Suzhou using Droid Sans for Chinese glyphs</string>
<string name="description_hillshade">Example raster-dem source and hillshade layer</string>
<string name="description_heatmaplayer">Use HeatmapLayer to visualise earthquakes</string>
</resources>

View File

@@ -70,4 +70,5 @@
<string name="activity_grid_source">Grid Source</string>
<string name="activity_local_glyph">Local CJK glyph generation</string>
<string name="activity_hillshade">Hillshade</string>
<string name="activity_heatmaplayer">Heatmap layer</string>
</resources>

View File

@@ -158,6 +158,8 @@ add_library(mbgl-android STATIC
platform/android/src/style/layers/fill_extrusion_layer.hpp
platform/android/src/style/layers/fill_layer.cpp
platform/android/src/style/layers/fill_layer.hpp
platform/android/src/style/layers/heatmap_layer.cpp
platform/android/src/style/layers/heatmap_layer.hpp
platform/android/src/style/layers/hillshade_layer.cpp
platform/android/src/style/layers/hillshade_layer.hpp
platform/android/src/style/layers/layer.cpp

View File

@@ -28,6 +28,9 @@ var layers = Object.keys(spec.layer.type.values).map((type) => {
}, []);
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
// disabled for now, see https://github.com/mapbox/mapbox-gl-native/issues/11172
if (name === 'heatmap-color') return memo;
spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]);
return memo;

View File

@@ -0,0 +1,134 @@
// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
#include "heatmap_layer.hpp"
#include <string>
#include "../conversion/property_value.hpp"
#include "../conversion/transition_options.hpp"
namespace mbgl {
namespace android {
/**
* Creates an owning peer object (for layers not attached to the map) from the JVM side
*/
HeatmapLayer::HeatmapLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
: Layer(env, std::make_unique<mbgl::style::HeatmapLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
}
/**
* Creates a non-owning peer object (for layers currently attached to the map)
*/
HeatmapLayer::HeatmapLayer(mbgl::Map& map, mbgl::style::HeatmapLayer& coreLayer)
: Layer(map, coreLayer) {
}
/**
* Creates an owning peer object (for layers not attached to the map)
*/
HeatmapLayer::HeatmapLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::HeatmapLayer> coreLayer)
: Layer(map, std::move(coreLayer)) {
}
HeatmapLayer::~HeatmapLayer() = default;
// Property getters
jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapRadius(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapRadius());
return jni::Object<jni::ObjectTag>(*converted);
}
jni::Object<TransitionOptions> HeatmapLayer::getHeatmapRadiusTransition(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
mbgl::style::TransitionOptions options = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapRadiusTransition();
return *convert<jni::Object<TransitionOptions>>(env, options);
}
void HeatmapLayer::setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay) {
mbgl::style::TransitionOptions options;
options.duration.emplace(mbgl::Milliseconds(duration));
options.delay.emplace(mbgl::Milliseconds(delay));
layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::setHeatmapRadiusTransition(options);
}
jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapWeight(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapWeight());
return jni::Object<jni::ObjectTag>(*converted);
}
jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapIntensity(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapIntensity());
return jni::Object<jni::ObjectTag>(*converted);
}
jni::Object<TransitionOptions> HeatmapLayer::getHeatmapIntensityTransition(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
mbgl::style::TransitionOptions options = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapIntensityTransition();
return *convert<jni::Object<TransitionOptions>>(env, options);
}
void HeatmapLayer::setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay) {
mbgl::style::TransitionOptions options;
options.duration.emplace(mbgl::Milliseconds(duration));
options.delay.emplace(mbgl::Milliseconds(delay));
layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::setHeatmapIntensityTransition(options);
}
jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapOpacity(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapOpacity());
return jni::Object<jni::ObjectTag>(*converted);
}
jni::Object<TransitionOptions> HeatmapLayer::getHeatmapOpacityTransition(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
mbgl::style::TransitionOptions options = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapOpacityTransition();
return *convert<jni::Object<TransitionOptions>>(env, options);
}
void HeatmapLayer::setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) {
mbgl::style::TransitionOptions options;
options.duration.emplace(mbgl::Milliseconds(duration));
options.delay.emplace(mbgl::Milliseconds(delay));
layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::setHeatmapOpacityTransition(options);
}
jni::Class<HeatmapLayer> HeatmapLayer::javaClass;
jni::jobject* HeatmapLayer::createJavaPeer(jni::JNIEnv& env) {
static auto constructor = HeatmapLayer::javaClass.template GetConstructor<jni::jlong>(env);
return HeatmapLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
}
void HeatmapLayer::registerNative(jni::JNIEnv& env) {
// Lookup the class
HeatmapLayer::javaClass = *jni::Class<HeatmapLayer>::Find(env).NewGlobalRef(env).release();
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<HeatmapLayer>(
env, HeatmapLayer::javaClass, "nativePtr",
std::make_unique<HeatmapLayer, JNIEnv&, jni::String, jni::String>,
"initialize",
"finalize",
METHOD(&HeatmapLayer::getHeatmapRadiusTransition, "nativeGetHeatmapRadiusTransition"),
METHOD(&HeatmapLayer::setHeatmapRadiusTransition, "nativeSetHeatmapRadiusTransition"),
METHOD(&HeatmapLayer::getHeatmapRadius, "nativeGetHeatmapRadius"),
METHOD(&HeatmapLayer::getHeatmapWeight, "nativeGetHeatmapWeight"),
METHOD(&HeatmapLayer::getHeatmapIntensityTransition, "nativeGetHeatmapIntensityTransition"),
METHOD(&HeatmapLayer::setHeatmapIntensityTransition, "nativeSetHeatmapIntensityTransition"),
METHOD(&HeatmapLayer::getHeatmapIntensity, "nativeGetHeatmapIntensity"),
METHOD(&HeatmapLayer::getHeatmapOpacityTransition, "nativeGetHeatmapOpacityTransition"),
METHOD(&HeatmapLayer::setHeatmapOpacityTransition, "nativeSetHeatmapOpacityTransition"),
METHOD(&HeatmapLayer::getHeatmapOpacity, "nativeGetHeatmapOpacity"));
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,50 @@
// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
#pragma once
#include "layer.hpp"
#include "../transition_options.hpp"
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class HeatmapLayer : public Layer {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/HeatmapLayer"; };
static jni::Class<HeatmapLayer> javaClass;
static void registerNative(jni::JNIEnv&);
HeatmapLayer(jni::JNIEnv&, jni::String, jni::String);
HeatmapLayer(mbgl::Map&, mbgl::style::HeatmapLayer&);
HeatmapLayer(mbgl::Map&, std::unique_ptr<mbgl::style::HeatmapLayer>);
~HeatmapLayer();
// Properties
jni::Object<jni::ObjectTag> getHeatmapRadius(jni::JNIEnv&);
void setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay);
jni::Object<TransitionOptions> getHeatmapRadiusTransition(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getHeatmapWeight(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getHeatmapIntensity(jni::JNIEnv&);
void setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay);
jni::Object<TransitionOptions> getHeatmapIntensityTransition(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getHeatmapOpacity(jni::JNIEnv&);
void setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay);
jni::Object<TransitionOptions> getHeatmapOpacityTransition(jni::JNIEnv&);
jni::jobject* createJavaPeer(jni::JNIEnv&);
}; // class HeatmapLayer
} // namespace android
} // namespace mbgl

View File

@@ -10,6 +10,7 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>

View File

@@ -5,6 +5,7 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
@@ -16,6 +17,7 @@
#include "custom_layer.hpp"
#include "fill_extrusion_layer.hpp"
#include "fill_layer.hpp"
#include "heatmap_layer.hpp"
#include "hillshade_layer.hpp"
#include "line_layer.hpp"
#include "raster_layer.hpp"
@@ -32,6 +34,7 @@ template <> struct PeerType<style::BackgroundLayer> { using Type = android::Back
template <> struct PeerType<style::CircleLayer> { using Type = android::CircleLayer; };
template <> struct PeerType<style::FillExtrusionLayer> { using Type = android::FillExtrusionLayer; };
template <> struct PeerType<style::FillLayer> { using Type = android::FillLayer; };
template <> struct PeerType<style::HeatmapLayer> { using Type = android::HeatmapLayer; };
template <> struct PeerType<style::HillshadeLayer> { using Type = android::HillshadeLayer; };
template <> struct PeerType<style::LineLayer> { using Type = android::LineLayer; };
template <> struct PeerType<style::RasterLayer> { using Type = android::RasterLayer; };
@@ -95,6 +98,7 @@ void registerNativeLayers(jni::JNIEnv& env) {
CustomLayer::registerNative(env);
FillExtrusionLayer::registerNative(env);
FillLayer::registerNative(env);
HeatmapLayer::registerNative(env);
HillshadeLayer::registerNative(env);
LineLayer::registerNative(env);
RasterLayer::registerNative(env);

View File

@@ -308,14 +308,15 @@ global.propertyDoc = function (propertyName, property, layerType, kind) {
doc += '* Predefined functions, including mathematical and string operators\n' +
'* Conditional expressions\n' +
'* Variable assignments and references to assigned variables\n';
const inputVariable = property.name === 'heatmap-color' ? '$heatmapDensity' : '$zoomLevel';
if (property["property-function"]) {
doc += '* Interpolation and step functions applied to the `$zoomLevel` variable and/or feature attributes\n';
doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable and/or feature attributes\n`;
} else if (property.function === "interpolated") {
doc += '* Interpolation and step functions applied to the `$zoomLevel` variable\n\n' +
doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable\n\n` +
'This property does not support applying interpolation or step functions to feature attributes.';
} else {
doc += '* Step functions applied to the `$zoomLevel` variable\n\n' +
'This property does not support applying interpolation functions to the `$zoomLevel` variable or applying interpolation or step functions to feature attributes.';
doc += `* Step functions applied to the \`${inputVariable}\` variable\n\n` +
`This property does not support applying interpolation functions to the \`${inputVariable}\` variable or applying interpolation or step functions to feature attributes.`;
}
}
return doc;
@@ -387,7 +388,7 @@ global.describeValue = function (value, property, layerType) {
throw new Error(`No description available for ${value[0]} expression in ${property.name} of ${layerType}.`);
}
}
switch (property.type) {
case 'boolean':
return value ? '`YES`' : '`NO`';

View File

@@ -23,6 +23,9 @@
"circle": {
"doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object."
},
"heatmap": {
"doc": "An `MGLHeatmapStyleLayer` is a style layer that renders a <a href=\"https://en.wikipedia.org/wiki/Heat_map\">heatmap</a>.\n\nA heatmap visualizes the spatial distribution of a large, dense set of point data, using color to avoid cluttering the map with individual points at low zoom levels. The points are weighted by an attribute you specify. Use a heatmap style layer in conjunction with point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nConsider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can alternatively cluster the source using the `MGLShapeSourceOptionClustered` option and render the data using an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer`."
},
"raster": {
"doc": "An `MGLRasterStyleLayer` is a style layer that renders georeferenced raster imagery on the map, especially raster tiles.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterSource` object or raster images loaded by an `MGLImageSource` object. For example, you could use a raster style layer to render <a href=\"https://www.mapbox.com/satellite/\">Mapbox Satellite</a> imagery, a <a href=\"https://www.mapbox.com/help/define-tileset/#raster-tilesets\">raster tile set</a> uploaded to Mapbox Studio, or a raster map authored in <a href=\"https://tilemill-project.github.io/tilemill/\">TileMill</a>, the classic Mapbox Editor, or Mapbox Studio Classic.\n\nRaster images may also be used as icons or patterns in a style layer. To register an image for use as an icon or pattern, use the `-[MGLStyle setImage:forName:]` method. To configure a point annotation’s image, use the `MGLAnnotationImage` class."
},
@@ -79,6 +82,11 @@
"doc": "The base color of this layer. The extrusion's surfaces will be shaded differently based on this color in combination with the `light` settings. If this color is specified with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacityco."
}
},
"paint_heatmap": {
"heatmap-color": {
"doc": "Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `$heatmapDensity` as input."
}
},
"paint_line": {
"line-pattern": {
"doc": "Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512)."
@@ -114,4 +122,4 @@
"doc": "Distance that the text's anchor is moved from its original placement."
}
}
}
}

View File

@@ -0,0 +1,189 @@
// This file is generated.
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLFoundation.h"
#import "MGLVectorStyleLayer.h"
NS_ASSUME_NONNULL_BEGIN
/**
An `MGLHeatmapStyleLayer` is a style layer that renders a <a
href="https://en.wikipedia.org/wiki/Heat_map">heatmap</a>.
A heatmap visualizes the spatial distribution of a large, dense set of point
data, using color to avoid cluttering the map with individual points at low
zoom levels. The points are weighted by an attribute you specify. Use a heatmap
style layer in conjunction with point or point collection features in vector
tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`,
`MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature`
instances in an `MGLShapeSource` object.
Consider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or
`MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point
data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can
alternatively cluster the source using the `MGLShapeSourceOptionClustered`
option and render the data using an `MGLCircleStyleLayer` or
`MGLSymbolStyleLayer`.
You can access an existing heatmap style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
otherwise, find it using the `MGLStyle.layers` property. You can also create a
new heatmap style layer and add it to the style using a method such as
`-[MGLStyle addLayer:]`.
### Example
```swift
let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes)
layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
[0: 0,
6: 1])
layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
[0: 1,
9: 3])
mapView.style?.addLayer(layer)
```
*/
MGL_EXPORT
@interface MGLHeatmapStyleLayer : MGLVectorStyleLayer
/**
Returns a heatmap style layer initialized with an identifier and source.
After initializing and configuring the style layer, add it to a map view’s
style using the `-[MGLStyle addLayer:]` or
`-[MGLStyle insertLayer:belowLayer:]` method.
@param identifier A string that uniquely identifies the source in the style to
which it is added.
@param source The source from which to obtain the data to style. If the source
has not yet been added to the current style, the behavior is undefined.
@return An initialized foreground style layer.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source;
#pragma mark - Accessing the Paint Attributes
/**
Defines the color of each point based on its density value in a heatmap. Should
be an expression that uses `$heatmapDensity` as input.
The default value of this property is an expression that evaluates to a rainbow
color scale from blue to red. Set this property to `nil` to reset it to the
default value.
You can set this property to an expression containing any of the following:
* Constant `UIColor` values
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
* Interpolation and step functions applied to the `$heatmapDensity` variable
This property does not support applying interpolation or step functions to
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *heatmapColor;
/**
Similar to `heatmapWeight` but controls the intensity of the heatmap globally.
Primarily used for adjusting the heatmap based on zoom level.
The default value of this property is an expression that evaluates to the float
`1`. Set this property to `nil` to reset it to the default value.
You can set this property to an expression containing any of the following:
* Constant numeric values
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
* Interpolation and step functions applied to the `$zoomLevel` variable
This property does not support applying interpolation or step functions to
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *heatmapIntensity;
/**
The transition affecting any changes to this layer’s `heatmapIntensity` property.
This property corresponds to the `heatmap-intensity-transition` property in the style JSON file format.
*/
@property (nonatomic) MGLTransition heatmapIntensityTransition;
/**
The global opacity at which the heatmap layer will be drawn.
The default value of this property is an expression that evaluates to the float
`1`. Set this property to `nil` to reset it to the default value.
You can set this property to an expression containing any of the following:
* Constant numeric values
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
* Interpolation and step functions applied to the `$zoomLevel` variable
This property does not support applying interpolation or step functions to
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *heatmapOpacity;
/**
The transition affecting any changes to this layer’s `heatmapOpacity` property.
This property corresponds to the `heatmap-opacity-transition` property in the style JSON file format.
*/
@property (nonatomic) MGLTransition heatmapOpacityTransition;
/**
Radius of influence of one heatmap point in points. Increasing the value makes
the heatmap smoother, but less detailed.
This property is measured in points.
The default value of this property is an expression that evaluates to the float
`30`. Set this property to `nil` to reset it to the default value.
You can set this property to an expression containing any of the following:
* Constant numeric values
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
* Interpolation and step functions applied to the `$zoomLevel` variable and/or
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *heatmapRadius;
/**
The transition affecting any changes to this layer’s `heatmapRadius` property.
This property corresponds to the `heatmap-radius-transition` property in the style JSON file format.
*/
@property (nonatomic) MGLTransition heatmapRadiusTransition;
/**
A measure of how much an individual point contributes to the heatmap. A value
of 10 would be equivalent to having 10 points of weight 1 in the same spot.
Especially useful when combined with clustering.
The default value of this property is an expression that evaluates to the float
`1`. Set this property to `nil` to reset it to the default value.
You can set this property to an expression containing any of the following:
* Constant numeric values
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
* Interpolation and step functions applied to the `$zoomLevel` variable and/or
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *heatmapWeight;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,210 @@
// This file is generated.
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "NSPredicate+MGLAdditions.h"
#import "NSDate+MGLAdditions.h"
#import "MGLStyleLayer_Private.h"
#import "MGLStyleValue_Private.h"
#import "MGLHeatmapStyleLayer.h"
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
@interface MGLHeatmapStyleLayer ()
@property (nonatomic, readonly) mbgl::style::HeatmapLayer *rawLayer;
@end
@implementation MGLHeatmapStyleLayer
- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source
{
auto layer = std::make_unique<mbgl::style::HeatmapLayer>(identifier.UTF8String, source.identifier.UTF8String);
return self = [super initWithPendingLayer:std::move(layer)];
}
- (mbgl::style::HeatmapLayer *)rawLayer
{
return (mbgl::style::HeatmapLayer *)super.rawLayer;
}
- (NSString *)sourceIdentifier
{
MGLAssertStyleLayerIsValid();
return @(self.rawLayer->getSourceID().c_str());
}
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
auto layerID = self.rawLayer->getSourceLayer();
return layerID.empty() ? nil : @(layerID.c_str());
}
- (void)setSourceLayerIdentifier:(NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
self.rawLayer->setSourceLayer(sourceLayerIdentifier.UTF8String ?: "");
}
- (void)setPredicate:(NSPredicate *)predicate
{
MGLAssertStyleLayerIsValid();
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
}
- (NSPredicate *)predicate
{
MGLAssertStyleLayerIsValid();
return [NSPredicate mgl_predicateWithFilter:self.rawLayer->getFilter()];
}
#pragma mark - Accessing the Paint Attributes
- (void)setHeatmapColor:(NSExpression *)heatmapColor {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::HeatmapColorPropertyValue>(heatmapColor);
self.rawLayer->setHeatmapColor(mbglValue);
}
- (NSExpression *)heatmapColor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getHeatmapColor();
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultHeatmapColor();
}
return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue);
}
- (void)setHeatmapIntensity:(NSExpression *)heatmapIntensity {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(heatmapIntensity);
self.rawLayer->setHeatmapIntensity(mbglValue);
}
- (NSExpression *)heatmapIntensity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getHeatmapIntensity();
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultHeatmapIntensity();
}
return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setHeatmapIntensityTransition:(MGLTransition )transition {
MGLAssertStyleLayerIsValid();
mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
self.rawLayer->setHeatmapIntensityTransition(options);
}
- (MGLTransition)heatmapIntensityTransition {
MGLAssertStyleLayerIsValid();
mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapIntensityTransition();
MGLTransition transition;
transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
return transition;
}
- (void)setHeatmapOpacity:(NSExpression *)heatmapOpacity {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(heatmapOpacity);
self.rawLayer->setHeatmapOpacity(mbglValue);
}
- (NSExpression *)heatmapOpacity {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getHeatmapOpacity();
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultHeatmapOpacity();
}
return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setHeatmapOpacityTransition:(MGLTransition )transition {
MGLAssertStyleLayerIsValid();
mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
self.rawLayer->setHeatmapOpacityTransition(options);
}
- (MGLTransition)heatmapOpacityTransition {
MGLAssertStyleLayerIsValid();
mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapOpacityTransition();
MGLTransition transition;
transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
return transition;
}
- (void)setHeatmapRadius:(NSExpression *)heatmapRadius {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(heatmapRadius);
self.rawLayer->setHeatmapRadius(mbglValue);
}
- (NSExpression *)heatmapRadius {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getHeatmapRadius();
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultHeatmapRadius();
}
return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
- (void)setHeatmapRadiusTransition:(MGLTransition )transition {
MGLAssertStyleLayerIsValid();
mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
self.rawLayer->setHeatmapRadiusTransition(options);
}
- (MGLTransition)heatmapRadiusTransition {
MGLAssertStyleLayerIsValid();
mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getHeatmapRadiusTransition();
MGLTransition transition;
transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
return transition;
}
- (void)setHeatmapWeight:(NSExpression *)heatmapWeight {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(heatmapWeight);
self.rawLayer->setHeatmapWeight(mbglValue);
}
- (NSExpression *)heatmapWeight {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getHeatmapWeight();
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultHeatmapWeight();
}
return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue);
}
@end

View File

@@ -8,6 +8,7 @@
#import "MGLLineStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLSymbolStyleLayer.h"
#import "MGLHeatmapStyleLayer.h"
#import "MGLHillshadeStyleLayer.h"
#import "MGLRasterStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
@@ -36,6 +37,7 @@
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/background_layer.hpp>
@@ -401,6 +403,8 @@ static NSURL *MGLStyleURL_trafficNight;
return [[MGLSymbolStyleLayer alloc] initWithRawLayer:symbolLayer];
} else if (auto rasterLayer = rawLayer->as<mbgl::style::RasterLayer>()) {
return [[MGLRasterStyleLayer alloc] initWithRawLayer:rasterLayer];
} else if (auto heatmapLayer = rawLayer->as<mbgl::style::HeatmapLayer>()) {
return [[MGLHeatmapStyleLayer alloc] initWithRawLayer:heatmapLayer];
} else if (auto hillshadeLayer = rawLayer->as<mbgl::style::HillshadeLayer>()) {
return [[MGLHillshadeStyleLayer alloc] initWithRawLayer:hillshadeLayer];
} else if (auto circleLayer = rawLayer->as<mbgl::style::CircleLayer>()) {

View File

@@ -157,7 +157,9 @@ namespace mbgl {
- (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> {
MGLAssertStyleLayerIsValid();
<% if (property["property-function"]) { -%>
<% if (property.name === 'heatmap-color') { -%>
auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::HeatmapColorPropertyValue>(heatmapColor);
<% } else if (property["property-function"]) { -%>
auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);
<% } else { -%>
auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>);

View File

@@ -10,6 +10,7 @@
#import "MGLConversion.h"
#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/style/conversion/data_driven_property_value.hpp>
#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
#include <mbgl/style/conversion/position.hpp>
#import <mbgl/style/types.hpp>
@@ -52,11 +53,20 @@ public:
return mbglValue.evaluate(evaluator);
}
// Convert an mbgl heatmap color property value into an mgl style value
NSExpression *toExpression(const mbgl::style::HeatmapColorPropertyValue &mbglValue) {
if (mbglValue.isUndefined()) {
return nil;
}
return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
/**
Converts an NSExpression to an mbgl property value.
*/
template <typename MBGLValue>
MBGLValue toPropertyValue(NSExpression *expression) {
typename std::enable_if_t<!std::is_same<MBGLValue, mbgl::style::HeatmapColorPropertyValue>::value,
MBGLValue> toPropertyValue(NSExpression *expression) {
if (!expression) {
return {};
}
@@ -85,6 +95,30 @@ public:
return *value;
}
/**
Converts an NSExpression to an mbgl property value.
*/
template <typename MBGLValue>
typename std::enable_if_t<std::is_same<MBGLValue, mbgl::style::HeatmapColorPropertyValue>::value,
MBGLValue> toPropertyValue(NSExpression *expression) {
if (!expression) {
return {};
}
NSArray *jsonExpression = expression.mgl_jsonExpressionObject;
mbgl::style::conversion::Error valueError;
auto value = mbgl::style::conversion::convert<mbgl::style::HeatmapColorPropertyValue>(
mbgl::style::conversion::makeConvertible(jsonExpression), valueError);
if (!value) {
[NSException raise:NSInvalidArgumentException
format:@"Invalid property value: %@", @(valueError.message.c_str())];
return {};
}
return *value;
}
private: // Private utilities for converting from mgl to mbgl values

View File

@@ -10,10 +10,10 @@ NS_ASSUME_NONNULL_BEGIN
is defined by an `MGLShapeSource` or `MGLVectorSource` object.
Create instances of `MGLCircleStyleLayer`, `MGLFillStyleLayer`,
`MGLFillExtrusionStyleLayer`, `MGLLineStyleLayer`, and `MGLSymbolStyleLayer` in
order to use `MGLVectorStyleLayer`'s properties and methods. Do not create
instances of `MGLVectorStyleLayer` directly, and do not create your own
subclasses of this class.
`MGLFillExtrusionStyleLayer`, `MGLHeatmapStyleLayer`, `MGLLineStyleLayer`, and
`MGLSymbolStyleLayer` in order to use `MGLVectorStyleLayer`'s properties and
methods. Do not create instances of `MGLVectorStyleLayer` directly, and do not
create your own subclasses of this class.
*/
MGL_EXPORT
@interface MGLVectorStyleLayer : MGLForegroundStyleLayer

View File

@@ -230,6 +230,24 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.layer(withIdentifier: "buildings"))
}
func testMGLHeatmapStyleLayer() {
let earthquakes = MGLShapeSource(identifier: "earthquakes", url: URL(string: "https://example.com/earthquakes.json")!, options: [:])
mapView.style?.addSource(earthquakes)
//#-example-code
let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes)
layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
[0: 0,
6: 1])
layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
[0: 1,
9: 3])
mapView.style?.addLayer(layer)
//#-end-example-code
XCTAssertNotNil(mapView.style?.layer(withIdentifier: "earthquake-heat"))
}
func testMGLSymbolStyleLayer() {
let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)

View File

@@ -0,0 +1,61 @@
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>
#import "MGLStyleLayer_Private.h"
#include <mbgl/style/layers/heatmap_layer.hpp>
@interface MGLHeatmapColorTests : XCTestCase <MGLMapViewDelegate>
@end
@implementation MGLHeatmapColorTests
- (void)testProperties {
MGLPointFeature *feature = [[MGLPointFeature alloc] init];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
auto rawLayer = layer.rawLayer->as<mbgl::style::HeatmapLayer>();
XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
@"heatmap-color should be unset initially.");
NSExpression *defaultExpression = layer.heatmapColor;
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
layer.heatmapColor = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(0.0), mbgl::Color::red(),
@"Setting heatmapColor to a constant value expression should update heatmap-color.");
XCTAssertEqualObjects(layer.heatmapColor, constantExpression,
@"heatmapColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
NSExpression *constantExpression2 = [NSExpression expressionWithFormat:@"%@", [MGLColor blueColor]];
NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($heatmapDensity, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@12: constantExpression2}];
layer.heatmapColor = functionExpression;
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(11.0), mbgl::Color::red(),
@"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color.");
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(12.0), mbgl::Color::blue(),
@"Setting heatmapColor to an expression depending on $heatmapDensity should update heatmap-color.");
XCTAssertEqualObjects(layer.heatmapColor, functionExpression,
@"heatmapColor should round-trip expressions depending on $heatmapDensity.");
layer.heatmapColor = nil;
XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
@"Unsetting heatmapColor should return heatmap-color to the default value.");
XCTAssertEqualObjects(layer.heatmapColor, defaultExpression,
@"heatmapColor should return the default value after being unset.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera expression is applied to heatmapColor.");
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a data expression is applied to heatmapColor.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
@end

View File

@@ -0,0 +1,296 @@
// This file is generated.
// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLStyleLayerTests.h"
#import "../../darwin/src/NSDate+MGLAdditions.h"
#import "MGLStyleLayer_Private.h"
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/transition_options.hpp>
@interface MGLHeatmapLayerTests : MGLStyleLayerTests
@end
@implementation MGLHeatmapLayerTests
+ (NSString *)layerType {
return @"heatmap";
}
- (void)testPredicates {
MGLPointFeature *feature = [[MGLPointFeature alloc] init];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
XCTAssertNil(layer.sourceLayerIdentifier);
layer.sourceLayerIdentifier = @"layerID";
XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
layer.sourceLayerIdentifier = nil;
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
layer.predicate = [NSPredicate predicateWithValue:NO];
XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
- (void)testProperties {
MGLPointFeature *feature = [[MGLPointFeature alloc] init];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
MGLHeatmapStyleLayer *layer = [[MGLHeatmapStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
XCTAssertNotEqual(layer.rawLayer, nullptr);
XCTAssertTrue(layer.rawLayer->is<mbgl::style::HeatmapLayer>());
auto rawLayer = layer.rawLayer->as<mbgl::style::HeatmapLayer>();
MGLTransition transitionTest = MGLTransitionMake(5, 4);
// heatmap-intensity
{
XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(),
@"heatmap-intensity should be unset initially.");
NSExpression *defaultExpression = layer.heatmapIntensity;
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
layer.heatmapIntensity = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue,
@"Setting heatmapIntensity to a constant value expression should update heatmap-intensity.");
XCTAssertEqualObjects(layer.heatmapIntensity, constantExpression,
@"heatmapIntensity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapIntensity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
{ -INFINITY, 0xff },
{ 18, 0xff },
}};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue,
@"Setting heatmapIntensity to a camera expression should update heatmap-intensity.");
XCTAssertEqualObjects(layer.heatmapIntensity, functionExpression,
@"heatmapIntensity should round-trip camera expressions.");
layer.heatmapIntensity = nil;
XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(),
@"Unsetting heatmapIntensity should return heatmap-intensity to the default value.");
XCTAssertEqualObjects(layer.heatmapIntensity, defaultExpression,
@"heatmapIntensity should return the default value after being unset.");
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.heatmapIntensityTransition = transitionTest;
auto toptions = rawLayer->getHeatmapIntensityTransition();
XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
MGLTransition heatmapIntensityTransition = layer.heatmapIntensityTransition;
XCTAssertEqual(heatmapIntensityTransition.delay, transitionTest.delay);
XCTAssertEqual(heatmapIntensityTransition.duration, transitionTest.duration);
}
// heatmap-opacity
{
XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(),
@"heatmap-opacity should be unset initially.");
NSExpression *defaultExpression = layer.heatmapOpacity;
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
layer.heatmapOpacity = constantExpression;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue,
@"Setting heatmapOpacity to a constant value expression should update heatmap-opacity.");
XCTAssertEqualObjects(layer.heatmapOpacity, constantExpression,
@"heatmapOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
{ -INFINITY, 0xff },
{ 18, 0xff },
}};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue,
@"Setting heatmapOpacity to a camera expression should update heatmap-opacity.");
XCTAssertEqualObjects(layer.heatmapOpacity, functionExpression,
@"heatmapOpacity should round-trip camera expressions.");
layer.heatmapOpacity = nil;
XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(),
@"Unsetting heatmapOpacity should return heatmap-opacity to the default value.");
XCTAssertEqualObjects(layer.heatmapOpacity, defaultExpression,
@"heatmapOpacity should return the default value after being unset.");
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
// Transition property test
layer.heatmapOpacityTransition = transitionTest;
auto toptions = rawLayer->getHeatmapOpacityTransition();
XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
MGLTransition heatmapOpacityTransition = layer.heatmapOpacityTransition;
XCTAssertEqual(heatmapOpacityTransition.delay, transitionTest.delay);
XCTAssertEqual(heatmapOpacityTransition.duration, transitionTest.duration);
}
// heatmap-radius
{
XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(),
@"heatmap-radius should be unset initially.");
NSExpression *defaultExpression = layer.heatmapRadius;
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
layer.heatmapRadius = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
@"Setting heatmapRadius to a constant value expression should update heatmap-radius.");
XCTAssertEqualObjects(layer.heatmapRadius, constantExpression,
@"heatmapRadius should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapRadius = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
{ -INFINITY, 0xff },
{ 18, 0xff },
}};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
@"Setting heatmapRadius to a camera expression should update heatmap-radius.");
XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
@"heatmapRadius should round-trip camera expressions.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
layer.heatmapRadius = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
@"Setting heatmapRadius to a data expression should update heatmap-radius.");
XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
@"heatmapRadius should round-trip data expressions.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
layer.heatmapRadius = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
@"Setting heatmapRadius to a camera-data expression should update heatmap-radius.");
XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
@"heatmapRadius should round-trip camera-data expressions.");
layer.heatmapRadius = nil;
XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(),
@"Unsetting heatmapRadius should return heatmap-radius to the default value.");
XCTAssertEqualObjects(layer.heatmapRadius, defaultExpression,
@"heatmapRadius should return the default value after being unset.");
// Transition property test
layer.heatmapRadiusTransition = transitionTest;
auto toptions = rawLayer->getHeatmapRadiusTransition();
XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
MGLTransition heatmapRadiusTransition = layer.heatmapRadiusTransition;
XCTAssertEqual(heatmapRadiusTransition.delay, transitionTest.delay);
XCTAssertEqual(heatmapRadiusTransition.duration, transitionTest.duration);
}
// heatmap-weight
{
XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(),
@"heatmap-weight should be unset initially.");
NSExpression *defaultExpression = layer.heatmapWeight;
NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"];
layer.heatmapWeight = constantExpression;
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
@"Setting heatmapWeight to a constant value expression should update heatmap-weight.");
XCTAssertEqualObjects(layer.heatmapWeight, constantExpression,
@"heatmapWeight should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapWeight = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
{ -INFINITY, 0xff },
{ 18, 0xff },
}};
propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
@"Setting heatmapWeight to a camera expression should update heatmap-weight.");
XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
@"heatmapWeight should round-trip camera expressions.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
layer.heatmapWeight = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
@"Setting heatmapWeight to a data expression should update heatmap-weight.");
XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
@"heatmapWeight should round-trip data expressions.");
functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
layer.heatmapWeight = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
@"Setting heatmapWeight to a camera-data expression should update heatmap-weight.");
XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
@"heatmapWeight should round-trip camera-data expressions.");
layer.heatmapWeight = nil;
XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(),
@"Unsetting heatmapWeight should return heatmap-weight to the default value.");
XCTAssertEqualObjects(layer.heatmapWeight, defaultExpression,
@"heatmapWeight should return the default value after being unset.");
}
}
- (void)testPropertyNames {
[self testPropertyName:@"heatmap-intensity" isBoolean:NO];
[self testPropertyName:@"heatmap-opacity" isBoolean:NO];
[self testPropertyName:@"heatmap-radius" isBoolean:NO];
[self testPropertyName:@"heatmap-weight" isBoolean:NO];
}
@end

View File

@@ -59,6 +59,7 @@
MGLTransition transitionTest = MGLTransitionMake(5, 4);
<% for (const property of properties) { -%>
<% if (property.name === 'heatmap-color') continue; -%>
// <%- originalPropertyName(property) %>
{
@@ -151,6 +152,7 @@
- (void)testPropertyNames {
<% for (const property of properties) { -%>
<% if (property.name === 'heatmap-color') continue; -%>
[self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>];
<% } -%>
}

View File

@@ -10,6 +10,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Styles and rendering
* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726))
* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983))
* A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642))

View File

@@ -192,6 +192,7 @@ In style JSON | In the SDK
`circle` | `MGLCircleStyleLayer`
`fill` | `MGLFillStyleLayer`
`fill-extrusion` | `MGLFillExtrusionStyleLayer`
`heatmap` | `MGLHeatmapStyleLayer`
`hillshade` | `MGLHillshadeStyleLayer`
`line` | `MGLLineStyleLayer`
`raster` | `MGLRasterStyleLayer`

View File

@@ -28,6 +28,8 @@
16376B471FFDB92B0000563E /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; };
16376B491FFEED010000563E /* MGLMapViewLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */; };
165D0CE720005419009A3C66 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */; };
170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */; };
1753ED421E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; };
1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; };
1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0666881EC64F8E001C16D7 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -226,6 +228,10 @@
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */; };
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */; };
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; };
8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; };
8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; };
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; };
927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */; };
927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -670,6 +676,8 @@
16376B3F1FFDB4B40000563E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
16376B401FFDB4B40000563E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewLayoutTests.m; sourceTree = "<group>"; };
170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHeatmapColorTests.mm; path = ../../darwin/test/MGLHeatmapColorTests.mm; sourceTree = "<group>"; };
170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLHeatmapStyleLayerTests.mm; path = ../../darwin/test/MGLHeatmapStyleLayerTests.mm; sourceTree = "<group>"; };
1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; };
1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; };
1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; };
@@ -801,6 +809,8 @@
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXEmbeddedMapViewController.h; sourceTree = "<group>"; };
632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXEmbeddedMapViewController.m; sourceTree = "<group>"; };
6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; };
8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; };
8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; };
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; };
927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXSnapshotsViewController.h; sourceTree = "<group>"; };
927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXSnapshotsViewController.m; sourceTree = "<group>"; };
@@ -1236,6 +1246,8 @@
FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */,
FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */,
35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */,
8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */,
8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */,
35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */,
3538AA1B1D542239008EC33D /* MGLForegroundStyleLayer.h */,
3538AA1C1D542239008EC33D /* MGLForegroundStyleLayer.mm */,
@@ -1306,6 +1318,8 @@
3575798F1D513EF1000B822E /* Layers */ = {
isa = PBXGroup;
children = (
170C43782028D49800863DF0 /* MGLHeatmapColorTests.mm */,
170C43792028D49800863DF0 /* MGLHeatmapStyleLayerTests.mm */,
DA2DBBCC1D51E80400D38FF9 /* MGLStyleLayerTests.h */,
DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */,
DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */,
@@ -1967,6 +1981,7 @@
DA8847F91CBAFA5100AB86E3 /* MGLPolygon.h in Headers */,
4049C2AC1DB6E05500B3F799 /* MGLPointCollection_Private.h in Headers */,
DA8847F81CBAFA5100AB86E3 /* MGLPointAnnotation.h in Headers */,
8989B17C201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */,
353933F21D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */,
DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */,
30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */,
@@ -2104,6 +2119,7 @@
DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */,
DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */,
8989B17D201A48EB0081CF59 /* MGLHeatmapStyleLayer.h in Headers */,
DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */,
357FE2DE1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */,
1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */,
@@ -2548,6 +2564,8 @@
409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */,
DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */,
170C437D2029D97900863DF0 /* MGLHeatmapStyleLayerTests.mm in Sources */,
170C437C2029D96F00863DF0 /* MGLHeatmapColorTests.mm in Sources */,
357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */,
35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */,
DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */,
@@ -2660,6 +2678,7 @@
35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */,
966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */,
353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
@@ -2752,6 +2771,7 @@
DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */,
966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */,
8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */,

View File

@@ -95,6 +95,7 @@ custom_categories:
- MGLCircleStyleLayer
- MGLFillStyleLayer
- MGLFillExtrusionStyleLayer
- MGLHeatmapStyleLayer
- MGLHillshadeStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer

View File

@@ -45,6 +45,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLSymbolStyleLayer.h"
#import "MGLRasterStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLHeatmapStyleLayer.h"
#import "MGLHillshadeStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"

View File

@@ -4,6 +4,7 @@
### Styles and rendering
* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726))
* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983))
* A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642))

View File

@@ -0,0 +1,16 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "heatmap.pdf",
"language-direction" : "left-to-right"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@@ -31,6 +31,9 @@
if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
return [NSImage imageNamed:@"symbol"];
}
if ([layer isKindOfClass:[MGLHeatmapStyleLayer class]]) {
return [NSImage imageNamed:@"heatmap"];
}
if ([layer isKindOfClass:[MGLHillshadeStyleLayer class]]) {
return [NSImage imageNamed:@"hillshade"];
}

View File

@@ -0,0 +1,809 @@
{
"version": 8,
"name": "Basic Heatmap",
"center": [
30.49860107152665,
50.459868549177486
],
"zoom": 14.033276876197775,
"bearing": 0,
"pitch": 0,
"sources": {
"mapbox": {
"url": "mapbox://mapbox.mapbox-streets-v7",
"type": "vector"
}
},
"sprite": "mapbox://sprites/mourner/cjcgg2bl16cf42snvcbbaf09z",
"glyphs": "mapbox://fonts/mourner/{fontstack}/{range}.pbf",
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "#dedede"
}
},
{
"id": "landuse_overlay_national_park",
"type": "fill",
"source": "mapbox",
"source-layer": "landuse_overlay",
"filter": [
"==",
"class",
"national_park"
],
"paint": {
"fill-color": "#d2edae",
"fill-opacity": 0.75
}
},
{
"id": "landuse_park",
"type": "fill",
"source": "mapbox",
"source-layer": "landuse",
"filter": [
"==",
"class",
"park"
],
"paint": {
"fill-color": "#d2edae"
}
},
{
"id": "waterway",
"type": "line",
"source": "mapbox",
"source-layer": "waterway",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"in",
"class",
"canal",
"river"
]
],
"paint": {
"line-color": "#a0cfdf",
"line-width": {
"base": 1.4,
"stops": [
[
8,
0.5
],
[
20,
15
]
]
}
}
},
{
"id": "water",
"type": "fill",
"source": "mapbox",
"source-layer": "water",
"paint": {
"fill-color": "#a0cfdf"
}
},
{
"id": "building",
"type": "fill",
"source": "mapbox",
"source-layer": "building",
"paint": {
"fill-color": "#d6d6d6"
}
},
{
"id": "tunnel_minor",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"==",
"structure",
"tunnel"
],
[
"in",
"class",
"link",
"motorway_link",
"path",
"pedestrian",
"service",
"street",
"street_limited",
"track"
]
]
],
"layout": {
"line-cap": "butt",
"line-join": "miter"
},
"paint": {
"line-color": "#efefef",
"line-width": {
"base": 1.55,
"stops": [
[
4,
0.25
],
[
20,
30
]
]
},
"line-dasharray": [
0.36,
0.18
]
}
},
{
"id": "tunnel_major",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"==",
"structure",
"tunnel"
],
[
"in",
"class",
"motorway",
"primary",
"secondary",
"tertiary",
"trunk"
]
]
],
"layout": {
"line-cap": "butt",
"line-join": "miter"
},
"paint": {
"line-color": "#fff",
"line-width": {
"base": 1.4,
"stops": [
[
6,
0.5
],
[
20,
30
]
]
},
"line-dasharray": [
0.28,
0.14
]
}
},
{
"id": "road_minor",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"in",
"class",
"link",
"motorway_link",
"path",
"pedestrian",
"service",
"street",
"street_limited",
"track"
],
[
"in",
"structure",
"ford",
"none"
]
]
],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#efefef",
"line-width": {
"base": 1.55,
"stops": [
[
4,
0.25
],
[
20,
30
]
]
}
}
},
{
"id": "road_major",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"in",
"class",
"motorway",
"primary",
"secondary",
"tertiary",
"trunk"
],
[
"in",
"structure",
"ford",
"none"
]
]
],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#fff",
"line-width": {
"base": 1.4,
"stops": [
[
6,
0.5
],
[
20,
30
]
]
}
}
},
{
"id": "bridge_minor case",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"==",
"structure",
"bridge"
],
[
"in",
"class",
"link",
"motorway_link",
"path",
"pedestrian",
"service",
"street",
"street_limited",
"track"
]
]
],
"layout": {
"line-cap": "butt",
"line-join": "miter"
},
"paint": {
"line-color": "#dedede",
"line-width": {
"base": 1.6,
"stops": [
[
12,
0.5
],
[
20,
10
]
]
},
"line-gap-width": {
"base": 1.55,
"stops": [
[
4,
0.25
],
[
20,
30
]
]
}
}
},
{
"id": "bridge_major case",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"==",
"structure",
"bridge"
],
[
"in",
"class",
"motorway",
"primary",
"secondary",
"tertiary",
"trunk"
]
]
],
"layout": {
"line-cap": "butt",
"line-join": "miter"
},
"paint": {
"line-color": "#dedede",
"line-width": {
"base": 1.6,
"stops": [
[
12,
0.5
],
[
20,
10
]
]
},
"line-gap-width": {
"base": 1.55,
"stops": [
[
4,
0.25
],
[
20,
30
]
]
}
}
},
{
"id": "bridge_minor",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"==",
"structure",
"bridge"
],
[
"in",
"class",
"link",
"motorway_link",
"path",
"pedestrian",
"service",
"street",
"street_limited",
"track"
]
]
],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#efefef",
"line-width": {
"base": 1.55,
"stops": [
[
4,
0.25
],
[
20,
30
]
]
}
}
},
{
"id": "bridge_major",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"==",
"structure",
"bridge"
],
[
"in",
"class",
"motorway",
"primary",
"secondary",
"tertiary",
"trunk"
]
]
],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#fff",
"line-width": {
"base": 1.4,
"stops": [
[
6,
0.5
],
[
20,
30
]
]
}
}
},
{
"id": "admin_country",
"type": "line",
"source": "mapbox",
"source-layer": "admin",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"all",
[
"<=",
"admin_level",
2
],
[
"==",
"maritime",
0
]
]
],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#8b8a8a",
"line-width": {
"base": 1.3,
"stops": [
[
3,
0.5
],
[
22,
15
]
]
}
}
},
{
"id": "road_major_label",
"type": "symbol",
"source": "mapbox",
"source-layer": "road_label",
"filter": [
"all",
[
"==",
"$type",
"LineString"
],
[
"in",
"class",
"motorway",
"primary",
"secondary",
"tertiary",
"trunk"
]
],
"layout": {
"symbol-placement": "line",
"text-field": "{name_en}",
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"text-transform": "uppercase",
"text-letter-spacing": 0.1,
"text-size": {
"base": 1.4,
"stops": [
[
10,
8
],
[
20,
14
]
]
}
},
"paint": {
"text-color": "#666",
"text-halo-color": "rgba(255,255,255,0.75)",
"text-halo-width": 2
}
},
{
"id": "place_label_other",
"type": "symbol",
"source": "mapbox",
"source-layer": "place_label",
"minzoom": 8,
"filter": [
"all",
[
"==",
"$type",
"Point"
],
[
"in",
"type",
"hamlet",
"island",
"neighbourhood",
"suburb",
"town",
"village"
]
],
"layout": {
"text-field": "{name_en}",
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"text-max-width": 6,
"text-size": {
"stops": [
[
6,
12
],
[
12,
16
]
]
}
},
"paint": {
"text-color": "#666",
"text-halo-color": "rgba(255,255,255,0.75)",
"text-halo-width": 1,
"text-halo-blur": 1
}
},
{
"id": "place_label_city",
"type": "symbol",
"source": "mapbox",
"source-layer": "place_label",
"maxzoom": 16,
"filter": [
"all",
[
"==",
"$type",
"Point"
],
[
"==",
"type",
"city"
]
],
"layout": {
"text-field": "{name_en}",
"text-font": [
"Open Sans Bold",
"Arial Unicode MS Bold"
],
"text-max-width": 10,
"text-size": {
"stops": [
[
3,
12
],
[
8,
16
]
]
}
},
"paint": {
"text-color": "#666",
"text-halo-color": "rgba(255,255,255,0.75)",
"text-halo-width": 1,
"text-halo-blur": 1
}
},
{
"id": "country_label",
"type": "symbol",
"source": "mapbox",
"source-layer": "country_label",
"maxzoom": 12,
"filter": [
"==",
"$type",
"Point"
],
"layout": {
"text-field": "{name_en}",
"text-font": [
"Open Sans Regular",
"Arial Unicode MS Regular"
],
"text-max-width": 10,
"text-size": {
"stops": [
[
3,
14
],
[
8,
22
]
]
}
},
"paint": {
"text-color": "#666",
"text-halo-color": "rgba(255,255,255,0.75)",
"text-halo-width": 1,
"text-halo-blur": 1
}
},
{
"id": "road-heatmap",
"type": "heatmap",
"source": "mapbox",
"source-layer": "road",
"paint": {
"heatmap-intensity": 1
}
}
]
}

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
id="svg4148"
version="1.1"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="heatmap.svg"
inkscape:export-filename="/Users/mxn/Desktop/symbol.png"
inkscape:export-xdpi="90.000244"
inkscape:export-ydpi="90.000244">
<defs
id="defs4150" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="23.25"
inkscape:cx="4.7741936"
inkscape:cy="7.6812068"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1280"
inkscape:window-height="755"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="1"
inkscape:snap-bbox="true" />
<metadata
id="metadata4153">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<path
style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:0.99369001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 11.237265,1036.8591 a 4.2655011,4.1972104 0 0 0 -4.2658922,4.1979 4.2655011,4.1972104 0 0 0 0.025231,0.427 5.3408028,5.2552965 0 0 0 -1.1586611,-0.1299 5.3408028,5.2552965 0 0 0 -5.34109749,5.2556 5.3408028,5.2552965 0 0 0 5.34109749,5.2557 5.3408028,5.2552965 0 0 0 5.3410983,-5.2557 5.3408028,5.2552965 0 0 0 -0.186318,-1.3664 4.2655011,4.1972104 0 0 0 0.244542,0.012 4.2655011,4.1972104 0 0 0 4.26589,-4.198 4.2655011,4.1972104 0 0 0 -4.26589,-4.1979 z"
id="path818"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#000000;fill-opacity:0.15686275;fill-rule:evenodd;stroke:none;stroke-width:0.99369001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 11.005859,1038.875 c -4.8895767,1.3679 0.321278,7.2823 2.416016,2.7578 0.450567,-1.4666 -0.923376,-2.9624 -2.416016,-2.7578 z m -5.5078121,4.5 c -5.49629855,0.4756 -2.1892763,9.0394 2.2363281,5.9121 2.667642,-1.8963 1.5392118,-6.8607 -2.2363281,-5.9121 z"
id="path836"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -179,6 +179,7 @@ In style JSON | In the SDK
`circle` | `MGLCircleStyleLayer`
`fill` | `MGLFillStyleLayer`
`fill-extrusion` | `MGLFillExtrusionStyleLayer`
`heatmap` | `MGLHeatmapStyleLayer`
`hillshade` | `MGLHillshadeStyleLayer`
`line` | `MGLLineStyleLayer`
`raster` | `MGLRasterStyleLayer`

View File

@@ -80,6 +80,7 @@ custom_categories:
- MGLCircleStyleLayer
- MGLFillStyleLayer
- MGLFillExtrusionStyleLayer
- MGLHeatmapStyleLayer
- MGLHillshadeStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer

View File

@@ -16,6 +16,8 @@
07D9474D1F67441B00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */; };
07F8E2F71F674C8800F794BB /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
07F8E2F81F674C9000F794BB /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */; };
170A82BF201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */; };
170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */; };
1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */; };
1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */; };
1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A11ECFB00300021D39 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -85,6 +87,9 @@
55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */; };
89462399200D199100DA8EF2 /* heatmap.json in Resources */ = {isa = PBXBuildFile; fileRef = 89462398200D199100DA8EF2 /* heatmap.json */; };
8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
894623A0200E748000DA8EF2 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */; };
92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */; };
920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */; };
@@ -298,6 +303,8 @@
07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; };
07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayerTests.mm; sourceTree = "<group>"; };
170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapColorTests.mm; sourceTree = "<group>"; };
1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; };
1F7454A01ECFB00300021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; };
1F7454A11ECFB00300021D39 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; };
@@ -369,6 +376,9 @@
55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
55FE0E8D1D100A0900FD240B /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/macos/config.xcconfig; sourceTree = "<group>"; };
89462398200D199100DA8EF2 /* heatmap.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = heatmap.json; sourceTree = "<group>"; };
8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; };
8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; };
92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; };
92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; };
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLSourceQueryTests.m; sourceTree = "<group>"; };
@@ -672,6 +682,8 @@
35602BF91D3EA99F0050646F /* MGLFillStyleLayer.mm */,
35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */,
35602BFE1D3EA9B40050646F /* MGLForegroundStyleLayer.mm */,
8946239A200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.h */,
8946239B200E73CA00DA8EF2 /* MGLHeatmapStyleLayer.mm */,
DAF25714201901C200367EF5 /* MGLHillshadeStyleLayer.h */,
DAF25713201901C100367EF5 /* MGLHillshadeStyleLayer.mm */,
DA8F25891D51CA540010E6B5 /* MGLLineStyleLayer.h */,
@@ -825,6 +837,7 @@
DA839EA61CC2E3400062CAFB /* Info.plist */,
96E027331E57C9A7004B8E66 /* Localizable.strings */,
DA839E981CC2E3400062CAFB /* Supporting Files */,
89462398200D199100DA8EF2 /* heatmap.json */,
);
name = "Demo App";
path = app;
@@ -875,6 +888,8 @@
DA8F257C1D51C5F40010E6B5 /* Layers */ = {
isa = PBXGroup;
children = (
170A82C2201FAFF800943087 /* MGLHeatmapColorTests.mm */,
170A82BE201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm */,
40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */,
40E1601B1DF216E6005EA6D9 /* MGLStyleLayerTests.m */,
DA2207BA1DC076930002F84D /* test-Bridging-Header.h */,
@@ -1232,6 +1247,7 @@
359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */,
DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */,
DA87A9A01DC9DC6200810D09 /* MGLValueEvaluator.h in Headers */,
8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */,
DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */,
DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */,
DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */,
@@ -1422,6 +1438,7 @@
DA839EA01CC2E3400062CAFB /* MapDocument.xib in Resources */,
353BAEF81D6463B8009A8DA9 /* amsterdam.geojson in Resources */,
96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */,
89462399200D199100DA8EF2 /* heatmap.json in Resources */,
DA839EA51CC2E3400062CAFB /* MainMenu.xib in Resources */,
DA5589771D320C41006B7F64 /* wms.json in Resources */,
DAE6C2E21CC304F900DB3429 /* Credits.rtf in Resources */,
@@ -1503,6 +1520,7 @@
07D9474B1F6743F000E37934 /* MGLAbstractShapeSource.mm in Sources */,
DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */,
DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */,
894623A0200E748000DA8EF2 /* MGLHeatmapStyleLayer.mm in Sources */,
3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */,
35602C011D3EA9B40050646F /* MGLForegroundStyleLayer.mm in Sources */,
408AA86A1DAEEE5D00022900 /* NSDictionary+MGLAdditions.mm in Sources */,
@@ -1565,11 +1583,13 @@
DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */,
DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.mm in Sources */,
40E1601D1DF217D6005EA6D9 /* MGLStyleLayerTests.m in Sources */,
170A82BF201BDD1B00943087 /* MGLHeatmapStyleLayerTests.mm in Sources */,
1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */,
DAF25721201902C100367EF5 /* MGLHillshadeStyleLayerTests.mm in Sources */,
DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.mm in Sources */,
DA87A99E1DC9DC2100810D09 /* MGLPredicateTests.mm in Sources */,
DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */,
170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */,
4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */,
DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */,

View File

@@ -44,6 +44,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLRasterStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLBackgroundStyleLayer.h"
#import "MGLHeatmapStyleLayer.h"
#import "MGLHillshadeStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"
#import "MGLSource.h"

View File

@@ -15,6 +15,7 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>

View File

@@ -23,24 +23,6 @@
"render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
"render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js",
"render-tests/heatmap-color/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-color/expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-color/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-intensity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-intensity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-intensity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-opacity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-opacity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-opacity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-radius/antimeridian": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-radius/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-radius/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-radius/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-radius/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-radius/pitch30": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-weight/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-weight/identity-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/heatmap-weight/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
"render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397",
"render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195",
"render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",

View File

@@ -7,9 +7,6 @@ const outputPath = 'src/mbgl/shaders';
var shaders = require('../mapbox-gl-js/src/shaders');
delete shaders.heatmap;
delete shaders.heatmapTexture;
require('./style-code');
writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.

View File

@@ -97,6 +97,8 @@ global.paintPropertyType = function (property, type) {
global.propertyValueType = function (property) {
if (isDataDriven(property)) {
return `DataDrivenPropertyValue<${evaluatedType(property)}>`;
} else if (property.name === 'heatmap-color') {
return `HeatmapColorPropertyValue`;
} else {
return `PropertyValue<${evaluatedType(property)}>`;
}

View File

@@ -1,5 +1,3 @@
var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8');
// Make temporary modifications here when Native doesn't have all features that JS has.
delete spec.layer.type.values.heatmap;

View File

@@ -85,6 +85,10 @@ public:
static ColorMode alphaBlended() {
return ColorMode { Add { One, OneMinusSrcAlpha }, {}, { true, true, true, true } };
}
static ColorMode additive() {
return ColorMode { Add { One, One }, {}, { true, true, true, true } };
}
};
constexpr bool operator!=(const ColorMode::Mask& a, const ColorMode::Mask& b) {

View File

@@ -60,6 +60,16 @@ static_assert(std::is_same<std::underlying_type_t<TextureFormat>, GLenum>::value
static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch");
static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch");
static_assert(std::is_same<std::underlying_type_t<TextureType>, GLenum>::value, "OpenGL type mismatch");
static_assert(underlying_type(TextureType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch");
#if MBGL_USE_GLES2 && GL_HALF_FLOAT_OES
static_assert(underlying_type(TextureType::HalfFloat) == GL_HALF_FLOAT_OES, "OpenGL type mismatch");
#endif
#if !MBGL_USE_GLES2 && GL_HALF_FLOAT_ARB
static_assert(underlying_type(TextureType::HalfFloat) == GL_HALF_FLOAT_ARB, "OpenGL type mismatch");
#endif
static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch");
static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch");
static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch");
@@ -116,6 +126,19 @@ void Context::initializeExtensions(const std::function<gl::ProcAddress(const cha
programBinary = std::make_unique<extension::ProgramBinary>(fn);
#endif
#if MBGL_USE_GLES2
constexpr const char* halfFloatExtensionName = "OES_texture_half_float";
constexpr const char* halfFloatColorBufferExtensionName = "EXT_color_buffer_half_float";
#else
constexpr const char* halfFloatExtensionName = "ARB_half_float_pixel";
constexpr const char* halfFloatColorBufferExtensionName = "ARB_color_buffer_float";
#endif
if (strstr(extensions, halfFloatExtensionName) != nullptr &&
strstr(extensions, halfFloatColorBufferExtensionName) != nullptr) {
supportsHalfFloatTextures = true;
}
if (!supportsVertexArrays()) {
Log::Warning(Event::OpenGL, "Not using Vertex Array Objects");
}
@@ -491,10 +514,10 @@ Context::createFramebuffer(const Texture& color,
}
UniqueTexture
Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) {
Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit, TextureType type) {
auto obj = createTexture();
pixelStoreUnpack = { 1 };
updateTexture(obj, size, data, format, unit);
updateTexture(obj, size, data, format, unit, type);
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures.
// We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus.
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
@@ -505,11 +528,11 @@ Context::createTexture(const Size size, const void* data, TextureFormat format,
}
void Context::updateTexture(
TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) {
TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit, TextureType type) {
activeTextureUnit = unit;
texture[unit] = id;
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), size.width,
size.height, 0, static_cast<GLenum>(format), GL_UNSIGNED_BYTE,
size.height, 0, static_cast<GLenum>(format), static_cast<GLenum>(type),
data));
}

View File

@@ -125,23 +125,29 @@ public:
// Create a texture from an image with data.
template <typename Image>
Texture createTexture(const Image& image, TextureUnit unit = 0) {
Texture createTexture(const Image& image,
TextureUnit unit = 0,
TextureType type = TextureType::UnsignedByte) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
return { image.size, createTexture(image.size, image.data.get(), format, unit) };
return { image.size, createTexture(image.size, image.data.get(), format, unit, type) };
}
template <typename Image>
void updateTexture(Texture& obj, const Image& image, TextureUnit unit = 0) {
void updateTexture(Texture& obj,
const Image& image,
TextureUnit unit = 0,
TextureType type = TextureType::UnsignedByte) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit);
updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit, type);
obj.size = image.size;
}
// Creates an empty texture with the specified dimensions.
Texture createTexture(const Size size,
TextureFormat format = TextureFormat::RGBA,
TextureUnit unit = 0) {
return { size, createTexture(size, nullptr, format, unit) };
TextureUnit unit = 0,
TextureType type = TextureType::UnsignedByte) {
return { size, createTexture(size, nullptr, format, unit, type) };
}
void bindTexture(Texture&,
@@ -232,6 +238,8 @@ public:
State<value::PixelTransferStencil> pixelTransferStencil;
#endif // MBGL_USE_GLES2
bool supportsHalfFloatTextures = false;
private:
State<value::StencilFunc> stencilFunc;
State<value::StencilMask> stencilMask;
@@ -259,8 +267,8 @@ private:
void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
UniqueBuffer createIndexBuffer(const void* data, std::size_t size, const BufferUsage usage);
void updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit);
void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit);
UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit, TextureType);
void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit, TextureType);
UniqueFramebuffer createFramebuffer();
UniqueRenderbuffer createRenderbuffer(RenderbufferType, Size size);
std::unique_ptr<uint8_t[]> readFramebuffer(Size, TextureFormat, bool flip);

View File

@@ -64,6 +64,15 @@ enum class TextureFormat : uint32_t {
#endif // MBGL_USE_GLES2
};
enum class TextureType : uint32_t {
UnsignedByte = 0x1401,
#if MBGL_USE_GLES2
HalfFloat = 0x8D61,
#else
HalfFloat = 0x140B,
#endif // MBGL_USE_GLES2
};
enum class PrimitiveType {
Points = 0x0000,
Lines = 0x0001,

View File

@@ -30,7 +30,7 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_normal_ed);
MBGL_DEFINE_ATTRIBUTE(uint8_t, 1, a_fade_opacity);
MBGL_DEFINE_ATTRIBUTE(uint8_t, 2, a_placed);
MBGL_DEFINE_ATTRIBUTE(uint8_t, 2, a_placed);
template <typename T, std::size_t N>
struct a_data {
@@ -142,6 +142,11 @@ struct a_halo_blur {
using Type = gl::Attribute<float, 1>;
};
struct a_weight {
static auto name() { return "a_weight"; }
using Type = gl::Attribute<float, 1>;
};
} // namespace attributes
struct PositionOnlyLayoutAttributes : gl::Attributes<

View File

@@ -0,0 +1,7 @@
#include <mbgl/programs/heatmap_program.hpp>
namespace mbgl {
static_assert(sizeof(HeatmapLayoutVertex) == 4, "expected HeatmapLayoutVertex size");
} // namespace mbgl

View File

@@ -0,0 +1,49 @@
#pragma once
#include <mbgl/programs/program.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/heatmap.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/style/layers/heatmap_layer_properties.hpp>
namespace mbgl {
namespace uniforms {
MBGL_DEFINE_UNIFORM_SCALAR(float, u_intensity);
} // namespace uniforms
class HeatmapProgram : public Program<
shaders::heatmap,
gl::Triangle,
gl::Attributes<
attributes::a_pos>,
gl::Uniforms<
uniforms::u_intensity,
uniforms::u_matrix,
uniforms::heatmap::u_extrude_scale>,
style::HeatmapPaintProperties>
{
public:
using Program::Program;
/*
* @param {number} x vertex position
* @param {number} y vertex position
* @param {number} ex extrude normal
* @param {number} ey extrude normal
*/
static LayoutVertex vertex(Point<int16_t> p, float ex, float ey) {
return LayoutVertex {
{{
static_cast<int16_t>((p.x * 2) + ((ex + 1) / 2)),
static_cast<int16_t>((p.y * 2) + ((ey + 1) / 2))
}}
};
}
};
using HeatmapLayoutVertex = HeatmapProgram::LayoutVertex;
using HeatmapAttributes = HeatmapProgram::Attributes;
} // namespace mbgl

View File

@@ -0,0 +1,7 @@
#include <mbgl/programs/heatmap_texture_program.hpp>
namespace mbgl {
static_assert(sizeof(HeatmapTextureLayoutVertex) == 4, "expected HeatmapTextureLayoutVertex size");
} // namespace mbgl

View File

@@ -0,0 +1,43 @@
#pragma once
#include <mbgl/programs/program.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/heatmap_texture.hpp>
#include <mbgl/style/properties.hpp>
#include <mbgl/util/geometry.hpp>
namespace mbgl {
namespace uniforms {
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_color_ramp);
} // namespace uniforms
class HeatmapTextureProgram : public Program<
shaders::heatmap_texture,
gl::Triangle,
gl::Attributes<attributes::a_pos>,
gl::Uniforms<
uniforms::u_matrix,
uniforms::u_world,
uniforms::u_image,
uniforms::u_color_ramp,
uniforms::u_opacity>,
style::Properties<>> {
public:
using Program::Program;
static LayoutVertex layoutVertex(Point<int16_t> p) {
return LayoutVertex{
{{
p.x,
p.y
}}
};
}
};
using HeatmapTextureLayoutVertex = HeatmapTextureProgram::LayoutVertex;
using HeatmapTextureAttributes = HeatmapTextureProgram::Attributes;
} // namespace mbgl

View File

@@ -6,6 +6,8 @@
#include <mbgl/programs/extrusion_texture_program.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/programs/heatmap_program.hpp>
#include <mbgl/programs/heatmap_texture_program.hpp>
#include <mbgl/programs/hillshade_program.hpp>
#include <mbgl/programs/hillshade_prepare_program.hpp>
#include <mbgl/programs/line_program.hpp>
@@ -30,6 +32,8 @@ public:
fillPattern(context, programParameters),
fillOutline(context, programParameters),
fillOutlinePattern(context, programParameters),
heatmap(context, programParameters),
heatmapTexture(context, programParameters),
hillshade(context, programParameters),
hillshadePrepare(context, programParameters),
line(context, programParameters),
@@ -55,6 +59,8 @@ public:
ProgramMap<FillPatternProgram> fillPattern;
ProgramMap<FillOutlineProgram> fillOutline;
ProgramMap<FillOutlinePatternProgram> fillOutlinePattern;
ProgramMap<HeatmapProgram> heatmap;
HeatmapTextureProgram heatmapTexture;
HillshadeProgram hillshade;
HillshadePrepareProgram hillshadePrepare;
ProgramMap<LineProgram> line;

View File

@@ -37,9 +37,14 @@ MBGL_DEFINE_UNIFORM_SCALAR(Size, u_texsize);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_camera_to_center_distance);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_fade_change);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_weight);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale);
namespace heatmap {
MBGL_DEFINE_UNIFORM_SCALAR(float, u_extrude_scale);
} // namespace heatmap
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_a);
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_a);
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_b);

View File

@@ -0,0 +1,98 @@
#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/programs/heatmap_program.hpp>
#include <mbgl/style/layers/heatmap_layer_impl.hpp>
#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.hpp>
namespace mbgl {
using namespace style;
HeatmapBucket::HeatmapBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers)
: mode(parameters.mode) {
for (const auto& layer : layers) {
paintPropertyBinders.emplace(
std::piecewise_construct,
std::forward_as_tuple(layer->getID()),
std::forward_as_tuple(
layer->as<RenderHeatmapLayer>()->evaluated,
parameters.tileID.overscaledZ));
}
}
void HeatmapBucket::upload(gl::Context& context) {
vertexBuffer = context.createVertexBuffer(std::move(vertices));
indexBuffer = context.createIndexBuffer(std::move(triangles));
for (auto& pair : paintPropertyBinders) {
pair.second.upload(context);
}
uploaded = true;
}
bool HeatmapBucket::hasData() const {
return !segments.empty();
}
void HeatmapBucket::addFeature(const GeometryTileFeature& feature,
const GeometryCollection& geometry) {
constexpr const uint16_t vertexLength = 4;
for (auto& points : geometry) {
for(auto& point : points) {
auto x = point.x;
auto y = point.y;
// Do not include points that are outside the tile boundaries.
// Include all points in Still mode. You need to include points from
// neighbouring tiles so that they are not clipped at tile boundaries.
if ((mode == MapMode::Continuous) &&
(x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue;
if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
// Move to a new segments because the old one can't hold the geometry.
segments.emplace_back(vertices.vertexSize(), triangles.indexSize());
}
// this geometry will be of the Point type, and we'll derive
// two triangles from it.
//
// ┌─────────┐
// │ 4 3 │
// │ │
// │ 1 2 │
// └─────────┘
//
vertices.emplace_back(HeatmapProgram::vertex(point, -1, -1)); // 1
vertices.emplace_back(HeatmapProgram::vertex(point, 1, -1)); // 2
vertices.emplace_back(HeatmapProgram::vertex(point, 1, 1)); // 3
vertices.emplace_back(HeatmapProgram::vertex(point, -1, 1)); // 4
auto& segment = segments.back();
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
uint16_t index = segment.vertexLength;
// 1, 2, 3
// 1, 4, 3
triangles.emplace_back(index, index + 1, index + 2);
triangles.emplace_back(index, index + 3, index + 2);
segment.vertexLength += vertexLength;
segment.indexLength += 6;
}
}
for (auto& pair : paintPropertyBinders) {
pair.second.populateVertexVectors(feature, vertices.vertexSize());
}
}
float HeatmapBucket::getQueryRadius(const RenderLayer& layer) const {
(void)layer;
return 0;
}
} // namespace mbgl

View File

@@ -0,0 +1,40 @@
#pragma once
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/heatmap_program.hpp>
#include <mbgl/style/layers/heatmap_layer_properties.hpp>
namespace mbgl {
class BucketParameters;
class HeatmapBucket : public Bucket {
public:
HeatmapBucket(const BucketParameters&, const std::vector<const RenderLayer*>&);
void addFeature(const GeometryTileFeature&,
const GeometryCollection&) override;
bool hasData() const override;
void upload(gl::Context&) override;
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<HeatmapLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
SegmentVector<HeatmapAttributes> segments;
optional<gl::VertexBuffer<HeatmapLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
std::map<std::string, HeatmapProgram::PaintPropertyBinders> paintPropertyBinders;
const MapMode mode;
};
} // namespace mbgl

View File

@@ -0,0 +1,178 @@
#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/programs/programs.hpp>
#include <mbgl/programs/heatmap_program.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/heatmap_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/intersection_tests.hpp>
namespace mbgl {
using namespace style;
RenderHeatmapLayer::RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl> _impl)
: RenderLayer(style::LayerType::Heatmap, _impl),
unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) {
}
const style::HeatmapLayer::Impl& RenderHeatmapLayer::impl() const {
return static_cast<const style::HeatmapLayer::Impl&>(*baseImpl);
}
std::unique_ptr<Bucket> RenderHeatmapLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
return std::make_unique<HeatmapBucket>(parameters, layers);
}
void RenderHeatmapLayer::transition(const TransitionParameters& parameters) {
unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
}
void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluated = unevaluated.evaluate(parameters);
passes = (evaluated.get<style::HeatmapOpacity>() > 0)
? (RenderPass::Translucent | RenderPass::Pass3D)
: RenderPass::None;
}
bool RenderHeatmapLayer::hasTransition() const {
return unevaluated.hasTransition();
}
void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) {
if (parameters.pass == RenderPass::Opaque) {
return;
}
if (parameters.pass == RenderPass::Pass3D) {
const auto& viewportSize = parameters.staticData.backendSize;
const auto size = Size{viewportSize.width / 4, viewportSize.height / 4};
if (!renderTexture || renderTexture->getSize() != size) {
if (parameters.context.supportsHalfFloatTextures) {
renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::HalfFloat);
try {
renderTexture->bind();
} catch (const std::runtime_error& ex) {
// can't render to a half-float texture; falling back to unsigned byte one
renderTexture = nullopt;
parameters.context.supportsHalfFloatTextures = false;
}
}
if (!renderTexture) {
renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte);
renderTexture->bind();
}
} else {
renderTexture->bind();
}
if (!colorRampTexture) {
colorRampTexture = parameters.context.createTexture(colorRamp, 1, gl::TextureType::UnsignedByte);
}
parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 1.0f }, {}, {});
for (const RenderTile& tile : renderTiles) {
assert(dynamic_cast<HeatmapBucket*>(tile.tile.getBucket(*baseImpl)));
HeatmapBucket& bucket = *reinterpret_cast<HeatmapBucket*>(tile.tile.getBucket(*baseImpl));
const auto extrudeScale = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
const auto stencilMode = parameters.mapMode != MapMode::Continuous
? parameters.stencilModeForClipping(tile.clip)
: gl::StencilMode::disabled();
parameters.programs.heatmap.get(evaluated).draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
stencilMode,
gl::ColorMode::additive(),
HeatmapProgram::UniformValues {
uniforms::u_intensity::Value{evaluated.get<style::HeatmapIntensity>()},
uniforms::u_matrix::Value{tile.matrix},
uniforms::heatmap::u_extrude_scale::Value{extrudeScale}
},
*bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.segments,
bucket.paintPropertyBinders.at(getID()),
evaluated,
parameters.state.getZoom(),
getID()
);
}
} else if (parameters.pass == RenderPass::Translucent) {
parameters.context.bindTexture(renderTexture->getTexture(), 0, gl::TextureFilter::Linear);
parameters.context.bindTexture(*colorRampTexture, 1, gl::TextureFilter::Linear);
const auto& size = parameters.staticData.backendSize;
mat4 viewportMat;
matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
const Properties<>::PossiblyEvaluated properties;
parameters.programs.heatmapTexture.draw(
parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
HeatmapTextureProgram::UniformValues{
uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
uniforms::u_image::Value{ 0 },
uniforms::u_color_ramp::Value{ 1 },
uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() } },
parameters.staticData.extrusionTextureVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.extrusionTextureSegments,
HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
parameters.state.getZoom(), getID());
}
}
void RenderHeatmapLayer::updateColorRamp() {
auto colorValue = unevaluated.get<HeatmapColor>().getValue();
if (colorValue.isUndefined()) {
colorValue = HeatmapLayer::getDefaultHeatmapColor();
}
const auto length = colorRamp.bytes();
for (uint32_t i = 0; i < length; i += 4) {
const auto color = colorValue.evaluate(static_cast<double>(i) / length);
colorRamp.data[i + 0] = std::floor(color.r * 255);
colorRamp.data[i + 1] = std::floor(color.g * 255);
colorRamp.data[i + 2] = std::floor(color.b * 255);
colorRamp.data[i + 3] = std::floor(color.a * 255);
}
if (colorRampTexture) {
colorRampTexture = nullopt;
}
}
bool RenderHeatmapLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float zoom,
const float bearing,
const float pixelsToTileUnits) const {
(void) queryGeometry;
(void) feature;
(void) zoom;
(void) bearing;
(void) pixelsToTileUnits;
return false;
}
} // namespace mbgl

View File

@@ -0,0 +1,48 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/style/layers/heatmap_layer_impl.hpp>
#include <mbgl/style/layers/heatmap_layer_properties.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/offscreen_texture.hpp>
namespace mbgl {
class RenderHeatmapLayer: public RenderLayer {
public:
RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl>);
~RenderHeatmapLayer() final = default;
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
const float,
const float) const override;
void updateColorRamp();
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
// Paint properties
style::HeatmapPaintProperties::Unevaluated unevaluated;
style::HeatmapPaintProperties::PossiblyEvaluated evaluated;
const style::HeatmapLayer::Impl& impl() const;
PremultipliedImage colorRamp;
optional<OffscreenTexture> renderTexture;
optional<gl::Texture> colorRampTexture;
};
template <>
inline bool RenderLayer::is<RenderHeatmapLayer>() const {
return type == style::LayerType::Heatmap;
}
} // namespace mbgl

View File

@@ -8,6 +8,7 @@
#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/layers/render_raster_layer.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/renderer/render_tile.hpp>
@@ -35,6 +36,8 @@ std::unique_ptr<RenderLayer> RenderLayer::create(Immutable<Layer::Impl> impl) {
return std::make_unique<RenderCustomLayer>(staticImmutableCast<CustomLayer::Impl>(impl));
case LayerType::FillExtrusion:
return std::make_unique<RenderFillExtrusionLayer>(staticImmutableCast<FillExtrusionLayer::Impl>(impl));
case LayerType::Heatmap:
return std::make_unique<RenderHeatmapLayer>(staticImmutableCast<HeatmapLayer::Impl>(impl));
}
// Not reachable, but placate GCC.

View File

@@ -14,6 +14,7 @@
#include <mbgl/renderer/layers/render_background_layer.hpp>
#include <mbgl/renderer/layers/render_custom_layer.hpp>
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/renderer/layers/render_heatmap_layer.hpp>
#include <mbgl/renderer/layers/render_hillshade_layer.hpp>
#include <mbgl/renderer/style_diff.hpp>
#include <mbgl/renderer/query.hpp>
@@ -185,6 +186,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
if (layerAdded || layerChanged) {
layer.transition(transitionParameters);
if (layer.is<RenderHeatmapLayer>()) {
layer.as<RenderHeatmapLayer>()->updateColorRamp();
}
}
if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
@@ -290,7 +295,11 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
RenderLayer* layer = getRenderLayer(layerImpl->id);
assert(layer);
if (!parameters.staticData.has3D && (layer->is<RenderFillExtrusionLayer>() || layer->is<RenderHillshadeLayer>())) {
if (!parameters.staticData.has3D && (
layer->is<RenderFillExtrusionLayer>() ||
layer->is<RenderHillshadeLayer>() ||
layer->is<RenderHeatmapLayer>())) {
parameters.staticData.has3D = true;
}

View File

@@ -0,0 +1,128 @@
// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
#include <mbgl/shaders/heatmap.hpp>
namespace mbgl {
namespace shaders {
const char* heatmap::name = "heatmap";
const char* heatmap::vertexSource = R"MBGL_SHADER(
#ifndef HAS_UNIFORM_u_weight
uniform lowp float a_weight_t;
attribute highp vec2 a_weight;
varying highp float weight;
#else
uniform highp float u_weight;
#endif
#ifndef HAS_UNIFORM_u_radius
uniform lowp float a_radius_t;
attribute mediump vec2 a_radius;
#else
uniform mediump float u_radius;
#endif
uniform mat4 u_matrix;
uniform float u_extrude_scale;
uniform float u_opacity;
uniform float u_intensity;
attribute vec2 a_pos;
varying vec2 v_extrude;
// Effective "0" in the kernel density texture to adjust the kernel size to;
// this empirically chosen number minimizes artifacts on overlapping kernels
// for typical heatmap cases (assuming clustered source)
const highp float ZERO = 1.0 / 255.0 / 16.0;
// Gaussian kernel coefficient: 1 / sqrt(2 * PI)
#define GAUSS_COEF 0.3989422804014327
void main(void) {
#ifndef HAS_UNIFORM_u_weight
weight = unpack_mix_vec2(a_weight, a_weight_t);
#else
highp float weight = u_weight;
#endif
#ifndef HAS_UNIFORM_u_radius
mediump float radius = unpack_mix_vec2(a_radius, a_radius_t);
#else
mediump float radius = u_radius;
#endif
// unencode the extrusion vector that we snuck into the a_pos vector
vec2 unscaled_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
// This 'extrude' comes in ranging from [-1, -1], to [1, 1]. We'll use
// it to produce the vertices of a square mesh framing the point feature
// we're adding to the kernel density texture. We'll also pass it as
// a varying, so that the fragment shader can determine the distance of
// each fragment from the point feature.
// Before we do so, we need to scale it up sufficiently so that the
// kernel falls effectively to zero at the edge of the mesh.
// That is, we want to know S such that
// weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO
// Which solves to:
// S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0
float S = sqrt(-2.0 * log(ZERO / weight / u_intensity / GAUSS_COEF)) / 3.0;
// Pass the varying in units of radius
v_extrude = S * unscaled_extrude;
// Scale by radius and the zoom-based scale factor to produce actual
// mesh position
vec2 extrude = v_extrude * radius * u_extrude_scale;
// multiply a_pos by 0.5, since we had it * 2 in order to sneak
// in extrusion data
vec4 pos = vec4(floor(a_pos * 0.5) + extrude, 0, 1);
gl_Position = u_matrix * pos;
}
)MBGL_SHADER";
const char* heatmap::fragmentSource = R"MBGL_SHADER(
#ifndef HAS_UNIFORM_u_weight
varying highp float weight;
#else
uniform highp float u_weight;
#endif
uniform highp float u_intensity;
varying vec2 v_extrude;
// Gaussian kernel coefficient: 1 / sqrt(2 * PI)
#define GAUSS_COEF 0.3989422804014327
void main() {
#ifdef HAS_UNIFORM_u_weight
highp float weight = u_weight;
#endif
// Kernel density estimation with a Gaussian kernel of size 5x5
float d = -0.5 * 3.0 * 3.0 * dot(v_extrude, v_extrude);
float val = weight * u_intensity * GAUSS_COEF * exp(d);
gl_FragColor = vec4(val, 1.0, 1.0, 1.0);
#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(1.0);
#endif
}
)MBGL_SHADER";
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
#pragma once
namespace mbgl {
namespace shaders {
class heatmap {
public:
static const char* name;
static const char* vertexSource;
static const char* fragmentSource;
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,42 @@
// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
#include <mbgl/shaders/heatmap_texture.hpp>
namespace mbgl {
namespace shaders {
const char* heatmap_texture::name = "heatmap_texture";
const char* heatmap_texture::vertexSource = R"MBGL_SHADER(
uniform mat4 u_matrix;
uniform vec2 u_world;
attribute vec2 a_pos;
varying vec2 v_pos;
void main() {
gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1);
v_pos.x = a_pos.x;
v_pos.y = 1.0 - a_pos.y;
}
)MBGL_SHADER";
const char* heatmap_texture::fragmentSource = R"MBGL_SHADER(
uniform sampler2D u_image;
uniform sampler2D u_color_ramp;
uniform float u_opacity;
varying vec2 v_pos;
void main() {
float t = texture2D(u_image, v_pos).r;
vec4 color = texture2D(u_color_ramp, vec2(t, 0.5));
gl_FragColor = color * u_opacity;
#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(0.0);
#endif
}
)MBGL_SHADER";
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
#pragma once
namespace mbgl {
namespace shaders {
class heatmap_texture {
public:
static const char* name;
static const char* vertexSource;
static const char* fragmentSource;
};
} // namespace shaders
} // namespace mbgl

View File

@@ -6,6 +6,7 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/hillshade_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
@@ -165,6 +166,8 @@ optional<std::unique_ptr<Layer>> Converter<std::unique_ptr<Layer>>::operator()(c
converted = convertVectorLayer<SymbolLayer>(*id, value, error);
} else if (*type == "raster") {
converted = convertRasterLayer(*id, value, error);
} else if (*type == "heatmap") {
converted = convertVectorLayer<HeatmapLayer>(*id, value, error);
} else if (*type == "hillshade") {
converted = convertHillshadeLayer(*id, value, error);
} else if (*type == "background") {

View File

@@ -8,6 +8,7 @@
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/hillshade_layer.hpp>
@@ -72,6 +73,7 @@ inline auto makeLayoutPropertySetters() {
return result;
}
@@ -166,6 +168,17 @@ inline auto makePaintPropertySetters() {
result["circle-stroke-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
result["circle-stroke-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
result["heatmap-radius"] = &setProperty<HeatmapLayer, DataDrivenPropertyValue<float>, &HeatmapLayer::setHeatmapRadius>;
result["heatmap-radius-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapRadiusTransition>;
result["heatmap-weight"] = &setProperty<HeatmapLayer, DataDrivenPropertyValue<float>, &HeatmapLayer::setHeatmapWeight>;
result["heatmap-weight-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapWeightTransition>;
result["heatmap-intensity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapIntensity>;
result["heatmap-intensity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapIntensityTransition>;
result["heatmap-color"] = &setProperty<HeatmapLayer, HeatmapColorPropertyValue, &HeatmapLayer::setHeatmapColor>;
result["heatmap-color-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapColorTransition>;
result["heatmap-opacity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapOpacity>;
result["heatmap-opacity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapOpacityTransition>;
result["fill-extrusion-opacity"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
result["fill-extrusion-opacity-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
result["fill-extrusion-color"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;

View File

@@ -5,6 +5,7 @@
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/style/conversion/data_driven_property_value.hpp>
#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
#include <mbgl/style/conversion/transition_options.hpp>
#include <string>

View File

@@ -0,0 +1,239 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/heatmap_layer_impl.hpp>
#include <mbgl/style/layer_observer.hpp>
// for constructing default heatmap-color ramp expression from style JSON
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
namespace mbgl {
namespace style {
HeatmapLayer::HeatmapLayer(const std::string& layerID, const std::string& sourceID)
: Layer(makeMutable<Impl>(LayerType::Heatmap, layerID, sourceID)) {
}
HeatmapLayer::HeatmapLayer(Immutable<Impl> impl_)
: Layer(std::move(impl_)) {
}
HeatmapLayer::~HeatmapLayer() = default;
const HeatmapLayer::Impl& HeatmapLayer::impl() const {
return static_cast<const Impl&>(*baseImpl);
}
Mutable<HeatmapLayer::Impl> HeatmapLayer::mutableImpl() const {
return makeMutable<Impl>(impl());
}
std::unique_ptr<Layer> HeatmapLayer::cloneRef(const std::string& id_) const {
auto impl_ = mutableImpl();
impl_->id = id_;
impl_->paint = HeatmapPaintProperties::Transitionable();
return std::make_unique<HeatmapLayer>(std::move(impl_));
}
void HeatmapLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
// Source
const std::string& HeatmapLayer::getSourceID() const {
return impl().source;
}
void HeatmapLayer::setSourceLayer(const std::string& sourceLayer) {
auto impl_ = mutableImpl();
impl_->sourceLayer = sourceLayer;
baseImpl = std::move(impl_);
}
const std::string& HeatmapLayer::getSourceLayer() const {
return impl().sourceLayer;
}
// Filter
void HeatmapLayer::setFilter(const Filter& filter) {
auto impl_ = mutableImpl();
impl_->filter = filter;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
const Filter& HeatmapLayer::getFilter() const {
return impl().filter;
}
// Visibility
void HeatmapLayer::setVisibility(VisibilityType value) {
if (value == getVisibility())
return;
auto impl_ = mutableImpl();
impl_->visibility = value;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
// Zoom range
void HeatmapLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
}
void HeatmapLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
}
// Layout properties
// Paint properties
DataDrivenPropertyValue<float> HeatmapLayer::getDefaultHeatmapRadius() {
return { 30 };
}
DataDrivenPropertyValue<float> HeatmapLayer::getHeatmapRadius() const {
return impl().paint.template get<HeatmapRadius>().value;
}
void HeatmapLayer::setHeatmapRadius(DataDrivenPropertyValue<float> value) {
if (value == getHeatmapRadius())
return;
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapRadius>().value = value;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
void HeatmapLayer::setHeatmapRadiusTransition(const TransitionOptions& options) {
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapRadius>().options = options;
baseImpl = std::move(impl_);
}
TransitionOptions HeatmapLayer::getHeatmapRadiusTransition() const {
return impl().paint.template get<HeatmapRadius>().options;
}
DataDrivenPropertyValue<float> HeatmapLayer::getDefaultHeatmapWeight() {
return { 1 };
}
DataDrivenPropertyValue<float> HeatmapLayer::getHeatmapWeight() const {
return impl().paint.template get<HeatmapWeight>().value;
}
void HeatmapLayer::setHeatmapWeight(DataDrivenPropertyValue<float> value) {
if (value == getHeatmapWeight())
return;
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapWeight>().value = value;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
void HeatmapLayer::setHeatmapWeightTransition(const TransitionOptions& options) {
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapWeight>().options = options;
baseImpl = std::move(impl_);
}
TransitionOptions HeatmapLayer::getHeatmapWeightTransition() const {
return impl().paint.template get<HeatmapWeight>().options;
}
PropertyValue<float> HeatmapLayer::getDefaultHeatmapIntensity() {
return { 1 };
}
PropertyValue<float> HeatmapLayer::getHeatmapIntensity() const {
return impl().paint.template get<HeatmapIntensity>().value;
}
void HeatmapLayer::setHeatmapIntensity(PropertyValue<float> value) {
if (value == getHeatmapIntensity())
return;
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapIntensity>().value = value;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
void HeatmapLayer::setHeatmapIntensityTransition(const TransitionOptions& options) {
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapIntensity>().options = options;
baseImpl = std::move(impl_);
}
TransitionOptions HeatmapLayer::getHeatmapIntensityTransition() const {
return impl().paint.template get<HeatmapIntensity>().options;
}
HeatmapColorPropertyValue HeatmapLayer::getDefaultHeatmapColor() {
conversion::Error error;
std::string rawValue = R"JSON(["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",0.1,"royalblue",0.3,"cyan",0.5,"lime",0.7,"yellow",1,"red"])JSON";
return *conversion::convertJSON<HeatmapColorPropertyValue>(rawValue, error);
}
HeatmapColorPropertyValue HeatmapLayer::getHeatmapColor() const {
return impl().paint.template get<HeatmapColor>().value;
}
void HeatmapLayer::setHeatmapColor(HeatmapColorPropertyValue value) {
if (value == getHeatmapColor())
return;
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapColor>().value = value;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
void HeatmapLayer::setHeatmapColorTransition(const TransitionOptions& options) {
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapColor>().options = options;
baseImpl = std::move(impl_);
}
TransitionOptions HeatmapLayer::getHeatmapColorTransition() const {
return impl().paint.template get<HeatmapColor>().options;
}
PropertyValue<float> HeatmapLayer::getDefaultHeatmapOpacity() {
return { 1 };
}
PropertyValue<float> HeatmapLayer::getHeatmapOpacity() const {
return impl().paint.template get<HeatmapOpacity>().value;
}
void HeatmapLayer::setHeatmapOpacity(PropertyValue<float> value) {
if (value == getHeatmapOpacity())
return;
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapOpacity>().value = value;
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
void HeatmapLayer::setHeatmapOpacityTransition(const TransitionOptions& options) {
auto impl_ = mutableImpl();
impl_->paint.template get<HeatmapOpacity>().options = options;
baseImpl = std::move(impl_);
}
TransitionOptions HeatmapLayer::getHeatmapOpacityTransition() const {
return impl().paint.template get<HeatmapOpacity>().options;
}
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,15 @@
#include <mbgl/style/layers/heatmap_layer_impl.hpp>
namespace mbgl {
namespace style {
bool HeatmapLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
assert(dynamic_cast<const HeatmapLayer::Impl*>(&other));
const auto& impl = static_cast<const style::HeatmapLayer::Impl&>(other);
return filter != impl.filter ||
visibility != impl.visibility ||
paint.hasDataDrivenPropertyDifference(impl.paint);
}
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,21 @@
#pragma once
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/heatmap_layer_properties.hpp>
namespace mbgl {
namespace style {
class HeatmapLayer::Impl : public Layer::Impl {
public:
using Layer::Impl::Impl;
bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
HeatmapPaintProperties::Transitionable paint;
};
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,9 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
#include <mbgl/style/layers/heatmap_layer_properties.hpp>
namespace mbgl {
namespace style {
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,40 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
#pragma once
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
namespace mbgl {
namespace style {
struct HeatmapRadius : DataDrivenPaintProperty<float, attributes::a_radius, uniforms::u_radius> {
static float defaultValue() { return 30; }
};
struct HeatmapWeight : DataDrivenPaintProperty<float, attributes::a_weight, uniforms::u_weight> {
static float defaultValue() { return 1; }
};
struct HeatmapIntensity : PaintProperty<float> {
static float defaultValue() { return 1; }
};
struct HeatmapOpacity : PaintProperty<float> {
static float defaultValue() { return 1; }
};
class HeatmapPaintProperties : public Properties<
HeatmapRadius,
HeatmapWeight,
HeatmapIntensity,
HeatmapColor,
HeatmapOpacity
> {};
} // namespace style
} // namespace mbgl

View File

@@ -8,6 +8,12 @@
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp>
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer_impl.hpp>
#include <mbgl/style/layer_observer.hpp>
<% if (type === 'heatmap') { -%>
// for constructing default heatmap-color ramp expression from style JSON
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/heatmap_color_property_value.hpp>
<% } -%>
namespace mbgl {
namespace style {
@@ -134,7 +140,13 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyV
// Paint properties
<% for (const property of paintProperties) { %>
<%- propertyValueType(property) %> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() {
<% if (property.name === 'heatmap-color') { -%>
conversion::Error error;
std::string rawValue = R"JSON(<%- JSON.stringify(property.default) %>)JSON";
return *conversion::convertJSON<<%- propertyValueType(property)%>>(rawValue, error);
<% } else { -%>
return { <%- defaultValue(property) %> };
<% } -%>
}
<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {

View File

@@ -25,6 +25,7 @@ struct <%- camelize(property.name) %> : <%- layoutPropertyType(property, type) %
<% } -%>
<% for (const property of paintProperties) { -%>
<% if (property.name === 'heatmap-color') continue; -%>
struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> {
static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; }
};

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/properties.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/heatmap_color_property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
#include <mbgl/renderer/property_evaluator.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
@@ -48,5 +49,27 @@ public:
static constexpr bool IsDataDriven = false;
};
/*
* Special-case paint property traits for heatmap-color, needed because
* heatmap-color values do not fit into the
* Undefined | Value | {Camera,Source,Composite}Function taxonomy that applies
* to all other paint properties.
*
* These traits are provided here--despite the fact that heatmap-color
* is not used like other paint properties--to allow the parameter-pack-based
* batch evaluation of paint properties to compile properly.
*/
class HeatmapColor {
public:
using TransitionableType = Transitionable<HeatmapColorPropertyValue>;
using UnevaluatedType = Transitioning<HeatmapColorPropertyValue>;
using EvaluatorType = PropertyEvaluator<Color>;
using PossiblyEvaluatedType = Color;
using Type = Color;
static constexpr bool IsDataDriven = false;
static Color defaultValue() { return {}; }
};
} // namespace style
} // namespace mbgl

View File

@@ -6,6 +6,7 @@
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/heatmap_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>

View File

@@ -11,20 +11,21 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default;
class OffscreenTexture::Impl {
public:
Impl(gl::Context& context_, const Size size_)
: context(context_), size(std::move(size_)) {
Impl(gl::Context& context_, const Size size_, const gl::TextureType type_)
: context(context_), size(std::move(size_)), type(type_) {
assert(!size.isEmpty());
}
Impl(gl::Context& context_,
const Size size_,
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_)
: context(context_), size(std::move(size_)), depth(&depth_) {
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_,
const gl::TextureType type_)
: context(context_), size(std::move(size_)), depth(&depth_), type(type_) {
assert(!size.isEmpty());
}
void bind() {
if (!framebuffer) {
texture = context.createTexture(size, gl::TextureFormat::RGBA);
texture = context.createTexture(size, gl::TextureFormat::RGBA, 0, type);
if (depth) {
framebuffer = context.createFramebuffer(*texture, *depth);
} else {
@@ -58,18 +59,21 @@ private:
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr;
const gl::TextureType type;
};
OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size)
: impl(std::make_unique<Impl>(context, std::move(size))) {
const Size size,
const gl::TextureType type)
: impl(std::make_unique<Impl>(context, std::move(size), type)) {
assert(!size.isEmpty());
}
OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size,
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer)
: impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) {
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer,
const gl::TextureType type)
: impl(std::make_unique<Impl>(context, std::move(size), renderbuffer, type)) {
assert(!size.isEmpty());
}

View File

@@ -12,10 +12,12 @@ class Texture;
class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
Size size = { 256, 256 });
Size size = { 256, 256 },
gl::TextureType type = gl::TextureType::UnsignedByte);
OffscreenTexture(gl::Context&,
Size size,
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&);
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&,
gl::TextureType type = gl::TextureType::UnsignedByte);
~OffscreenTexture();
OffscreenTexture(OffscreenTexture&&);
OffscreenTexture& operator=(OffscreenTexture&&);