Mapbox gl directions API - mapbox

So I am writing an app which allows an admin user to create a journey around a specific location with different stopping points.
For displaying a map, adding markers, flyTo location and etc I am using Mapbox GL.
I was using cURL implementation of Mapbox API to get driving directions and then draw a line on a map
So as an example of a cURL call I recieve a list of coordinates which represent my directions.
The Problem comes when I try to connect these points on a map.
As an example of HTML with some JS
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.18.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.18.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = '<ACCESS TOKEN>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v8',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addSource("route", {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-155.088899,19.722942],[-155.08565,19.72472],[-155.084661,19.723701],[-155.083569,19.723139],[-155.079557,19.722262],[-155.074227,19.721938],[-155.069939,19.722545],[-155.070061,19.721225],[-155.07007,19.711726]
]
}
}
});
map.addLayer({
"id": "route",
"type": "line",
"source": "route",
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
});
</script>
</body>
</html>
You can see a set of coordinates that will be connected to draw a line. I was wondering if there is a way to connect these points so that the line will follow only the road (for driving)?
To explain it better, this is a close zoom of the output
I know it's quite generic explanation of my problem, but I hope it's understandable.
I have been trying to do some magic with Mapbox Gl Directions API but no luck, as I have to add a contoller which I dont want to. I only need to draw a route and not allow a public user to be able to modify it.
Any advices?

Not sure if I understood correctly, but when you send you request to get directions include in the url 'overview=full'. This will return a more detailed path, so you have no need to use Matching API after.
Example:
`https://api.mapbox.com/directions/v5/mapbox/driving/-74.50,40;-80,50?overview=full&geometries=geojson&access_token=

I've been trying to do this today and succeded to get directions into a map and remove the start and end control by pulling down the mapbox-gl-directions project from git hub and making a minor mod to src/directions.js
I commented out lines 48 to 52
// Add controllers to the page
//new Inputs(inputEl, store, this.actions, this.map);
//new Instructions(directionsEl, store, {
// hoverMarker: this.actions.hoverMarker,
// setRouteIndex: this.actions.setRouteIndex
//}, this.map);
This was relatively easy to test with the setup in npm and my own test files by running the npm setup as per https://github.com/mapbox/mapbox-gl-directions/blob/master/CONTRIBUTING.md
npm install & npm start & open http://localhost:9966/example/
I also added the line:
localStorage.setItem('MapboxAccessToken', '<TOKEN HERE>');
below the require('../); in example/index.js. While you have the example running in npm you can access a new bundled version of the plugin from http://localhost:9966/example/bundle.js
I was then able to change the line
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-directions/v2.0.0/mapbox-gl-directions.js'></script>
to
<script src='http://localhost:9966/example/bundle.js'></script>
There's a whole bunch of magic going in on in the example runner in npm which I don't know anything about but it all just worked on my machine. Hope this helps. The directions line works with animations and pitch, zoom and bearing animations. See a screen grab below:
UPDATE: Adding reply to here so I can show a screen grab. #Andrejus, as this hack relies on the mapbox gl directions plugin behaviour rather than drawing the paths from scratch you get road following built in and you have access to the directions API to add waypoints to do your A->B->C->D as so:
map.on('load', function() {
directions.setOrigin(start);
directions.addWaypoint(0, [-0.07571203, 51.51424049]);
directions.addWaypoint(1, [-0.12416858, 51.50779757]);
directions.setDestination(end);
});
The documentation says you can have up to 25 way points.
The mapbox-gl-directions plugin doesn't have an option to turn off the on-screen controls, they are added in the onAdd(map) method of the Directions class which is called when the directions are added to the map. I wanted to see if I could remove them earlier and was experimenting, thus the hack. To make a flexible solution, it might be possible to add an option passed in to the constructor of the Directions class. There are other options passed in there although these seem to be bound to parameters for the call to the Directions API:
var directions = new mapboxgl.Directions({
unit: 'metric',
profile: 'cycling'
});
So there might be a better solution than that. I've been using Mapbox for <1 day and don't know much about how its written or structured.
Note also that the code alterations are in a plugin, not the core of mapbox gl, so relatively isolated. BTW this plugin is a wrapper for the cURL API you were calling which returned the array of points. Presumably the source on GitHub will include code which does the road following where it renders the line, if that's definitely what you want to do.

Actually it was easier that I thought, All I had to do is to get a response from directions API, and pass a returned array to Mapbox Map Matching, that way it returns a perfect route. All documented in Mapbox API

Related

How to use Ordance Survey vector tiles with React-Leaflet?

I'm unsure of the correct syntax to add Ordnance Survey vector tiles to a React-Leaflet application.
The example code at https://labs.os.uk/public/os-data-hub-examples/os-vector-tile-api/vts-3857-basic-map loads some vector tile libraries from Mapbox:
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.13.1/mapbox-gl.js"></script>
<script src="https://unpkg.com/mapbox-gl-leaflet/leaflet-mapbox-gl.js"></script>
then uses this JavaScript syntax to load the OS vector tiles:
// Load and display vector tile layer on the map.
var gl = L.mapboxGL({
style: 'https://api.os.uk/maps/vector/v1/vts/resources/styles?key=' + apiKey,
transformRequest: url => {
return {
url: url += '&srs=3857'
}
}
});
(I've verified that my OS api key works in the stand-alone demo.)
How can accomplish the equivalent using React and Leaflet?
I'm using React-Leaflet to add Leaflet functionality to my React app, and I've tried adding react-leaflet-vector-tile-layer - I've verified that this works for vector tile layers supplied by Mapbox Studio:
<VectorTileLayer
styleUrl="mapbox://styles/my-org/my-style"
accessToken="my-key"
/>
I'm trying to use this approach for the Ordnance Survey vector tile layer too but it's not working as I probably have the syntax wrong:
<VectorTileLayer
styleUrl="https://api.os.uk/maps/vector/v1/vts/resources/styles?key=my-key"
/>
No error message is shown but the OS vector tile layer does not appear on the map. In the developer console I can see a PBF file has been downloaded but it doesn't draw on the map. Could this be because I'm missing the transformRequest function in their example? Assuming it's required, how can I add this transformation request when using react-leaflet-vector-tile-layer?
The answer came from Ted Piotrowski, the developer of the react-leaflet-vector-tile-layer library. I needed to add the transformRequest parameter using this syntax:
<VectorTileLayer
styleUrl="https://api.os.uk/maps/vector/v1/vts/resources/styles?key=my-key"
transformRequest={url => { return { url: url += '&srs=3857' }}}
/>

What I need to put "url" in mapbox addSource

This is my first time using mapbox and I can't figure out how to addSource when map load.
Below is the sample code.
I uploaded 'KML' file for tilesets and I want to use this tileset for source, but I don't know how to write 'url' part.
I also want to know what is 'source-layer'. What should I write in the 'source-layer?
I am sorry I know this is very basic question, but I really need to know.
Please help me.
Thanks.
map.on('load', function() {
// Add the source to query. In this example we're using
// county polygons uploaded as vector tiles
map.addSource('counties', {
"type": "vector",
"url": "mapbox://mapbox.82pkq93d" <<---here
});
map.addLayer({
"id": "counties",
"type": "fill",
"source": "counties",
"source-layer": "original", <<---source layer
"paint": {
"fill-outline-color": "rgba(0,0,0,0.1)",
"fill-color": "rgba(0,0,0,0.1)"
}
}, 'place-city-sm'); // Place polygon under these labels.
});
EDIT:
Your tile url should also be fine like this:
mapbox://{}
It is a bit tricky to find in the documentation: When uploading KML you are creating tileset for which you should get a map ID. With the map ID you can either request separate tiles using a tile url like this:
/v4/{map_id}/{zoom}/{x}/{y}{#2x}.{format}
You can use the tile url when adding a source
map.addSource({
type: 'vector',
tiles: ['https://api.mapbox.com/v4/{map_id}/{zoom}/{x}/{y}.mvt']
});
Or you can request a TileJSON metadata object and use this to add the source:
map.addSource({
type: 'vector',
url: 'https://api.mapbox.com/v4/{map_id}.json' // <-- tileJSON url
});
For your source layer question: Vector tiles include multiple "layers" of data/geometry, when adding a map layer you need to define which source-layer the map layer refers to. E.g. you can have a single vector tile set consisting of line strings and points (two different source-layers), but your map layer should only render on of them. You can either check to tile JSON to see what source-layers are included in your tile set or create a map style in mapbox studio, using your uploaded tile set as a source.

How to use a remote wmts and generate its tiles in mapbox

according to How to implement a tile source to mapbox-gl i have an issue on a raster tile source in mapbox gl JS, which probably seems to fit to your declaration that mapbox only supports x/y/z and not lat/lon tile coordinates. I've trouble on this with the following tile source (WMTS): https://www.wmts.nrw.de/geobasis/wmts_nw_dop20/1.0.0/WMTSCapabilities.xml.
I want to include this wmts as source and add as layer like this:
map.on("load", function() {
map.addSource("wmts-layer", {
"type": "raster",
"tiles":['https://www.wmts.nrw.de/geobasis/wmts_nw_dop20/tiles/nw_dop20/EPSG_3857_16/{z}/{x}/{y}.jpeg'],
"tileSize": 256
});
map.addLayer({
"id": "wmts-layer",
"source": "wmts-layer",
"type": "raster",
"visibility": "visible",
"source-layer": "nw_dop20",
});
});
it does not work at all, Tiles get loaded but are empty images!
Can anyone point out whats the problem here?
cheers phil
You seem to be using Mapbox-GL-JS correctly (although you don't need source-layer). For whatever reason, that service is returinng blank tiles for example.
Thank you for investigation Steve,
this is really weird and for some reason the service seems to be unusable in mapbox gl as it serves different coordinates on each zoomlevel than it is specified in the tilescheme of mapbox (tms or xyz)!
see:
https://github.com/mapbox/mapbox-gl-js/issues/6089

How to generate "highlighted country" maps

How can I generate a map like this using OSM? I want the map to highlight single country and fade others. Also, if the country is small I want to show it on the globe in a small thumbnail.
Oke let’s using JavaScript, because I think there is the biggest variety of libraries. Here I will explain a few approaches creating an interactive webmap.
1) The first approach is using plain svg:
You can download or draw with your vector software of choice the base map as svg. Then you could assign every country polygon a unique ID, and inside JS you can access the SVG with a mouseevent like click or mouseover. All the information of the country could be stored outside JS in a .json (easier) or .xml file. With the ID you getting from the SVG event you can get the fitting information from your .json. Maybe that reinvent the wheel, but it’s highly adjustable. But I think if you only want a static maps its simpler if you are using a more complex library.
2) The second approach is using a library for svg interaction:
The very popular D3.js or Raphael.js
3) The third approach is using a thematic webmapping library based on svg:
Use JQVMAP (former vectormap.js) or the very new austrian project mapmap.js
4) Use a topographic webmapping library
Here you can use the open libraries leaflet.js or openlayers.js. With these, the best approach will be, that you add your countries as a .geojson. Geojson is a very nice format that allows you interacting with your countries with the most geographical softwares.
5) Create und use your own tiles
This approach is the most performant solution but not the simplest to implement.
Here is a very nice tutorial explaining five approaches. But I think the simplest is using TileMill.
6) Using a mapserver
If you are not familiar with mapservers, this should only be considered when you are going to implement an extensive application. Nice Mapservers are Deegree and the popular Geoserver.
All of these approaches have their pros und cons but I think one of these solutions will fit your needs and I wish you the best successes!
You can use google Geo Chart. You can highlight any country you wanted.
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {
'packages':['geomap'],
// Note: you will need to get a mapsApiKey for your project.
// See: https://developers.google.com/chart/interactive/docs/basic_load_libs#load-settings
'mapsApiKey': 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY'
});
google.charts.setOnLoadCallback(drawMap);
function drawMap() {
var data = google.visualization.arrayToDataTable([
['Country', 'Popularity'],
['Germany', 200],
['United States', 300],
['Brazil', 400],
['Canada', 500],
['France', 600],
['RU', 700]
]);
var options = {};
options['dataMode'] = 'regions';
var container = document.getElementById('regions_div');
var geomap = new google.visualization.GeoMap(container);
geomap.draw(data, options);
};
</script>
</head>
<body>
<div id="regions_div" style="width: 900px; height: 500px;"></div>
</body>
</html>
For more info here is the link
https://developers.google.com/chart/interactive/docs/gallery/geomap?csw=1

UIWebview creating SVG 'on the fly'

This is a continuation from a previous post regarding manipulation of SVG in a UIWebview. For more background info please see here first: UIWebview manipulating SVG 'on the fly'
Now I am trying to create SVG on the fly within the same frame work.
I have tried using the createElementNS method in Javascript without success.
Here is my failed attempt:
NSString *string = #"var svgDocument=document.getElementById('circle').getSVGDocument();var shape=svgDocument.createElementNS('http://www.w3.org/2000/svg', 'greencircle');shape.setAttributeNS(null, 'cx', 25);shape.setAttributeNS(null, 'cy', 25);shape.setAttributeNS(null, 'r', 20);shape.setAttributeNS(null, 'fill', 'green');svgDocument.appendChild(shape);";
[webView stringByEvaluatingJavaScriptFromString:string];
Could somebody please show me an example of how to create a simple circle with a similar approach as above. Or if there is a better way to create SVG graphics on the fly then I'd love to know!
Thanks.
You're actually pretty much there.
The second argument to createElementNS should be the type of element you're creating (circle) rather than an identifier (greencircle) e.g.
var shape=svgDocument.createElementNS('http://www.w3.org/2000/svg', 'circle');
You can set an id with setAttributeNS instead.
shape.setAttributeNS(null, 'id', 'greencircle');
Also, append to svgDocument.documentElement rather than just svgDocument, otherwise you'll get an error:
svgDocument.documentElement.appendChild(shape);
As an aside if you aren't already the best way to quickly test all this stuff is in Chrome or Safari on your desktop with the developer tools turned on. Makes things much easier to debug.
So if you're working with the files mentioned in the earlier question about manipulating SVG you could prototype with:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SVG</title>
<script>
function make_circle() {
// test new Javascript code here before compacting it
var svgDocument=document.getElementById('circle').getSVGDocument();
var shape=svgDocument.createElementNS('http://www.w3.org/2000/svg', 'circle');
shape.setAttributeNS(null, 'id', 'greencircle');
shape.setAttributeNS(null, 'cx', 25);
shape.setAttributeNS(null, 'cy', 25);
shape.setAttributeNS(null, 'r', 20);
shape.setAttributeNS(null, 'fill', 'green');
svgDocument.documentElement.appendChild(shape);
}
</script>
</head>
<body>
<!-- click on link to test the code -->
<a onclick='make_circle();'>Change color</a>
<object id="circle" data="circle.svg" width="250" height="250" type="image/svg+xml"/>
</body>
</html>
Obviously you can't test any of the touch events this way. :(
In terms of a better way as your Javascript gets more complex it might be worth working out how to keep everything in a separate .js file in your app bundle and then loading it into the webview by creating and inserting a dynamically created tag with stringByEvaluatingJavaScriptFromString.