I am quite new to this , really need guidance,
I understand that with Mapbox Map Matching API, i will get the raw out put of correct location, but what I dont understand, how can i use mapbox map-matching API output with mapbox-gl-js to create correct map?
Do i need to use mapbox-match-js for this ? if yes , how do i pass the geo json?
https://github.com/mapbox/mapbox-match.js/tree/master
L.mapbox.mapmatching(geojson, options, function (error, layer) {
layer.addTo(map);
layer.setStyle({
color: '#9a0202',
weight: 4,
opacity: 0.8
});
});
there is a demo html here, but the raw out put is not same as map-matching API out put,
Can any one please help me with this ?
Thanks for your help.
I had the same problem and this is my solution:
1) Add <script src="https://unpkg.com/mapbox#1.0.0-beta7/dist/mapbox-sdk.js"></script> in my index.html file.
2) Use mapbox.matching:
var mapboxClient = new MapboxClient(mapboxgl.accessToken);
mapboxClient.matching(
// dataToAddLine: array like:
// [
// [13.418805122375488, 52.50059890747071],
// [13.419144630432129, 52.50109481811523]
// ]
dataToAddLine.features[0].geometry.coordinates
, function(err, res) {
// do something with res
console.log(res);
}
)
You can test res coordinates here: geojson
I hope this solution helps you too.
Related
I need to design an application using a feature layer stored in ArcGIS online. Using a geocoder/search, I need to be able to enter an address and select a distance (1 block, 2 blocks, etc). The result will show the new point, a distance radius, and all points within the radius. I would also like to have a table of the results.
What I need is exactly like this app created by Derek Eder from DataMade: https://carto-template.netlify.app/, except mine needs the data stored in a secured ArcGIS layer. Can anyone point me to an example, tutorial, etc with an esri-leaflet implementation similar to this application? I have spent the past five days trying to convert the code, and I feel like I am getting no where.
Here is a link to guthub: https://github.com/datamade/searchable-map-template-carto
-------UPDATE-------
Seth - I can get the layer to display; however, the query to join the searched point with the layer does not work. I imagine I’m leaving something out, because the console error reads “token required”. See below:
const radius = 1610;
/**************************************************************************************************/
// ArcGIS Authoization
/**************************************************************************************************/
$("#loginModal").modal({ backdrop: 'static', keyboard: false });
// submit element of form
var submitBtn = document.getElementById('btnArcGISOnline');
// add event listener to form
submitBtn.addEventListener('click', addServicesFromServer);
// create map and set zoom level and center coordinates
var map = L.map('mapCanvas', {
}).setView([30.46258, -91.13171], 12);
// set basemap to Esri Streets
L.esri.basemapLayer('Streets').addTo(map);
var layerurl = 'secure/layer/URL';
var tokenUrl = 'https://www.arcgis.com/sharing/generateToken';
// function to make request to server
function serverAuth(server, username, password, callback) {
L.esri.post(server, {
username: username,
password: password,
f: 'json',
expiration: 86400,
client: 'referer',
referer: window.location.origin
}, callback);
}
// function to run when form submitted
function addServicesFromServer(e) {
// prevent page from refreshing
e.preventDefault();
// get values from form
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
// generate token from server and add service from callback function
serverAuth(tokenUrl, username, password, function (error, response) {
if (error) {
return;
}
// add layer to map
var featureLayer = L.esri.featureLayer({
url: layerurl,
opacity: 1,
token: response.token
});
featureLayer.addTo(map);
$("#loginModal").modal("hide");
}); // end serverAuth call
} // end addServicesFromServer call
// HARNESS GEOCODER RESULTS
let circle;
// GeoSearch
const search = L.esri.Geocoding.geosearch({
useMapBounds: false,
expanded: true,
collapseAfterResult: false
});
search.addTo(map);
search.on("results", (results) => {
if (results && results.latlng) {
if (circle) {
circle.remove();
}
circle = L.circle(results.latlng, { radius });
circle.addTo(map);
queryLayer(results.latlng);
}
});
// SET UP QUERY FUNCTION
function queryLayer(point) {
const query = L.esri.query({ url: layerurl }).nearby(point, radius);
query.run(function (error, featureCollection, response) {
if (error) {
console.log(error);
return;
}
console.log(featureCollection.features);
populateList(featureCollection.features);
});
}
// WRITE RESULTS INTO A LIST
function populateList(features) {
const list = document.getElementById("results-list");
let listItems = "";
features.forEach((feature) => {
listItems =
listItems +
`
<li>
Place: ${feature.properties?.Location} <br>
Lat: ${feature.properties?.Latitude} <br>
Lng: ${feature.properties?.Longitude} <br>
</li>
`;
list.innerHTML = listItems;
});
}
I attempted to pass the token to the query as pasted below, but then I get an invalid token error.
var layerUrl_token = layerurl + "?token=" + response.token;
I also tried using turf.js, but I haven’t been successful. I know turf.js uses long/lat, but I haven’t even been able to get the correct syntax to pull the lat and long from the feature layer.
What you're trying to do is not too hard. While there are a handful of tutorials on different parts of what you want to do, let's piece things together. I'm going to use esri-leaflet-geocoder for my search functionality, as its consistent with esri-leaflet, and IMO its one of the best geocoders available for leaflet.
Setting up the geocoder
After setting up a basic leaflet map, let's import esri-leaflet and esri-leaflet-geocoder, and create a geocoder:
import L from "leaflet";
import * as EL from "esri-leaflet";
import * as ELG from "esri-leaflet-geocoder";
const search = ELG.geosearch({
useMapBounds: false,
expanded: true,
collapseAfterResult: false
});
search.addTo(map);
Don't forget to add the geocoder css to your html, as shown in the documentation example.
Add your layer:
const layerurl = "YOUR_LAYER_URL";
const featureLayer = EL.featureLayer({ url: layerurl });
featureLayer.addTo(map);
If you are using an authenication-required layer, you will need to get a token and use it as one of the options in featurelayer, (featureLayer({ url: layerurl, token: token })). If you're not sure how to get a token, make a comment and I can add some code for that, but there are some nice tutorials already available for that.
Harness the results of the search
The ELG.geosearch comes with a results event that you can harness. It is called when the user selects one of the results in the autocomplete dropdown of the geosearch. In that event, we can get the location data of location the user selected. We center the map there (which is a default of the geosearch actually), draw a circle with a given radius, and perform a query (more on that layer):
let circle;
search.on("results", (results) => {
if (results && results.latlng) {
if (circle) {
circle.remove();
}
circle = L.circle(results.latlng, { radius });
circle.addTo(map);
queryLayer(results.latlng);
}
});
Query the layer
Now we know the latlng of the location the user selected from the search. We can create an esri-leaflet query, which can query your feature layer in various ways. We'll see up a nearby query, which will query the layer for any features within a given radius of a point:
function queryLayer(point) {
const query = EL.query({ url: layerurl }).nearby(point, radius);
query.run(function (error, featureCollection, response) {
if (error) {
console.log(error);
return;
}
populateList(featureCollection.features);
});
}
If you are querying an authenticated layer, you'll need to add a token to the request. I'm fairly certain the way to do this is like so:
function queryLayer(point) {
const query = EL.query({ url: layerurl })
.token(<your_token_here>)
.nearby(point, radius);
// ... same as above
}
You may also be able to run a query directly off of your layer:
featureLayer.query().nearby(point, radius)
I'm not as familiar with this second way, but you can read more about it here: Query a feature layer.
Render to the page
Once we .run the query, we will have access to the results in the form of a featureCollection. We can loop through the features of that featureCollection and render some HTML:
function populateList(features) {
const list = document.getElementById("results-list");
let listItems = "";
features.forEach((feature) => {
listItems =
listItems +
`
<li>
Place: ${feature.properties?.Location} <br>
Lat: ${feature.properties?.Latitude} <br>
Lng: ${feature.properties?.Longitude} <br>
</li>
`;
list.innerHTML = listItems;
});
}
In this particular example, I am using a point layer I made that is being served through arcgis online. This point layer does not have address data, so feature.properties doesn't contain any address info. For your featurelayer, the attributes of your layer will be available in a feature.properties. So depending on what's there, you might want to use feature.properties?.address or whatever. This last bit is just an example, you will probably customize that a lot differently for your own purposes.
Working Codesandbox
Try searching heavily populated areas in this example. Note that in this featurelayer there are many overlapping locations, so there are more results in the list than it looks like there are markers on the map.
Also note, this example I'm posting using esri-leaflet and esri-leaflet-geocoder versions 2^. These were just updated to versions 3 about 1-2 months ago, and the new versions require use of an API key in the geocoder and in the layer declaration, so if you want to use the latest versions (recommended), you will need to add those in. I used version 2 so as not to expose an API key in a sandbox (and I sort of hate the new API key requirement . The new arcgis developers documentation for esri-leaflet has some examples of that, but the official documentation has not yet been updated to match those examples.
I got the "lat" and "lng" of all the Bike Points from the TFL's API. I am trying to show all the Bike Points as markers on the React-leaflet map.
I have successfully got the data and have filtered it to the right format [51.505, -0.09] and mapping each one on to the Marker
component <Marker position={data.position} /> .
The problem is the markers not showing up on the map.
I have hard coded some data and it works so I don't understand what I am doing wrong with the live data. I will be very grateful if someone could help me? I have been stuck on this for all night.
Here is my problem in Jsfiddle:
Problem on jsfiddle!
There is a typo here, you need to return element (refer Lists and Keys docs for a more details):
{this.state.bikeMarkers.map(data => {
return <Marker position={data.position}></Marker>
^^^^^^
missing
})}
filterData function looks redundant in the provided example, data could be retrieved and filtered in the first place like this:
axios.get(`https://api.tfl.gov.uk/bikepoint`).then(res => {
let markers = res.data.map(location => {
return { key: location.id, position: [location.lat, location.lon] };
});
this.setState({
bikeMarkers: markers
});
});
Updated jsFiddle
I am exporting Google Directions routes as KML and displaying them on a Mapbox map by reading them with Omnivore and adding them to the map,
The Google KML stores each route as two Places (the start and end points) and one LineString (the route). In Mapbox I would like to show only the routes, that is to filter out the markers somehow. I'm displaying markers out of my own database and the Google markers clutter it up.
Here is my code. I change the styling of the LineStrings just to show that I can, but do not know what magic call(s) to make to not display the Points.
Thanks.
runLayer = omnivore.kml('data/xxxx.kml')
.on('ready', function() {
var llBnds = runLayer.getBounds();
map.fitBounds(llBnds);
this.eachLayer(function (layer) {
if (layer.feature.geometry.type == 'LineString') {
layer.setStyle({
color: '#4E3508',
weight: 4
});
}
if (layer.feature.geometry.type == 'Point') {
//
// Do something useful here to not display these items!!
//
}
});
})
.addTo(map);
Welcome to SO!
Many possible solutions:
Most straight forward from the code you provided, just use the removeLayer method on your runLayer Layer Group when you get a 'Point' feature.
Cleaner solution would be to filter out those features before they are even converted into Leaflet layers, through a custom GeoJSON Layer Group passed as 3rd argument of omnivore.kml, with a specified filter option:
var customLayer = L.geoJSON(null, {
filter: function(geoJsonFeature) {
// my custom filter function: do not display Point type features.
return geoJsonFeature.geometry.type !== 'Point';
}
}).addTo(map);
var runLayer = omnivore.kml('data/xxxx.kml', null, customLayer);
You can also use the style and/or onEachFeature options on customLayer to directly apply your desired style on your LineString.
I'm new using browserify and VueJS. I have a project where I'd like to use the map service Leaflet. So as i show in the code down below I try to require it, I have pulled it in using npm. If I look in the compiled JS file i can find Leaflet there. So that seems to work..
I'll show a image of my resulting map when i try to initialize it in the browser:
Broken Leaflet map
So as you can see the tiles are behaving really wierd, and nothing works as it should. Draging the map does really strange things..
This is the code for the components JS.
var L = require('leaflet');
module.exports = {
data: function () {
return {
map: false,
samples: [],
messages: [],
customer_id: '',
year: ''
}
},
methods: {
fetch: function (id, year, successHandler) {
var that = this
that.$set('customer_id', id)
that.$set('year', year)
successHandler(id);
}
},
ready: function () {
var map = L.map('map').setView([41.3921, 2.1705], 13);
L.Icon.Default.imagePath = 'images/';
var osmTiles = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var attribution = '© OpenStreetMap contributors';
L.tileLayer(osmTiles, {
maxZoom: 18,
attribution: attribution
}).addTo(map);
},
route: {
data: function (transition) {
this.fetch(this.$route.params.customer_id, this.$route.params.year, function (data) {
transition.next({customer_id: data})
})
}
}}
Right now I tried require:ing the leaflet package in my components script file.
Somewhere along the line Leaflet breaks.. I get no errors in the console when viewing the map, but as you can see it doesn't look good.
I would be really happy for all the help i can get!
Edit:
I just tried using chart.js in my project, required in the same way as leaflet, works like a charm.. Can't understand why leaflet wont work.
/ Simon
You are missing the Leaflet style sheet. Add it (for the correct version) using a link tag:
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
I am looking for solution to add filter (not checkbox) to my site. I have this code loading blank map from Mapbox and added Markers from JSON file. I was trying to add setFilter function, but probably I am using it wrong. I would like to filter items by category property from my JSON file.
<script>
L.mapbox.accessToken = '*************';
var baseLayer = L.mapbox.tileLayer('test****');
var markers = L.markerClusterGroup();
// CALL THE GEOJSON HERE
jQuery.getJSON("locations.geojson", function(data) {
var geojson = L.geoJson(data, {
onEachFeature: function (feature, layer) {
// USE A CUSTOM MARKER
layer.setIcon(L.mapbox.marker.icon({'marker-symbol': 'circle-stroked', 'marker-color': '004E90'}));
// ADD A POPUP
layer.bindPopup("<h1>" + feature.properties.title + "</h1><p>" + feature.properties.description + "</p><p><a href=' + feature.properties.website + '>" + feature.properties.website + "</a></p>");
layer.on('mouseover', function (e) {
this.openPopup();
});
layer.on('mouseout', function (e) {
this.closePopup();
});
}
});
markers.addLayer(geojson);
// CONSTRUCT THE MAP
var map = L.map('map', {
searchControl: {layer: markers},
zoom: 6,
center: [51.505, -0.09],
maxZoom: 13
})
.setView([62.965, 19.929], 5)
.fitBounds(markers.getBounds());
baseLayer.addTo(map);
markers.addTo(map);
L.control.fullscreen().addTo(map);
});
</script>
Could you please help me add filter buttons (something like here: https://www.mapbox.com/mapbox.js/example/v1.0.0/filtering-markers)
PS: I think I tried all examples from Mapbox website, yet seems my skills are very limited here.
Thank you
I was trying to add setFilter function, but probably I am using it wrong. I would like to filter items by category property from my JSON file.
This code example is using L.geoJson to load the markers into your map. Like the Mapbox example, you'll need to use L.mapbox.featureLayer instead, since it includes the setFilter function and L.geoJson does not.
tmcw's answer is correct, L.mapbox.featureLayer is confusing
this tutorial helped me!
https://www.mapbox.com/mapbox.js/example/v1.0.0/custom-marker-tooltip/