I'm loading a GeoJSON file of aircraft tracks into Leaflet. I'd like to plot current locations as icons and previous 'tracks' as lines. Showing all the tracks gets busy as aircraft increase, so I'd like to be able to toggle them on only when the aircraft is clicked on.
Can/how should I show/hide the LineString separately from the marker dynamically?
I've found suggestions to set the style transparent but .setStyle applies to the Feature, not the geometry.
Summarised GeoJSON:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"reg": "G-CGWP",
"type": "fixed",
"model": "website.profile"
},
"id": 12,
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "LineString",
"coordinates": [
[
-0.319281196282617,
52.08664390758181
],
[
-1.076445537370006,
52.79518475653341
],
[
-0.098191354875297,
51.94810149137197
],
[
-0.940941846648286,
53.508162348603435
]
]
},
{
"type": "Point",
"coordinates": [
-0.940941846648286,
53.508162348603435
]
}
]
}
},
...
]
}
I do have control of the GeoJSON so can change that.
Since LineString collection is represented as GeometryCollection you could consider to flatten GeometryCollection into LineString geometries, (for example via Turf.js flatten function):
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "LineString",
"coordinates": [
[-105.00341892242432, 39.75383843460583],
[-105.0008225440979, 39.751891803969535]
]
},
{
"type": "LineString",
"coordinates": [
[-105.0008225440979, 39.751891803969535],
[-104.99820470809937, 39.74979664004068]
]
}
]
}
}
]
}
into
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-105.00341892242432, 39.75383843460583],
[-105.0008225440979, 39.751891803969535]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-105.0008225440979, 39.751891803969535],
[-104.99820470809937, 39.74979664004068]
]
}
}
]
}
and then apply style per layer
layer.on({
click: function(e) {
toggleLayerVisibility(e.target);
}
});
where
var selectedLayerId = null;
function toggleLayerVisibility(layer) {
if (selectedLayerId) {
geojson.resetStyle(layer);
selectedLayerId = null;
} else {
//hide a layer
layer.setStyle({
opacity: 0,
fillOpacity: 0.0
});
selectedLayerId = layer._leaflet_id;
}
}
Here is an example
var data = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "GeometryCollection",
geometries: [
{
type: "LineString",
coordinates: [
[-105.00341892242432, 39.75383843460583],
[-105.0008225440979, 39.751891803969535]
]
},
{
type: "LineString",
coordinates: [
[-105.0008225440979, 39.751891803969535],
[-104.99820470809937, 39.74979664004068]
]
},
{
type: "LineString",
coordinates: [
[-104.99820470809937, 39.74979664004068],
[-104.98689651489258, 39.741052354709055]
]
}
]
}
},
{
type: "Feature",
geometry: {
type: "MultiPolygon",
coordinates: [
[
[
[-105.00432014465332, 39.74732195489861],
[-105.00715255737305, 39.7462000683517],
[-105.00921249389647, 39.74468219277038],
[-105.01067161560059, 39.74362625960105],
[-105.01195907592773, 39.74290029616054],
[-105.00989913940431, 39.74078835902781],
[-105.00758171081543, 39.74059036160317],
[-105.00346183776855, 39.74059036160317],
[-105.00097274780272, 39.74059036160317],
[-105.00062942504881, 39.74072235994946],
[-105.00020027160645, 39.74191033368865],
[-105.00071525573731, 39.74276830198601],
[-105.00097274780272, 39.74369225589818],
[-105.00097274780272, 39.74461619742136],
[-105.00123023986816, 39.74534214278395],
[-105.00183105468751, 39.74613407445653],
[-105.00432014465332, 39.74732195489861]
],
[
[-105.00361204147337, 39.74354376414072],
[-105.00301122665405, 39.74278480127163],
[-105.00221729278564, 39.74316428375108],
[-105.00283956527711, 39.74390674342741],
[-105.00361204147337, 39.74354376414072]
]
],
[
[
[-105.00942707061768, 39.73989736613708],
[-105.00942707061768, 39.73910536278566],
[-105.00685214996338, 39.73923736397631],
[-105.00384807586671, 39.73910536278566],
[-105.00174522399902, 39.73903936209552],
[-105.00041484832764, 39.73910536278566],
[-105.00041484832764, 39.73979836621592],
[-105.00535011291504, 39.73986436617916],
[-105.00942707061768, 39.73989736613708]
]
]
]
}
}
]
};
var map = L.map("map").setView([39.74739, -105], 14);
L.tileLayer(
"https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw",
{
maxZoom: 18,
attribution:
'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: "mapbox.light"
}
).addTo(map);
var data_flatten = turf.flatten(data);
var geojson = L.geoJSON(data_flatten, {
style: {
fillColor: '#1c9099',
weight: 8
},
onEachFeature: onEachFeature
}).addTo(map);
var selectedLayerId = null;
function toggleLayerVisibility(layer) {
if (selectedLayerId) {
geojson.resetStyle(layer);
selectedLayerId = null;
} else {
//hide a layer
layer.setStyle({
opacity: 0,
fillOpacity: 0.0
});
selectedLayerId = layer._leaflet_id;
}
}
function onEachFeature(feature, layer) {
layer.on({
click: function(e) {
toggleLayerVisibility(e.target);
}
});
}
#map {
width: 600px;
height: 400px;
}
<link
rel="stylesheet"
href="https://unpkg.com/leaflet#1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""
/>
<script
src="https://unpkg.com/leaflet#1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""
></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/5.1.5/turf.js"
></script>
<div id="map"></div>
Related
How can I get the ID I gave to a polygon by clicking?
var geojson = {
"type": "FeatureCollection",
"features": [{
"postalAreaId": item.PostalAreaId,
"type": "Feature",
"properties": {
"name": "Coors Field",
"amenity": "Baseball Stadium",
"popupContent": "This is where the Rockies play!"
},
"geometry": {
"type": "Polygon",
"coordinates": []
}
}]
};
You can use the OnEachFeature option when you draw your features on the map... A possible solution would be:
var geojson = {
"type": "FeatureCollection",
"features": [
{
"postalAreaId": 1,
"type": "Feature",
"properties": {
"name": "Coors Field",
"amenity": "Baseball Stadium",
"popupContent": "This is where the Rockies play!"
},
"geometry": {
"type": "Point",
"coordinates": [-105.00341892242432, 39.75383843460583],
}
},
{
"postalAreaId": 2,
"type": "Feature",
"properties": {
"name": "Coors Field",
"amenity": "Baseball Stadium",
"popupContent": "This is where the Rockies play!"
},
"geometry": {
"type": "Point",
"coordinates": [-105.0008225440979, 39.751891803969535],
}
}]
};
var map = L.map('map').setView([39.74739, -105], 13);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'Imagery © Mapbox',
id: 'mapbox/light-v9',
tileSize: 512,
zoomOffset: -1
}).addTo(map);
function onEachFeature(feature, layer) {
layer.on('click', function (e) {
alert(feature.postalAreaId)
})
}
L.geoJSON(geojson, {
onEachFeature: onEachFeature
}).addTo(map);
html, body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 400px;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<div id='map'></div>
In this case, I'm using click but you could also use mousein and mouseout. Basically, inside the onEachFeature function you're adding a listener that will check events that happen on each feature of the map.
I have a heat map in Qgis and with the qgis2web plugin I generated the preview page. However, it showed nothing on the page. I did some research, did some tests, but I'm not able to generate a leaflet heat map with data from a geoJson file. Can someone help me?
Follows the generated code.
Thanks.
Follows model of the file with the data:
var json_dados_centroids = {
"type": "FeatureCollection",
"name": "dados",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "VINC": "6" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489342251452101, -23.66511439561954 ] ] } },
{ "type": "Feature", "properties": { "VINC": "1" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489439968502133, -23.665105357310239 ] ] } },
{ "type": "Feature", "properties": { "VINC": "0" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489537955929293, -23.665096808547311 ] ] } },
{ "type": "Feature", "properties": { "VINC": "0" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.559119794597237, -23.653810429167063 ] ] } },
{ "type": "Feature", "properties": { "VINC": "0" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489140609582904, -23.665134091460494 ] ] } }
]
}
var centroids_hm = geoJson2heat(json_dados_centroids, 'VINC');
var layer_centroids = new L.heatLayer(centroids_hm, {
attribution: '',
interactive: true,
radius: 30,
max: 11917,
minOpacity: 1,
gradient: {0: '#fcfdbf', 0.019608: '#fcf4b6', 0.039216: '#fdebac', 0.0588942: '#fde2a3', 0.078431: '#fed89a',
0.098039: '#fecf92', 0.117647: '#fec68a', 0.137255: '#febd82', 0.156863: '#feb47b',
0.176471: '#feaa74', 0.196078: '#fea16e', 0.215686: '#fd9869', 0.235294: '#fc8e64',
0.254902: '#fb8560', 0.27451: '#f97b5d', 0.294118: '#f7725c', 0.313725: '#f4695c',
0.333333: '#f1605d', 0.352941: '#ec5860', 0.372549: '#e75263', 0.392157: '#e04c67',
0.411765: '#d9466b', 0.431373: '#d2426f', 0.45098: '#ca3e72', 0.470588: '#c23b75',
0.490196: '#ba3878', 0.509804: '#b2357b', 0.529412: '#aa337d', 0.54902: '#a1307e',
0.568627: '#992d80', 0.588235: '#912b81', 0.607843: '#892881', 0.627451: '#812581',
0.647059: '#792282', 0.666667: '#721f81', 0.686275: '#6a1c81', 0.705882: '#621980',
0.72549: '#5a167e', 0.745098: '#52137c', 0.764706: '#4a1079', 0.784314: '#420f75',
0.803922: '#390f6e', 0.823529: '#311165', 0.843137: '#29115a', 0.862745: '#21114e',
0.882353: '#1a1042', 0.901961: '#140e36', 0.921569: '#0e0b2b', 0.941176: '#090720',
0.960784: '#050416', 0.980392: '#02020b', 1: '#000004'}
}
);
layer_centroids.setData(centroids_hm);
function geoJson2heat(geojson, weight) {
return geojson.features.map(function(feature) {
return [
feature.geometry.coordinates[1],
feature.geometry.coordinates[0],
feature.properties[weight]
];
});
}
map.addLayer(layer_centroids);
First, your GeoJSON contains geometry in the form of multipoints. However, in 'geoJson2heat' you parse points.
I would advise converting the geometry to point (in the 'json_dados_centroids' variable).
Also layer_centroids.setData(centroids_hm) needs to be removed.
Something like that
var json_dados_centroids = {
"type": "FeatureCollection",
"name": "dados",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [{
"type": "Feature",
"properties": {
"VINC": "6"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489342251452101,
-23.66511439561954
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "1"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489439968502133,
-23.665105357310239
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "0"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489537955929293,
-23.665096808547311
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "0"
},
"geometry": {
"type": "Point",
"coordinates": [-46.559119794597237,
-23.653810429167063
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "0"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489140609582904,
-23.665134091460494
]
}
}
]
}
var centroids_hm = geoJson2heat(json_dados_centroids, 'VINC');
var layer_centroids = new L.heatLayer(centroids_hm, {
attribution: '',
interactive: true,
radius: 30,
max: 11917,
minOpacity: 1,
gradient: {
0: '#fcfdbf',
0.019608: '#fcf4b6',
0.039216: '#fdebac',
0.0588942: '#fde2a3',
0.078431: '#fed89a',
0.098039: '#fecf92',
0.117647: '#fec68a',
0.137255: '#febd82',
0.156863: '#feb47b',
0.176471: '#feaa74',
0.196078: '#fea16e',
0.215686: '#fd9869',
0.235294: '#fc8e64',
0.254902: '#fb8560',
0.27451: '#f97b5d',
0.294118: '#f7725c',
0.313725: '#f4695c',
0.333333: '#f1605d',
0.352941: '#ec5860',
0.372549: '#e75263',
0.392157: '#e04c67',
0.411765: '#d9466b',
0.431373: '#d2426f',
0.45098: '#ca3e72',
0.470588: '#c23b75',
0.490196: '#ba3878',
0.509804: '#b2357b',
0.529412: '#aa337d',
0.54902: '#a1307e',
0.568627: '#992d80',
0.588235: '#912b81',
0.607843: '#892881',
0.627451: '#812581',
0.647059: '#792282',
0.666667: '#721f81',
0.686275: '#6a1c81',
0.705882: '#621980',
0.72549: '#5a167e',
0.745098: '#52137c',
0.764706: '#4a1079',
0.784314: '#420f75',
0.803922: '#390f6e',
0.823529: '#311165',
0.843137: '#29115a',
0.862745: '#21114e',
0.882353: '#1a1042',
0.901961: '#140e36',
0.921569: '#0e0b2b',
0.941176: '#090720',
0.960784: '#050416',
0.980392: '#02020b',
1: '#000004'
}
});
function geoJson2heat(geojson, weight) {
return geojson.features.map(function(feature) {
return [
feature.geometry.coordinates[1],
feature.geometry.coordinates[0],
feature.properties[weight]
];
});
}
map.addLayer(layer_centroids);
P.S. "VINC": "6" - strange value of intensity.
I have a web page which fetches some marker pin locations from a local database and also a collection of features from a remote GeoJSON source (API).
Currently, there are two check boxes available to the user to allow them to choose which of the two layers they want to view. This all works fine:
<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 Locations data
var LayerLocations = L.layerGroup();
// Format the popup markers for the Locations
function forEachFeature(feature, layer) {
// Image
var ImageContent
if (feature.properties.ImageURL) {
ImageContent = "<img src='" + feature.properties.ImageURL + "' width='300' height='200' /><br /><br />"
} else if (feature.properties.YouTubeID) {
ImageContent = "<img src='https://img.youtube.com/vi/" + feature.properties.YouTubeID + "/hqdefault.jpg' width='300' height='200' /><br /><br />"
} else {
ImageContent = ""
}
// Build the popup content
var popupContent = "<h4>" +
feature.properties.Title +
Author +
"</h4>" +
ImageContent +
CommentsContent +
"View and discuss this location.";
layer.bindPopup(popupContent);
}
// Build layer: Locations
fetch("JSONMapPoints.json")
.then(function (response) { return response.json() })
.then(function (data) {
// Create a L.GeoJSON out of the data
var locations = L.geoJson(data, {
onEachFeature: forEachFeature,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {
icon: L.icon({
iconUrl: "images/pins/" + feature.properties.CategoryID + ".png",
iconSize: [32, 37],
iconAnchor: [10, 32],
popupAnchor: [5, -30]
}),
})
}
});
// Add the L.GeoJSON instance to the empty layergroup
map.fitBounds(locations.getBounds());
LayerLocations.addLayer(locations).addTo(map);
});
// Create an empty layergroup for the Guardian UTM data
var LayerGuardianUTM = L.layerGroup();
// Style the Guardian UTM features
function setStyle(feature) {
return {
fillColor: feature.properties.fillColor,
color: feature.properties.strokeColor,
fillOpacity: feature.properties.fillOpacity,
opacity: feature.properties.strokeOpacity
};
}
// Build Layer: Guardian UTM
function getGuardianUTMdata() {
LayerGuardianUTM.clearLayers();
fetch("https://example.com/v2/mapdata/geojson?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 a L.GeoJSON out of the data
var featuresAA = L.geoJson(dataGuardianUTM, {
style: setStyle,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, { icon: L.icon({ iconUrl: feature.properties.iconUrl }), })
},
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.name);
},
});
// Add the L.GeoJSON instance to the empty layergroup
LayerGuardianUTM.addLayer(featuresAA).addTo(map);
});
}
// 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, {
'Locations': LayerLocations,
'Restrictions & Hazards': LayerGuardianUTM
}).addTo(map);
</script>
I wish to increase the functionality available to the end user.
Instead of a single checkbox to turn on/off the Restrictions & Hazards layer, I wish to iterate through the GeoJSON returned from the API and dynamically build a series of checkboxes under the Layer button/icon based on the feature.properties.filters.name.
So when the user clicks on the Layers button icon they should see a series of checkboxes which would allow them to pick and choose which of the features in the GeoJSON they wish to view.
The GeoJSON returned from the API is dynamic and its content changes based on the users location and zoom level.
An example of the GeoJSON is:
{
"isCompleteData": true,
"excludedData": [],
"countriesInViewport": [],
"nationalFlightRestrictions": [],
"features": [
{
"geometry": {
"coordinates": [
[
-2.6300508975982666,
53.536331176757812
],
[
-2.6293964385986328,
53.533683776855469
],
[
-2.6288816928863525,
53.531524658203125
],
[
-2.6228303909301758,
53.529739379882813
],
[
-2.6218380928039551,
53.528053283691406
],
[
-2.6206841468811035,
53.526073455810547
]
],
"type": "LineString"
},
"id": "A05B59534A594F20583A3B8EB479F211E507F265",
"properties": {
"hazardFactor": "40",
"hazardFactorName": "Warning",
"fillColor": "#ffbb00",
"strokeColor": "#b88702",
"fillOpacity": "0.35",
"strokeWidth": "1",
"strokeOpacity": "0.8",
"detailedCategory": "power:line",
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=power_line.png",
"name": "Power Line",
"category": "groundHazard",
"filters": [
{
"name": "Ground Hazards",
"property": "show",
"active": true
}
],
"display": {
"category": "Ground Hazard",
"detailedCategory": "Power Line",
"title": "Ground Hazard",
"sections": [
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=power_line.png",
"title": "Power Hazard",
"text": "The highlighted area is believed to contain power infrastructure. Power infrastructure presents heightened risk of damage to your equipment and critical National infrastructure."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Summary",
"text": "Yellow zones indicate regions where operation of your drone may raise security, privacy or safety concerns."
}
],
"actions": []
}
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-2.6228303909301758,
53.529739379882813
],
"type": "Point"
},
"id": "6EB24E66D75083A4A135296C12BE004D79629818",
"properties": {
"hazardFactor": "40",
"hazardFactorName": "Warning",
"fillColor": "#ffbb00",
"strokeColor": "#b88702",
"fillOpacity": "0.35",
"strokeWidth": "1",
"strokeOpacity": "0.8",
"detailedCategory": "power:tower",
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=power_tower.png",
"name": "Power Pylon",
"category": "groundHazard",
"filters": [
{
"name": "Ground Hazards",
"property": "show",
"active": true
}
],
"display": {
"category": "Ground Hazard",
"detailedCategory": "Power Pylon",
"title": "Ground Hazard",
"sections": [
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=power_tower.png",
"title": "Power Hazard",
"text": "The highlighted area is believed to contain power infrastructure. Power infrastructure presents heightened risk of damage to your equipment and critical National infrastructure."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Summary",
"text": "Yellow zones indicate regions where operation may raise security, privacy or safety concerns."
}
],
"actions": []
}
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
[
[
-2.6234986782073975,
53.533077239990234
],
[
-2.6215133666992187,
53.528900146484375
],
[
-2.6183879375457764,
53.529270172119141
],
[
-2.6178712844848633,
53.529655456542969
]
]
],
"type": "Polygon"
},
"id": "557952B3668AC5DF5C583BE8E8C1840D97B5ABD4",
"properties": {
"hazardFactor": "40",
"hazardFactorName": "Warning",
"fillColor": "#ffbb00",
"strokeColor": "#b88702",
"fillOpacity": "0.35",
"strokeWidth": "1",
"strokeOpacity": "0.8",
"detailedCategory": "landuse:cemetery",
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=landuse_cemetery.png",
"name": "Wigan Borough Cemetery",
"category": "groundHazard",
"filters": [
{
"name": "Ground Hazards",
"property": "show",
"active": true
}
],
"display": {
"category": "Ground Hazard",
"detailedCategory": "Cemetery",
"title": "Wigan Borough Cemetery",
"sections": [
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Summary",
"text": "Yellow zones indicate regions where operation of your drone may raise security, privacy or safety concerns."
}
],
"actions": []
}
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
[
[
-3.235,
53.53694
],
[
-3.05278,
53.45944
],
[
-3.20139,
53.38583
],
[
-3.02778,
53.24083
],
[
-2.73028,
53.10722
]
]
],
"type": "Polygon"
},
"id": "616CB45B9DA924146E9A5483843B588B36F0AD31",
"properties": {
"hazardFactor": "60",
"hazardFactorName": "Danger",
"fillColor": "#ffffff",
"strokeColor": "#ffffff",
"fillOpacity": "0.2",
"strokeWidth": "1",
"strokeOpacity": "0.8",
"detailedCategory": "type:tma",
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=class_type_tma.png",
"airac": {
"to": "2019-08-15",
"from": "2019-07-18"
},
"altitudeFloor": {
"datum": "Msl",
"meters": 1066.7999983784639,
"feet": 3499.9999946799994
},
"altitudeCeiling": {
"datum": "Sps",
"meters": 7467.5999886492482,
"feet": 24499.99996276
},
"name": "MANCHESTER TMA 1",
"listOrderHint": "1000",
"category": "airspace",
"designator": "EGCC1",
"airspaceType": "TMA",
"filters": [
{
"name": "Upper Airspace",
"property": "show",
"active": false
},
{
"name": "Type TMA",
"property": "show",
"active": true
}
],
"display": {
"category": "airspace",
"detailedCategory": "Type TMA",
"title": "MANCHESTER TMA 1",
"sections": [
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Altitude",
"text": "This piece of airspace is in effect above 1067m / 3500ft MSL"
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Terminal control area",
"text": "Control area normally established at the confluence of ATS routes in the vicinity of one or more major aerodromes."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Regulated Airspace",
"text": "This airspace has a specific classification."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Summary",
"text": "Red zones are regulated high-risk areas."
}
],
"actions": []
}
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
[
[
-3.05278,
53.45944
],
[
-2.06667,
53.575
],
[
-2.83333,
53.53333
],
[
-3.05278,
53.45944
]
]
],
"type": "Polygon"
},
"id": "BC69E04789D9A790DB5B29B0EE2804D42E4FA12A",
"properties": {
"hazardFactor": "60",
"hazardFactorName": "Danger",
"fillColor": "#ffffff",
"strokeColor": "#ffffff",
"fillOpacity": "0.2",
"strokeWidth": "1",
"strokeOpacity": "0.8",
"detailedCategory": "class:d",
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=class_d.png",
"airac": {
"to": "2019-08-15",
"from": "2019-07-18"
},
"altitudeFloor": {
"datum": "Msl",
"meters": 761.99999884176,
"feet": 2499.9999961999997
},
"altitudeCeiling": {
"datum": "Msl",
"meters": 1066.7999983784639,
"feet": 3499.9999946799994
},
"name": "MANCHESTER CTA 1",
"listOrderHint": "600",
"category": "airspace",
"designator": "EGCC1",
"airspaceClass": "D",
"airspaceType": "CTA",
"filters": [
{
"name": "Upper Airspace",
"property": "show",
"active": false
},
{
"name": "Class D",
"property": "show",
"active": true
}
],
"display": {
"category": "airspace",
"detailedCategory": "Class D",
"title": "MANCHESTER CTA 1",
"sections": [
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Altitude",
"text": "This piece of airspace is in effect above 762m / 2500ft MSL"
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Regulated Airspace",
"text": "This airspace has a specific classification."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Summary",
"text": "Red zones are regulated high-risk areas."
}
],
"actions": []
}
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
[
[
-10,
54.56667
],
[
-9,
54.75
],
[
-8.25,
55.33333
]
]
],
"type": "Polygon"
},
"id": "11DD2D3CBA8992F29E49A277FC322D19FCD67066",
"properties": {
"hazardFactor": "60",
"hazardFactorName": "Danger",
"fillColor": "#ffffff",
"strokeColor": "#ffffff",
"fillOpacity": "0.2",
"strokeWidth": "1",
"strokeOpacity": "0.8",
"detailedCategory": "type:cta",
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=class_type_cta.png",
"airac": {
"to": "2019-08-15",
"from": "2019-07-18"
},
"altitudeFloor": {
"datum": "Sps",
"meters": 7467.5999886492482,
"feet": 24499.99996276
},
"altitudeCeiling": {
"datum": "Sps",
"meters": 20116.799969422464,
"feet": 65999.99989968
},
"name": "UPPER AIRSPACE CTA",
"listOrderHint": "1000",
"category": "airspace",
"designator": "EGUP",
"airspaceType": "CTA",
"filters": [
{
"name": "Upper Airspace",
"property": "show",
"active": false
},
{
"name": "Type CTA",
"property": "show",
"active": true
}
],
"display": {
"category": "airspace",
"detailedCategory": "Type CTA",
"title": "UPPER AIRSPACE CTA",
"sections": [
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Altitude",
"text": "This piece of airspace is in effect above FL244.9999996276"
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Control area",
"text": "A controlled airspace extending upwards from a specified limit above the earth."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Regulated Airspace",
"text": "This airspace has a specific classification."
},
{
"iconUrl": "https://aa-ne-prod-public-api.example.com//v1/map/icon?icon=warning.png",
"title": "Summary",
"text": "Red zones are regulated high-risk areas."
}
],
"actions": []
}
},
"type": "Feature"
}
],
"bbox": [
-2.6261,
53.5288,
-2.6201,
53.5308
],
"type": "FeatureCollection"
}
Based on the example GeoJSON the Layers button should contain checkboxes for:
Locations
Ground Hazards
Upper Airspace
Is this even possible?!
Create more than one L.GeoJSON instance, and leverage their filter option:
A Function that will be used to decide whether to include a feature or not. The default is to include all features:
function (geoJsonFeature) {
return true;
}
Note: dynamically changing the filter option will have effect only on newly added data. It will not re-evaluate already included features.
e.g.:
fetch("JSONMapPoints.json")
.then(function (response) { return response.json() })
.then(function (data) {
var locations = L.geoJson(data, {
filter: function(feat) { return feat.properties.filters.name === 'Location'},
/* etc */
});
var hazards = L.geoJson(data, {
filter: function(feat) { return feat.properties.filters.name === 'Hazard'},
/* etc */
});
var airspace = L.geoJson(data, {
filter: function(feat) { return feat.properties.filters.name === 'Air Space'},
/* etc */
});
});
With those different L.GeoJSON instances, adding them to a layers control is just a matter of calling addOverlay(), e.g.
layersControl.addOverlay(locations, "Locations");
layersControl.addOverlay(hazards, "Hazards);
layersControl.addOverlay(airspace, "Air Space");
Beware of scope, though. A newbie JS mistake is to assume that a variable will magically exist in the scope where it's needed. In this particular case, I would ensure that the layers control is already instantiated when the GeoJSON layers are created, and add them to said layers control.
A more convoluted approach would be to automatically detect the categories or filters, iterating through the GeoJSON structure to get them, e.g.:
fetch("JSONMapPoints.json")
.then(function (response) { return response.json() })
.then(function (data) {
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
var categories = new Set();
for (var i in data) {
var feature = data[i];
categories.add(feature.properties.filters.name);
}
Then iterate through the categories to programmatically create the L.GeoJSON instances, paying attention to the appropriate closures:
categories.forEach(function(category) {
var layer = L.geoJSON(data, {
filter: function(f){ f.properties.filters.name === category }
/* etc */ });
layersControl.addOverlay(layer, category);
});
// And we're done here.
});
This is a bit over-engineered for a dataset needing three filters, but would work nicely when there's a larger amount.
I've exported shapefile data to geoJson with Qgis, before i try published in leaflet map, but it's not working.
The code i've used is below, where geojson file is "gj2.geojson".
Where is wrong?
GEOJSON is like:
{
"type": "FeatureCollection",
"name": "gj2",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "DN": 976 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -36.647916182333311, -8.742916206999967 ], [ -36.647082848999979, -8.742916206999967 ], [ -36.647082848999979, -8.743749540333299 ], [ -36.647916182333311, -8.743749540333299 ], [ -36.647916182333311, -8.742916206999967 ] ] ] } },
{ "type": "Feature", "properties": { "DN": 971 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -36.647082848999979, -8.742916206999967 ], [ -36.646249515666646, -8.742916206999967 ], [ -36.646249515666646, -8.745416206999966 ], [ -36.647082848999979, -8.745416206999966 ], [ -36.647082848999979, -8.742916206999967 ] ] ] } },
{ "type": "Feature", "properties": { "DN": 966 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -36.646249515666646, -8.742916206999967 ], [ -36.645416182333307, -8.742916206999967 ], [ -36.645416182333307, -8.743749540333299 ], [ -36.646249515666646, -8.743749540333299 ], [ -36.646249515666646, -8.742916206999967 ] ] ] } },
{ "type": "Feature", "properties": { "DN": 975 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -36.647916182333311, -8.743749540333299 ], [ -36.647082848999979, -8.743749540333299 ], [ -36.647082848999979, -8.744582873666634 ], [ -36.647916182333311, -8.744582873666634 ], [ -36.647916182333311, -8.743749540333299 ] ] ] } },
{ "type": "Feature", "properties": { "DN": 965 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -36.646249515666646, -8.743749540333299 ], [ -36.645416182333307, -8.743749540333299 ], [ -36.645416182333307, -8.744582873666634 ], [ -36.646249515666646, -8.744582873666634 ], [ -36.646249515666646, -8.743749540333299 ] ] ] } },
{ "type": "Feature"....
THE CODE i've try:
<!DOCTYPE html>
<html>
<head>
<title>GeoJSON tutorial - Leaflet</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.2.0/dist/leaflet.css" integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.2.0/dist/leaflet.js" integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log==" crossorigin=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id='map'></div>
<script src="geojson.js" type="text/javascript"></script>
<script>
var map = L.map('map').setView([-9,-36], 13);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.light'
}).addTo(map);
var geojson = L.geoJSON('gj2.geojson').addTo(map);
</script>
</body>
</html>
The problem is that you are trying to specify a filename as a parameter to L.geoJSON(..) but it needs a Javascript object containing the geoJson definition. Add the following to the beginning of your geoJson file (geojson.js):
var myGeoJson =
So that the file starts like this:
var myGeoJson = {
"type": "FeatureCollection",
"name": "gj2",
...
...
And then add a semicolon at the end of the file. This way you have a variable 'myGeoJson' representing the geoJson definition.
Then change this line:
var geojson = L.geoJSON('gj2.geojson').addTo(map);
to this:
var geojson = L.geoJSON(myGeoJson).addTo(map);
The Leaflet GeoJSON example page has a similar example towards the top of the page here: Using GeoJSON with Leaflet
I'm reading a GeoJSON file and importing the polygons (and other stuff) into mapbox-gl draw using draw.set(geoJSON). How do I color individual polygons by an attribute in the properties of a feature. Example:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
//"id": "the most unique id in the world",
"properties": {
"class_id": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
79.30961608886719,
61.57192958204744
],
[
79.34309005737303,
61.57192958204744
],
[
79.34309005737303,
61.57871162332267
],
[
79.30961608886719,
61.57871162332267
],
[
79.30961608886719,
61.57192958204744
]
]
]
}
},
{
"type": "Feature",
"properties": {
"class_id": 2
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
79.35201644897461,
61.58271478278019
],
[
79.35115814208984,
61.573972521656835
],
[
79.38188552856444,
61.57192958204744
],
[
79.35201644897461,
61.58271478278019
]
]
]
}
},
}
The idea is that we color the features with class_id = 1 as red, class_id = 2 as blue, and class_id = 3 as green. How do we do that?
You need set userProperties to true for the properties of a feature will be available for styling. And use prefix user.
And use case expression:
var Draw = new MapboxDraw({
userProperties: true,
styles: [{
'id': 'gl-draw-polygon-fill-inactive',
'type': 'fill',
'filter': ['all', ['==', 'active', 'false'],
['==', '$type', 'Polygon'],
['!=', 'mode', 'static']
],
'paint': {
'fill-color': [
"case",
['==', ['get', "user_class_id"], 1], "#00ff00",
['==', ['get', "user_class_id"], 2], "#0000ff",
'#ff0000'
],
'fill-outline-color': '#3bb2d0',
'fill-opacity': 0.5
}
}...
[ http://jsfiddle.net/5Lotf4ka/ ]