How to draw multiple circles with different radius in MapBox GL JS? - 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?

Related

Mapbox GeoJSON or Plain Lat and Long for Database Storage

I am new to working with maps. My issue is I don't understand if I need to store data in my database as geoJSON or just store the coordinates and create custom markers with popups which is what I need to do. My database is MongoDB, although it shouldn't really matter for this. In geoJSON Mapbox parses the data like so:
const geojson = {
'type': 'FeatureCollection',
'features': [
{
// feature for Mapbox DC
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [
-77.03238901390978, 38.913188059745586
]
},
'properties': {
'title': 'Mapbox DC'
}
},
{
// feature for Mapbox SF
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-122.414, 37.776]
},
'properties': {
'title': 'Mapbox SF'
}
}
]
}
The example data
map.current.addSource('points', {
'type': 'geojson',
'data': geojson
});
The data being parsed (part of the code, it's in load image for the custom markers thing I was talking about it.)

How to use react-map-gl to draw line between two point

I am trying to draw a line between two points using react-map-gl library. I can not find example from the official document, So I am trying to reproduce same behavior from following code snippet which use Mapbox library
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function() {
map.addSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': [
[-122.483696, 37.833818],
[-122.493782, 37.833683]
]
}
}
});
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#888',
'line-width': 8
}
});
});
Here is the sandbox, I do not see any errors on the console but the line is not displayed:
https://codesandbox.io/s/draw-line-between-two-point-v0mbc?file=/src/index.js:214-226
The code in the sandbox actually works (for me anyway), but is misleading because the line drawn is nowhere near the viewport.
A couple of things to note are that coordinates are an array given in [long, lat] which may not be what most people would assume. For example, if you cut and paste [lat,long] from google maps for San Fransisco, you get [37.77909036739809, -122.41510269913951]. Then you'll have to reverse those and put them in:
const dataOne = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[-122.41510269913951, 37.77909036739809],
[39.5423, -77.0564]
]
}
};
Also, the sample code has some cruft in it. Edit the variable dataOne not the other unused place.
Now you'll see a line from San Fransisco to some random spot in the middle of Antarctica that was really easy to miss.
Just in case the link goes bad, the full code is:
import React, { Component } from "react";
import { render } from "react-dom";
import ReactMapGL, { Source, Layer } from "react-map-gl";
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 38.63738602787579,
longitude: -121.23576311149986,
zoom: 6.8,
bearing: 0,
pitch: 0,
dragPan: true,
width: 600,
height: 600
}
};
}
render() {
const { viewport } = this.state;
const MAPBOX_TOKEN =
"pk.eyJ1Ijoic21peWFrYXdhIiwiYSI6ImNqcGM0d3U4bTB6dWwzcW04ZHRsbHl0ZWoifQ.X9cvdajtPbs9JDMG-CMDsA";
const dataOne = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[-122.41510269913951, 37.77909036739809],
[39.5423, -77.0564]
]
}
};
return (
<ReactMapGL
{...viewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
onViewportChange={(newViewport) => {
this.setState({ viewport: newViewport });
}}
>
<Source id="polylineLayer" type="geojson" data={dataOne}>
<Layer
id="lineLayer"
type="line"
source="my-data"
layout={{
"line-join": "round",
"line-cap": "round"
}}
paint={{
"line-color": "rgba(3, 170, 238, 0.5)",
"line-width": 5
}}
/>
</Source>
</ReactMapGL>
);
}
}
render(<App />, document.getElementById("root"));

Color title mapbox

Does anyone know how to change the title color of a marker on a mapbox,
I would like home to be in blue, I tried to add paint : color-size:"blue", but it doesn't work,
thank you for your help!!
map.on('load', function () {
map.loadImage(
'https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png',
function (error, image) {
if (error) throw error;
map.addImage('custom-marker', image);
map.addSource('points', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': [
{
// feature for Mapbox
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [6.157902659395512,49.3612254277963],
},
'properties': {
'title': 'Lieu de repas',
'scale': 2,
},
}
]
}
});
map.addLayer({
'id': 'points',
'type': 'symbol',
'source': 'points',
'layout': {
'icon-image': 'custom-marker',
'text-field': ['get', 'title'],
"text-size": 28,
'text-font': [
'Open Sans Regular',
],
'text-offset': [0, 1.25],
'text-anchor': 'top',
}
});
}
);
});
You could possibly create an HTML marker for that in a way similar to this:
var el = document.createElement('div');
el.innerHTML = 'Lieu de repas';
el.style.color = 'blue';
new mapboxgl.Marker(el)
.setLngLat([6.157902659395512,49.3612254277963])
.addTo(map);

Adding multiple sources and layers with .each?

I try to simplify my codebase for an ongoing project. I render different lines from different sources. Adding the source and layer one by one works as expected. But if I put this in an object it won't work. I even have no errors in the console. Now I am stuck at the following code
var trip_entries = {
t2: {
trip_id: 'trip-entry-2',
trip_geojson: '[[-0.15591514, 51.51830379],[-0.07571203, 51.51424049],[-0.08533793, 51.50438536],[-0.085793, 51.5036],[-0.084793, 51.503336],[-0.089793, 51.505336]]'
},
t3: {
trip_id: 'trip-entry-3',
trip_geojson: '[[-0.15514, 51.518],[-0.075703, 51.515],[-0.085793, 51.50],[-0.0793, 51.506],[-0.08473, 51.50336],[-0.0893, 51.536]]'
},
};
// Set route
$.each(trip_entries,function(key,value){
// Add sources
map.addSource(value.trip_id,{
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': value.trip_geojson
}
},
],
},
});
// Add layers
map.addLayer({
'id': value.trip_id,
'type': 'line',
'source': value.trip_id,
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': 'rgb(160,160,160)',
'line-width': 6,
}
});
});
You are passing the geoJSON as a string, rather than an array of coordinates. You should remove the ' ' on both of your geoJSONs to get the code to run. See below:
mapboxgl.accessToken =
"pk.eyJ1IjoicGxtYXBib3giLCJhIjoiY2s3MHkzZ3VnMDFlbDNmbzNiajN5dm9lOCJ9.nbbtDF54HIXo0mCiekVxng";
var map = new mapboxgl.Map({
container: "map", // container id
style: "mapbox://styles/mapbox/streets-v11", // stylesheet location
center: [-0.084793, 51.503336], // starting position [lng, lat]
zoom: 11 // starting zoom
});
var trip_entries = {
t2: {
trip_id: "trip-entry-2",
trip_geojson: [
[-0.15591514, 51.51830379],
[-0.07571203, 51.51424049],
[-0.08533793, 51.50438536],
[-0.085793, 51.5036],
[-0.084793, 51.503336],
[-0.089793, 51.505336]
]
},
t3: {
trip_id: "trip-entry-3",
trip_geojson: [
[-0.15514, 51.518],
[-0.075703, 51.515],
[-0.085793, 51.5],
[-0.0793, 51.506],
[-0.08473, 51.50336],
[-0.0893, 51.536]
]
}
};
map.on("load", function () {
// Set route
$.each(trip_entries, function (key, value) {
// Add sources
map.addSource(value.trip_id, {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "LineString",
coordinates: value.trip_geojson
}
}
]
}
});
// Add layers
map.addLayer({
id: value.trip_id,
type: "line",
source: value.trip_id,
layout: {
"line-join": "round",
"line-cap": "round"
},
paint: {
"line-color": "rgb(160,160,160)",
"line-width": 6
}
});
});
});
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/v1.9.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.9.1/mapbox-gl.css" rel="stylesheet" />
<div id="map"></div>

fill-extrusion not displaying correctly with 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/ ]