Mapbox - Visualisation issue with passed data using jinja2 - javascript

I'm trying to pass the coordinates for a polygon from my flask app to the html containing the mapbox gl using jinja2.
My flask app looks something like this:
#app.route('/<path:subpath>', methods=['POST', 'GET'])
def show_subpath(subpath):
if request.method == 'POST':
try:
data = request.form
name = 'Eine Karte'
poly = coords
return render_template('/map.html', name=name, polygon=poly)
except:
return 'That didnt work'
The script section containing the map in map.html looks like this:
<script>
mapboxgl.accessToken = 'TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [12.940985, 54.063782],
zoom: 10
});
var geom = '{{ polygon }}';
map.on('load', function() {
map.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl
})
);
})
map.on('load', function () {
map.addSource('iso', {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': geom
}
}
});
map.addLayer({
'id': 'iso',
'type': 'fill',
// Use "iso" as the data source for this layer
'source': 'iso',
'layout': {},
'paint': {
// The fill color for the layer is set to a light purple
'fill-color': '#088',
'fill-opacity': 0.8
}
});
});
</script>
When I use "console.log(geom)" at the end of the script I can see, that the data is passed correctly. However the polygon isn't display on the map. When I hard code the coordinates they appear on the map. How can I visualize the passed data?

Very simple - you passed the polygon to mapbox as a string, while it requires a GeoJSON object. I replicated your code and got
Error {message: "Input data given to 'maine' is not a valid GeoJSON object."}
What you want to do is remove the quotes from your geom variable. Just to be on the safe side, you can use tojson so that it escapes certain characters so it doesn't cause unexpected errors, especially when you're using variables in <script> tags.
Modify var geom = '{{ polygon }}'; into:
var geom = {{ polygon|tojson }};
and it should work!
You can also remove the additional variable if you're not doing any extra processing:
map.addSource('maine', {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
// These coordinates outline Maine.
'coordinates':
{{ polygon|tojson }}
}
}
});
Also, make sure that when you pass the arguments from flask, you're not adding quotes to make it into a string object.

Related

Retrieving and using multiple JSON data from MapBox Geocoding API - JavaScript console reads "undefined is not an object"

I have a JS script that gives an error when I try to use JSON data from the MapBox Geocoding API on two cities (city, state), in creating a new geojson FeatureCollection with the two places (cities) represented by markers. The markers work when I put raw coordinates in the geojson FeatureCollection geometries, but when I try to use the data from the API, I get "undefined is not an object" - even though I know the object is there, where I logged it in the console. What am I doing wrong?
<div id='map' style='width: 300px; height: 200px;'></div>
<script>
mapboxgl.accessToken = 'MY_ACCESS_TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-96, 37.8],
zoom: 3
});
var userData = {};
function saveUserData(input) {
userData = input;
console.log(userData);
}
var partnerData = {};
function savePartnerData(input) {
partnerData = input;
console.log(partnerData);
}
fetch('https://api.mapbox.com/geocoding/v5/mapbox.places/<%= #user.location.strip %>.json?limit=1&access_token=MY_ACCESS_TOKEN')
.then(res => res.json())
.then(data => saveUserData(data));
fetch('https://api.mapbox.com/geocoding/v5/mapbox.places/<%= prayer_partner.location.strip %>.json?limit=1&access_token=MY_ACCESS_TOKEN')
.then(res => res.json())
.then(data => savePartnerData(data));
const geojson = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: userData.features[0].geometry.coordinates
},
properties: {
title: '<%= #user.name %>',
description: '<%= #user.location %>'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: partnerData.features[0].geometry.coordinates
},
properties: {
title: '<%= prayer_partner.name %>',
description: '<%= prayer_partner.location %>'
}
}
]
};
// add markers to map
for (const feature of geojson.features) {
// create a HTML element for each feature
const el = document.createElement('div');
el.className = 'marker';
// make a marker for each feature and add to the map
new mapboxgl.Marker(el).setLngLat(feature.geometry.coordinates)
.setPopup(
new mapboxgl.Popup({ offset: 25 }) // add popups
.setHTML(
`<h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>`
)
).addTo(map);
}
</script>
and my JavaScript console reads:
[Error] TypeError: undefined is not an object (evaluating 'userData.features[0]')
Global Code (1:175)
[Log] {type: "FeatureCollection", query: ["madison", "wi"], features: Array, attribution: "NOTICE: © 2022 Mapbox and its suppliers. All right…y not be retained. POI(s) provided by Foursquare."} (1, line 150)
[Log] {type: "FeatureCollection", query: ["minot", "nd"], features: Array, attribution: "NOTICE: © 2022 Mapbox and its suppliers. All right…y not be retained. POI(s) provided by Foursquare."} (1, line 157)
you are trying to read userData.features[0] before userData initialize. make function data => saveUserData(data) async or write code below in .then(). Read more about async in javascript
Also dont use var, use let instead. var is deprecated

Mapbox GL setData to update layer

In my angular app I am using directions api and trying to add a route path from one direction to another. On the first time when making the ajax request route path is creating properly but from the second time i can not see the route path.
I am getting this error from second time onwards of the ajax request - Layer with id "route" already exists on this map
Is there any way to update the source and layer in mapbox?
drawRoute() {
const url = `https://api.mapbox.com/directions/v5/mapbox/driving/${this.startData?.lng},${this.startData?.lat};${this.endData?.lng},${this.endData?.lat}?alternatives=true&geometries=geojson&steps=true&access_token=${environment.mapbox.accessToken}`;
this._http.get(url).subscribe({
next: (result) => {
const geojson: any = {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: result.routes[0]
}
};
if (this.map?.getSource('route')) {
const source: mapboxgl.GeoJSONSource = this.map?.getSource('route') as
mapboxgl.GeoJSONSource;
source.setData(geojson);
} else {
this.map?.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: result.routes[0].geometry.coordinates
}
}
});
}
this.map?.addLayer({
id: 'route',
type: 'line',
source: 'route',
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': '#1F5ED8',
'line-width': 2
}
});
},
error: (err) => {}
})
}
I think that the setData() method that is available for GeoJSON sources in Mapbox GL JS is what you are looking for. The method allows you to update the underlying source data and triggers a map re-render. The data-driven styling should then kick in and style your updates layers as desired.
https://docs.mapbox.com/mapbox-gl-js/api/sources/#geojsonsource#setdata
Here is a pseudo-code example
map.getSource("source-id").setData(updatedGeoJSONData);
Hope this helps! I have been writing a series of guides for Mapbox that you might be interested in too. Here are some links:
The Mapbox Developer's Handbook
A Complete Guide to Sources and Layers in React and Mapbox GL JS

How to add hover effect to new layer created with custom geojson

I've built a map and added a layer that highlights a specific neighborhood, I'd like to add a hover effect to the layer. just like in this example https://docs.mapbox.com/mapbox-gl-js/example/hover-styles
I got as far as creating my own layer with the geojson but the example I am trying to follow uses an external data source whereas I am using my own. I tried to reference my data but I don't think I am doing it correctly. Pleases see this link with a working version showing the layer highlighting the neighborhood.
This is the link to what I have so far https://jsfiddle.net/jrax4pvm/1/
Here's my JS
mapboxgl.accessToken =
'pk.eyJ1IjoibGVvc29ubmVrdXM5NSIsImEiOiJjazAxdHcyZWExMHBjM2lwN2psMDhheXQwIn0.KpEYrurG0lE55PLKMuYtKw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/leosonnekus95/ck11gbbaz0neb1cmrunqmijkf',
zoom: 15,
center: [174.7570008, -36.8658221]
});
map.on('load', function () {
'id': 'Uptown',
'type': 'fill',
'source': {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates':
[
[ /* Co-ordinates here..*/ ]]
}
}
},
'layout': {},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.8
}
});
});
I'd really like to make this layer hoverable/clickable and suspect I have to create a combined version of these two examples
https://docs.mapbox.com/mapbox-gl-js/example/geojson-polygon/
https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/
and would like some guidance.
You'll want to add map.on('mouseenter') and map.on('mouseleave') functions which target your layer to your code like this:
map.on('mouseenter', 'Uptown', function(e) {
map.setPaintProperty('Uptown', 'fill-color', '#FF0000');
});
map.on('mouseleave', 'Uptown', function() {
map.setPaintProperty('Uptown', 'fill-color', '#1F06F0'));
});
I've updated your code in another JSFiddle (https://jsfiddle.net/pjleonard37/jfd0bsha/) with these changes.
Disclaimer: I work at Mapbox

If I have this mapbox API call in a function, why does this evented callback only occasionally get called?

I have the following setup using the mapbox API. When using geolocate.watchPosition on my laptop and walking around to simulate real time tracking of a user's position on a map, the map marker only updates very occasionally. What am I doing wrong? Is there something wrong with the callback timing? I'm assuming map.on('locationfound'...) will be called when the map.locate() function completes successfully.
L.mapbox.accessToken = 'xxxx';
var map = L.mapbox.map('map', 'mapbox.high-contrast');
var myLayer = L.mapbox.featureLayer().addTo(map);
map.on('locationfound', function(e) {
map.fitBounds(e.bounds);
console.log('location found callback');
myLayer.setGeoJSON({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [e.latlng.lng, e.latlng.lat]
},
properties: {
'title': 'Here I am!',
'marker-color': '#ff8888',
'marker-symbol': 'star'
}
});
function myFunc(position) {
map.locate(); //part of mapbox API
}
watchId = navGeolocate.watchPosition(myFunc, {}, {});

Mapbox clear marker not working

I'm trying to remove marker from my map before I add a different one but the suggested method, although throwing no error, doesn't remove my marker.
$scope.geo.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [$scope.gig.lng, $scope.gig.lat]
},
properties: {
title: $scope.gig.venue,
description: $scope.gig.address + ' ' + $scope.gig.postcode,
'marker-size': 'medium',
'marker-color': '#676767'
}
});
/* show on map */
var markerLayer = L.mapbox.markerLayer().setGeoJSON({
type: 'FeatureCollection',
features: $scope.geo
}).addTo(map);
map.setZoom(13);
map.panTo($scope.geo[0].geometry.coordinates.reverse());
markerLayer.eachLayer(function(m) {
});
According to the documentation I should be able to then call the following to clear all markers but it does nothing.
L.mapbox.markerLayer().clearLayers();
Am I doing something wrong? If not is there a nuclear way of resetting the map?
L.mapbox.markerLayer().clearLayers();
L.mapbox.markerLayer() is a function that creates a new marker layer: this call is creating a new marker layer, and clearing the markers in it.
In your code, you have the lines
var markerLayer = L.mapbox.markerLayer().setGeoJSON({
type: 'FeatureCollection',
features: $scope.geo
}).addTo(map);
You are creating a new marker layer with the L.mapbox.markerLayer() and naming it with the variable markerLayer. So, to clear the markers in this layer, you would call:
markerLayer.clearLayers();

Categories

Resources