Leaflet-Omnivore make external file.geojson available to javascript variable - leaflet

I'm using the Omnivore extension to load my GeoJSON files into a Mapbox/ Leaflet map. Later, I'm using Turf.js on this data.
I'm loading the external GeoJSON file to the map as a customLayer, but having trouble making the GeoJSON data available in the variable pts for use later.
var polyLayer = L.geoJson(null, {
style: function(feature) {
return { color: '#f00', weight: 1, opacity: 0.9
};
}
}).addTo(map);
var polys = omnivore.geojson('polys.geojson', null, polyLayer);
var ptsLayer = L.geoJson(null, {
onEachFeature: labelEachFeature,
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, style(feature))
;}
});
var pts = omnivore.geojson('pts.geojson', null, ptsLayer);
polyLayer.on('mouseover', function(e) {
var matchingPoints = turf.featurecollection([])
matchingPoints.features = pts.features.filter(function(pt) {
if(pt.properties.servesbldg === e.layer.feature.properties.bldg_no) return true
})
ptsLayer.setGeoJSON(matchingPoints);
});
function getPoints(building, pts) {
var matchingPoints = turf.featurecollection([])
matchingPoints.features = pts.features.filter(function(pt) {
if(pt.properties.servesbldg === building.properties.bldg_no) return true
})
return matchingPoints
}
Here's a GIST of what I've got including the GeoJSON files.

Instead of using omnivore to grab the pts layer... Why not declare a pts variable, use jQuery.getJSON to grab pts.geojson and assign that response to the pts variable, then call L.geoJson(pts).addTo(map). Then you have the layer added, and you have the original geojson structure available for use in other places, like you need.

Related

How to style multiple Feature types in Leaflet from a single GeoJSON FeatureCollection

I have Point, MultiPolygon, and Linestring features in a single FeatureCollection. I want to style Points with circleMarker(), and the others normally. I add Points to an idToFeature object for manipulation with JavaScript, but can't add the non-point features.
Using Leaflet 1.3.3, if I use pointToLayer:, Points are styled fine, and the other types do render, with a default blue (don't know why, as they aren't points). I have tried using onEachFeature: instead, and can style non-points that way, but can't figure how to generate circleMarkers for points.
What I have
(can't style MultiPolygons, or add them to my idToFeature object for JS manipulation)
// geom is an array of features inc. Point, MultiPolygon, Linestring
renderPlaces = function(geoms) {
data = {"type":"FeatureCollection","features":geoms}
idToFeature = {}
mappy.createPane('placePane');
mappy.getPane('placePane').style.zIndex = 200;
features = L.geoJSON(data, {
pane: 'placePane',
pointToLayer: function (feature, latlng) {
identifier = feature.properties.id;
if(feature.type=='Point'){
marker = L.circleMarker(latlng, styles.place_default)
.bindPopup(feature.properties.title+' (id:'+identifier+')');
// add to array for programmatic selection
idToFeature[identifier] = marker
return marker
}
}
}).addTo(map);
}
What I tried (one take anyway)
features = L.geoJSON(data, {
style: function (feature) {
if(feature.type=="Point"){
return {color: "#009900"};
} else {
return {color: "#000099"};
}
}
onEachFeature: function(feature, layer) {
if(feature.type == 'Point'){
console.log('point', feature)
marker = L.circleMarker(latlng, styles.place_default)
return marker
}
}
}).addTo(map);
I'd prefer not to break the GeoJSON into separate type-specific FeatureCollections, feel certain I shouldn't have to...but how?
I cobbled a solution. It doesn't seem ideal, but works: use both pointToLayer and onEachFeature, one after the other.
features = L.geoJSON(data, {
pointToLayer: function (feature, latlng) {
count_features +=1;
identifier = feature.properties.whgid;
if(feature.type=='Point'){
marker = L.circleMarker(latlng, styles.marker_default
).bindPopup(feature.properties.title+' (whg id:'+identifier+')');
// add to array for selection
idToFeature[identifier] = marker
return marker
}
},
onEachFeature: function(feature,layer) {
identifier = feature.properties.whgid;
if(feature.type != 'Point'){
layer.setStyle(styles.default)
idToFeature[identifier] = layer
}
}
}).addTo(map);

Leaflet: Layer check box state is reset every time the map moves or zooms

I have the following code which fetches some remote GeoJSON from an API and displays the results on a Leaflet map:
<script>
// Center the map
var map = L.map('map').setView([54.233669, -4.406027], 6);
// Attribution
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=REMOVED', {
attribution: 'Map © OpenStreetMap',
id: 'mapbox.streets'
}).addTo(map);
// Create an empty layergroup for the data
var LayerUTMGroundHazards = L.layerGroup();
var LayerUTMAirspace = L.layerGroup();
// Style the features
function setStyle(feature) {
return {
fillColor: feature.properties.fillColor,
color: feature.properties.strokeColor,
fillOpacity: feature.properties.fillOpacity,
opacity: feature.properties.strokeOpacity
};
}
// Build Guardian UTM
function getGuardianUTMdata() {
// Clear the current Layer content
LayerUTMGroundHazards.clearLayers();
LayerUTMAirspace.clearLayers();
// Define what we want to include
function FuncGroundHazards(feature) {
if (feature.properties.category === "groundHazard") return true
}
function FuncAirspace(feature) {
if (
(feature.properties.category === "airspace" || feature.properties.category === "airport")
&& feature.properties.detailedCategory !== "uk:frz"
) return true
}
// Build the layers
fetch("https://example.com/json?n=" + map.getBounds().getNorth() + "&e=" + map.getBounds().getEast() + "&s=" + map.getBounds().getSouth() + "&w=" + map.getBounds().getWest(), { headers: { 'Authorization': 'REMOVED', 'X-AA-DeviceId': 'mySite' } })
.then(function (responseGuardianUTM) { return responseGuardianUTM.json() })
.then(function (dataGuardianUTM) {
// Create Layer: Ground Hazards
var featuresUTMGroundHazards = L.geoJson(dataGuardianUTM, {
filter: FuncGroundHazards,
style: setStyle,
pointToLayer: function (feature, latlng) { return L.marker(latlng, { icon: L.icon({ iconUrl: feature.properties.iconUrl, iconSize: [25, 25], }), }) },
onEachFeature: function (feature, layer) { layer.bindPopup(feature.properties.name); },
});
// Add the L.GeoJSON instance to the empty layergroup
LayerUTMGroundHazards.addLayer(featuresUTMGroundHazards).addTo(map);
});
// other layers are here (removed from this example)
}
// Update the Guardian UTM layer if the map moves
map.on('dragend', function () { getGuardianUTMdata(); });
map.on('zoomend', function () { getGuardianUTMdata(); });
// Layer controls
var layerControl = new L.Control.Layers(null, {
'Airspace Restrictions': LayerUTMAirspace,
'Ground Hazards': LayerUTMGroundHazards
// other layers are here (removed from this example)
}).addTo(map);
</script>
The problem is that every time the map is moved or zoomed, all of the Layer checkboxes are reset to Checked again, regardless of how many were checked before the map moved. They do not honour / remember their state when the map moves.
Given my code above, how can I store or preserve the checkbox state for each of the multiple Layers that I have so they are not reset every time the map is moved?
EDIT:
Here is a working fiddle. Remove the checkbox from the 'Ground Hazards', then move or zoom the map, you will see how it puts a tick back in the box again
https://jsfiddle.net/hdwz1b6t/1/
You're (re-)adding LayerUTMGroundHazards every time. This line here...
// Add the L.GeoJSON instance to the empty layergroup
LayerUTMGroundHazards.addLayer(featuresUTMGroundHazards).addTo(map);
...is not only adding featureUTMGroundHazards to layerUTMGroundHazards, it's also (re-)adding layerUTMGroundHazards to the map.
And quoting from https://leafletjs.com/examples/layers-control/ :
The layers control is smart enough to detect what layers we’ve already added and have corresponding checkboxes and radioboxes set.
So when you do LayerUTMGroundHazards.addTo(map);, the checkboxes reset.

Changing overlay layers when switching base layer

I have built a leaflet map with two base layers, and each of these base layers will have their own unique points of interest. The points of interest are being stored as geojson that I loop over to create multiple overlays for different categories. So when viewing the default base layer you would see layers for Show All, Cat1, Cat2 etc.
I need a way to be able to attach overlay layers to a base layer, or remove all overlay layers and then load the relevant ones when the base layer changes.
I tried using the following, which worked to switch categories, with the baselayerchange event, but the overlay layers were still displaying when I switched base layers.
layerControl._layers.forEach(function(layer){
if(layer.overlay){
map.removeLayer(layer.layer)
}
});
I've been searching for a couple of days now for an answer to this without any luck, any help is greatly appreciated.
EDIT
Posting additional code for context. This is not the entirety of the code, there are some plugins that I'm not including code for and have excluded definitions for a several variables, but this should provide better insight into how things are working.
//Initialize the map
var map = new L.Map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
crs: crs1848,
attributionControl: false,
layers: [pano1848]
});
//add controls to the map
var layerControl = L.control.layers(null, null, {position: 'bottomleft'}).addTo(map);
//building category layers from geojson
var types = ['African Americans', 'Art Architecture Culture', 'Education Religion Reform', 'Everyday Life', 'Immigrants', 'Science Inventions', 'Transportation Industry Commerce'];
types.forEach(function(type){
var catType = type.replace(/\s/g,"");
var catPoints = L.geoJson(mapData, {
filter: function(feature, layer){
var cat = feature.properties['category'];
return cat.indexOf(catType) >= 0;
},
onEachFeature: function (feature, layer) {
layer.bindTooltip(feature.properties.name);
(function(layer, properties){
//Create Numeric markers
var numericMarker = L.ExtraMarkers.icon({
icon: 'fa-number',
markerColor: 'yellow',
number: feature.properties['id']
});
layer.setIcon(numericMarker);
layer.on('click', function() {
$.ajax({
url:feature.properties['url'],
dataType:'html',
success: function(result){
$('#detailContainer').html(result);
$('#overlay').fadeIn(300);
}
});
});
})(layer, feature.properties);
}
});
layerControl.addOverlay(catPoints, catType);
});
//Base Layer Change Event
map.on('baselayerchange', function(base){
var layerName;
layerControl._layers.forEach(function(layer){
if(layer.overlay){
map.removeLayer(layer.layer)
}
});
if(base._url.indexOf('1848') >= 0){
map.options.crs = crs1848;
map.fitBounds([
crs1848.unproject(L.point(mapExtent1848[2], mapExtent1848[3])),
crs1848.unproject(L.point(mapExtent1848[0], mapExtent1848[1]))
]);
var southWest = map.unproject([0, 8192], map.getMaxZoom());
var northEast = map.unproject([90112, 0], map.getMaxZoom());
map.setMaxBounds(new L.LatLngBounds(southWest, northEast));
map.addLayer(allPoints);
layerName = '1848 Panorama';
}
else if(base._url.indexOf('2018') >= 0){
map.options.crs = crs2018;
map.fitBounds([
crs2018.unproject(L.point(mapExtent2018[2], mapExtent2018[3])),
crs2018.unproject(L.point(mapExtent2018[0], mapExtent2018[1]))
]);
var southWest = map.unproject([0, 8192], map.getMaxZoom());
var northEast = map.unproject([49152, 0], map.getMaxZoom());
map.setMaxBounds(new L.LatLngBounds(southWest, northEast));
layerName = '2018 Panorama'
}
miniMap.changeLayer(minimapLayers[layerName]);
//map.setView(map.getCenter(), map.getZoom());
});
You may create global variable call "overlays", and remove it like an example below.
Here is the similar example to illustrate your problem jsFiddle
var overlays = {
'Name 1': catPoints,
'Name 2': catType
};
L.control.layers(null, overlays).addTo(map);
// Whenever you want to remove all overlays:
for (var name in overlays) {
map.removeLayer(overlays[name]);
}

Leaflet Draw not taking properties when converting FeatureGroup to GeoJson

I'm unable to convert my Layer properties into the properties of the GEOJson object using Leaflet(0.7.7)/Leaflet.Draw(latest). My workflow is:
1 Create Map: var map = L.map('#map', options);
2 Create a FeatureGroup: features= new L.FeatureGroup();
3 Add to the Leaflet Map: map.addLayer(features);
4 On the draw:created event, I'm capturing e.layer and adding a bunch of properties:
var layer = e.layer;
layer.properties = { Title: 'Hello' };
features.addLayer(layer);
geo_features = features.toGeoJSON();
However, my geo_features always have empty property attributes in each of the features and I can't figure it out!
iH8's initial answer was almost correct.
To specify properties that will appear in a vector layer's GeoJSON export (i.e. through its .toGeoJSON() method), you have to fill its feature.type and feature.properties members:
var myVectorLayer = L.rectangle(...) // whatever
var feature = myVectorLayer.feature = myVectorLayer.feature || {};
feature.type = "Feature";
feature.properties = feature.properties || {};
feature.properties["Foo"] = "Bar";
Now myVectorLayer.toGeoJSON() returns a valid GeoJSON feature object represented by:
{
"type": "Feature",
"properties": {
"Foo": "Bar"
// More properties that may be pre-filled.
},
"geometry": // The vector geometry
}
A (kind of ugly workaround) is using a L.GeoJSON layer and add the drawn layer's GeoJSON to it by using it's addData method. Afterwards grab the last layer in the L.GeoJSON layer's _layers object. At that point the layer has a valid GeoJSON feature property you can edit:
var geojson = new L.GeoJSON().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: geojson
}
}).addTo(map);
map.on('draw:created', function (e) {
geojson.addData(e.layer.toGeoJSON());
var layers = geojson._layers,
keys = Object.keys(layers),
key = keys[keys.length - 1],
layer = layers[key];
layer.feature.properties = {
'Foo': 'Bar'
};
});
For your L.GeoJSON call include feature callback onEachFeature to options
L.GeoJSON(featureData,{onEachFeature:function(feature,layer){
//console.log(feature,layer);
// do something like
feature.setStyle( convertLayerOptionsFromFeatureProperties( feature.properties ) );
}} )

Leaflet elevation profiles with multiple GeoJSON features

I have a map which loads a GeoJSON containing two features (lines). I would like to use Leaflet Elevation plugin to display an elevation profile for each of those lines. The problem with my code is that when I click a feature, there is no data added to the elevation profile.
var map = L.map('map').setView([44.635, 22.653], 11);
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
function addData(e) {
var el = L.control.elevation();
el.addData(e);
map.addControl(el);
}
function onEachFeature(feature, layer) {
layer.on({
click: addData
});
}
var trails = new L.GeoJSON.AJAX('https://googledrive.com/host/0B55_4P6vMjhITEU4Ym9iVG8yZUU/map.geojson', {
onEachFeature: onEachFeature
}).addTo(map);
Here's a JSFiddle of my work: http://jsfiddle.net/pufanalexandru/eaok0Lnz/3/
To anyone who might be interested, I think I found a way to achieve this:
var trails = new L.GeoJSON.AJAX('https://googledrive.com/host/0B55_4P6vMjhITEU4Ym9iVG8yZUU/map.geojson', {
onEachFeature: function onEachFeature(feature, layer) {
layer.on({
click: function () {
el.clear();
el.addData(feature);
}
});
}
}).addTo(map);