Error with Mapbox GL integration when adding markers: "Cannot read property 'coordinates' of undefined" - mapbox

I've been working on creating a dynamically updated Mapbox GL integration inside Webflow's CMS. I've succeeded in creating an array of features that can be read by Mapbox's API, but these features won't show on the map because the coordinates are not being read by the function that creates the map markers.
I receive the following error Cannot read property 'coordinates' of undefined at map-test-page:139 which is where a longitude and latitude is assigned to the current marker via this line: .setLngLat(marker.geometry.coordinates)
Partial solution was found here, but the code I've integrated into my site doesn't have featuresIn or featuresAt functions which seem to be the only way to include a includeGeometry: true parameter.
I'm curious if I need to rethink how I've created markers and do something with a function like map.on('click', ...) reference here.
Here is a minimal version that reproduces my issue.
If you're familiar with the Webflow interface you can view a read-only version of the site.
Any help would be greatly appreciated!
Here is the Mapbox script I'm using on the page:
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/strawpari/ckp2nu3dw6cwu17nt4kqwf1vv',
center: [-13.723969, 48.360542],
zoom: 2,
pitch: 0,
bearing: 0,
antialias: true,
interactive: true
});
var geojson = {
type: 'FeatureCollection',
features: farmerArray,
};
// add markers to map
geojson.features.forEach(function(marker) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
// make a marker for each feature and add to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.setPopup(new mapboxgl.Popup({ offset: 25 }) // add popups
.setHTML('<img src=\'' + marker.properties.image + '\' width=\'50\' height=\'50\' border-radius=\'50%\'>' + '<h3>' + marker.properties.title + '</h3><p>' + marker.properties.description + '</p>'))
.addTo(map);
});
And here is the code embedded in each CMS item that adds a farmer's information to the farmerArray which is being read by Mapbox. Text in double-quotations "" is a placeholder for the dynamic information populated by the CMS.
var farmerArrayItem =
JSON.stringify({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: ["longitude", "latitude"]
},
properties: {
title: "name",
description: "text",
image: "imagepath"
}
});
farmerArray.push(farmerArrayItem);

It doesn't appear that you have tried using the Developer Tools to debug your code. That's definitely a skill you should pick up.
With the dev tools, you will quickly see that the value of marker at the critical point is a string:
So you just need to use JSON.parse:
geojson.features.forEach(function(markerString) {
const marker = JSON.parse(markerString);
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
// make a marker for each feature and add to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)

Related

Mapbox GL JS with Maki icons by Marker

I generated a list of Maki Icons I want to use via the original icon editor.
drawMarkers() {
let self = this;
const mapboxgl = require("mapbox-gl");
let data = this.draw.getAll();
data.features.forEach((feature) => {
if (feature.geometry.type == "Point") {
var icon = feature.properties.icon;
var title = feature.properties.title;
if (typeof title === "undefined") {
title = "Info";
} else {
var popup = new mapboxgl.Popup({ offset: 25 }) // add popups
.setHTML(`<h3>${title}</h3>`);
var marker = new mapboxgl.Marker({
color: '#333',
draggable: false,
scale: 1,
})
.setLngLat(feature.geometry.coordinates)
.setPopup(popup)
.addTo(self.map);
}
});
The Markers are showed correctly on the Mapbox.
The GeoJSON is like this:
"type":"FeatureCollection",
"features":[
{
"id":"c749de6a6eac6b1cfdda890e7c665e0d",
"type":"Feature",
"properties":{
"icon":"ferry",
"title":"This should show a Ferry icon",
"portColor":"#d9eb37"
},
"geometry":{
"coordinates":[
6.12,
22.44
],
"type":"Point"
}
},
I want the Maki Icons also added in the Marker, but I cannot find any documentation of how icons can be used inside the Mapbox Marker.
Who can help me out? I'm using the Mapbox GL JS for Web.
It depends on how you've created your map. You can either:
Create your own map style using the Maki icons you've generated. This is done using the Mapbox studio to create your custom map style, then adding it to your application.
Create custom markers that use the maki .svg files you've created. This can be done by passing a custom element to the new mapboxgl.Marker() function. So, instead of:
var marker = new mapboxgl.Marker({
color: '#333',
draggable: false,
scale: 1,
})
you would pass:
var marker = new mapboxgl.Marker(customElement)
where customElement uses the data from your icons variable.
I'm not sure if you're using plain JS here, but there's some examples on the mapbox docs of ways you can do this.
Because you've generated your own list of Maki icons, I'd suggest downloading them and maybe host them somewhere so that you can get away with creating your markers with <img src="link to hosted maki marker"/> or something of the sort

Open Layers 5.3.0 project, problem with Point customization

I'm learning openstreetmaps and openlayers. I started with application approach (Parcel + openlayers). Most of examples you find here, that could possibly help me are used with older code, which as I understand does not support all features such as Clusters and other stuff. I tried them and was not able to make it work with new environment (of cause it was not just copy-past). My question is relatively simple, I want to customize Features with [Points][1], docs say I can set their style by [setStyle][2] they also have example where it actually works. I used that example to start but whatever style I describe in there I see no new point on a map, and there are none any errors in Parsel or in browser console. If I do not use setStyle I see my point on a map. I tried different ways to set a style but none of them actually worked for me.
I do it like that, first I set style:
var iconStyle = new Style({
fill: 'red'
});
After that I add point to features array like that
features.push( new Feature({
geometry: new Point(coordinates),
address: 'Адрес точки 2',
ordererName: 'Имя человека 2',
})
);
and afterwards I set a style for a point:
features[1].setStyle(iconStyle);
and put all of that into map:
var source = new VectorSource({
features: features
});
var vectorLayer = new VectorLayer({
source: source
});
var raster = new TileLayer({
source: new OSM()
});
//EPSG:3857 - web
//EPSG:4326 - GPS
var map = new Map({
layers: [
raster,
//source,
//clusters,
vectorLayer
],
target: 'map',
view: new View({
center: transform(map_center, 'EPSG:4326', 'EPSG:3857'),
zoom: 13
})
});
So my question is how to set a style for a point and actually see on a map? If you also capable to suggest how to add such a custom Point on click on a map after you created a map with layers and points that is highly appreciated. Thanks in advance.
Your style setup isn't complete, to display a point your need to style it as an image, for example a red filled circle
var iconStyle = new Style({
image: new CircleStyle({
radius: 10,
fill: new Fill({
color: 'red'
})
})
});
If adding a feature after creating the map it might look like this:
map.on('click', function(event) {
var feature = new Feature({
geometry: new Point(event.coordinate)
...
...
});
feature.setStyle(...);
source.addFeature(feature);
});

Add markers on the fly to geoJSON array with mapbox-gl-js

I've seen several examples of Mapbox maps with multiple markers but the marker locations are pre-programmed into a geoJSON array such as the one here.
I'd like to be able to add a marker to the map via a method and keep existing markers. The markers would be created from the built-in geocoder search. It seems like it is possible with the old mapbox.js with something along the lines of this:
L.geoJson(geojsonFeature, { ... }).addTo(map);
However, I cannot seem to find documentation for such a method/functionality with mapbox-gl-js. I'd like to be able to keep track of these markers and edit/delete them like in this fiddle. Am I missing something?
Here is my current code that only works with one marker. If you add a new marker, it currently replaces the old. I'd like to keep adding them from the geocoder hook:
mapboxgl.accessToken = 'xxx';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-79.4512, 43.6568],
zoom: 13
});
var geocoder = new mapboxgl.Geocoder({
container: 'geocoder-container'
});
map.addControl(geocoder);
map.on('load', function() {
map.addSource('single-point', {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": []
}
});
map.addLayer({
"id": "point",
"source": "single-point",
"type": "circle",
"paint": {
"circle-radius": 5,
"circle-color": "#007cbf"
}
});
var el = document.createElement('div');
el.id = 'marker';
var markerObject;
map.addControl(new mapboxgl.NavigationControl());
geocoder.on('result', function(ev) {
var placeName = JSON.stringify(ev.result.place_name);
console.log(placeName);
var popup = new mapboxgl.Popup({offset:[0, -30]})
.setText(ev.result.place_name);
markerObject = new mapboxgl.Marker(el, {offset:[-25, -25]})
.setLngLat(ev.result.geometry.coordinates)
.setPopup(popup)
.addTo(map);
});
});
This code is structured with the lines
var el = document.createElement('div');
el.id = 'marker';
var markerObject;
outside of the geocoder.on('result' method. If you want a new marker added every time that the callback provided to geojson.on('result' fires, then this is the issue: you're trying to use the same div element for multiple markers. Mapbox GL JS doesn't clone or duplicate that element for you: the expectation is that the element argument of a new mapboxgl.Marker is a new element.
So, to fix this issue, you would move the above lines inside of the callback for geocoder.on('result'.

Leaflet-Realtime plugin with GeoJson and multiple markers

Hello guys i am trying to update my markers position but i don't manage to find out how to remove the old ones. All i get is a "history" of the marker. I didnt any examples that will help me. I hope someone will give me a clue to go on.
Many thanks to Per Liedman for the awesome job.
var shipLayer = L.layerGroup();
var ships = L.icon({
iconUrl: '../icons/ship-icon.png',
iconSize: [30, 30]
});
var realtime = L.realtime({
url: 'jsonServlet/ships.json',
crossOrigin: true,
type: 'json'
}, {
interval: 5 * 1000,
pointToLayer: function (feature, latlng) {
marker = L.marker(latlng, {icon: ships});
marker.bindPopup('mmsi: ' + feature.properties.mmsi +
'<br/> course:' + feature.properties.hdg+
'<br/> speed:' + feature.properties.sog);
marker.addTo(shipLayer);
return marker;
}
});
controlLayers.addOverlay(geojson, 'Ships');
By default L.realtime uses the id property of a feature to update it. As you explained in the comments, the identifier of your ships are in the GeoJSON feature's mmsi property and there is no id. You will need to add this to your L.realtime setup in options:
getFeatureId: function(featureData){
return featureData.properties.mmsi;
}
See here: https://jsfiddle.net/chk1/hmyxb6ur/

OpenLayers 3 - change base map from ol.layer.Tile (Bing) to ol.layer.Image (static image)

I need high-res map images for my application (solar power system design). Bing Maps in OL is good for finding the right building, but too low-res for laying out solar panels. So, I want to use a small high-res static map for doing the layout. Here's what I have currently. First load the Bing Maps layer:
var layers = [];
var baseBingMapLayer = new ol.layer.Tile({
source: new ol.source.BingMaps({
key: 'XXXXX',
imagerySet: 'AerialWithLabels',
})
});
layers.push(baseBingMapLayer);
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center: [-13569845.9277,4485666.89612],
zoom: 5,
})
});
Then when I want to load the static map, the strategy is to remove the Bing Maps layer and then add the static image layer. I'm doing the following:
var extent = [0, 0, 1024, 768];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
var staticURL =
"https://maps.googleapis.com/maps/api/staticmap"
+ "?center=37.7431569802915,-121.4451930197085&"
+ "zoom=20&size=1024x768&scale=2&zoom=3&"
+ "format=jpg&maptype=satellite"
+ "&key=XXX";
map.removeLayer(baseBingMapLayer);
var imageLayer = new ol.layer.Image({
source: new ol.source.ImageStatic({
url: staticURL,
imageSize: [1024,768],
projection: projection,
imageExtent: extent
})
});
var imageLayerView = new ol.View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 2
});
map.addLayer(imageLayer);
map.addView(imageLayerView);
Needless to say, this isn't working. I just get a blank screen with no exceptions thrown.
I actually had some success using jQuery to just empty the entire map div and start over with a new map object. However this seems to cause other problems and didn't seem like the right approach to me.
I'm going to continue working on this problem, but thought I would post since I'm sure I won't be the last person to try this little stunt :-)
Gary