Cannot "stringify" geoJSON data - javascript

Using Leaflet JavaScript I am trying to pull data directly from GeoServer using an Ajax link. In order to put it nicely in a DataTables table, I need to JSON.stringify it per DataTables instructions. I get a "Circular structure". Is there any other way to do this?
Here is my code:
Get Selected Features from GeoServer via Ajax
function handleJson(data) {
selectedFeature = L.geoJson(data, {
onEachFeature: function (feature, layer) {
},
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, {
radius: 5,
color: '#3006e8',
weight: 5,
opacity: 100,
fillOpacity: 100
});
}
});
selectedFeature.addTo(drawnItems);
Now here is where I would idealy use JSON.stringify to achieve these results provide by a very helpful person over at datatables.net....
http://live.datatables.net/sokitihe/3/edit
I think var selectedFeature would be the data to JSON.stringify correct? I have tried that and it didn't work.

You can export the GeoJSON LayerGroup directly into a FeatureCollection using:
selectedGeoJSON = selectedFeature.toGeoJSON();
Then you can do what you want with it, stringify it if you wish.
JSON.stringify(selectedGeoJSON);

#Ethan McCoy 's answer seems resolved my problem. My code like the following:
const leaflet = require('leaflet')
leaflet.then(L => {
// response is geojson
const data=L.geoJson(response).toGeoJSON()
return JSON.stringify(data)
})

Related

Toggle GeoJSON layer in leaflet sidebar

How can I toggle the GeoJSON layers in my leaflet map as I would the L.marker layers?
https://jsfiddle.net/mawr35vj/
Please pardon me if this is a simple question, I'm still new to leaflet and have spent all day on this.
Here is the GeoJSON I would like toggled in the sidebar.
fetch('https://data.cityofnewyork.us/resource/ek5y-zcyf.geojson?$where=latitude is not null')
.then(function (response) {
// Read data as JSON
return response.json();
})
.then(function (data2) {
// Create the Leaflet layer for the data
var complaintLayer = L.geoJson(data2, {
// We make the points circles instead of markers so we can style them
pointToLayer: function (geoJsonPoint, latlng) {
return L.circleMarker(latlng);
},
// Then we can style them as we would other features
style: function (geoJsonFeature) {
return {
fillColor: '#0000ff',
radius: 6,
fillOpacity: 0.7,
stroke: false
};
}
});
});
-I tried assigning it a "var"
-I tried adding "complaintLayer" in the overlays as I did with the L.marker
-And many other various things that I can't remember but is obviously not working...
Update:
I'm trying to load the GeoJSON and assign it a variable, but having difficulty. I'm looking at this and related threads: How can I assign the contents of a geojson file to a variable in Javascript?
I got it to work if I just copy and paste the GeoJSON into the script, but having difficulty if I want to load it from a local file or API.
You can put complaintLayer in an array for a marker control, but the variable must be in the right scope - from the code you've posted it looks like it's local to the function it's populated in, so it won't be visible outside.
Per peeebeee's suggestion, I fixed the issue by loading the data and putting them into a "promise."
you can see a working example below:
https://jsfiddle.net/4x3sk1va/
Example of a promise below (taken from https://glitch.com/#ebrelsford)
// Fetch collisions data from our Glitch project
var collisionsFetch = fetch('https://cdn.glitch.com/03830164-a70e-47de-a9a1-ad757904d618%2Fpratt-collisions.geojson?1538625759015')
.then(function (response) {
// Read data as JSON
return response.json();
});
// Fetch lanes data from our Glitch project
var lanesFetch = fetch('https://cdn.glitch.com/fcedf615-7fef-4396-aa74-2e03fc324d71%2Fpratt-bike-routes.geojson?1538628711035')
.then(function (response) {
// Read data as JSON
return response.json();
});
// Once both have loaded, do some work with them
Promise.all([collisionsFetch, lanesFetch])
.then(function (fetchedData) {
console.log('Both datasets have loaded');
// Unpack the data from the Promise
var collisionsData = fetchedData[0];
var laneData = fetchedData[1];
// Add data in the order you want--first goes on the bottom
L.geoJson(collisionsData).addTo(map);
L.geoJson(laneData).addTo(map);
});

Leaflet throwing "Uncaught TypeError: Cannot read property 'lat' of undefined" when adding new marker to map

I'm trying to loop through the results of an ajax call to create markers for sensors on click. For now, I just want the markers to show up when I click a button. This is not working. After debugging, it might be an issue with the timing / concurrency issue, but previous, similar issues resolved it using window.SetTimeOut(). This did not work for me. I'm including this code below and screenshots of the error messages:
Screen Shot of Leaflet where latlng object is null
Screen shot of error in console
This is my code
function sensorLayer(response) {
response.forEach(function(item) {
if (item["Latitude"] !== null && item["Longitude"] !== null) {
window.setTimeout(() => {
var mark = new L.marker((parseFloat(item["Latitude"]), parseFloat(item["Longitude"])))
.addTo(map);
}, 100);
}
});
}
Here's the call:
document.getElementById("sensorSwitch").addEventListener("click", function(){ $.ajax({
url: ' ',//I deleted this
data: {
db: ' ',
q: "SELECT MEAN(\"pm2.5 (ug/m^3)\") from airQuality where time >='2017-09-06T00:00:00Z'"
},
success: function (response){
console.log(response); });
Any help is appreciated! It won't let me add more that 2 links, otherwise I'd include an example of the data that I get back (oversharing isn't caring, I suppose).
You probably want to instantiate an L.latLng with your coordinates, or pass them as an array, instead of wrapping your 2 coordinates with normal parenthesis (lat, lng):
var mark = L.marker(
L.latLng(
parseFloat(item["Latitude"]),
parseFloat(item["Longitude"])
)
);
or:
var mark = L.marker([
parseFloat(item["Latitude"]),
parseFloat(item["Longitude"])
]);
BTW, you should definitely avoid using an SQL query originated from Client code. This is a classic security hole for code injection.
The problem may be because of passing an array and than in the receiver function again adding a parenthesis for coords access.
For example you have function that accept an object that has an array that holds the lat and lng coords
when you call this function and pass it the object having coords and inside a function accessing it using array bracket can cause this issue
Just remove the [] from array coords
_renderWorkoutMarker(workout) {
L.marker(workout.coords)
.addTo(this.#map)
.bindPopup(
L.popup({
maxWidth: 250,
minWidth: 100,
autoClose: false,
closeOnClick: false,
className: `${workout.type}-popup`,
})
)
.setPopupContent('workout')
.openPopup();
}
in the code above I mistakenly added the brackets like L.marker([workout.coords]) but the actual code is L.marker(workout.coords)
Thanks

Leaflet resizes geoJson layer on update

I need to update leaflet map with aggregated data on zoom change, so that when user changes the zoom, aggregation is performed on the backend and the data is sent to the map.
I have a problem with displaying the data from backend, because when data is changed, map isn't updated and then if I pan, the map is updated, but the layer with markers is shifted and resized to fit the viewport, so markers are not in their actual coordinates (its better explained by the attached images).
Initial map state:
After zoom and update (map didn't detect changes):
After small pan (map detected changes, but the geoJson layer is shifted and resized):
Code for updating the map (it removes the old geoJson layer and creates the new one to draw):
refreshMap(data) {
if (this.layer) {
this.layer.clearLayers();
}
let pointToLayer = (feature, latlng) => {
let radius = 10 + Math.log2(feature.properties.count);
let circleMarker = L.circleMarker(latlng, {
radius,
fillColor: uiSettings.map.cluster.fillColor,
fillOpacity: uiSettings.map.cluster.fillOpacity,
color: uiSettings.map.cluster.strokeColor,
weight: uiSettings.map.cluster.strokeWeight,
opacity: uiSettings.map.cluster.strokeOpacity,
});
circleMarker.bindTooltip(`${feature.properties.count}`, { permanent: true });
return circleMarker;
};
this.layer = L.geoJSON(data, { pointToLayer });
this.layer.addTo(this.map);
}
I wasn't able to google similar cases, so maybe somebody had the problem with this or can say what can cause it?

Bing Maps Api clear a layer

I have a page that works well it loads a Bing Map and creates a layer which is then filled with Polygons. I then need to reload the JSON data that makes the polygons and this again works, however the data is then added to another layer so appears over the top. I have tried to delete the layer, clear the layer etc etc but nothing seems to work.
Any idea please.
This is the function that does it all...
function AddData() {
dataLayer = new Microsoft.Maps.Layer();
Microsoft.Maps.loadModule('Microsoft.Maps.GeoJson', function () {
var featureCollection = Microsoft.Maps.GeoJson.read(json, {
polygonOptions: {
strokeColor: 'LightSkyBlue',
strokeThickness: 2
}
});
for (var i = 0; i < featureCollection.length; i++) {
var fillColour = featureCollection[i].metadata.FillColor;
featureCollection[i].setOptions({ fillColor: fillColour });
Microsoft.Maps.Events.addHandler(featureCollection[i], 'click', displayClickBox);
Microsoft.Maps.Events.addHandler(featureCollection[i], 'mouseover', displayMouseOverBox);
Microsoft.Maps.Events.addHandler(featureCollection[i],'mouseout', displayMouseOut);
dataLayer.add(featureCollection[i], 0);
}
map.layers.insert(dataLayer);
});
}
var getJson = function () {
var onContentComplete = function (response) {
//Load the JSON data into the local variable for use latter in the project...
json = response.data;
//load the map now that we have the polygon data...
AddData();
};
var onError = function (reason) {
//An error has occured so display a message to the user...
$scope.error = "Server communication error please try again...";
//Log the error to the console for admin debug...
console.log(reason.data);
};
//Load the JSON for the map polygons into memory ready for display...
$http.get("../JSON/MapData.json")
.then(onContentComplete, onError);
}
As I have said I have tried to clear the layer first using
dataLayer.clear();
But that seems to do nothing.
Help please as I have been working at this for hours now.
Thanks
Cliff.
By the sounds of things you want all data to render in a single layer, but instead it is rendering two or more layers. This happens because you are creating a new instance of dataLayer every time the AddData function is called. Additionally, this overwrites the local variable for dataLayer and thus you lose the reference to the original layer that you are trying to clear/delete. If you want to clear or delete the layer, do that before initializing the new layer. Try adding the following to the top of your AddData function:
if(dataLayer){
map.layers.remove(dataLayer);
}
Alternatively, reuse the layer by clearing it if it exists of or creating it if it doesn't:
if(dataLayer){
dataLayer.clear();
}else{
dataLayer = new Microsoft.Maps.Layer();
}

How to filter TopoJSON features by property in Leaflet.js?

I need to color countries on a map depending on a some value (number of content items for a country). If that value is 0 (null), the respective country should not be coloured/displayed.
In terms of Leaflet.js, this means, I have a GeoJSON file containing a feature for each country of the world. However, the feature is only rendered (added to map) if the number of content items is greater than 0. When using GeoJSON input file, this works already similar to the answer to Leaflet.js: is it possible to filter geoJSON features by property?
This is the snippet for GeoJSON:
var mapLayer = L.geoJson(world, {
filter: function(feature, layer) {
return getContentItems(feature.properties.ISO2, "count");
},
onEachFeature: function (feature, layer) {
var contentCount = getContentItems(feature.properties.ISO2, "count");
if (contentCount) {
layer.setStyle({
'weight': 1,
'fillOpacity': .75,
'fillColor': "#0077c8", // Actually a function depending on contentCount
'opacity': .75,
'color': "#0077c8"
});
}
}
});
Now, the GeoJSON file is a whopping 11 MB large due to details on the map. I learned about TopoJSON, which is pretty awesome since the source file is now less than 2 MB with the same grade of detail. I also managed to get the TopoJSON data to be displayed on the map, however, I can't figure out, how to apply the filter.
Here's my current snippet for adding the TopoJSON layer:
L.TopoJSON = L.GeoJSON.extend({
addData: function(jsonData) {
if (jsonData.type === "Topology") {
for (key in jsonData.objects) {
geojson = topojson.feature(jsonData, jsonData.objects[key]);
L.GeoJSON.prototype.addData.call(this, geojson);
}
}
else {
L.GeoJSON.prototype.addData.call(this, jsonData);
}
}
});
var topoLayer = new L.TopoJSON();
$.getJSON('scripts/world.topo.json')
.done(addTopoData);
function addTopoData(topoData) {
topoLayer.addData(topoData);
topoLayer.addTo(map);
topoLayer.eachLayer(handleLayer);
}
function handleLayer(layer) {
layer.setStyle({'opacity': 0}); // Too late.
}
I tried to add the filter function to the GeoJSON extension within the TopoJSON declaration without luck. The handleLayer() function is to late, I think, the features have already been added.
EDIT:
I am able to remove a layer, if the content count is 0 by changing the handleLayer() function to
function handleLayer(layer) {
var contentCount = getContentItems(layer.feature.properties.ISO2, "count");
if (contentCount == 0) {
map.removeLayer(layer);
}
}
For performance purposes, I would however like to filter the features before being drawn. I am just stuck now where the filter function needs to be added.
After some deep Javascript reading about inheritance just as suggested by FranceImage, I found the solution I was looking for myself. Instead of just creating a new TopoJSON object with var topoLayer = new L.TopoJSON();, I am able to pass the custom filter functions as options:
var topoLayer = new L.TopoJSON(null, {
filter: function(feature, layer) {
return getNN(feature.properties.ISO2, "check");
},
onEachFeature: function (feature, layer) {
var contentCount = getContentItems(feature.properties.ISO2, "count");
if (contentCount) {
layer.setStyle({
'weight': 1,
'fillOpacity': .75,
'fillColor': "#0077c8",
'opacity': .75,
'color': "#0077c8"
});
}
}
});
No need to remove layers with the handleLayers function.

Categories

Resources