Reputation: 1411
I am using map box api with react, I get 2 kind of data set from backend, one with let's say bike's parking points and another is data set is places where accident happened, I want to display both data set with different markers/icons in mapbox, how should I do that?
currently to display single data set I am using below code.
import React, { useState, useEffect, useRef } from "react";
import { useDispatch, useSelector } from 'react-redux';
import mapboxgl from 'mapbox-gl';
import {getBikeInfo, mapDetails} from './features/counter/getInfo/getDetails.js'
<link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet"></link>
function App() {
const dispatch = useDispatch();
const [dataLoaded, setDataLoaded] = useState(false);
const dataToDisplay = useSelector(mapDetails);
mapboxgl.accessToken = 'pk.eyJ1IjoidmloYW5nMTYiLCJhIjoiY2ttOHowc2ZhMWN2OTJvcXJ0dGpiY21pNyJ9.hK5Wxwby89E7tKWoBoY5bg';
const mapContainer = useRef(null);
let styles = {
'display' : 'none'
}
useEffect(() => {
// dispatch(getBikeInfo())
var map = new mapboxgl.Map({
container: mapContainer.current,
style: 'mapbox://styles/mapbox/light-v10',
center: [-96, 37.8],
zoom: 3
});
map.on('load', function () {
// Add an image to use as a custom marker
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);
// Add a GeoJSON source with 2 points
map.addSource('points', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': dataToDisplay //list of all coordinates along require data
}
});
// Add a symbol layer
map.addLayer({
'id': 'points',
'type': 'symbol',
'source': 'points',
'layout': {
'icon-image': 'custom-marker',
// get the title name from the source's "title" property
'text-field': ['get', 'title'],
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 1.25],
'text-anchor': 'top'
}
});
}
);
});
setDataLoaded(true);
}, [dataToDisplay]);
useEffect(() => {
dispatch(getBikeInfo())
},[])
return (
<div className="district-map-wrapper" style={dataLoaded ? undefined : {display: 'none'}}>
<div id="districtDetailMap" className="map">
<div style={{ height: "100%" }} ref={mapContainer}>
</div>
</div>
</div>
);
}
export default App;
below is sample data to populate
type: "Feature",
geometry: {
type: "Point",
coordinates: [
-74.04281705617905,
40.71458403535893,
]
},
properties: {
title: 'some field'
}
Solution: based on @tylerban's answer I have update my code like this:
map.on('load', function () {
// Add an image to use as a custom marker
loadDataFromPara( dataToDisplay, 'https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png', 'bikeLocation', 'bikePoints', 'bike-marker')
loadDataFromPara( collisionInfo, marker, 'collisionLocation', 'collisionPoints', 'collision-marker')
});
setDataLoaded(true);
function loadDataFromPara( data, image1, sourceName, layerName, imageMarker ){
map.loadImage(
image1,
function (error, image) {
if (error) throw error;
map.addImage(imageMarker, image);
// Add a GeoJSON source with 2 points
map.addSource(sourceName, {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': data
}
});
// Add a symbol layer
map.addLayer({
'id': layerName,
'type': 'symbol',
'source': sourceName,
'icon-allow-overlap': true,
'layout': {
'icon-image': imageMarker,
// get the title name from the source's "title" property
'text-field': ['get', 'title'],
'icon-allow-overlap': true,
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 1.25],
'text-anchor': 'top'
}
});
}
);
}
Upvotes: 1
Views: 1961
Reputation: 516
Are the bike parking points and accident locations contained in the same source (dataset) or are they two different sources and layers that you are adding to the map?
If the bike parking and accidents are contained in the same source, you can use data driven styling in Mapbox to assign an icon based on a field in the data. So if you have a field in the source that can be used to determine if a point represents a bike parking spot or accident you would key off of that.
Here is some pseudo code that illustrates how to do this.
map.addLayer({
id: 'points',
type: 'symbol',
source: 'points',
layout: {
'icon-image': [
'match',
[ 'get', 'type' ], // type corresponds to the field name you are keying off of
[ 'bike parking' ],
'custom-marker',
[ 'pedestrian' ],
'custom-marker-2',
'custom-marker' // fallback icon
],
// get the title name from the source's "title" property
'text-field': [ 'get', 'title' ],
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [ 0, 1.25 ],
'text-anchor': 'top'
}
})
If the bike parking and accidents are contained in two separate sources, you will just need to add a layer for each and assign an icon when adding the layer to the map.
Here is some pseudo code
// Add a bike parking layer
map.addLayer({
'id': 'bike-parking',
'type': 'symbol',
'source': 'bike-parking-source',
'layout': {
'icon-image': 'custom-marker',
// get the title name from the source's "title" property
'text-field': ['get', 'title'],
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 1.25],
'text-anchor': 'top'
}
});
// Add an accidents layer
map.addLayer({
'id': 'accidents',
'type': 'symbol',
'source': 'accidents-source',
'layout': {
'icon-image': 'custom-marker-2',
// get the title name from the source's "title" property
'text-field': ['get', 'title'],
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 1.25],
'text-anchor': 'top'
}
});
I have written a number of in depth posts on working with React and Mapbox and will link to them below in case you find them useful. I am also in the process of writing a Mapbox Developer's Handbook that contains the ins and outs of how to build powerful map driven applications.
Upvotes: 2