I am using leaflet and mapbox and I use the function to print the id nuber on the screen when hovering over the line in the map.
geojson = L.geoJson(lines, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = '<h4><b>Links<b></h4>' + (props ?
'<b>Link' + props.id + '</b><br />'
: 'Hover over a link');
};
info.addTo(map);
The way I draw the line is by the following geoJSON file
var lines = {
"type": "FeatureCollection",
"features": [{
"type": "LineString",
"coordinates": [
[103.85, 1.28992],
[103.89, 1.294],
[103.83, 1.216]
],
"properties": { "id": "1" }
}, {
"type": "LineString",
"properties": { "id": "2" },
"coordinates": [
[103.5, 1.292],
[103.9, 1.4],
[103.3, 1.21]
]
}, {
"type": "LineString",
"properties": { "id": "3" },
"coordinates": [
[103.6, 1.291],
[103.6, 1.39],
[103.3, 1.29]
]
}]
};
but when i hover over the line it shows "undefined" instead of showing the id.Any help is appreciated
Where is the declaration of your onEachFeature? Where is update called from? Your code is incomplete.
Besides, your GeoJSON is non-conformant to the GeoJSON specifications. A FeatureCollection must contain Features, and each Feature must have a Geometry.
So instead of:
{
"type": "LineString",
"properties": { "id": "3" },
"coordinates": [[103.6,1.291],[103.6,1.39],[103.3,1.29]]
}
it should be something like:
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [[103.6,1.291],[103.6,1.39],[103.3,1.29]]
},
"properties": {
"id": "3"
},
}
Related
I have GeoJSON data that contains URLs. Not all of the features have url data. I have a pop up which shows the name and a link to the url. I'd like to be able to only show the link to URL when the feature URL is not null but will always show the name as a minimum. My code is below:
const tackleshop_point = {
"type": "FeatureCollection",
"name": "tackleshop",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [{
"type": "Feature",
"properties": {
"gid": 1,
"name": "test 1",
"url": "www.google.com"
},
"geometry": {
"type": "Point",
"coordinates": [-2.284362363619518, 50.983444094390933]
}
},
{
"type": "Feature",
"properties": {
"gid": 7,
"name": "test 2",
"url": null
},
"geometry": {
"type": "Point",
"coordinates": [-2.283893608524902, 50.981411456895998]
}
}
]
}
const tackleshop = L.geoJSON(tackleshop_point, {}).bindPopup(function(layer) {
let cap_name = layer.feature.properties.name.replace(/(^\w{1})|(\s+\w{1})/g, letter => letter.toUpperCase());
return `<p>${cap_name}</p><a href="http://${layer.feature.properties.url}" target="_blank">View<a>`
/******/
;
}).addTo(map);
Instead of using the bindPopup method with a function, which finds out too late that the feature does not have a URL to show, in which case you actually want no popup, you can leverage the onEachFeature option of the L.geoJSON factory to attach a popup conditionally:
A Function that will be called once for each created Feature, after it has been created and styled. Useful for attaching events and popups to features.
const tackleshop = L.geoJSON(tackleshop_point, {
onEachFeature(feature, layer) {
const url = feature.properties.url;
if (url) { // Attach the popup only when the url is specified
layer.bindPopup(`<a href="http://${url}">View<a>`);
}
}
}).addTo(map);
I am loading markers from a JSON file (located here):
map.data.loadGeoJson('https://api.myjson.com/bins/tz9ze');
What I am trying to achieve:
load name from the json node properties and display it as a title.
load icon from the json node properties and display it as the marker icon.
How can i achieve that?
Use a Style function (from the documentation):
Declarative style rules
If you want to update the style of a large number of overlays, such as markers or polylines, you typically have to iterate through each overlay on your map and set its style individually. With the Data layer, you can set rules declaratively and they will be applied across your entire data set. When either the data, or the rules, are updated, the styling will be automatically applied to every feature. You can use a features properties to customize its style.
Like this:
map.data.setStyle(function(feature) {
return {
icon: feature.getProperty("icon"),
title: feature.getProperty("name")
}
});
proof of concept fiddle
JSON data:
//JSON file content:
var geoJson = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [35.29182, 32.917633]
},
"properties": {
"name": "Adrian",
"icon": "http://maps.google.com/mapfiles/ms/micons/mechanic.png"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [35.0611, 33.2815]
},
"properties": {
"name": "Chase",
"icon": "http://maps.google.com/mapfiles/ms/micons/mechanic.png"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [34.8621, 33.0613]
},
"properties": {
"name": "Lincoln",
"icon": "http://maps.google.com/mapfiles/ms/micons/mechanic.png"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [35.1551, 33.2527]
},
"properties": {
"name": "Jayden",
"icon": "http://maps.google.com/mapfiles/ms/micons/mechanic.png"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [34.9047, 33.0816]
},
"properties": {
"name": "Cameron",
"icon": "http://maps.google.com/mapfiles/ms/micons/mechanic.png"
}
}]
};
code snippet:
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 8,
center: {
lat: 33.2815,
lng: 35.0611
}
});
// Load GeoJSON.
map.data.loadGeoJson('https://api.myjson.com/bins/tz9ze');
map.data.setStyle(function(feature) {
return {
icon: feature.getProperty("icon"),
title: feature.getProperty("name")
}
});
}
html,
body,
#map {
height: 100%;
margin: 0;
padding: 0;
}
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>
I am trying to move the element height of an entity that is loaded from a geojson file in cesium.js. I'm using the example App.js file from Cesium Workshop/
How would I modify the code below to adjust the height of the data entities so that they are pinned to the elevation of the terrain. The geojson file that I have has an altitude value of 0. This altitude needs to match the terrain at that location. When I render this data many of the points are under the terrain. I assume I also need to lookup the terrain height, convert the data to Cartesian coordinated back and forth to polar coordinates to add the offset into the entity.
var geojsonOptions = {
clampToGround : true
};
var testLinePromise = Cesium.GeoJsonDataSource.load('./Source/SampleData/testLines.geojson', geojsonOptions);
// Save an new entity collection of neighborhood data
var testLines;
testLinePromise.then(function(dataSource) {
// Add the new data as entities to the viewer
viewer.dataSources.add(dataSource);
testLines = dataSource.entities;
// Get the array of entities
var testLineEntities = dataSource.entities.values;
for (var i = 0; i < testLineEntities.length; i++) {
var entity = testLineEntities[i];
if (Cesium.defined(entity.polygon)) {
// Tells the polygon to color the terrain. ClassificationType.CESIUM_3D_TILE will color the 3D tileset, and ClassificationType.BOTH will color both the 3d tiles and terrain (BOTH is the default)
entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN;
// Generate Polygon center
var polyPositions = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
var polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions).center;
polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter);
entity.position = polyCenter;
// Generate labels
entity.label = {
text : entity.name,
showBackground : true,
scale : 0.6,
horizontalOrigin : Cesium.HorizontalOrigin.CENTER,
verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
distanceDisplayCondition : new Cesium.DistanceDisplayCondition(10.0, 8000.0),
disableDepthTestDistance : 100.0
};
}
}
});
Here is the geojson file
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "623dd9c1-26cb-4248-8e41-ebc8c3a16af5",
"description": "descr A",
"company": "ACME"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-114.5305072,
20.61238237,
0
],
[
-111.9486,
10.46916111,
0
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "15966b86-26d0-4e0b-ba6e-7fa0c98bfe5e",
"description": "descr C",
"company": "ACME"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-135.9989702,
45.7677603,
0
],
[
-134.5305072,
50.61238237,
0
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "00ca37f5-de78-4d5b-88b5-d39808ff8143",
"description": "descr D",
"company": "ACME"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-125.9989702,
65.7677603,
0
],
[
-129.0238961,
67.96685,
0
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "22e5e02f-fa43-445e-a2eb-29ad760a964c",
"description": "descr E",
"company": "ACME"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-105.9989702,
55.7677603,
0
],
[
-100.7366501,
55.67798889,
0
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "c4aefe46-a2a6-4a97-ac36-571c9e370d31",
"description": "descr A",
"company": "ACME"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-111.994653,
78.84398434,
0
],
[
-128.4816326,
16.68839404,
0
]
]
}
}
]
}
I have a map with Geojson polygons where each polygon is identified by a unique ID. I have a separate Geojson of point markers where each point is associated with a polygon with the same polygon ID, as well as a unique identifier for each individual point.
The initial map display is of only polygons. Each polygon click will display the points that are associated with the clicked polygon. When a new polygon is clicked, I want to plot the points associated with the new polygon while simultaneously removing the previously plotted points. So each time the user clicks a polygon, ONLY points within that polygon are ever displayed.
Here is some minimally reproducible code: https://jsfiddle.net/3v7hd2vx/1146/
var polys = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"polygon": "one"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-87.33032226562499,
37.90953361677018
],
[
-85.374755859375,
37.90953361677018
],
[
-85.374755859375,
39.036252959636606
],
[
-87.33032226562499,
39.036252959636606
],
[
-87.33032226562499,
37.90953361677018
]
]
]
}
},
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"polygon": "two"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-85.3857421875,
37.90953361677018
],
[
-83.3642578125,
37.90953361677018
],
[
-83.3642578125,
39.04478604850143
],
[
-85.3857421875,
39.04478604850143
],
[
-85.3857421875,
37.90953361677018
]
]
]
}
},
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"polygon": "three"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-87.34130859375,
36.90597988519294
],
[
-83.3642578125,
36.90597988519294
],
[
-83.3642578125,
37.91820111976663
],
[
-87.34130859375,
37.91820111976663
],
[
-87.34130859375,
36.90597988519294
]
]
]
}
}
]
};
var pts = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "one",
"animal": "bear"
},
"geometry": {
"type": "Point",
"coordinates": [
-86.72607421875,
38.77121637244273
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "one",
"animal": "fish"
},
"geometry": {
"type": "Point",
"coordinates": [
-86.583251953125,
38.487994609214795
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "two",
"animal": "tiger"
},
"geometry": {
"type": "Point",
"coordinates": [
-84.276123046875,
38.38472766885085
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "two",
"animal": "lizard"
},
"geometry": {
"type": "Point",
"coordinates": [
-85.067138671875,
38.70265930723801
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "three",
"animal": "dog"
},
"geometry": {
"type": "Point",
"coordinates": [
-83.880615234375,
37.483576550426996
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "three",
"animal": "horse"
},
"geometry": {
"type": "Point",
"coordinates": [
-85.93505859374999,
37.709899354855125
]
}
}
]
};
var map = L.map("map", {
center: [37.5, -85],
zoom: 7
});
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png").addTo(map);
var defaultStyle = {
fillColor: "whitesmoke",
color: "#171717",
fillOpacity: 1,
weight: 1
};
// create polygon geojson, add to map
var polyJson = L.geoJson(polys, defaultStyle).addTo(map);
// empty layer group to add markers to
var layerGroup = L.layerGroup();
// loop through polygon layers for events
polyJson.eachLayer(function(layer){
// zoom to bbox on each polygon click
layer.on("click", function(e){
var bbox = e.target.getBounds();
var sw = bbox.getSouthWest();
var ne = bbox.getNorthEast();
map.fitBounds([sw, ne]);
// the clicked polygon identifier
var clickedPoly = e.target.feature.properties.polygon;
// pts added to map when they match the clickedpoly id
L.geoJson(pts, {
pointToLayer: function(feature, latlng){
if(feature.properties.id === clickedPoly){
var clicked = L.circleMarker(latlng, {
color: "red"
}).addTo(map).addTo(layerGroup);
}
}
});
});
});
polyJson.on("click", function(){
if(map.hasLayer(layerGroup)){
map.removeLayer(layerGroup);
}else{
map.addLayer(layerGroup);
//layerGroup.removeLayers();
}
});
In this example, the entire layerGroup of points is removed every other click based on the conditional statement at the end of the code block. But each new click continues to append markers to the layerGroup, so ALL selected markers are displayed every other click.
My inclination is to have each "group" of markers named by the polygon that they lie within, so when displayed markers do not match the currently clicked polygon ID, they will be removed both from the map AND from the layerGroup. I've worked extensively with Leaflet in R, but I'm not sure how to accomplish this in JS (or if the logic is even the same in the different languages). I can't manually define each layer because I'll be iterating through many dynamically loaded polygons and points.
One simple way to fill your layer with teh desired points would be to:
make your layerGroup a permanent layer,
empty this layer when the user clicks on a polygon with layerGroup.clearLayers()
fill layerGroup with the valid markers (determined by the filter property) and let Leaflet handle the markers without interfering with pointToLayer.
Basically, simplify things:).
For example:
layer.on("click", function(e){
var bbox = e.target.getBounds();
var sw = bbox.getSouthWest();
var ne = bbox.getNorthEast();
map.fitBounds([sw, ne]);
// the clicked polygon identifier
var clickedPoly = e.target.feature.properties.polygon;
layerGroup.clearLayers();
layerGroup.addLayer(L.geoJson(pts, {
pointToLayer: function(feature, latlng){
return L.circleMarker(latlng, {
color: "red"
});
},
filter: function (feature) {
return feature.properties.id === clickedPoly;
}
}));
});
And a demo https://jsfiddle.net/3v7hd2vx/1147/
I'm trying to change geojson marker icon on click following various exemple found on the web but I can't get it to work, it raise this error message:
TypeError: e.target.setIcon is not a function
Bellow the snippet
var map = L.map('map',{ center: [44.1, 3.5], zoom: 10});
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: 'OpenStreetMap'}).addTo(map);
var icon = L.icon({
iconUrl: 'https://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/256/Map-Marker-Marker-Outside-Azure.png',
iconSize: [ 48, 48 ],
});
var data = {
"type": "FeatureCollection",
"name": "test",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "NUM": 1 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 3.75, 44.25 ] ] } },
{ "type": "Feature", "properties": { "NUM": 2 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 3.25, 44.0 ] ] } }
]}
L.geoJson(data,{
pointToLayer: function (feature, latlng) {
return L.marker(latlng);//, {icon: icon});
},
onEachFeature: function (feature, layer) {
layer.on('click', function (e){
e.target.setIcon(icon);
})
}
}).addTo(map);
what's wrong?
The issue is originating from the GeoJSON. It does seem to be a valid GeoJSON because it plots on the map, but it must be tripping up Leaflet somehow. I think the fact that it's a MultiPoint feature is the cause.
Changing the GeoJSON to:
var data = {
"type": "FeatureCollection",
"name": "test",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "NUM": 1 }, "geometry": { "type": "Point", "coordinates": [ 3.75, 44.25 ] } },
{ "type": "Feature", "properties": { "NUM": 2 }, "geometry": { "type": "Point", "coordinates": [ 3.25, 44.0 ] } }
]}
makes it work. (Note: I just changed "type" from MultiPoint to Point and removed the double brackets, i.e. [ [ 3.25, 44.0 ] ] became[ 3.25, 44.0 ].)
I'm not sure if it's possible to use MultiPoint geometry with the onEachFeature function, but it seems to be working with single point geometry.