I am having some problems with setting an icon for a feature layer. I keep getting layer.setIcon is not a function and similar errors. How can I change the icon style for this layer?
var layer = L.mapbox.featureLayer()
.loadURL(attrs.geoJsonSource)
.addTo(map);
layer.on('ready', function() {
this.eachLayer(function(layer){
layer.setIcon(L.mapbox.marker.icon({
'marker-color': '#8834bb',
'marker-size': 'large',
'marker-symbol': 'restaurant'
}))
});
map.fitBounds(featureLayer.getBounds());
});
You can take a look at https://www.mapbox.com/mapbox.js/example/v1.0.0/custom-marker/ and http://leafletjs.com/examples/custom-icons/ to get more information, but apparently you may fit your need:
using your own icon style. (FIRST)
and/or
using geoJSON file icon style. (SECOND)
The code:
var map = L.mapbox.map('map', 'mapbox.streets').setView([40, -74.50], 9);
var layer = L.mapbox.featureLayer().addTo(map);
layer.on('layeradd', function(e) {
var marker = e.layer,feature = marker.feature;
// TWO POSSIBILITIES
// FIRST // your own method to define how icon will be rendered
marker.setIcon(L.mapbox.marker.icon({
'marker-color': '#8834bb',
'marker-size': 'large',
'marker-symbol': 'restaurant'
}));
// SECOND // use json directly to define how icon will be rendered
//marker.setIcon(L.mapbox.marker.icon(feature.properties.icon));
});
layer.setGeoJSON(geoJson);
assuming the geoJSON file look like this:
var geoJson = [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-75.00, 40]
},
"properties": {
"title": "Small astronaut",
"icon": {
'marker-color': '#0034bb',
'marker-size': 'large',
'marker-symbol': 'restaurant'
}
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-74.00, 40]
},
"properties": {
"title": "Big astronaut",
"icon": {
'marker-color': '#8834bb',
'marker-size': 'large',
'marker-symbol': 'restaurant'
}
}
}];
I am not sure why, but none of the proposed solutions work for me. Instead I have to iterate through the layers of the layer.
layer.on('layeradd', function(e) {
var marker = e.layer, feature = marker.feature;
e.layer.getLayers().forEach(function(marker) {
marker.setIcon(L.mapbox.marker.icon({
'marker-color': '#8834bb',
'marker-size': 'large',
'marker-symbol': 'restaurant'
}));
})
});
You can use the simple style spec to style the geojson. Looks like this needs to happen before you add it to the feature layer. You could try running eachLayer instead of the for loop, then adding that layer to another feature layer, once the geojson has the style/icons you want. This is modified from the original example. Or you could just use the Leaflet pointToLayer function as shown below.
var key = 'your key here'
L.mapbox.accessToken = key;
var map = L.mapbox.map('map')
.setView([37.8, -96], 3);
var geojson = [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-77.031952, 38.913184]
},
properties: {
title: 'Title'
}
}
];
//Option A - set the properties of the geojson using the simple style spec supported in mapbox.js for mapbox feature layers
/*for(i = 0; i < geojson.length; i++) {
geojson[i].properties['marker-color'] = '#63b6e5';
geojson[i].properties['marker-size'] = 'large';
geojson[i].properties['marker-symbol'] = 'rocket';
}*/
//Option B - use the native leaflet function for points - very simple and extendable to other icon plugins
var features = L.geoJson(geojson, {
pointToLayer: function(feature, latlng){
return new L.marker(latlng, {
icon: L.mapbox.marker.icon({
'marker-color': '#00f',
'marker-symbol': 'star'
})
})
}
}).addTo(map);
body { margin:0; padding:0; }
.map { position:absolute; top:0; bottom:0; width:100%; }
<script src="https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.js"></script>
<link href='https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.css' rel='stylesheet' />
<div id='map' class='map'></div>
Related
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"));
I based my map on the mapbox example. Markers there are set to 'circles'. How to add a custom marker by url, in case of the following code?
function makeGeoJSON(csvData) {
csv2geojson.csv2geojson(
csvData,
{
latfield: "Latitude",
lonfield: "Longitude",
delimiter: ","
},
function(err, data) {
data.features.forEach(function(data, i) {
data.properties.id = i;
});
geojsonData = data;
// Add the the layer to the map
map.addLayer({
id: "locationData",
type: "circle",
source: {
type: "geojson",
data: geojsonData
},
paint: {
"circle-radius": 5, // size of circles
"circle-color": "green", // color of circles
"circle-stroke-color": "white",
"circle-stroke-width": 1,
"circle-opacity": 0.7
}
});
}
);
You need to use map.loadImage and map.addImage to add the custom icon, as in this Mapbox example:
map.loadImage('http://placekitten.com/50/50', function(error, image) {
if (error) throw error;
// Add the loaded image to the style's sprite with the ID 'kitten'.
map.addImage('kitten', image);
});
Then you need to use a symbol layer referencing that icon (kitten in this case).
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/
I'm working on a project and I'm using Leaflet. From an object list extracted from the DB, I can currently display markers with custom icons. Now I'm working to display polylines from data I got in DB. With Javascript, I get the list and I arrive to display polylines but they are all blue where I want them to have differents colors depending on a property of the polyline.
var geoJsonFluxMatiere = {
'type': 'FeatureCollection',
'features': []
};
for (indexfluxMatiere = 0; indexfluxMatiere < listeFluxMatiere.length; indexfluxMatiere++) {
var tableauFluxMatiere = {
type: 'Feature',
properties: {
'id': listeFluxMatiere[indexfluxMatiere].idFluxMatiere,
'fluxPrimaire': listeFluxMatiere[indexfluxMatiere].fluxPrimaire
},
geometry: {
'type': 'LineString',
'coordinates': [
[listeFluxMatiere[indexfluxMatiere].posXDepart, listeFluxMatiere[indexfluxMatiere].poxYDepart],
[listeFluxMatiere[indexfluxMatiere].posXArrivee, listeFluxMatiere[indexfluxMatiere].posYArrivee]
]
}
}
geoJsonFluxMatiere['features'].push(tableauFluxMatiere);
}
var layerFluxMatiere = L.geoJson(geoJsonFluxMatiere, {
pointToLayer: function (feature, latlng) {
if(feature.properties.fluxPrimaire == true){
var polylineFluxMatiere = new L.polyline(
feature.geometry.coordinates,
{
color: 'red',
}
);
}else{
var polylineFluxMatiere = new L.polyline(
feature.geometry.coordinates,
{
color: 'green',
}
);
}
return polylineFluxMatiere;
},
}).addTo(map);
Coordinates are ok and the polylines are displayed where they have to but it's like color's parameter is ignored.
Did I do something wrong ?
By the way, I'm sorry for my english if it is not perfect.
Thank you !
Raphaël
When using .geoJSON(), you use the style option to style features. There are two ways to do it, and you will probably want to pass a function to the styles property that performs your check agains fluxPrimaire. Here is an example from the geoJSON Docs:
var states = [{
"type": "Feature",
"properties": { "party": "Republican" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[-104.05, 48.99],
[-97.22, 48.98],
[-96.58, 45.94],
[-104.03, 45.94],
[-104.05, 48.99]
]]
}
}, {
"type": "Feature",
"properties": { "party": "Democrat" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[-109.05, 41.00],
[-102.06, 40.99],
[-102.03, 36.99],
[-109.04, 36.99],
[-109.05, 41.00]
]]
}
}];
L.geoJSON(states, {
//this is where you will perform your check to change the polyline color
style: function (feature) {
switch (feature.properties.party) {
case 'Republican': return { color: "#ff0000" };
case 'Democrat': return { color: "#0000ff" };
}
}
}).addTo(map);
In your case, you can try something like:
var layerFluxMatiere = L.geoJson(geoJsonFluxMatiere, {
style: function (feature) {
if(feature.properties.fluxPrimaire == true){
return { color: '#ff0000' };
}else{
return { color: '#0000ff' };
}
},
}).addTo(map);
The pointToLayer option of Leaflet GeoJSON factory is used only for "Point" type geometries.
For "LineString" (polyline) type, you can use the style option instead. Note that it should directly return style options, not a layer.
does some Leaflet guru has an idea, what's the easiest way to make a CircleMarker draggable in Leaflet v1.0.3?
It's easy to do it for "standard" markers by using the "draggable"-option. But such an option doesn't exist for CircleMarker. I tried it by using several Events, but the problem is, that not the marker is being moved but the underlying map.
Another possibility could be the use of "stopPropagation"-Function (but just for DOMEvents). Or the use of "removeEventParent"... if the "parent" of the CircleMarker is the map and its events?
Regarding to the Documentation there also DOMUtility/Draggable-class. Is this what I need?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Draggable Markers</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.0.3/dist/leaflet.js"></script>
<style>
body {padding: 0; margin: 0;}
html, body, #map {height: 100%;}
</style>
</head>
<body>
<div id="map"></div>
<script>
var layerOsm = new L.TileLayer('https://{s}.api.mapbox.com/v4/mapbox.outdoors/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoicHBldGUiLCJhIjoiY2lsdmE2ZmQ2MDA4OHZxbTZpcmx1emtqbSJ9.Two7SPSaIZysqgOTrrLkRg', {
subdomains: 'ab', maxZoom: 20, noWrap:true, attribution:'Mapbox | OpenStreetMap' });
var map = new L.Map('map').addLayer(layerOsm).setView(new L.LatLng(47.8, 13.0), 14);
L.marker([47.8, 13.0], {draggable:true}).addTo(map);
var circle = L.circleMarker([47.81, 13.01], {radius:30}).addTo(map);
circle.on('mousedown', function () {
map.on('mousemove', function (e) {
circle.setLatLng(e.latlng);
});
});
map.on('mouseup', function(){
map.removeEventListener('mousemove');
})
</script>
</body>
</html>
Leaflet v1.0+ solution:
var marker = L.circleMarker([41.91847, -74.62634]).addTo(map)
// extract trackCursor as a function so this specific
// "mousemove" listener can be removed on "mouseup" versus
// all listeners if we were to use map.off("mousemove")
function trackCursor(evt) {
marker.setLatLng(evt.latlng)
}
marker.on("mousedown", function() {
map.dragging.disable()
map.on("mousemove", trackCursor)
})
map.on("mouseup", function() {
map.dragging.enable()
map.off("mousemove", trackCursor)
})
To make this behaviour more re-useable we could encapsulate it in a function (JS ES6 syntax):
function moveableMarker(map, marker) {
function trackCursor(evt) {
marker.setLatLng(evt.latlng)
}
marker.on("mousedown", () => {
map.dragging.disable()
map.on("mousemove", trackCursor)
})
marker.on("mouseup", () => {
map.dragging.enable()
map.off("mousemove", trackCursor)
})
return marker
}
You can then make a marker draggable / moveable like so:
const moveable = moveableMarker(map, marker)
These examples helped construct the above solution:
Akshay Agrawal's JS Fiddle example
Jedidiah Hurt's Leaflet 1.0 draggable circle
Found another answer at https://github.com/w8r/Leaflet.Path.Drag/
I just added the Leaflet.Path.Drag.js. Now I can read in from my REST service all my sites and move them.
var data = {
"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "Point",
"coordinates": [
-73.7979125, 42.704642
]
},
"type": "Feature",
"properties": {
"popupContent": "This is Point 1. "
},
"id": 51
},
{
"geometry": {
"type": "Point",
"coordinates": [
-73.630371,42.698585
]
},
"type": "Feature",
"properties": {
"popupContent": "This is Point 2. "
},
"id": 52
}
]
};
var map = L.map('map', {editable: true}).setView([43, -74], 8);
var osm=new L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',{
attribution: '© OpenStreetMap //contributors'}).addTo(map);
function onEachFeature(feature, layer) {
var popupContent = feature.properties.popupContent
layer.bindPopup(popupContent);
layer.on('dragend', function(e){
console.log(layer.getLatLng().lat);
console.log(layer.getLatLng().lng);
});
}
var mymarker =L.geoJSON(data, {
style: function (feature) {
return feature.properties && feature.properties.style;
},
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng,{ draggable: true }, {
radius: 8,
fillColor: "#ff7800",
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
}
}).addTo(map);