fill-extrusion not displaying correctly with mapbox-gl-js - mapbox-gl-js

This is what my dataset looks like:
Seattle Crime Dataset
What I want to do is change the extrusion height based on the frequency column. I can successfully display these as points but I'm struggling with it whenever I use fill-extrusion. Can you help point me in the right direction?
map.addLayer({
'id': 'total',
'type': 'circle',
'source': {
type: 'vector',
url: 'mapbox://askakdagr8.9tklrr8g'
},
'source-layer': 'GroupedOutput-9i6ink',
'paint': {
// make circles larger as the user zooms from z12 to z22
'circle-radius': {
'base': 1.75,
'stops': [
[12, 2],
[22, 180]
]
},
'circle-color': '#ff7770'
}
});

Since the mapbox-gl-js does not currently have functionality for extruding a circle, you need to replace the points with a polygon, and interpolating the circle, for example, by a function turf.circle:
map.on('sourcedata', function(e) {
if (e.sourceId !== 'total') return
if (e.isSourceLoaded !== true) return
var data = {
"type": "FeatureCollection",
"features": []
}
e.source.data.features.forEach(function(f) {
var object = turf.centerOfMass(f)
var center = object.geometry.coordinates
var radius = 10;
var options = {
steps: 16,
units: 'meters',
properties: object.properties
};
data.features.push(turf.circle(center, radius, options))
})
map.getSource('extrusion').setData(data);
})
[ http://jsfiddle.net/zjLek40n/ ]

Related

How to have two mapbox raster layers with different opacities?

Im using Mapbox GL API, and I run into the issue that if I add 2 tile layers, that the opacity of the second layer in the paint object is ignored. Does anyone have any idea why this is? In the browser both tile layers have opacity 1.
let style1 = {
id: "source1-tile",
type: "raster",
source: "source1",
paint: {
"raster-opacity": 1.0
},
}
this.map.addLayer(style1);
let style2 = {
id: "source2-tile",
type: "raster",
source: "source2",
paint: {
"raster-opacity": 0.5
},
}
this.map.addLayer(style2);
// print result
console.log(this.map.getStyle().layers)
// this shows the following:
/*
[
{
id: "source1-tile"
paint: Object { "raster-opacity": 1 }
source: "source1"
type: "raster"
},
{
id: "source2-tile"
source: "source2"
type: "raster"
}
]
*/
Pay attention to add layers in map.load event. I've made this example based on mapbox-gl examples. Easily You could add more raster layers with different opacity.
mapboxgl.accessToken = 'pk.eyJ1IjoibHN0aXoiLCJhIjoiY2s5dGtnNTZ2MWVybzNobjEyam0yd2E3MyJ9.6dCvGbS93SKGMbOqZA4Qag';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [-87.62, 41.86],
zoom: 9
});
map.on('load', () => {
map.addSource('chicago', {
'type': 'raster',
'url': 'mapbox://mapbox.u8yyzaor'
});
map.addLayer({
'id': 'chicago',
'source': 'chicago',
'type': 'raster'
});
map.setPaintProperty(
'chicago',
'raster-opacity',
0.5
);
});

How to draw multiple circles with different radius in MapBox GL JS?

I get some information from a AJAX and I create a GeoJson looking like
var waypointGeojson = {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'properties': {
'id': waypoint.poi_id,
'name': waypoint.name,
'iconSize': [100, 100]
},
'geometry': {
'type': 'Point',
'coordinates': [waypoint.longitude, waypoint.latitude],
}, {
'type': 'Feature',
'properties': {
'id': waypoint.poi_id,
'name': waypoint.name,
'iconSize': [25,25]
}, {
'geometry': {
'type': 'Point',
'coordinates': [waypoint.longitude, waypoint.latitude],
}, 'type': 'Feature',
'properties': {
'id': waypoint.poi_id,
'name': waypoint.name,
'iconSize': [35,35]
},
'geometry': {
'type': 'Point',
'coordinates': [waypoint.longitude, waypoint.latitude],
},
]
};
Then I add the source to the map like this :
map.addSource('waypoints', {
'type': 'geojson',
'data': waypointGeojson,
});
Then I loop around the Features to get the data and show the markers on my map.
waypointGeojson.features.forEach(function(marker) {
var radius = Number(marker.properties.iconSize[0]);
var latitude = Number(marker.geometry.coordinates[1]);
var el = document.createElement('div');
el.className = 'marker';
el.style.backgroundColor = 'rgba(230, 56, 18, 0.5)' ;
el.style.width = radius + 'px';
el.style.height = radius + 'px';
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);
}
At this step my map looks like :
Map at Zoom 15.32
However I want the Circle to be adjusted while I zoom in/zoom out.
For instance, if I zoom in :
Map at Zoom 17.32 The circle radius has not been adjusted (obviously!)
If you have any idea how I could do that with MapBox GL JS ?
I did try to use the formula (from here), with no success :
const metersToPixelsAtMaxZoom = (meters, latitude) =>
meters / 0.075 / Math.cos(latitude * Math.PI / 180)
If I use the method described here, then I put the
waypointGeojson.features.forEach(function(marker) {
var id = Number(marker.properties.id);
var radius = Number(marker.properties.iconSize[0]);
var latitude = Number(marker.geometry.coordinates[1])
map.addLayer({
"id": "circle"+id,
"type": "circle",
"source": "waypoints",
// "layout": {
// "visibility": "none"
// },
"paint": {
"circle-radius": {
stops: [
[0, 0],
[20, metersToPixelsAtMaxZoom(radius, latitude)]
],
base: 2
},
"circle-color": "red",
"circle-opacity": 0.4
}
});
});
I get 3 circles per point see here. This method is good because zoom in/out doesn't alter the size, but how to only have the corresponding circle on my point?

leaflet highlight part of polyline

in leaflet.js i have polyline in openstreet map:
point1(long, lat), 2(long, lat)....pointXY(long,lat)
Is possible (and how) highlight or change color only segment from point 2 to 5, no matter how to invoke.
I try add another polyline, but with more segments its mess with more and more duplicit points.
Working sample:
http://next.plnkr.co/edit/FyUzMG9Y9NrbYrzL
i looking for turn segment between [40,10],[10,10],[10,30] red. If its possible, by index [2,3,4]
thanks for help
You can try GeoJson layer to highlight a line. Just make sure that geojson coordinates are in LngLat format.
var map = L.map('map', {
center: [0, 0],
zoom: 0,
layers: [
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap contributors',
}),
],
});
var data = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [[30, 10], [45, 45]],
},
properties: {
color: 'green',
},
},
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [[10, 40], [10, 10], [30, 10]],
},
properties: {
color: 'red',
},
},
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [[-45, -45], [10, 40]],
},
properties: {
color: 'black',
},
},
],
};
var geoJsonLayer = L.geoJson(data, {
onEachFeature: function(feature, layer) {
if (layer instanceof L.Polyline) {
layer.setStyle({
color: feature.properties.color,
});
}
},
}).addTo(map);
Plunker: http://next.plnkr.co/edit/s4Q72s7RyOLp714U?preview
Ref: https://embed.plnkr.co/plunk/XqzvdR

Mapbox ID layer cannot be selected

I have added layer in mapbox, and then add click on it to trigger popups. That works fine and looks like this:
map.addLayer({
"id": "circle",
"type": "circle",
"source": "companies",
"paint": {
"circle-radius": 20,
"circle-color": "#C6DB3E",
"circle-opacity": {
"stops": [[3, 0.1], [22, 0.8]]
}
}
});
And here I select that layer for triggering popup:
map.on('click', function (e) {
var features = map.queryRenderedFeatures(e.point, {
layers: ["circle"]
});
if (!features.length) {
return;
}
var feature = features[0];
console.log(feature);
// Populate the popup and set its coordinates and content
var popup = new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML('...')
.addTo(map);
});
But problem appears when I changed layer to use dynamic circle-radius, and layer now looks like this:
map.addLayer({
"id": "circle",
"type": "circle",
"source": "companies",
"paint": {
"circle-radius": {
property: 'Size',
type: 'identity'
},
"circle-color": "#C6DB3E",
"circle-opacity": {
"stops": [[3, 0.1], [22, 0.8]]
}
}
});
This layers is also printed properly to the map. But I cannot click on it to get a popup. So after changing circle-radius, ID is not clickable.
Funny is that if I consoleLog ID's with map.getStyle().layers, ID appears in console, with all other layers.
No errors.
The style syntax for circle-radius is not valid. See Mapbox Style Spec for expressions or this other answer.
Also: You can simplify the click handler by providing the id of the layer as the second parameter:
map.on('click', 'circle', function (e) {
var features = e.features;
if (!features.length) {
return;
}
var feature = e.features[0];
var popup = new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(feature.properties.someProperty)
.addTo(map);
});
Mapbox has an example of this on their site: https://www.mapbox.com/mapbox-gl-js/example/popup-on-click/
I updated mapbox and it worked fine, with code I posted in question. I also tried what u suggested and it works too. Thanks #Eczajk! At the end I ended up with this code for circle radius:
"circle-radius": {
property: 'Size',
type: 'exponential',
stops: [
[4, 4],
[170, 170]
]
}
And here is explained example: https://www.mapbox.com/help/gl-dds-map-tutorial/

How can I show the count of points in the particular latitude and longitude in Mapboxgl?

I want to show the marker as well as the number of points available in a particular latitude and longitude.
I expect something Similar as above.
But i have added another 2 layers to show different color and number on it. If I do this way, I am getting "undefined" error while showing popup. Since the data takes from the other layer. If it takes from layer "locations" it works as expected. But when we have multiple occurences popup content shows "undefined". Below is my implementation output and code
map.on('load', function () {
map.addSource("place", {
type: "geojson",
data: liveData,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
map.addLayer({
"id": "locations",
"type": "circle",
"source": "location",
"paint": {
"circle-radius": 7,
"circle-color": "#FFF000",
"circle-stroke-width": 4,
"circle-stroke-color": "#FFFFFF"
}
});
map.addLayer({
id: "clusters",
type: "circle",
source: "location",
filter: ["has", "point_count"],
paint: {
"circle-color": {
property: "point_count",
type: "interval",
stops: [
[0, "#FFF000"],
[2, "#DC143C"],
]
},
"circle-radius": {
property: "point_count",
type: "interval",
stops: [
[0, 7],
[2, 7],
]
}
}
});
map.addLayer({
id: "cluster-count",
type: "symbol",
source: "location",
filter: ["has", "point_count"],
layout: {
"text-field": "{point_count_abbreviated}",
"text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"],
"text-size": 12,
"text-offset": [0, 5]
}
});
map.on('click', 'locations', function (e) {
let htmlString = "";
for (let i = 0; i < e.features.length; i++) {
htmlString = htmlString + e.features[i].properties.description;
if (i != e.features.length - 1) {
htmlString = htmlString + "</br>";
}
}
new mapboxgl.Popup()
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(htmlString)
.addTo(map);
});
}
My working fiddle
I want to achieve this as first picture or popup should work in my approach?
As far as I know getting all features that get combined in the cluster is currently not possible via mapbox's cluster feature.
See here: https://github.com/mapbox/mapbox-gl-js/issues/3318