I am trying to display a polygon from an external geojson file, the data loads but does not update the polygon in real time.
The polygon is added but color is not updated after interval when level changes.
Heres is my code:
L.realtime({
url: 'js/areas.json',
crossOrigin: true,
type: 'json'
}, {
interval: 60 * 1000,
onEachFeature: function (feature, latlng) {
var level = feature.properties.level;
if (level == 0) {
var polygon = L.polygon(latlng._latlngs, {
color: '#51F03B',
opacity: 0.3,
fillOpacity: 0.1
}).addTo(map);
} else if (level == 1) {
var polygon = L.polygon(latlng._latlngs, {
color: '#F43B19',
opacity: 0.3,
fillOpacity: 0.1
}).addTo(map);
}
return polygon;
},
updateFeature: function (feature, oldLayer, newLayer) {
var level = feature.properties.level;
if (!oldLayer) {
return;
}
if (level== 0) {
oldLayer.setStyle({color: '#51F03B'});
} else if (level == 1) {
oldLayer.setStyle({color: '#F43B19'});
}
return oldLayer;
}
});
If i don´t return oldLayer, the polygon color changes but don´t remove the previous polygon.
geoJson file:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"level": 0,
"id": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-75.360297, 6.071571],
[-76.005083, 6.063846],
[-76.051694, 6.511708],
[-75.298149, 6.573451]
]]
}
}]
}
I show markers and more in this way but I don't know if being polygons is different.
The way I worked with "real-time" with polygon was cleaning the previous polygon and creating a new one. With that in mind, you will need to keep track of the layers that you have created (like in an array), a method to clear that layer (or clear all layers, there's a leaflet method for that) and a method to set a timeOut to call an update method.
I say "real-time" because currently, I keep asking back-end for an update using a timeOut function.
first, when you received the geojson draw the polygon, add it to your map and call the setTimeout with your update method.
second, you will need a method to remove the old layer, something like this:
const resetPolygonArray = polygonId => {
myPolygon = polygonArray.filter(polygon => {
if (polygon.id != polygonId) {
return myPolygon
} else {
map_machiney.removeLayer(myPolygon.geojson)
}
})
}
even though you can use that array to store the polygon and the marker related to it, like this structure:
polygonArray.push({
id: polygonId,
geojson: geojson,
marker: marker
})
Related
What I am trying to achieve here is to paint the building when someone searches an address. This can be achieved with map on click event. But it is not workable with geocoder result event. To get the features from building layer I have to pass {x, y} point to the layer which can be achieved with click event. But when I am using geocoder "result" event, it is giving {latitude, longitude} coordinates not {x, y} point. I also tried to convert these coordinates with map.project() but not correctly point. Is there workaround to achieve this? Checkout my code:
const bounds = [
[-97.846976993, 30.167105159], // Southwest coordinates
[-97.751211018, 30.242129961], // Northeast coordinates
];
const map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/smallcrowd/cl07a4926001b15pnu5we767g",
center: [-79.4512, 43.6568],
zoom: 13,
// maxBounds: bounds,
});
// Add the control to the map.
const geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl,
});
map.on("load", function () {
var layers = map.getStyle().layers;
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === "symbol" && layers[i].layout["text-field"]) {
labelLayerId = layers[i].id;
break;
}
}
map.addLayer(
{
id: "3d-buildings",
source: "composite",
"source-layer": "building",
type: "fill",
minzoom: 10,
paint: {
"fill-color": "#aaa",
},
},
labelLayerId
);
map.addSource("currentBuildings", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [],
},
});
map.addLayer(
{
id: "highlight",
source: "currentBuildings",
type: "fill",
minzoom: 15,
paint: {
"fill-color": "#f00",
},
},
labelLayerId
);
// Working perfectly on click, it is painting the address but I do not want the address to be clicked rather it should be painted on searched.
map.on("click", "3d-buildings", (e) => {
map.getSource("currentBuildings").setData({
type: "FeatureCollection",
features: e.features,
});
});
//not working because the 3d-building layer wants input as x,y point which I tried to convert result's coordinates into point but map.project is giving wrong points as compared to mouse clicked points
geocoder.on("result", (e) => {
var coordinates = e.result.geometry.coordinates;
const point = map.project(coordinates);
const selectedFeatures = map.queryRenderedFeatures(point, {
layers: ["3d-buildings"],
});
console.log(point);
map.getSource("currentBuildings").setData({
type: "FeatureCollection",
features: selectedFeatures.features,
});
});
});
Any help will be appreciated !
So if I understand correctly:
User searches for an address
Map zooms to center around the chosen address
Now you want to know what is the screen X/Y coordinate of the chosen address
It's easy: it's exactly in the center of the viewport. So you can do just something like:
const x = map.getContainer().getClientRects()[0].width / 2;
const y = map.getContainer().getClientRects()[0].height / 2;
You will likely have to wait until the map actually finishes moving and the source features have loaded, after step 1. I would use map.once('idle', ...)
I have a response data from a Radar Layer API like this:
{
"Date": "2020-04-18T04:00:05+03:00",
"Source": 2,
"Kml": [
{
"Polygons": [
{
"Polygon": [
{ "Cordinates": [25.8409, 51.6199] },
{ "Cordinates": [25.8341541, 51.619873] },
{ "Cordinates": [25.834177, 51.61238] },
{ "Cordinates": [25.8308582, 51.5936356] },
{ "Cordinates": [25.8275185, 51.5823822] }
....
....
]
}
],
"Color": "#47C247"
},
{
"Polygons": [
{
"Polygon": [
{ "Cordinates": [26.1740189, 50.5239372] },
{ "Cordinates": [26.1841354, 50.5238838] },
{ "Cordinates": [26.1909122, 50.53136] },
{ "Cordinates": [26.1977215, 50.5463562] }
....
....
]
}
],
"Color": "#47C247"
},
...
...
I want to create a Radar map using this data.
I tried to create polygons using each data and created a set interval function to loop through each polygon for 250ms so that it acts as an animation.
setInterval(() => {
deleteAllShape();
// console.log(data);
data.Kml.map((polygons) => {
const shape = polygons.Polygons.map((polygon) => {
const newMapData = [];
polygon.Polygon.map((obj) => {
const path = { lat: obj.Cordinates[0], lng:
obj.Cordinates[1] };
newMapData.push(path);
});
poly = new google.maps.Polygon({
paths: newMapData,
strokeColor: polygons.Color,
strokeOpacity: 0.8,
strokeWeight: 0,
fillColor: polygons.Color,
fillOpacity: 0.35,
draggable: false,
editable: false,
});
poly.setMap(map);
allPolygons.push(poly);
});
});
},250)
function deleteAllShape() {
poly = null;
for (let i = 0; i < allPolygons.length; i++) {
allPolygons[i].setMap(null);
}
}
This is working to an extend. But the problem is the map and the browser slows down and hangs up after creating some polygons.
When I researched on several radar maps (eg: windy.com) :-
I found that they are rendering images on the map. My question is how to create images using above data and create a radar map?
This may not answer your question, as I'm unclear what you're trying to do with images instead of polygons. However, it might speed things up.
Currently you loop over allPolygons every 250ms, preventing all previous polygons from appearing on the map.
Then you draw a new polygon, and add it into allPolygons, so it gets removed on the next iteration in 250ms. That's all fine.
However, as the number of polygons increase, you'll be increasing the size of that for loop each time:
for (let i = 0; i < allPolygons.length; i++) {
So it'll get progressively slower as you draw more polygons. You don't say how many polygons you're adding, but I'd guess it's a lot.
Instead, all you need to do is hide the most recently created polygon. All the previous ones in allPolygons will already have been hidden, so you don't need to call setMap(null) on every polygon, as it's just the most recent one that's not already set to null.
Maybe something like:
function deleteAllShape() {
allPolygons[allPolygons.length - 1].setMap(null);
}
Alternatively, just this, if you don't need allPolygons for anything else, save you having to store them in that array.
poly.setMap(null);
Also, instead of creating poly then calling poly.setMap(map);, just do
poly = new google.maps.Polygon({
paths: newMapData,
strokeColor: polygons.Color,
strokeOpacity: 0.8,
strokeWeight: 0,
fillColor: polygons.Color,
fillOpacity: 0.35,
draggable: false,
editable: false,
map
});
I have a .js file with coordinates for internships:
var internships = [{
"features": [
{"type":"Feature","properties":{"category":"entretient","Name":"green"},"geometry":{"type":"Point","coordinates":[50.807149, 3.162994]}},
{"type":"Feature","properties":{"category":"securité","Name":"blue"},"geometry":{"type":"Point","coordinates":[50.334421, 3.290146]}},
{"type":"Feature","properties":{"category":"secretaria","Name":"red"},"geometry":{"type":"Point","coordinates":[50.744787, 2.256216]}}
]
}];
I've found this bit of code allowing me to create layers depending on a property and here what my JS looks like:
$.getScript("CoordinatesPdC.js");
function mapLoad() {
var sécuritéLayer = new L.LayerGroup();
var secrétariatLayer = new L.LayerGroup();
var entretientLayer = new L.LayerGroup();
var map = L.map('map').setView([50.2910, 2.7775], 8);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, '
}).addTo(map);
var marker = L.marker([50.2910, 2.7775]).addTo(map);
var entretientLayer = L.geoJson(internships, {
filter: function (feature, layer) {
return (feature.properties.category === "entretient");
}
}).addTo(map);
var sécuritéLayer = L.geoJson(internships, {
filter: function (feature, layer) {
return (feature.properties.category === "sécurité");
}
}).addTo(map);
var secrétariatLayer = L.geoJson(internships, {
filter: function (feature, layer) {
return (feature.properties.category === "secrétariat");
}
}).addTo(map);
}
window.onload = mapLoad;
But now I have to create the markes assigned to these layers, how can I achieve that?
Your markers are already assigned to each later. In your example, you create a layer (with all of its markers) and immediately add it to the map using .addTo(map); Here's the code responsible for it.
var sécurité = L.geoJson(internships, {
filter: function (feature, layer) {
return (feature.properties.category === "sécurité");
}
}).addTo(map);
Now, you probably want to only display a certain layer based on user input. If so, I suggest adding the related layer to the map on a click event. Then when the event is triggered a layer is added. Here's the code for doing that. sécurité.addTo(map)
A layer is removed using map.removeLayer(sécurité);
Below is a working example based on your initial code. (I did write it in jQuery as my vanilla JavaScript could be better) You can also view it on jsFiddle here.
I left some comments in the code to explain what each part does. I hope that helps you with your understanding.
var internships = [{
"features": [{
"type": "Feature",
"properties": {
"category": "entretient",
"Name": "green"
},
"geometry": {
"type": "Point",
"coordinates": [3.162994, 50.807149]
}
},
{
"type": "Feature",
"properties": {
"category": "securité",
"Name": "blue"
},
"geometry": {
"type": "Point",
"coordinates": [3.290146, 50.334421]
}
},
{
"type": "Feature",
"properties": {
"category": "secretaria",
"Name": "red"
},
"geometry": {
"type": "Point",
"coordinates": [2.256216, 50.744787]
}
}
]
}];
$(document).ready(function() {
// Create an object to keep track of active layers and each layer with its markers
const layers = {
active: [],
entretientLayer: new L.LayerGroup(),
sécuritéLayer: new L.LayerGroup(),
secrétariatLayer: new L.LayerGroup(),
};
// create the map
var map = L.map('map').setView([50.8010, 3.1675], 6,5);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, '
}).addTo(map);
// based on the category assign a marker to the layer
layers.entretientLayer = L.geoJson(internships, {
filter: function(feature, layer) {
return (feature.properties.category === "entretient");
}
})
layers.sécuritéLayer = L.geoJson(internships, {
filter: function(feature, layer) {
return (feature.properties.category === "securité");
}
})
layers.secrétariatLayer = L.geoJson(internships, {
filter: function(feature, layer) {
return (feature.properties.category === "secretaria");
}
})
// register click event
$('button').on('click', function(e) {
const layerName = e.target.name;
// if a layer is already active, remove it from the map and the active array
if (layers.active.includes(layerName)) {
layers.active = layers.active.filter(layer => layer !== layerName);
map.removeLayer(layers[layerName]);
} else {
// add the layer to the map and to the active array
layers.active.push(layerName);
layers[layerName].addTo(map);
}
});
});
#map {
height: 140px;
width: 100%;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet-src.js"></script>
<div class="button-group">
<button name="entretientLayer">entretient</button>
<button name="sécuritéLayer">sécurité</button>
<button name="secrétariatLayer">secrétariat</button>
</div>
<p></p>
<div id="map"></div>
UPDATE: updated leaflet.js to version 1.3.3.
The difference with the update is that each layer needs to be initialised using the new key word. Code is updated to reflect the change.
I have a geo JSON variable as below:
line_01.js
var lines= {
"type":"FeatureCollection",
"features": [
{
"type": "Feature",
"geometry":{"type":"LineString",
"coordinates":[[103.85909,1.2941],[103.85895,1.2940450000000001],[103.85881,1.29399]]},
"properties": {"id":"01","score":10}
},
....//more 100 lines
]};
So when I click a button I need to replace the variable lines with
line_02.js
var lines= {
"type":"FeatureCollection",
"features": [
{
"type": "Feature",
"geometry":{"type":"LineString",
"coordinates":[[103.8436,1.2893],[103.8890,1.2956],[103.8432,1.2874]]},
"properties": {"id":"03","score":09}
},
..../ the rest lines
]};
So my button click function is
$('#update_map').click(function(){
$("#grid_name").html("SINGAPORE");
updatemap();
});
function updatemap(){
if (geojson) {
geojson.remove();
console.log("removed");
}
//here I have to replace the lines variable to the new one
geojson = L.geoJson(lines, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
}
So which means it will erase the previous line(layer) and replace a new line.is it possible?Any help is appreciated.Thank you.
From the looks of it you want to update the coordinates and properties field when a user clicks a button. If so then you do
function updatemap(){
if (geojson) {
geojson.remove();
console.log("removed");
}
//here I have to replace the lines variable to the new one
if (lines){
// updates lines if necessary
lines["coordinates"] = [New coordinates]
lines["properties"] = {new : score}
}
geojson = L.geoJson(lines, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
}
I have a single GeoJSON FeatureCollection object that contains over 2000 features. In the GeoJSON object, each feature is part of one category like so:
{
"type": "Feature",
"properties": {
"category": "Electrical",
"Name": "Plane No 2"
},
"geometry": {
"type": "Point",
"coordinates": [
94.5703125,
58.722598828043374
]
}
},
{
"type": "Feature",
"properties": {
"category": "Military",
"Name": "Base 1"
},
"geometry": {
"type": "Point",
"coordinates": [
104.4140625,
62.91523303947614
]
}
},
In my actual data, there are a total of about 38 categories (each feature is only assigned to one category).
Is using a JavaScript Switch Statement in my situation a practical solution in order to give each point its own styling? Or, is there a better way?
I am doing something like this in my code:
L.geoJson(mygeoJson, {
onEachFeature: function (feature, layer){
layer.bindPopup(L.Util.template(popupTemplate, feature.properties));
},
pointToLayer: function (feature, latlng){
return L.circleMarker(latlng, gjsonOptions);
},
// 3 of the 38 categories are listed here as an example
style: function(feature){
switch(feature.properties.category){
case 'Electrical': return { color: '#fb8072'};
case 'Military': return { color: '#b3de69'};
case 'Aviation': return { color: '#80b1d3'};
}
}
}).addTo(map);
Demo link here
I think one should add the colors on the clientside, just as he/she did in the code example. adding the colors to each GeoJSON feature will needlessly bloat your transfer. If you really want to add them to your collection you could create some sort of legend property in your collection object like so:
var collection = {
"type": "FeatureCollection",
"properties": {
"legend": {
"Electrical": "#fb8072",
"Military": "#b3de69",
"Aviation": "#80b1d3"
}
}
"features": [...]
}
So that when you create your GeoJSON layer you can add them on the fly:
L.geoJson(collection, {
'style': function (feature) {
return {
'color': collection.properties.legend[feature.properties.category]
}
}
}).addTo(map);
You could instead of storing the legend in the collection object, store it in your code/script somewhere:
var legend = {
"Electrical": "#fb8072",
"Military": "#b3de69",
"Aviation": "#80b1d3"
}
L.geoJson(collection, {
'style': function (feature) {
return {
'color': legend[feature.properties.category]
}
}
}).addTo(map);
Edit after comments:
If you need to set L.Marker icons you should use the pointToLayer function:
L.geoJson(collection, {
'pointToLayer': function (feature, latlng) {
return new L.Marker(latlng, {
'icon': new L.Icon({
'iconUrl': 'icons/' + feature.properties.category + '.png'
...
})
})
}
}).addTo(map);
You're currently using L.CircleMarker which doesn't support the icon option. It's a path which only supports the pathoptions:
http://leafletjs.com/reference.html#path-options
Here's a nice tutorial on creating L.Marker's with custom icons:
http://leafletjs.com/examples/custom-icons.html