Mapbox GL JS : Drag 1 point in many points map - drag

We have a requirement where-in we have to provide drag drop facility for markers / points. The example https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/ works perfect for 1 marker. Because it is hardcoded to geojson.features[0].geometry.coordinates = [coords.lng, coords.lat];
However, in a multi point scenario how to set the geojson for respective feature which was dragged?
Kindly let know if any further details required.
Thankx

You can achieve that by storing the current marker and applying the change on it during onMove.
I have added a property to each object to define an id:
var geojson = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 0]
},
properties: {
id: 1
}
},
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 1]
},
properties: {
id: 2
}
}
]
};
On the onmousedown event on point layer, store the current feature id
Here features[0] is usable because the event fired on point so the first feature is ever the clicked one
map.on("mousedown", "point", function(e) {
currentFeatureId = e.features[0].properties.id;
// Prevent the default map drag behavior.
e.preventDefault();
canvas.style.cursor = "grab";
map.on("mousemove", onMove);
map.once("mouseup", onUp);
});
After that, in the onMove method you can use it to move the right feature :
function onMove(e) {
var coords = e.lngLat;
// Set a UI indicator for dragging.
canvas.style.cursor = "grabbing";
// Update the Point feature in `geojson` coordinates
// and call setData to the source layer `point` on it.
var currentMarker = geojson.features.find(obj => {
return obj.properties.id === currentFeatureId
})
currentMarker.geometry.coordinates = [coords.lng, coords.lat];
map.getSource("point").setData(geojson);
}
See working exemple here : https://codepen.io/tmacpolo/pen/moxmpZ

Related

Use a property of an interface as reference to retrieve another property value of the same interface

In TypeScript, if I have an interface (or an array? Not keen on terminologies) like this:
export const AnimalColours = [
{ value: 'blue', desc: 'Blue Whale' },
{ value: 'black', desc: 'Black Bear' },
{ value: 'orange', desc: 'Orange Cat' },
] as IAnimalColours[]
On the UI, I have a variable that will have any value of the string that must correspond to the AnimalColours.value:   
let inputOrEventValue: string = "orange" // can be taken from input or event
How do I get to output the description by referencing inputOrEventValue to AnimalColours?
let outPutstring: string = // My value Must use AnimalColours.value
Pseudo:
If inputOrEventValue has a value
Compare with AnimalColours.value
If EQUAL, return AnimalColours.desc
So that if  inputOrEventValue = orange
The outPutstring would take AnimalColours[2].desc (value: "Orange Cat")
One solution is:
for (animalColour in IAnimalColours) {
if (animalColour.value === InputOrEventValue) {
outputString = animalColour.desc;
}
}
Are there other, more elegant solutions?
So far the best I can do is:
this.animalColours.forEach(element => {
if (this.inputOrEventValue === element.value) {
this.outputString = element.desc;
}
});
Feel free to post any other answer.

Feeding array to json for use in turf.polygon, structure problems

I have a linestring and a polygon and I am using turf.booleanIntersect() to determine if the line goes through the polygon. The example that i have tested and works is:
var poly1 = turf.polygon([
[
[148.535693, -29.6],
[154.553967, -29.64038],
[154.526554, -33.820031],
[148.535693, -33.6],
[148.535693, -29.6]
]
]);
//const p1 = L.geoJSON(poly1).addTo(mymap);
console.log("TEST: " + turf.booleanIntersects(line, poly1));
In my real code I read the polygon values from a file and need to insert them into an array which needs to be converted into a "GeoJSON Feature or Geometry" (from webpage).
I am having trouble getting the array to json convert correct.
var polygonlines = [];
var start = [long,lat];
polygonlines.push([start]); //add multiple of these points to the to polygonlines array
//create my json
var geojsonPolygon =
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": polygonlines
}
}
var turfpolygon = turf.polygon(geojsonPolygon.data.geometry.coordinates); //ERROR HERE
const p2 = L.geoJSON(turfpolygon).addTo(mymap);
var result = turf.booleanIntersects(line, turfpolygon)
The error I get is "Uncaught Error Error: Each LinearRing of a Polygon must have 4 or more Positions."
I can't quite get the structure of the geojsonPolygon correct. I think that it is look at geojsonPolygon Array(1) in attached picture instead of Array(10), but I can't work out how to fix it.
Would love some help getting this structure fixed. Thank you :)
p.s. please ignore values of lat/longs, just examples.
I have seen this question but it hasn't helped How to feed JSON data of coordinates to turf.polygon?
Ongoing answer..., content and code will be edited.
Here is a LIVE demo code that you can run to see how it works. It may help to answer you question.
Usage:
click Run code snippet button
click Full page in top-right corner to see map and console
//Using turf_polygon object style
var poly1 = turf.polygon([
[
[148.535693, -29.6],
[154.553967, -29.64038],
[154.526554, -33.820031],
[148.535693, -33.6],
[148.535693, -29.6]
]
]);
// Using geojson style data
// Coordinates are slightly different from poly1
// This can be used as a good example to compare/contrast with your implementation
// This geojson of poly2 works, you can see it on the map.
var poly2 = {
type: "Feature",
properties: { id: 102, name: "Poly_2" },
geometry: {
type: "Polygon",
coordinates: [
[
[148, -29],
[154, -29],
[154, -33],
[148, -33],
[148, -29]
]
]
}
};
var line12 = turf.lineString([[144, -30], [153, -31.8], [159, -32]]);
var line34 = turf.lineString([[144, -20], [160, -30]]);
/* Init and draw Leaflet map */
var map;
function initMap(coords, zoom) {
// initialize map container
map = L.map("mapid").setView(coords, zoom);
// get the stamen toner-lite tiles
var Stamen_Toner = L.tileLayer(
"https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.{ext}",
{
attribution:
'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap',
subdomains: "abcd",
minZoom: 0,
maxZoom: 20,
ext: "png"
}
);
// add the tiles to the map
Stamen_Toner.addTo(map);
//disable scroll wheel zoom
map.scrollWheelZoom.disable();
}
/* Leaflet use (lat,long) */
var coordinates = [-30, 150]; //[lat,long]
var zoom = 5;
initMap(coordinates, zoom);
//Add polygons and lines
L.geoJson(turf.featureCollection([poly1, poly2, line12, line34])).addTo(map);
// Intersection op
var result1 = turf.booleanIntersects(line12, poly1); //True
var result2 = turf.booleanIntersects(line34, poly1); //False
console.log(result1, result2);
//EOF
#mapid { height: 480px; width: 800px}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<!-- Make sure you put this AFTER Leaflet's CSS -->
<script src="https://unpkg.com/leaflet#1.3.4/dist/leaflet.js" integrity="sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA==" crossorigin=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/6.5.0/turf.min.js"></script>
<div id="mapid"></div>
How to get the polygonline array into geojson for use in turf polygon:
var polygonlines = [];
//these 2 lines occur multiple times in a loop
{
var start = [vol.lines[k].start.lng, vol.lines[k].start.lat];
polygonlines.push(start);
}
var geojsonPolygon =
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [polygonlines]
}
var turfpolygon = turf.polygon(geojsonPolygon.geometry.coordinates);
turf.booleanIntersects(line, turfpolygon)

How to generate an array of objects in Alpine.data with Livewire Eloquent collection and Adding properties in those js objects

and thanks for your attention and help,
I have a Collection in my livewire controller. This collection contains a list of players, with some properties : here we will just focus on id and name.
So we can imagine that we have 3 players in the collection :
Players[0] : 'id'=>1, 'name'=>'Yann';
Players[1] : 'id'=>2, 'name'=>'Robert';
Players[2] : 'id'=>3, 'name'=>'Jessica';
I need to get these players in my alpine data.
I can easily get these players in Alpine with the #js method :
window.addEventListener('alpine:init', () => {
Alpine.data('data', () => ({
players: #js($players),
}))
})
So, now I have in my alpine.data :
players: [
{ id: 1, name: 'Yann' },
{ id: 2, name: 'Robert' },
{ id: 3, name: 'Jessica' },
]
Now I can easily display the players in my html with a template x-for :
<template x-for="player in players">
<p x-text="player.name"></p>
</template>
But I want to add some additionnal properties in each player object. Those properties will be updated in front depending user's actions.
I would like to get something like this :
players: [
{ id: 1, name: 'Yann', touched20: 0, touched15: 0 },
{ id: 2, name: 'Robert', touched20: 0, touched15: 0 },
{ id: 3, name: 'Jessica', touched20: 0, touched15: 0 },
]
All additionnal properties are the same for each object in the array, so I imagine i could use a foreach loop to put them in the objects.
But I can't see and don't understand how i can include a loop in my alpine.data script to do this.
Anyone could help me ?
I edit my question because I found a solution :
I just make a loopPlayers function outside of my alpine data and call this function in the alpine data :
function loopPlayers() {
let players = [];
const livewirePlayers = #js($players);
livewirePlayers.forEach(element => {
players.push(element);
});
players.forEach(function(element) {
element.touched15 = 0;
})
return players;
}
And in alpine.data :
players: loopPlayers(),
Now I have my collection of players from my livewire controller & I have new properties for each element of the collection in the js data
That was easy, as usual I guess :)

How to display text on MapBox at specific coordinates?

I am currently using MapBox GL JS in my React project.
I want to know how to display some text at specific coordinates after an interaction is over. I get the coordinates of the location from a function getMinCoords()
My current code is as follows
stop() {
let nextState = this.state;
if (nextState !== 'Complete') {
nextState = 'Stop';
}
if (this.vertices.length > 2) {
this.annotation.geometries = [{
type: 'Polygon',
coordinates: this.GetPolygonCoordinates(),
properties: {
annotationType: 'polygon',
},
}];
} else {
this.annotation.geometries = [];
}
console.log(this.getMinCoords());
this.map.off('click', this.onClickCallback);
this.setState(nextState);
this.flushState();
}
The function which takes care of annotations is as follows:
this.geometries.forEach((geometry) => {
switch (geometry.type) {
case 'Point':
case 'LineString':
case 'Polygon': {
const styling = GetStyles(geometry, styles);
const feature = GetFeatureFromGeometry(geometry, this.properties);
if (this.properties.annotationType === 'label') {
styling.textAnchor = 'center';
}
feature.properties = { ...feature.properties, ...styleProps };
features.push(feature);
break;
}
Before the state is flushed, I want to display the area at the coordinates returned by getMinCoords. I have another function getArea(), which returns the area. I just need to display it on the map
You can create an empty Layer once your map is instantiated with empty source like below:
Code Snippet:
//Empty Source
const textGeoJsonSource = {
type: 'geojson',
data: featureCollection //Initially this is empty
};
//Text Layer with textGeoJsonSource
const sectionTextLayer: mapboxgl.Layer = {
"id": "sectionTextLayer",
"type": "symbol",
"source": textGeoJsonSource,
"paint": {
"text-color": textColor, //Color of your choice
"text-halo-blur": textHaloBlur,
"text-halo-color": textHaloColor,
"text-halo-width": textHaloWidth,
"text-opacity": textOpacity
},
"layout": {
"text-field": ['get', 't'], //This will get "t" property from your geojson
"text-font": textFontFamily,
"text-rotation-alignment": "auto",
"text-allow-overlap": true,
"text-anchor": "top"
}
};
this.mapInstance.addLayer(sectionTextLayer);
Then later on set the source of "textGeoJsonSource" to below once you have the coordinates( like getMinCoords in your case ):
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [ //Pass your coordinates here
5.823,
-6.67
]
},
"properties": {
"t": "18C" //Text you want to display on Map
}
}
]
}
Please note few things:
Your feature collection will need point coordinates in order to show text on Map on that point. If you have a polygon, first get centroid of that polygon for that you can use turf:
http://turfjs.org/docs/#centroid
Helpful Links: Centering text label in mapbox-gl-js?

Why my push-method is not working using Ionic?

I am working with ionic 3 and I have problems with an alert controller when I try to push an element in mi array. I do not what is wrong with my code, I think that I only need to receive the parameters and push it to complete the action but I only get a big error when I try to execute my code.
I'm so sorry, I know that my English is so bad.
CODE
addPregunta() {
const prompt = this.alertCtrl.create({
title: "Login",
message: "Enter a name for this new album you're so keen on adding",
inputs: [
{
name: "title",
placeholder: "Title"
}
],
buttons: [
{
text: "Cancel",
handler: data => {
console.log("Cancel clicked");
}
},
{
text: "Save",
handler: data => {
const preObj = {
type: "radio",
label: data.title
};
this.preguntas.push(preObj);
this.changeData(data.title);
this.mermaidStart();
}
}
]
});
prompt.present();
}
ARRAY
preguntas: object[];
ERROR
preguntas: object[]; The preguntas property is defined but it's not initialised with a value.
console.log(this.preguntas) // will be undefined
The problem is in the save handler:
{
text: "Save",
handler: data => {
const preObj = {
type: "radio",
label: data.title
};
this.preguntas.push(preObj); // <-- the problem is with this line
this.changeData(data.title);
this.mermaidStart();
}
When this.preguntas.push(preObj) is called for the first time. this.preguntas is undefined, array.push will not work because this.preguntas is not an array.
The options you have are to initialise the preguntas property as an array, or check the value in the handler before your call .push.
Option 1
Initialise the property as an array
preguntas: object[] = [];
Option 2
Check the value in the save handler before pushing.
There are countless ways to check or even use an immutable approach
// similar style with your existing code
if(this.preguntas) {
this.preguntas.push(preObj);
} else {
this.preguntas = [preObj];
}
// immutable approach
this.preguntas = [...this.preguntas, preObj]
You have declared the variable as an Array type but you did not initialize it so fails when you are trying to push into it.

Resources