I could not find json file in OSMBuilding - leaflet

I make clickable buildings with OSM Buildings. When I click a building, I get Building ID. However, I could not get height of a building because I cannot fing JSON file to get this value.
Codes are here:
var map = new L.Map('map');
map.setView([52.52111, 13.40988], 16, false);
new L.TileLayer('https://{s}.tiles.mapbox.com ...
https://codepen.io/osmbuildings/pen/JdaaRM
To sum up, I want to get height of a building with using OSM Building.

The height is contained in the JSON file.
See this example here: https://b.data.osmbuildings.org/0.2/anonymous/tile/16/35210/21491.json. It contains a properties field which in turn contains height and levels:
"id": "w24273225",
"type": "Feature",
"properties": {
"height": 65,
"levels": 17
},
"geometry": {
"type": "Polygon",
"coordinates": [
[...]
]
}
The height and level information is only present if the building in OSM actually has this information. This isn't the case for every building.

Related

Add Layer Groups and Markers in a loop ( from json)

I just started working with Leaflet. I want to create a map for a game, so this is a map created with L.CRS.Simple.
I have been able to set the map image, and add marker manually.
Now, I want to create markers and layers groups dynamicly from a json file that I generate in PHP from a sqlite database.
My json is this one for now : https://grounded.dubebenjamin.com/api/markers.json
My json is a list of type(layergroup) inside which I have put another object containing all the markers for the type(layergroup) :
Structure :
{
"landmarks": {
"id": 1,
"slug": "landmark",
"type": "Landmarks",
"markers": [
{"id": 1, "title": "Title of marker 1 of layergroup1"},
{"id": 2, "title": "Title of marker 2 of layergroup1"}
]
},
"science": {
"id": 2,
"slug": "science",
"type": "Science Points",
"markers": [
{"id": 1, "title": "Title of marker 1 of layergroup2"}
]
}
}
= Where landmarks and science are layergroup.
From this json, my plan was to have a first loop, where for each type I want to create a layer group, then create the markers for this layer group and assign the markers to the layer groups.
Manually, I understand the usage of L.marker and L.layerGroup, but where I am stuck, is how to do that in a loop, and defined the layoutgroup name from the json data.
From now, I have just been able to create the marker, but not the layer group. You can see my progress here: https://grounded.dubebenjamin.com/
If you need more precision, just ask me.
Use this code:
var layergroups = {};
fetch('https://grounded.dubebenjamin.com/api/markers.json').then(response => response.json())
.then((data)=>{
layergroups = {};
for(type in data){
var lg = L.layerGroup().addTo(map)
layergroups[type] = lg;
if(data[type].markers){
var markers = data[type].markers;
markers.forEach((marker)=>{
L.marker([marker.x,marker.y]).bindPopup(marker.desc).addTo(lg);
})
}
}
})
First fetch the data from the server, then loop through the layergroups and add them to the map and then to the Object/Array.
Then you create the markers and add them to the layergroup.
You can get the layergroups over the Object layergroups but keep in mind that the request is async.

Adding custom markers to the mapbox gl

I would like to add a custom marker to my map. I am using a mapbox gl script.
The only documentation that I found related to this topic is this one https://www.mapbox.com/mapbox-gl-js/example/geojson-markers/.
I tried to customize given example and I managed to add a marker and modify it a little changing the size, but since I don't understand all the parameters, I don't know how to add my own marker. Is there any documentation that is more detailed?
Here is my code:
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiYWl3YXRrbyIsImEiOiJjaXBncnI5ejgwMDE0dmJucTA5aDRrb2wzIn0.B2zm8WB9M_qiS1tNESWslg';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/aiwatko/cipo0wkip002ddfm1tp395va4', //stylesheet location
center: [7.449932,46.948856], // starting position
zoom: 14.3, // starting zoom
interactive: true
});
map.on('load', function () {
map.addSource("markers", {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [7.4368330, 46.9516040]
},
"properties": {
"title": "Duotones",
"marker-symbol": "marker",
}
}]
}
});
map.addLayer({
"id": "markers",
"type": "symbol",
"source": "markers",
"layout": {
"icon-image": "{marker-symbol}-15",
"icon-size": 3
}
});
});
</script>
Thanks in advance!
Oktawia
There are two ways to customize markers in Mapbox.
Raster Markers in Mapbox
See this link on Mapbox.com for Custom marker icons. That example shows how to use a .png as a marker.
SVG Markers in Mapbox
You are pretty close to modifying the icons, but take some time to familiarize yourself with the parameters.
The icon-image may be the harder one to understand. It takes the property "marker-symbol": "marker" from the GeoJson, and "icon-image": "{marker-symbol}-15", to create the final result of marker-15.
This brings up a further question: where/how are the markers defined?!?
The markers also come from Mapbox and are called Maki Icons. You can change the "marker-symbol" to aquarium or cafe to see the results.
From the Mapbox GL Style Reference
icon-size — Scale factor for icon. 1 is original size, 3 triples the size.
icon-image — A string with {tokens} replaced, referencing the data property to pull from.

Mapbox Matching API switched coordinates

Edit: I reformatted the question as it was pointed out to me that the problem is not in the Mapbox API.
As of a few days ago an application using mapbox matching API coupled with leaflet.js started drawing polylines on the other side of the planet.
While the path it self looks as it should be, leaflet drew it on another continent.
The Mapbox Matching API returns the response following GEOJson standard as:
{
"code": "Ok",
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": "e`s`YmyazuAg`#y]uo#ej#eQcOkFkEyCeCaYmUuFoMkWgOo[_ReGqDoGyDmVaPaU|RoDlDsa#z`#sTdSXJf#j#??Jr#Mp#ML[Z}#ZiAHgAK]MaJzJsUbWkSxRyHpHuLhLuFzFqDrDq[`\\oPrPyK|KmU|TuLzKyClCr#ZZf#Fj#Ij#e#b#zn#fZ}Zf]gCdKmHxQErC[jV",
"properties": {
"confidence": 0.45413768894813844,
"distance": 1366.4,
"duration": 243.3,
"matchedPoints": [
[13.658131, 45.532583],
[13.659851, 45.534127],
[13.661445, 45.535438],
[13.662397, 45.535398],
[13.663582, 45.534237],
[13.666378, 45.531441],
[13.666457, 45.529215]
],
"indices": [0, 1, 2, 3, 4, 5, 6]
}
}]
}
Using the code below to draw the layer with leaflet.js results in a polyline drawn on a wrong position.
L.mapbox.mapmatching(geojson, options, function (error, layer) {
layer.addTo(map);
layer.setStyle({
color: '#3c8dbc',
weight: 4,
opacity: 0.8
});
//fit bounds to added layer
map.fitBounds(layer.getBounds());
The result being:
While the polyline it self is as it should be, the position is not.
I speculate the problem is that leaflet expect the coordinates to be in format [latitude, longitude] while the Mapbox matching API returns GEOJson format namely [longitude, latitude].
Should I manually switch the coordinates of the response before drawing it or am I just doing it wrong?
Thanks.
Edit 2 : It appears the problem is in fact in different formats. More about it can be found in this post
Changing the order of the coordinates in the returned object is very tedious hence an elegant solution to this would be greatly appropriated.

Missing info of feature when getting it from Leaflet's event.layer.toGeoJSON()

I'm using L.geoJson and adding layer to my map, then with items.on('click', function (event) {}) displaying info of selected object which is stored in event.layer (getting info with toGeoJSON()).
Problem is, when there are some of the items, everything seems to work, but now when there are >1000 polygons, some of the data when using on('click') does not contain my info of the feature inside event.layer.
What could be a problem?
ADDITIONAL INFO:
Our GeoJSON looks something like this, it has additional data like ID and various properties.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": 1,
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
"properties": {"prop1": "value1"}
},
{
"type": "Feature",
"id": 2,
"geometry": {"type": "Point", "coordinates": [142.0, 15.5]},
"properties": {"prop1": "value2"}
}
]
}
I put everything on a map:
data = L.geoJson(data);
allItems.clearLayers().addLayer(data);
Features are displayed on a map.
Then I listen for clicks on the features on the map:
allItems.on('click', function (event) {
// On many of the features this is empty,
// on some data can be retrieved.
// On some that doesn't have ID, properties
// are empty too
console.log(event.layer.toGeoJSON().id);
});
GeoJSON has been checked and ID and properties ARE THERE.
Here's a little explanation about how to handle click events on L.GeoJSON layer and/or it's features. I've commented the code to explain what is going on and added an example on Plunker for you so you can test the concept.
Example on Plunker: http://plnkr.co/edit/TcyDGH?p=preview
Code:
var geoJsonLayer = L.geoJson(data, {
// Executes on each feature in the dataset
onEachFeature: function (featureData, featureLayer) {
// featureData contains the actual feature object
// featureLayer contains the indivual layer instance
featureLayer.on('click', function () {
// Fires on click of single feature
console.log('Clicked feature layer ID: ' + featureData.id);
});
}
}).on('click', function () {
// Fires on each feature in the layer
console.log('Clicked GeoJSON layer');
});
As for your code, i'm quite confused as to where the allItems comes from. A layerGroup or something like that? Trying to capture clicks on individual features in the GeoJSON layer on that object won't work, because it won't be able to differentiate between the features. Same goes for the handler on the GeoJSON layer, it will fire, but won't know which feature you clicked. The handler in the onEachFeature function will. But i'm assuming you understand by now if your understanding the code/example above.

Definition of a Leaflet layer

What exactly does a layer represent in the Leaflet Mapping Library?
Conceptually, to me a layer would represent a single tier of some type of feature or object; for example all image tiles representing the base level map would be represented on a single layer, a set of polygons representing states in the US may be on their own separate layer.
Specifically looking at L.GeoJSON.addGeoJSON(geojson), it reads that each new polygon created is placed in it's own layer (and then maybe merged with the layer you're calling the method on?). My use case is that I need to add many geoJSON objects one at a time and want to ensure I'm not creating many unnecessary layers (or if I am, if this is actually a bad thing).
Thank you.
In Leaflet anything that can be added to the map is a layer. So polygons, circles, markers, popups, tiles are all layers. You can combine layers in a L.LayerGroup (or FeatureGroup), if you for example want to treat a set of polygons as a single layer. So maybe your interpretation of layers matches better with what is modelled by L.LayerGroup in Leaflet.
L.GeoJSON is a LayerGroup (specifically a FeatureGroup) that is initialized from GeoJSON. Each new polygon is added to the L.GeoJSON LayerGroup using addLayer, which is the method for adding anything (that is a layer) to a LayerGroup. It does not create a new layer for each polygon (other than the L.Polygon which is already considered a layer). It only creates new FeatureGroups (LayerGroups) for a GeometryCollection and MultiPoints, which (I assume) is in order to preserve the structure from the GeoJSON.
If you want to add geoJSON objects to the same LayerGroup one at a time, you can just call L.GeoJSON.geometryToLayer to convert your GeoJSON object, and then add it to your LayerGroup using L.LayerGroup.addLayer.
As you mentioned, "layer" is a concept coming from a wider scope than simply the Leaflet implementation.
It is an abstract concept of "collection" in the context of Geospatial data.
A tier is also an alternative name, but I see "layer" being used more, in several standards & technologies.
The first lines here describe it simply enough:
https://doc.arcgis.com/en/arcgis-online/reference/layers.htm
In the context of Leaflet you can have as many layers as you want and it is not necessary to "spare" them.
More than thinking to optimization of the technical implementation, I'd put effort more in trying to identify "layers" (according to your business domain) as logical-groups of geospatial data that belongs together.
Specifically looking at L.GeoJSON.addGeoJSON(geojson), it reads that each new polygon created is placed in its own layer (and then maybe merged with the layer you're calling the method on?).
one Leaflet layer => one GeoJSON Feature (or set of Feature, given that FeatureCollection extends Feature).
there will be no merge: Leaflet will replace the whole layer with newly generated geospatial data, when you add GeoJSON data. Merging might be possible with custom implementation, but I don't know if it is advisable.
My use case is that I need to add many geoJSON objects one at a time and want to ensure I'm not creating many unnecessary layers (or if I am, if this is actually a bad thing).
It is not a bad thing per-sé, to have many layers; as long as concrete performance concerns don't arise.
If you want to reduce the number of layers, then put effort in modelling the structure of your GeoJSON so that most of the related objects are contained in a single GeoJSON/Layer (grouping by domain), instead of having a layer for each individual object (grouping by technical implementation).
E.g. a layer related to "risk areas"
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"title": "virus spread area",
"risk": "high"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
114.521484375,
30.89279747750818
],
[
113.89251708984374,
30.64972717137329
],
[
114.28253173828124,
30.21635515266855
],
[
114.521484375,
30.89279747750818
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Wuhan Institute of Virology",
"risk": "high"
},
"geometry": {
"type": "Point",
"coordinates": [
114.35462951660156,
30.543338954230222
]
}
}
]
}
instead of having one layer for the Polygon:
{
"type": "Feature",
"properties": {
"title": "virus spread area",
"risk": "high"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
114.521484375,
30.89279747750818
],
[
113.89251708984374,
30.64972717137329
],
[
114.28253173828124,
30.21635515266855
],
[
114.521484375,
30.89279747750818
]
]
]
}
}
and a separated (but actually related) one for the Point:
{
"type": "Feature",
"properties": {
"name": "Wuhan Institute of Virology",
"risk": "high"
},
"geometry": {
"type": "Point",
"coordinates": [
114.35462951660156,
30.543338954230222
]
}
}
(imho) both features conceptually belong to the same Layer.