Adding onclick call for mapbox icons - javascript

I'm struggling with adding a click event to points on my mapbox map. I'm having to add the source from a backend sql query into a hbs template. I've tried just adding business1 as the source without the for loop but i get an invalid geojson object warning. If i add just 'locations' as the id obviously it gives me a warning that the id already exists on the map.
So how i can add an onclick call for dynamics id's?
How i load the points
business1 = {{{businesses}}}
for(i=0;i<business1.length;i++){
// Add the data to your map as a lyer
map.addLayer({
id: 'locations'+[i],
type: 'symbol',
minzoom: zoomThreshold,
// Add a GeoJSON source containing place coordinates and information.
source: {
type: 'geojson',
data: business1[i]
},
layout: {
'icon-image': 'circle-stroked-15',
'icon-allow-overlap': true,
}
});
}
How the map click is called-with the added [i] just to show what i'm thinking
map.on('click', function(e) {
var features = map.queryRenderedFeatures(e.point, {
layers: ['locations'+[i]] // replace this with the name of the layer
});
if (!features.length) {
return;
}
What else i've tried-but always returns the same location regardless of what is clicked
for(i=0; i<business1.length;i++){
var features = []
var feature = [i]
feature.dataPosition = i;
var clickedNameClicked = names[this.dataPosition]
console.log(clickedNameClicked)
features.push(business1[i]);
}

I definitely don't recommend adding a new source / layer for each business. That will not be performant if you have a bunch of layers, and I think it's adding too much complexity to your on click logic.
I've tried just adding business1 as the source without the for loop but i get an invalid geojson object warning.
This is a strong indication that there's something awry with your business data that you should address before adding it to your map and worrying about click events. I would recommend using a tool like http://geojsonlint.com/ to see what is going on there.
Once you have valid geojson, it will be much easier to add click events to your icons. https://docs.mapbox.com/mapbox-gl-js/example/queryrenderedfeatures-around-point/
⚠️ Disclaimer: I currently work at Mapbox ⚠️

Related

Mapbox 3D building mapping dynamically

I am trying to achieve dynamic 3D model of a building which i choose during search criteria, so far the code i have done is below.
map.on('load', function () {
// Listen for the `geocoder.input` event that is triggered when a user
// makes a selection
geocoder.on('result', function (ev) {
debugger;
var layers = map.getStyle().layers;
var styleSpec = ev.result;
var styleSpecBox = document.getElementById('json-response');
var styleSpecText = JSON.stringify(styleSpec, null, 2);
var syntaxStyleSpecText = syntaxHighlight(styleSpecText);
styleSpecBox.innerHTML = syntaxStyleSpecText;
map.addSource('floorplan', {
// GeoJSON Data source used in vector tiles, documented at
// https://gist.github.com/ryanbaumann/a7d970386ce59d11c16278b90dde094d
'type': 'geojson',
'data': 'https://docs.mapbox.com/mapbox-gl-js/assets/indoor-3d-map.geojson'
});
map.addLayer({
'id': 'room-extrusion',
'type': 'fill-extrusion',
'source': 'floorplan',
'paint': {
// See the Mapbox Style Specification for details on data expressions.
// https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions
// Get the fill-extrusion-color from the source 'color' property.
'fill-extrusion-color': ['get', 'color'],
// Get fill-extrusion-height from the source 'height' property.
'fill-extrusion-height': ['get', 'height'],
// Get fill-extrusion-base from the source 'base_height' property.
'fill-extrusion-base': ['get', 'base_height'],
// Make extrusions slightly opaque for see through indoor walls.
'fill-extrusion-opacity': 0.5
}
});
});
});
As i have tried to add this json URL (https://docs.mapbox.com/mapbox-gl-js/assets/indoor-3d-map.geojson) which i have found on this link: https://docs.mapbox.com/mapbox-gl-js/example/3d-extrusion-floorplan/
It only shows a fixed location in 3D of a building which is given on the second URL.
Now actually I want to achieve a specific 3D building on the map as only dynamic when i use search criteria.
The GeoJSON data at this file which you are using contains polygon features along with coordinates representing where in the geographic space these polygons are located. The extrude polygons for 3D indoor mapping example which you are referencing creates the "3D model" being display by taking the two-dimensional geometries specified by the polygon features in the GeoJSON used as a source, and then adding a corresponding layer which makes use of fill extrusions to visually extrude these two-dimensional polygons into three dimensions.
So, unless you change the contents of the GeoJSON file used as the source, it is expected behavior that the "3D model" of the building will continue to display at the same geographic location. If the geocoder returns the location of a particular building based on the input search criteria, you will still need to specify which GeoJSON polygons should be extruded to create the extruded model of the building. The Geocoding API response body for an address search itself likely won't be enough for this, since only a coordinate will be returned representing the location of this POI. As such, you would need to integrate some custom building data or another custom workflow to generate the polygons needed to extrude a variety of building polygon geometries.

Find leaflet id of the latest layer created in Leaflet

I am adding layers to a layergroup "drawnitems" using the leaflet draw library
drawnItems.addLayer(L.polygon(polygon));
I need to find the leaflet ID of the layer created using the above code immediately upon it's creation. This happens in a loop. I am adding multiple layers.
My goal is to save shapes that have been edited by the user into the DB. These shapes are previously created and then stored in the DB and then rendered on the map. For this I am planning to use leaflet id assigned to each shape and then find the corresponding db entry using some logic in the "draw:edited" event.
Okey to simply get the ID of the latest layer added you can do the following:
for(layer of polyLayers) {
drawnItems.addLayer(layer);
var leafletId = layer._leaflet_id
};
But I think for your approach that would not be the best solution, because leaflet ID are not stable. It would be better to create layer variables dynamically and assign an ID from you database, where the layers are stored. Then you are able to get every layer by it's database ID (e.g. myPolyLayers[idFromDataBase]). Just do:
var myPolyLayers = {};
for(layer of polyLayers) {
var id = Math.floor((Math.random() * 500) + 1); // The ID from the database
myPolyLayers[id] = layer
drawnItems.addLayer(layer);
//var leafletId = layer._leaflet_id
};
console.log(myPolyLayers[id]) // Get the layer with the ID given before
In this snippet, the ID where I choosed a random number should be the ID from you database.
I solved this. I created a "hashmap" between the database id and the leaflet shapes id (leaflet_id) while loading shapes on the map. Then in the "draw:edited" I found the database id of the object edited by the user with the help of the "key" = leaflet_id. Thus, I was able to save changes made by the user in the same database record.
var polygon = new L.Polygon([
[51.51, -0.1],
[51.5, -0.06],
[51.52, -0.03]
]);
polygon.options.id = 1;
drawnItems.addLayer(polygon);
As simple as get _leaflet_id of your latest layer created, for example as you if I am drawing polygons, for example:
let polygon = L.polygon([<my coords>], {color: 'red'});
// Added this way
let layer = polygon.addTo(this.drawElementsLayer);
// or this way (is the same)
this.drawElementsLayer.addLayer(polygon);
console.log(panelPolygon._leaflet_id) // n
>> 47 (in my eample case).

Leaflet Routing Machine waypoints added as markers

I'm trying to create a leaflet routing machine route in my web app using waypoints stored in a database. I'm first creating the control:
var routeControl = L.Routing.control({waypoints: [null]}).addTo(map);
getTestRoute("1");
The function 'getTestRoute' pulls in my waypoints from a db as lat/long pairs and adds them to the control, here's the function:
function getTestRoute(route){
$.ajax({
url: './get_route.php',
type: 'POST',
dataType: 'JSON',
data: {
getRoute: route
},
success: function (res) {
$.each(res,function(key,value){
var newLatLng = new L.LatLng(res[key].latitude, res[key].longitude);
routeControl.spliceWaypoints(0, 0, newLatLng);
})
}
});
}
My issue is that instead of seeing a route on the map I'm seeing my waypoints (five in total) added to the map as markers with no routing between them.
Please please help!!!
Here is a fiddle of my issue: http://jsfiddle.net/c4yfy4ek/46/
As can be seen on the fiddle no routes are created between the points, and the points are added as markers (which I'm assuming is unexpected??).
The problem is the use of spliceWaypoints combined with an undocumented feature in Leaflet Routing Machine.
When the control is created, it will automatically add waypoints such that the route always has a start and an end waypoint, it such waypoints are not already supplied. That means: whatever you do, getWaypoints will always return an array with at least two entries; if not provided, these waypoints will however have their latLng properties set to undefined, indicating the location has not yet been provided.
In the code in question, the control is created and will get two waypoints added implicitly by Leaflet Routing Machine, with undefined locations. Later, you add a couple of new waypoints, but the implicit two waypoints remain, without locations. No route will be calculated, since two waypoints are missing locations.
The solution is to build the array of waypoints first, and then call setWaypoints, instead of spliceWaypoints.
See an updated version of the fiddle: http://jsfiddle.net/c4yfy4ek/53/

Reload points on a Map in using the Google Maps API v3

I'm trying to reload points on a map based on when someone clicks a button.
I have a couple json objects saved in memory, "data" and "data2". The following code takes the "data2" JSON file and sets it equal to the "data" JSON file and then triggers a map reload event.
dataholder = data
function myFunction() {
data = dataholder
window.data = data2;
google.maps.event.trigger(map, 'resize');
}
Currently, the map won't reload with the new set of data. However, I am not getting any error messages.
There's no map reload event. The resize event, when it comes from a window resize, will at most recalculate the map bounds and reproject the existing features (markers, polylines, polygons). For this case in particular, I don't think that manually triggering the resize event will have any effect.
In second place, you say you have two json files, but your code suggest you instead have two json objects already in memory (meaning you don't need to perform aditional requests to retrieve the value of data2). The rest of the answer will assume that.
If your original collection of markers came from "data", which is a collection of LatLng pairs, and you want to replace the markers for the contents of "data2" the process would be:
1.- Push the markers into an object where you can find them later on
var markers=[];
for(onemarker in data) {
var newmarker=new google.maps.Marker({map: map, position: new google.maps.LatLng({onemarker.lat, onemarker.lng}) });
markers.push(newmarker);
}
2.- When you want to replace the markers, first purge the markers array
while(markers.length) {
var oldmarker=markers.pop();
oldmarker.setMap(null);
};
3.- Fill the markers array with the data from the data2 object
for(onemarker in data2) {
var newmarker=new google.maps.Marker({map: map, position: new google.maps.LatLng({onemarker.lat, onemarker.lng}) });
markers.push(newmarker);
}
For this kind of markers, as for the regular features, there's no instantaneous binding between the object and the underlying data. This is slightly different for the new google.maps.Data() layer for which you can skip the iteration and just feed it a geoJSON array. You still need to have an object to store the current markers, or else you won't have a way to access them when you want to remove them.

How to get object and save it to a new one

I'm trying to build a webmap using Leaflet. My data are stored in a zipped shapefile, so I'm using the shapefile-js, which allows me to load and use the data. My program loads the zipped shapfile and adds the content to a geoJson object.
The next big thing: I want to grap the geoJson object to visualize the data in a table view using for example datatables.net or json Table.
Firstly find some code below:
var m = L.map('map', {
center : [51.3, 10.3],
zoom : 6,
minZoom : 6,
maxZoom : 10
});
var geo = L.geoJson({
features : []
}, {
onEachFeature : function (feature, layer) {
layer.on({
click : displayInfos
});
layer.bindLabel(layer.feature.properties.GEN, {
direction : 'auto'
});
},
style : style,
}).addTo(m);
// add the shapefile to the geojson object `geo`
shp('zipped_shapefile.zip').then(function (data) {
geo.addData(data);
});
Now I have some questions about it:
1.) As already said, now I want to access the data as a (geo)json object. Using Chrome's developer tools, I ask for the 'geo'object and I get the following output:
e {options: Object, _layers: Object, _initHooksCalled: true, _leaflet_id: 22, _map: e…}
Here is the question of why: Why is this not already a FeatureCollection type? This is only the case, if I use geo.toGeoJSON(); , where geo is the L.geoJson object (check code above).
2.) I've tried to solve the problem with a workaround. So...using the developer console I type
> geo2 = geo.toGeoJSON();
and I get the following result:
Object {type: "FeatureCollection", features: Array[412]}
This is exactly, what I want to have, isn't it? It's a json object... In my opinion, I only have to add this line at the end of my code (see the big code-block), but by doing it, I only get an empty FeatureCollection object.
> geo2
Object {type: "FeatureCollection", features: Array[0]}
I need to know, how I can store the geo-object (from whatever reason the geo-object is not accessible as a json object (question 1)) as a json object (question 2). As previously mentioned: it works by using the console, but not by placing the lines in my code :-/
Any help is mich appreciated :)

Categories

Resources