How to add a marker to Mapbox on click event? - javascript

I'm a french student in computering and I have to use Mapbox but since I create a class I'm stuck by this error.When I wasn't in a class it worked perfectly but now it's fully broken.And I saw on some topics it could come from safari but I already tested it on Mozilla and it still broken.
This is my class.
constructor() {
//Temporary array of currentMarkers
let currentMarkers=[];
let type ="";
//Create the map
mapboxgl.accessToken = 'private data';
this.mapbox = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40], // starting position
zoom: 9 // starting zoom
});
//Add search bar from a plugin
let geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
placeholder: 'Saissisez une adresse', // Placeholder text for the search bar
marker: {
color: 'orange'
},
mapboxgl: mapboxgl
});
this.mapbox.addControl(geocoder);
const mbox = this;
this.mapbox.on("click",function(){
this.getcoordonates();
});
//Allow us to create marker just with a research
geocoder.on('result', function(e) {
//Deleting all current markers
if (currentMarkers!==null) {
for (let i = currentMarkers.length - 1; i >= 0; i--) {
currentMarkers[i].remove();
}
}
//Add the markers who come with the research
this.addMarker(e.result.center[0],e.result.center[1]);
// Delete the last marker who got placed
//currentMarkers[currentMarkers.length - 1].remove();
});
this.addPoint(2.333333 ,48.866667 ,"supervisor");
}
//split the JSON.stringify(e.lngLat.wrap()) to get the coordinates
async mysplit(m){
let z = m.split(',');
let x = z[0].replace("{\"lng\":","");
let g = z[1].replace("\"lat\":","");
let y = g.replace("}","");
await addMarker(x,y);
}
//Add a marker on click after the excution of mysplit() function
async addMarker(x,y) {
// tmp marker
let oneMarker= new mapboxgl.Marker()
.setLngLat([x,y])
.addTo(this.mbox);
currentMarkers.push(oneMarker);
}
// Get the coordinates and send it to split function
getcoordonates(){
mbox.on('click',function(e){
let m = JSON.stringify(e.lngLat.wrap());
this.mbox.mysplit(m);
});
}
addPoint(y,x,type)
{
let color ="";
let t = type;
if(t == "supervisor")
{
color = "grey";
}else if (t =="fieldworker") {
color = "red";
}else if (t == "intervention") {
color = "blue";
}else alert("Nous ne pouvons pas ajouter votre marker\nLatitude : "+x+"\nLongitude :"+y+"\n car "+t+" n'est pas un type reconnu" );
let myMarker = new mapboxgl.Marker({"color": color})
.setLngLat([y,x])
.addTo(this.mbox);
}
}
Thanks for help and have a good day :) ! Sorry if my English isn't that good.

As the first step, the
this.mapbox.on("click", function(e){
this.getcoordonates();
});
was called outside of any method of the Map class. It would most probably belong to the constructor (as discussed above in the question's comments section).
Next, the callback changes the scope of this, so it is not anymore pointing to a Map instance. A common solution to this issue is to store/backup this before, something like:
constructor() {
...
const thisObject = this;
this.mapbox.on("click", function(e) {
thisObject.getcoordonates();
});
}
Update:
The logic of code in its current form tries to add a new click event listener every time the map is clicked. Which is not desired. This getcoordonates() function is not really needed. Instead this should work (never tested it, it is based on your code):
constructor() {
...
const mbox = this;
this.mapbox.on("click", function(e) {
let m = JSON.stringify(e.lngLat.wrap());
mbox.mysplit(m);
});
}
Remarks:
There is no real logic behind JSON-encoding the lngLat object before calling mysplit just to decode it there.
You won't need it here, but the reverse of the JSON.stringify() is JSON.parse(). There is no need to work with it on a string level, like the current mysplit() method does.
Instead, the mysplit() method should be called directly with the e.lngLat object as its argument.
Going further, since there is no "splitting" (decoding) really needed, the mysplit() method isn't really needed either.
In the end, something like this should work:
constructor() {
...
const mbox = this;
this.mapbox.on("click", function(e) {
await mbox.addMarker(e.lngLat.lng, e.lngLat.lat);
});
}

Related

routingParams.departure has no effect for calculateIsoline

I'm using the H.platform.routingService().calculateIsoline method and had expected that the routeParams.departure property would have an effect on the result.
However, changing the date and/or time of day has no effect on the calculated isoline.
In the code below, startLocs is an array of geocode objects with lat and lng
let queryDateString = queryDate.format('YYYY-MM-DDTHH:mm:ss');
startLocs.forEach(loc => {
var routingParams = {
mode: 'fastest;car;',
start: `geo!${loc.geocode.lat},${loc.geocode.lng}`,
range: 600,
rangetype: 'time',
departure: queryDateString
};
// Define a callback function to process the isoline response.
var onResult = result => {
var center = new H.geo.Point(
result.response.center.latitude,
result.response.center.longitude
),
isolineCoords = result.response.isoline[0].component[0].shape,
linestring = new H.geo.LineString(),
isolinePolygon,
isolineCenter;
// Add the returned isoline coordinates to a linestring:
isolineCoords.forEach(function(coords) {
linestring.pushLatLngAlt.apply(linestring, coords.split(','));
});
// Create a polygon and a marker representing the isoline:
isolinePolygon = new H.map.Polygon(linestring);
isolineCenter = new H.map.Marker(center);
// Add the polygon and marker to the map:
this.markerGroup.addObject(isolineCenter);
this.polylineGroup.addObject(isolinePolygon);
};
// Get an instance of the routing service:
var router = this.platform.getRoutingService();
// Call the Routing API to calculate an isoline:
router.calculateIsoline(routingParams, onResult, function(error) {
console.log(error)
});
});
this.isLoading = false;
} catch (err) {
console.log('failed processing isochrones', err);
}
Regardless of the value of queryDateString in this example, the results are identical.
The documentation states that the ReST APIs query params map to properties in the routeParams so I expected that the departure property should have an effect. Does anyone know if that's not the case?
EDIT:
Updated to include working example in case anyone stumbles across this:
let queryDateString = queryDate.format('YYYY-MM-DDTHH:mm:ss');
let onResult = result => {
let center = new H.geo.Point(
result.response.center.latitude,
result.response.center.longitude
)
let isolineCoords = result.response.isoline[0].component[0].shape;
let linestring = new H.geo.LineString();
let isolinePolygon;
let isolineCenter;
// Add the returned isoline coordinates to a linestring:
isolineCoords.forEach(function(coords) {
linestring.pushLatLngAlt.apply(linestring, coords.split(','));
});
// Create a polygon and a marker representing the isoline:
isolinePolygon = new H.map.Polygon(linestring);
isolineCenter = new H.map.Marker(center);
//let isolineObj = [isolineCenter, isolinePolygon];
// Add the polygon and marker to the map:
this.markerGroup.addObject(isolineCenter);
this.polylineGroup.addObject(isolinePolygon);
};
let router = this.platform.getRoutingService();
startLocs.forEach(loc => {
let routingParams = {
mode: 'fastest;car;traffic:enabled',
start: `geo!${loc.geocode.lat},${loc.geocode.lng}`,
range: this.maxTime * 60,
rangetype: 'time',
departure: queryDateString
};
// Call the Routing API to calculate an isoline:
router.calculateIsoline(routingParams, onResult, function(error) {
alert(error.message);
});
});
}
catch (err) {
console.log('failed processing isochrones', err);
}
finally{
this.isLoading = false;
}
The mode is missing the traffic part. Please try to add this '&mode=fastest;car;traffic:enabled'. Then you will also get for example you sent a different shape for e.g. 10:00 am.
Here we have some extended example for visualizing isolines:
https://tcs.ext.here.com/examples/v3/isoline_routing
This might be interesting for you too.

Ace Editor: Can't get rid of Marker

I am writing a simple widget that simulates a simple 8-bit CPU. For that I am abusing the Ace Editor, as you can see at the center of the image, as my "RAM"-view.
I want to highlight the line that corresponds to the value of the program counter and I am using addMarker() to do so.
However, I can't seem to get rid of that marker once I have set it. _marker is a private member that holds the value of the last marker set. But for some reason removeMarker(_marker) has no effect:
/**
*
*/
setMarker: function(position) {
//if(_marker != null) {
window.cpuRamView.session.removeMarker(_marker);
//}
_marker = new window.Range(position, 0, position, _content[position].length);
window.cpuRamView.session.addMarker(
_marker, "programCounterLocation", "fullLine"
);
}
What am I doing wrong here? :/
add marker returns an id, and removeMarker requires that id, so you can do something like
var Range = require("ace/range").Range // not the window Range!!
var _range
setMarker = function(position) {
if(_range != null) {
window.cpuRamView.session.removeMarker(_range.id);
}
_range = new Range(position, 0, position, _content[position].length);
_range.id = window.cpuRamView.session.addMarker(
_range, "programCounterLocation", "fullLine"
);
}
if(this.marker) {
this.editor.getSession().removeMarker(this.marker);
}
this.marker = this.editor.getSession().addMarker(
new Range(prop('errorLine')(formulaError), prop('errorPosition')(formulaError), prop('errorLine')(formulaError), prop('errorPosition')(formulaError) + 5), style.errorMarker, 'text');
}
set a variable marker to receive the return value, just like this:
marker=editor.session.addMarker(range, "myMarker", "fullLine");
and then remove this marker, like this:
editor.session.removeMarker(marker);

How to get the area string from a polygon using leaflet.draw

I am trying to get the area measurements of polygons so I can list them in a table to the side of the map, next to the name of the polygon. This is what I have tried with no success:
$("#polygon").on("click", function (){
createPolygon = new L.Draw.Polygon(map, drawControl.options.polygon);
createPolygon.enable();
}
var polygon = new L.featureGroup();
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'polygon') {
polygons.addLayer(layer);
}
var seeArea = createPolygon._getMeasurementString();
console.log(seeArea); //Returns null
}
Any help on this would be appreciated!
You can access the geometry utility library provided with Leaflet.
var area = L.GeometryUtil.geodesicArea(layer.getLatLngs());
In your example, you are trying to access a control itself, which is what the variable createPolygon is assigned to. Instead, you want to take the area of the layer that got drawn.
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'polygon') {
polygons.addLayer(layer);
var seeArea = L.GeometryUtil.geodesicArea(layer.getLatLngs());
console.log(seeArea);
}
}
Once you verify you are getting the area, you can just assign it to the variables that populate the table next to the map.
Note: area will be in squareMeters by default
I found that none of the above answers worked for calculating the area of non-contiguous polygons. Here's an example polygon where the above functions returned an area of 0:
For anyone who needs to do that, here is the code that worked for me (using the L.GeometryUtil function from Leaflet.draw):
var poly = // Your polygon layer here; may or may not be contiguous
var area = 0;
for (island of poly.getLatLngs()) {
// If the polygon is non-contiguous, access the island
if (island.length < 2) {
island = island[0]
}
// Sum the area within each "island"
area += L.GeometryUtil.geodesicArea(island);
}
L.GeometryUtil.geodesicArea(layer.getLatLngs())[0] should get you the area.
But I ended up using leaflet-geoman-free to do the drawing and use turf.js to get the area.
map.pm.enableDraw('Polygon', {
snappable: true,
snapDistance: 20,
});
map.on('pm:create', e => {
const layer = e.layer
alert(turf.area(layer.toGeoJSON()))
});
add corrections:
var seeArea = L.GeometryUtil.geodesicArea(layer.getLatLngs()[0]);
console.log(seeArea);

Set object variable from inside method

I'm stuck at this issue where I can't seem to assign a new value to the created object variable. See below:
// Vesselposition class
function vessel(name,ajaxName,dataUrl,pointLimit,polylineColor,iconUrl) {
this.name = name;
this.ajaxName = ajaxName;
this.dataUrl = dataUrl;
this.pointLimit = pointLimit;
this.polylineColor = polylineColor;
this.iconUrl = iconUrl;
// Global variables
this.lat=0;
this.lng=0;
this.latlng;
this.dateTime, this.vesselIcon, this.marker, this.polyline, this.localTemp, this.localWindSpeed, this.localWindDir;
this.countryName, this.countryCode, this.localTime, this.localSunrise, this.localSunset, this.countryFlag;
this.localTemp, this.localWindSpeed, this.localWindDir, this.myOptions, this.ib;
// Function gets position data
this.getData = function() {
$.when(
$.getJSON(this.dataUrl, { vessel: this.ajaxName, limit: this.pointLimit })
).done(function (data){
this.path = [];
// Create vessel icon for marker
this.vesselIcon = new google.maps.MarkerImage(this.iconUrl,
// This marker is 60 pixels wide by 58 pixels tall.
new google.maps.Size(60, 58),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is centered at 30,29 pixels.
new google.maps.Point(30, 29)
);
if (data.markers.length < 1) {
document.getElementById("map_canvas").innerHTML = "<h2>There was a problem obtaining vessel data, wait a couple of minutes and refresh your browser!</h2>";
} else {
for(i=0;i<data.markers.length;i++) {
// Assign lat,lng, id, dateTime and heading
this.lat = data.markers[i].marker.lat;
this.lng = data.markers[i].marker.lng;
What I want to accomplish is to assign this.lat and this.lng the coordinate values inside the for-loop. Later on, those values should be passed on to the getData method.
Please help guys! Been stuck on this for 3 hours searching the web!
Try this:
//keep a reference to this
var self = this;
// Function gets position data
this.getData = function() {
....
//use self here
self.lat=
I found a solution. By using jQuery's proxy function.. I got it to work.
// Function gets position data
this.getData = function() {
$.when(
$.getJSON(this.dataUrl, { vessel: this.ajaxName, limit: this.pointLimit })
).done($.proxy(function (data){
this.path = [];
),this}
Using the same class variables as first provided.. the key is to use $.proxy method in the .done function with the scope "this".

js call function stored in 2D array

I have an array:
var markerArray = [];//global var
var markerCnt = 0;//global var
for(var x=0;x<10;x++){
markerArray[markerCnt] = new Array(agency, address, marker, onMarkerClick);
//agency and agency = string
//marker = google maps marker
//onMarkerClick = function
markerCnt++;
}
How do I call a specified onMarkerClick function?
Would I just do:
markerArray[0][3]();
As an alternative solution, you can also do this:
var markerArray = [];
var markerCnt = 0;
for(var x=0;x<10;x++){
markerArray[markerCnt] = {
'agency' : agency,
'address' : address,
'marker' : marker,
'click' : onMarkerClick
};
markerCnt++;
}
//To call the click
markerArray[0].click();
The answer to your question is yes.
You can execute any function stored in an array, no matter how many dimensions.
// perfectly valid
markerArray[0][3]()
// as is this
someArray[0][1][7][2]()
To go a little bit beyond than just answering your question, I would suggest using an array of objects so you don't have to execute an array member. This will increase readability of your code and saves you a few hours if you look at it in 6 months trying to figure out what you did.
var markerArray = [];//global var
var markerCnt = 0;//global var
for(var x=0;x<10;x++){
markerArray[markerCnt] = {
agency: agency
address: address
marker: marker
onMarkerClick: onMarkerClick
};
//agency and agency = string
//marker = google maps marker
//onMarkerClick = function
markerCnt++;
}
// then reference your function
markerArray[0].onMarkerClick();

Categories

Resources