How to show nodes and ways on OpenStreetMap using Leaflet? - openstreetmap

I was able to correctly query data from the OSM API.
Then, I've been able to show nodes on the map with the following codes:
for (let i = 0; i < data.elements.length; i++) {
let pos;
let marker;
const e = data.elements[i];
if (e.id in this._ids) {
continue;
}
this._ids[e.id] = true;
if (e.type === 'node') {
pos = L.latLng(e.lat, e.lon);
}
else{
pos = L.latLng(e.center.lat, e.center.lon);
}
if (this.options.markerIcon) {
marker = L.marker(pos, { icon: this.options.markerIcon });
}
else {
marker = L.circle(pos, 80, {
stroke: false,
fillColor: '#0000ff',
fillOpacity: 0.9
});
}
}
which gives me the following visual output:
MyOutput-Image
How can I also show lines how it is done on OverpassTurbo:
OverpassTurbo-Image

What does your OSM API query looks like? I assume it's an overpass query, in which case, you might have put an out center at the end, which returns the center of each way.
Over pass out statement :
center: This adds only the centre of the above mentioned bounding box to ways and relations. Note: The center point is not guaranteed to lie inside the polygon (example).
Therefore, each way has a long/lat which is in the middle, which is what you plot as a marker using the e.center.lat
example of a way with out center :
{
"type": "way",
"id": 2263973,
"center": {
"lat": 51.5070645,
"lon": -0.1359499
},
"nodes": [
9789823,
9789817,
3933850766,
...
],
"tags": {...}
}
You could either go through each associated node, and look them up and get the lat and lon. Or use the out geom option which returns the coordinates or the nodes directly:
{
"type": "way",
"id": 2263973,
"bounds": {
"minlat": 51.5064574,
"minlon": -0.1363268,
"maxlat": 51.5076716,
"maxlon": -0.1355731
},
"nodes": [
9789823,
9789817,
...
],
"geometry": [
{ "lat": 51.5064574, "lon": -0.1355731 },
{ "lat": 51.5069432, "lon": -0.1360143 },
{ "lat": 51.5072896, "lon": -0.1363027 },
{ "lat": 51.5073314, "lon": -0.1363255 },
{ "lat": 51.5073531, "lon": -0.1363268 },
{ "lat": 51.5073965, "lon": -0.1363228 },
{ "lat": 51.5074324, "lon": -0.1363027 },
{ "lat": 51.5074691, "lon": -0.1362638 },
{ "lat": 51.5076716, "lon": -0.1356893 }
],
"tags": {...}
}
Notice how you also get the min and max values for latitude and in longitude in a bounds attribute. You can then go through the geometry array, extract all the lat and lon in a latlngs array, and use:
var polyline = L.polyline(latlngs, {color: 'blue'}).addTo(map); to draw a line. See Leaflet doc on polyline
And you can get this, for [highway~motorway]

Related

How can I measure the distance between where I'm at the moment and the first circle I have drawn on the map?

I have an application where I can draw a route so a simulation start where a boat is following that route. What I want to know is how I could measure the distance between me and the first Circle that I draw in the route(so the start point of the boat)
the circle on the right is my current location and the circle on the left is the first dot that I have drawn.
this is how I get my current location
navigator.geolocation.getCurrentPosition(position => {
console.log(position.coords.latitude, position.coords.longitude);
});
and this is how I draw and get the coordinates of the route
// gives the function to be able to draw lines in the map to make a route
var draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
line_string: true
},
styles: [
// ACTIVE (being drawn)
// line stroke
{
"id": "gl-draw-line",
"type": "line",
"filter": ["all", ["==", "$type", "LineString"], ["!=", "mode", "static"]],
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#3b9ddd",
"line-dasharray": [0.2, 2],
"line-width": 4,
"line-opacity": 0.7
}
},
{
"id": "gl-draw-polygon-and-line-vertex-halo-active",
"type": "circle",
"filter": ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
"paint": {
"circle-radius": 10,
"circle-color": "#FFF"
}
},
// vertex points
{
"id": "gl-draw-polygon-and-line-vertex-active",
"type": "circle",
"filter": ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
"paint": {
"circle-radius": 6,
"circle-color": "#3b9ddd",
},
}
]
});
map.addControl(new mapboxgl.FullscreenControl());
map.addControl(geolocate)
map.addControl(new mapboxgl.NavigationControl());
map.addControl(draw);
// add create, update, or delete actions
map.on('draw.create', updateRoute);
map.on('draw.update', updateRoute);
map.on('draw.delete', removeRoute);
// use the coordinates you just drew to make your directions request
function updateRoute() {
removeRoute(); // overwrite any existing layers
var data = draw.getAll();
var lastFeature = data.features.length - 1;
coords = data.features[lastFeature].geometry.coordinates;
}
So basically I want to measure between the distance between position.coords.latitude, position.coords.longitude and coords[0]
You can calculate the distance between two coordinates using LngLat.distanceTo()
Something like:
const start = new Mapbox.LngLat(...coords);
const end = new Mapbox.LngLat(position.coords.longitude, position.coords.latitude);
const dist = start.distanceTo(end);

Getting speed limit based on a single coordinate using here REST API

I want to retrieve speed limit based on a single coordinate(of a road in the USA) using HERE REST API(not PDE). So, I only got the api_key(no app code). I searched and found some solutions which were deprecated and required the PDE api. If anybody knows the latest solution please let me know. Thanks. Anyway I got something which gives speed limit for two coordinates(not my case) and if I put the two coordinates same I get the wrong(very small) value for speed limit. which is given bellow:
https://router.hereapi.com/v8/routes?apiKey=APP_KEY&transportMode=car&origin=-37.956650,145.220673&destination=-37.956650,145.220673&spans=speedLimit&return=polyline
{
"routes": [
{
"id": "0a903f38-aecd-4850-a87e-0848c7583b8a",
"sections": [
{
"id": "4aa23cb0-0209-4e31-bca8-32f4cfb9f98e",
"type": "vehicle",
"departure": {
"time": "2021-02-25T12:52:34+11:00",
"place": {
"type": "place",
"location": {
"lat": -37.9566675,
"lng": 145.2206532
},
"originalLocation": {
"lat": -37.9566501,
"lng": 145.220673
}
}
},
"arrival": {
"time": "2021-02-25T12:52:34+11:00",
"place": {
"type": "place",
"location": {
"lat": -37.9566675,
"lng": 145.2206532
},
"originalLocation": {
"lat": -37.9566501,
"lng": 145.220673
}
}
},
"polyline": "BG1j2soC6iy_0IAA",
"spans": [
{
"offset": 0,
"speedLimit": 27.7777786
}
],
"transport": {
"mode": "car"
}
}
]
}
]
}
Is there any latest rest api service provided by Here that gives speed limit for a geo coordinate(not the pde one)?

dc.js and dc.leaflet.js; wrong type of map returned

I'm trying to make a dashboard using dc.js. It has a few charts and a choroplethChart. It all worked fine, but I needed to add leaflet to the map. I've followed this sample and used dc.leaflet.js library, but instead of choroplethChart it returns Markers (see picture)
(this is how it looked before using leaflet)
The code is below and this is where geojson resides:
var usChart = dc_leaflet.choroplethChart("#us-chart");
usChart.width(1000)
.height(450)
.dimension(stateDim)
.group(totalDemandByStation)
.center([ 51.4963, -0.143 ])
.zoom(11)
.geojson(statesJson)
.colors(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"])
.colorDomain([0, max_state])
.colors(['#fff7ec','#fee8c8','#fdd49e','#fdbb84','#fc8d59','#ef6548','#d7301f','#b30000','#7f0000'])
.colorAccessor(function(d,i) {
return d.value;
})
.featureKeyAccessor(function(feature) {
return feature.properties.name;
})
.renderPopup(true)
.popup(function(d,feature) {
return feature.properties.name+" : "+d.value;
})
.legend(dc_leaflet.legend().position('bottomright'));
//https://github.com/dc-js/dc.js/issues/419
usChart.on("preRender", function(chart) {
chart.colorDomain(d3.extent(chart.data(), chart.valueAccessor()));
})
usChart.on("preRedraw", function(chart) {
chart.colorDomain(d3.extent(chart.data(), chart.valueAccessor()));
})
I'm not an expert here, but the choropleth is expecting map data rather than point data. The features in your geojson are points:
{
"crs": {
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
},
"type": "name"
},
"features": [
{
"geometry": {
"coordinates": [
-0.013071299999987,
51.510716
],
"type": "Point"
},
"properties": {
"id": "940GZZDLALL",
"labelX": 30,
"lines": [
{
"name": "DLR"
}
],
"name": "All Saints",
"tfl_intid": 850
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
0.061052699999989,
51.51427850000001
],
"type": "Point"
},
"properties": {
"id": "940GZZDLBEC",
"labelX": -30,
"lines": [
{
"name": "DLR"
}
],
"name": "Beckton",
"tfl_intid": 895
},
"type": "Feature"
},
...
To draw a choropleth, Leaflet will need features whose types are Polygon.
So my guess is that Leaflet is punting and drawing markers

overpass-api ways query include coordinates

I'm trying to query Hotels in a specific area. Unfortunately I need to query nodes and ways, because some Hotels are only stored as way and some as nodes.
http://overpass.osm.rambler.ru/cgi/interpreter?data=%28way[tourism~hotel]%28around:5000,50.10707,8.76040%29%29;out;
The result of a way-query doesn't contain any coordinates, only lots of node-refs:
<nd ref="1438833423"/>
Is there a way also to include the coordinates of the nodes in the output or do have to start lots of additional queries for each node?
Tom
Yes, there are several ways to also get geometries for non-node features via the Overpass API. The easiest is probably to use the center output mode (replace out; with out center;): http://overpass-turbo.eu/s/4O4. Other options would be to fetch the node references in a recursion step: http://overpass-turbo.eu/s/4O5 or using the full geometry output mode.
PS: Don't forget that a hotel can also be mapped as a (multipolygon) relation in OSM, not just as a node or way.
Necromancing.
And to actually answer the question:
[out:json];
way(29858799);
out ids geom;
Which will give you the geometries for each point in the selected way.
{
"version": 0.6,
"generator": "Overpass API",
"osm3s": {
"timestamp_osm_base": "2017-10-06T13:59:02Z",
"copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
},
"elements": [
{
"type": "way",
"id": 29858799,
"bounds": {
"minlat": 47.3604067,
"minlon": 8.5342631,
"maxlat": 47.3612503,
"maxlon": 8.5352457
},
"geometry": [
{ "lat": 47.3612503, "lon": 8.5351944 },
{ "lat": 47.3612252, "lon": 8.5342631 },
{ "lat": 47.3610145, "lon": 8.5342755 },
{ "lat": 47.3610212, "lon": 8.5345227 },
{ "lat": 47.3606405, "lon": 8.5345451 },
{ "lat": 47.3606350, "lon": 8.5343411 },
{ "lat": 47.3604067, "lon": 8.5343545 },
{ "lat": 47.3604120, "lon": 8.5345623 },
{ "lat": 47.3604308, "lon": 8.5352457 },
{ "lat": 47.3606508, "lon": 8.5352328 },
{ "lat": 47.3606413, "lon": 8.5348784 },
{ "lat": 47.3610383, "lon": 8.5348551 },
{ "lat": 47.3610477, "lon": 8.5352063 },
{ "lat": 47.3612503, "lon": 8.5351944 }
]
}
]
}
https://overpass-turbo.eu/
All the options for out are listed here:
https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Print_.28out.29

addingand using marker variable with geoJsonData and markercluster with leaflet.js

I've been searching for hours and hours now, and i'm still stuck. I have a feeling it is something easy/stupid i'm missing.
I am using the example of markercluster from GitHub. I have 2 different markers (simply 2 different colors) that i would like to show which i would defined in the json format.
I have followed guide from leaflet site to define different markers.
I added my variable to the json part, but i can not figure out how to make the map load the different markers. It's either giving me no map or error; or it still uses the default blue marker.
here is my code:
<script type="text/javascript">
var geoJsonData = {
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id":"1", "properties": { "address": "2","marker": "cmIcon"}, "geometry": { "type": "Point", "coordinates": [175.2209316333,-37.8210922667 ] } },
{ "type": "Feature", "id":"2", "properties": { "address": "151","marker": "otherIcon" }, "geometry": { "type": "Point", "coordinates": [175.2238417833,-37.80975435 ] } },
{ "type": "Feature", "id":"3", "properties": { "address": "21","marker": "cmIcon" }, "geometry": { "type": "Point", "coordinates": [175.2169955667,-37.818193 ] } },
{ "type": "Feature", "id":"4", "properties": { "address": "14","marker": "otherIcon" }, "geometry": { "type": "Point", "coordinates": [175.2240856667,-37.8216963 ] } },
{ "type": "Feature", "id":"5", "properties": { "address": "38B","marker": "cmIcon" }, "geometry": { "type": "Point", "coordinates": [175.2196982333,-37.8188702167 ] } },
{ "type": "Feature", "id":"6", "properties": { "address": "38","marker": "otherIcon" }, "geometry": { "type": "Point", "coordinates": [175.2209942 ,-37.8192782833 ] } }
]
};
var cloudmade = L.tileLayer('http://{s}.tile.cloudmade.com/{key}/997/256/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © 2011 OpenStreetMap contributors, Imagery © 2011 CloudMade',
key: 'BC9A493B41014CAABB98F0471D759707'
});
var LeafIcon = L.Icon.extend({
options: {
shadowUrl: 'marker/marker-shadow.png',
iconSize: [32, 32],
shadowSize: [36, 20],
iconAnchor: [22, 94],
shadowAnchor: [4, 62],
popupAnchor: [-3, -76]
}
});
var cmIcon = new LeafIcon({iconUrl: 'marker/marker-cm.png'}),
otherIcon = new LeafIcon({iconUrl: 'marker/marker-others.png'});
var map = L.map('map')
.addLayer(cloudmade);
var markers = new L.MarkerClusterGroup();
var geoJsonLayer = L.geoJson(geoJsonData, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.address);
}
});
markers.addLayer(geoJsonLayer);
map.addLayer(markers);
map.fitBounds(markers.getBounds());
function onLocationFound(e) {
var radius = e.accuracy / 2;
L.marker(e.latlng).addTo(map)
.bindPopup("Vous etes ici").openPopup();
L.circle(e.latlng, radius).addTo(map);
}
function onLocationError(e) {
alert(e.message);
}
map.on('locationfound', onLocationFound);
map.on('locationerror', onLocationError);
map.locate({setView: true, maxZoom: 16});
</script>
I suspect that i need to tell leaflet to get the marker variable, probably in
var geoJsonLayer = L.geoJson(geoJsonData, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.address);
}
});
but i cannot make it work.
i even tried as suggested some where else:
var geoJsonLayer = L.geoJson(geoJsonData, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.address),
layer.setIcon(feature.properties.marker);
}
});
i'm still getting an error: Uncaught TypeError: Object cmIcon has no method 'createIcon'
does anybody have an idea on how to do that?
Any help would be greatly appreciated.
Thank you in advance.
You need to tell the MarkerClusterGroup how to create the icon. The marker cluster docs show an example with a DivIcon like this:
var markers = new L.MarkerClusterGroup({
iconCreateFunction: function(cluster) {
return new L.DivIcon({ html: '<b>' + cluster.getChildCount() + '</b>' });
}
});
You can use your custom icon instead like this:
var markers = new L.MarkerClusterGroup({
iconCreateFunction: function(cluster) {
// decide which icon you want to use
return (cluster.getChildCount() > 10) ? cmIcon : otherIcon;
}
});