I have little Leaflet application where the app get geoJson objects from server, and display it, specially LineString. The JSON parser that i use on server side works properly. And the client side script was ok too.
But some reasons I would like to draw arrow on the routes, and I can't figure out how to do it when using L.geoJson().
Code with L.geoJson():
getJsonFrom(routeQueryURL, params, function(data) {
var a = L.geoJson(data, {
onEachFeature: bindRouteDirection,
}).addTo(map);
});
Because I don't want to change anything on server side, I tried this:
getJsonFrom(routeQueryURL, param, function(data) {
$.each(data, function(index, feature) {
var polyline = new L.Polyline(feature.geometry.coordinates, {
color: feature.properties.color,
opacity: 0.8
}).addTo(routeMapLayer);
var decorator = L.polylineDecorator(polyline, {
patterns: [{
offset: 25,
repeat: 50,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
pathOptions: {
stroke: true,
color: feature.properties.color,
fillOpacity: 0.8,
polygon: false,
weight: 3
}
})
}]
}).addTo(routeMapLayer);
map.addLayer(routeMapLayer);
});
});
So i access the array of coordinates from the geoJson object, and some other data, and draw the polyline directly on to map.The problem is that it's put my route into the middle of middle east instead of Hungary, so it's actually swap the coordinates. Why does L.Polyline handle the different form L.geoJson()?
Use L.GeoJSON.coordsToLatLng() and read why sometimes coordinates are lat-lng and sometimes lng-lat.
Related
Using Open Layers and leaflet-sidebar-v2, I've added the sidebar to my map, this works. However, I also need to add another layer to my map, this layer will outline each country. I have the coordinates stored in a 'borders.json' file. I'm attempting to use D3.json to to import the border coordinates and then L.geoJson to add the new layer to my map.
I'm currently getting the following error message:
Uncaught TypeError: t.getLayerStatesArray is not a function
Here is the relevant part of my code..
var map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM(),
}),
],
view: new ol.View({
center: ol.proj.transform([7, 51.2], "EPSG:4326", "EPSG:3857"),
zoom: 3,
}),
});
var sidebar = new ol.control.Sidebar({
element: "sidebar",
position: "left",
});
map.addControl(sidebar);
d3.json(("borders.json"), function (json){
function style(feature) {
return {
fillColor: "transparent",
weight: 1,
opacity: 0.4,
color: 'grey',
fillOpacity: 0.3
}
}
geojson = L.geoJson(json, {
style: style,
}).addTo(map);
})
I think I might be adding the geojson layer to my map incorrectly, but I can't figure out what is wrong. I've spent quite a bit of time playing with it, but no luck.
Any helps is appreciated.
Cheers,
Beat
It might be hard to tell what the problem is without knowing other possible relevant parts of your code. I'd start by checking that the contents of borders.json follows valid GeoJSON format.
This is likely unrelated to your question, but is there a reason that you've declared style as a function like function style(feature) { ... }?
It looks like the style attribute of L.geoJson accepts an object rather than a function.
Ive been trying to create a Mapbox plot like this one: https://docs.mapbox.com/mapbox-gl-js/example/data-driven-circle-colors/) using javascript.
Im trying to create this using data from psql (with feature columns including lat and long) passed through a Python (flask) backend into my html file. I can get the linked plot to work, but can't see how to get my own data in there. Ive tried map.addSource with a GeoJSON file of features but I just can't get it to work. I feel like if I could replicate the data in the link they use I could plot it but I can't see the data!
Any ideas would be great! Im new to JS so the although ive read the documentation I might be missing bits.
Here is the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Style circles with a data-driven property</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = '<your access token here>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
zoom: 12,
center: [-122.447303, 37.753574]
});
map.on('load', function() {
/* Sample feature from the `examples.8fgz4egr` tileset:
{
"type": "Feature",
"properties": {
"ethnicity": "White"
},
"geometry": {
"type": "Point",
"coordinates": [ -122.447303, 37.753574 ]
}
}
*/
map.addSource('ethnicity', {
type: 'vector',
url: 'mapbox://examples.8fgz4egr'
});
map.addLayer({
'id': 'population',
'type': 'circle',
'source': 'ethnicity',
'source-layer': 'sf2010',
'paint': {
// make circles larger as the user zooms from z12 to z22
'circle-radius': {
'base': 1.75,
'stops': [
[12, 2],
[22, 180]
]
},
// color circles by ethnicity, using a match expression
// https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-match
'circle-color': [
'match', ['get', 'ethnicity'],
'White',
'#fbb03b',
'Black',
'#223b53',
'Hispanic',
'#e55e5e',
'Asian',
'#3bb2d0',
/* other */
'#ccc'
]
}
});
});
</script>
</body>
</html>
Mapbox GL JS supports GeoJSON source or Vector source to show vector data. i.e there's no other choice to show the data. Here's some options.
Vector tileset
Generating Vector tileset is a bit heavy operation but it is a good choice for client performance
1-1. Host on Mapbox: Managing vector tileset hosted on Mapbox server by using MTS.
1-2. Host on your server: Creating vector tileset on your server by using tippecanoe tool and hosting on your server.
GeoJSON
In general, you will create an endpoint that returns full GeoJSON generated from the database. If the size of GeoJSON is large, it is a nice idea to implement endpoint that takes coordinate and returns nearby data.
In this case, I ended up using geojson in Python to create the json file from the SQLAlchemy data and then passed that from the back end and passed it through to the html file using jinja2.
Then I did:
var data = JSON.parse(‘{{ json_data | tojson }}’)
And then sourced this data into my map!!!
Took a bit of playing around but this seems the smoothest way to get the SQLAlchemy data onto mapbox. Things might get slower when working with more than 200 rows of data But it’s a good solution for now!
I want to add clustering to my map from mapbox, I followed the getting started as they suggest:
https://www.mapbox.com/install/js/cdn-add/
And now I have a map, I also added some of my own markers, which are users from my backend that have lats and longs.
So I have a map and some markers.
Now, I want to add clustering, and in the example:
https://docs.mapbox.com/mapbox-gl-js/example/cluster/
they add a source:
map.addSource("earthquakes", {
})
which I don't have, and at this moment I don't think I even need. because I can see the tiles and my markers.
I thought about adding a source, but which source? I don't need a source, I already have what I needed, the tiles and the markers... but the cluster option is in the addSource method on the map. So.. I'm lost here.
This is what they show in the example, but as I said, I don't have or need any other source, as I already see the tiles and my markers, I just want to cluster.
map.addSource("earthquakes", {
type: "geojson",
data: "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson",
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
});```
I found the solution,
I had to create a geoJson object from my users like this:
const geoJsonMarkers = users.map( (place, i ) => {
const [placeLng, placeLat] = place.location.coordinates;
const position = { lat: placeLat, lng: placeLng };
return {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [position.lng, position.lat]
},
"properties": {
"name": place.name
}
}
})
const usersArray = {
"features": geoJsonMarkers
}
And then pass it to the data property of the source like so:
map.on('load', function() {
// Add a new source from our GeoJSON data and set the
// 'cluster' option to true. GL-JS will add the point_count property to your source data.
map.addSource("users", {
type: "geojson",
// Point to GeoJSON data. This example visualizes all M1.0+ earthquakes
// from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program.
data: usersArray,
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
});
...
the addSource name is arbitrary, could be anything in my case are users so I called it users.
I am trying to add a custom marker in React Google Maps and have each marker rotate based on the direction that the item is heading on the road.
I am able to make the rotation work when I use path for an svg instead of a url referencing an image. The problem with path is that I cannot get fillColor to work, only strokeColor. It would also be nice to have a complex shape and path is somewhat restricting in the type of shape that you can add to it, because cannot pass multiple paths into the string.
Please note that heading is getting the proper direction via my api and I have verified that it works.
Here is my current code, any help is appreciated. Thanks!
const createIcon = (heading) => ({
width: '15px',
url:
'my image path',
rotation: heading || 0, // set the rotation prop to indicate direction
});
Do you have a <\Marker> component being rendered? This is what I was able to get working for rotating a Marker. No issues with fillColor either.
render() {
return <Marker
position={{ lat: this.state.longitude, lng: this.state.latitude }}
icon={
{
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
scale: 3,
strokeColor: "#FFFFFF",
fillColor: "#0000FF",
fillOpacity: 1,
rotation: 215
}
}
/>
}
I am using the MarkerCluster.js to create clustering in my google maps api. The clusters work how I want them however I want to style differently than yellow, blue and red circles. I was trying to use the MarkerStyleOptions and it says you have an array of styles with the smallest cluster icon first and the biggest last. I tried to create this below but I am getting really confused about what syntax to use and can't find any good examples.
var clusterStyles = [
[opt_textColor: 'white'],
[opt_textColor: 'white'],
[opt_textColor: 'white']
];
var mcOptions = {
gridSize: 50,
styles: clusterStyles,
maxZoom: 15
};
var markerclusterer = new MarkerClusterer(map, cluster, mcOptions);
What you need to do is use the url to specify which images to use instead of the blue/yellow/red images currently being used. And probably a good idea to include the height and width options too.
var clusterStyles = [
{
textColor: 'white',
url: 'path/to/smallclusterimage.png',
height: 50,
width: 50
},
{
textColor: 'white',
url: 'path/to/mediumclusterimage.png',
height: 50,
width: 50
},
{
textColor: 'white',
url: 'path/to/largeclusterimage.png',
height: 50,
width: 50
}
];
It's never too late to post a rather helpful answer, so additionally you can look through the whole MarkerClusterer Documentation for IconStyle
UPDATE
There's also google maps v3 utility on github as stated by ehcanadian
According to latest docs, renderer does the trick. It allows you to use a Marker w/all its styling options - see Marker documentation:
new MarkerClusterer({
renderer: {
render: ({ markers, _position: position }) => {
//here is where you return a Marker
//and style it w/custom label/icon props
return new google.maps.Marker({
position: {
lat: position.lat(),
lng: position.lng(),
},
label: String(markers.length),
});
},
},
...etc
});
You can now pass your own renderer to your MarkerClusterer Object which returns a google.maps.Marker like this:
const renderer = {
render({ count, position }) {
return new google.maps.Marker({
label: { text: String(count), color: "white", fontSize: "10px" },
position,
// adjust zIndex to be above other markers
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
});
}
}
// use this if you use NPM version
const markerCluster = new MarkerClusterer({ map, markers, renderer });
// Use this if you use the CDN version
// const markerCluster = new markerClusterer.MarkerClusterer({ markers, map , renderer});
Inside the render method, you can customize the cluster maker the same way you would customize normal marker. See https://developers.google.com/maps/documentation/javascript/custom-markers for more details. For example, if you just want to add a specific icon.png you could do it like this:
const icon = {
url: '/path/to/your/icon.png',
scaledSize: new google.maps.Size(50, 50),
};
const renderer = {
render({ count, position }) {
return new google.maps.Marker({
label: { text: String(count), color: "white", fontSize: "10px" },
position,
icon,
// adjust zIndex to be above other markers
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
});
}
}
You can also find some clustor marker render example at https://googlemaps.github.io/js-markerclusterer/public/renderers/
See https://developers.google.com/maps/documentation/javascript/marker-clustering for a full example how to setup MarkerCluster.
History Details:
The MarkerCluster library has been rewritten in 2021. One goal was to change the render process. The cluster icon itself is now a google.maps.Marker and can thus be styled just as normal marker. The mcOptions in the constructor is gone.
The proposed design is to encapsulate the logic for clustering and rendering into interfaces that the developer can extend specifically algorithm and renderer. The algorithm computes the clusters and the renderer generates a google.maps.Marker to represent clusters instead of using a google.maps.OverlayView.
https://github.com/googlemaps/js-markerclustererplus/issues/300
https://github.com/googlemaps/js-markerclustererplus/issues/293
https://docs.google.com/document/d/1X8hQaHdVvpB-12bOQ_qbgfGFet8gPRTtHyO_EihcpsA/edit