Leaflet build polygon from data from MYSQL - leaflet

Good evening I'm trying to build a polygon with some data taken from a select in my MYSQL DB, the result is that I don't see the polygon but the points.
if I enter the coordinates manually it works by building the polygon, if I use the variables of my selection the db shows me only the points corresponding to the coordinates.
I hope I was clear (I attach code)
$sql = "SELECT `LAT`,`LONG` FROM `dati`WHERE `AREA` LIKE '%001%'";
$result = $conn->query($sql); // while($r=mysqli_fetch_array($q))
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
// Creating latlng object
var latlngs = [[<?php echo $row["LAT"] ?> ,<?php echo $row["LONG"] ?>,] ];
// Creating a polygon
var polygon = L.polygon(latlngs, {color: 'red'});
// Adding to polygon to map
polygon.addTo(map);
</script>
<?php
}
} else {
echo "0 results";
}
$conn->close();
?>

Polygon is an array of points https://leafletjs.com/reference-1.7.1.html#polygon . In your code polygon is created by one point.
you should build valid leaflet polygon string, like this
$polygonData = [];
while($row = $result->fetch_assoc()) {
$polygonData[] = sprintf('[%s, %s]', $row["LAT"], $row["LONG"])
}
$polygonData = sprintf('[%s]', implode(',', $polygonData))
or some else way to generate such string format
[[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]
then out of while cycle
var latlngs = <?php echo $polygonData ?>
var polygon = L.polygon(latlngs, {color: 'red'});
polygon.addTo(map);

Related

How to highlight features with same property in Leaflet

I have a map with many polygons stored in a GeoJSON file. I want to display these polygons on the map whereby polygons with the same feature 'BROAD_CATEGORY' are highlighted on mouseover on the map legend.
The only examples and documentation I have seen to do this relate to number values rather than text strings (e.g. this one).
This is my legend code for the map at the moment:
var legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend');
labels = ['<strong>Language(s) of Origin</strong>'],
categories = ['Aboriginal clan','Action','Body part','Colour','Function of place','Historic event or place','Industry','Local estate','Nature','Other local places','Person','Place outside Australia','Portmanteau','Ship','Unknown'];
for (var i = 0; i < categories.length; i++) {
div.innerHTML +=
labels.push(
'<i class="circle" style="background:' + getColor(categories[i]) + '"></i> ' +
(categories[i] ? categories[i] : '+'));
}
div.innerHTML = labels.join('<br>');
return div;
};
legend.addTo(map);

Saving user input from Popup in local storage not working properly

i have a marker with pop i was able to save user input from a form in my popup but the problem is it will only save if i have to click somewhere on the map first. heres my code...
L.marker([63.233627, 5.625])
.addTo(map)
.bindPopup('<form><select class="fodd" id="fodd-1"><option value="false">false</option><option
value="true">true</option></select><button type="button" id="btnInsert">Save</button></form>')
.on('click', foddStatus);
L.marker([72.181804, 45])
.addTo(map)
.bindPopup('<form><select class="fodd" id="fodd-2"><option value="false">false</option><option
value="true">true</option></select><button type="button" id="btnInsert">Save</button></form>')
.on('click', foddStatus);
function foddStatus(e) {
var btnInsert = document.getElementById("btnInsert");
btnInsert.onclick = function () {
// get user input when button is saved is clicked
var foddValue = document.querySelector('.fodd').value;
var foddLoc = document.querySelector('.fodd').id;
var midFodd = ":";
var var1 = foddLoc + midFodd;
var new_data = foddLoc + midFodd + foddValue;
console.log(new_data);
// if there is nothing saved on storage then save an empty array
if (localStorage.getItem('foddstatus') == null){
localStorage.setItem('foddstatus','[]');
}
// get old data and slap it to the new data
var old_data = JSON.parse(localStorage.getItem('foddstatus'));
old_data.push(new_data);
// save the old + new data to local storage
localStorage.setItem('foddstatus', JSON.stringify(old_data));
// console.log(key);
}
}
so what happens here on the first marker that I click the save button, it will save the values in localstorage, then when I go to the second marker and click the save button, it won't actually save on local storage. I have to click somewhere else on the map first in order to save the second marker's input on my localstorage.
this is a bit other approach to achive your goal, but maybe it helps.
This is only a test, without any validation etc. so please be aware of it!
I used here simple divs in the popups, inside an input and a button. Every button has its onclick attribute to call storeData function, which sets the localStorage key-value if the input is not empty. I used the input ids as keys.
The example snippet:
let ls = window.localStorage;
var map = L.map('map').setView([41, 21], 5);
var darkMap = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png', {
maxZoom: 20,
attribution: '© Stadia Maps, © OpenMapTiles © OpenStreetMap contributors'
}).addTo(map);
var marker1 = L.marker([40, 10]).addTo(map);
var marker2 = L.marker([40, 30]).addTo(map);
marker1.bindPopup('<div><input class="ipt" id="ipt1" /><button onclick="storeData(this);" class="save">SAVE</button></div>');
marker2.bindPopup('<div><input class="ipt" id="ipt2" /><button onclick="storeData(this);" class="save">SAVE</button></div>');
function storeData(btn) {
let ipt = btn.parentNode.querySelector('.ipt');
if (ipt.value != '') {
ls.setItem(ipt.id, ipt.value);
let str = 'Data stored in localStorage with key: ' + ipt.id + ', value: ' + ls.getItem(ipt.id);
alert(str);
} else {
console.log('Empty input!');
}
}
And a working fiddle (Please open in non-incognito).

marker with polyline while dragging the marker using leaflet

Hi I have connection between marker with polyline like this Image .
I am attaching a sample here.
How Can I make drag possible that when I drag the the marker with polyline.
example , If I drag the marker 3 it should also update the polyline point and where ever I put the marker 3 polyline should connect with marker 3.
I need this type of drag event that can update the polyline also when dragging the marker.
I am using leaflet for this purpose but still unable to solve the dragging logic of marker with polyline.
Here is the sample code I am using
$http.get("db/getConnectionData.php").then(function (response) {
$scope.links1 = response.data.records;
// $scope.showArrow();
angular.forEach($scope.links1, function(value, i) {
var source_panoId = $scope.links1[i].s_panoId;
var dest_panoId = $scope.links1[i].d_panoId;
var sPanoID = $scope.links1[i].sourcePano_id;
var dPpanoID = $scope.links1[i].destPano_id;
angular.forEach($scope.panoramas, function(value, index) {
if($scope.panoramas[index].panoId == source_panoId){
if($scope.links.indexOf($scope.panoramas[index])== -1){
$scope.links.push($scope.panoramas[index]);
}
var SlatLang = $scope.panoramas[index].project_latLng ;
var SLatLngArr = SlatLang.split(",");
var Slat = parseFloat(SLatLngArr[0]);
var Slang = parseFloat(SLatLngArr[1]);
var polypoint1 = [Slat, Slang];
angular.forEach($scope.panoramas, function(value, index1) {
if($scope.panoramas[index1].panoId == dest_panoId){
if($scope.links.indexOf($scope.panoramas[index1])== -1){
$scope.links.push($scope.panoramas[index1]);
}
var DlatLang = $scope.panoramas[index1].project_latLng ;
var DLatLngArr = DlatLang.split(",");
var Dlat = parseFloat(DLatLngArr[0]);
var Dlang = parseFloat(DLatLngArr[1]);
var polypoint2 = [Dlat, Dlang];
// Draw seperate polyline for each connection
polyline = L.polyline([[Slat, Slang],[Dlat, Dlang]],
{
color: 'blue',
weight: 5,
opacity: .7,
}
).addTo(map);
$scope.polycoords.push(polyline);
}
});
}
});
Here is the code that I am using to make drag of marker with polyline
angular.forEach($scope.panoramas, function(value, index4){
$scope.markers[index4].on('dragstart', function(e){
var latlngs = polyline.getLatLngs(),
latlng = $scope.markers[index4].getLatLng();
for (var i = 0; i < latlngs.length; i++) {
if (latlng.equals(latlngs[i])) {
this.polylineLatlng = i;
}
}
});//dragstart
$scope.markers[index4].on('drag', function(e){
var latlngs = polyline.getLatLngs(),
latlng = $scope.markers[index4].getLatLng();
latlngs.splice(this.polylineLatlng, 1, latlng);
polyline.setLatLngs(latlngs);
});//drag
$scope.markers[index4].on('dragend', function(e){
delete this.polylineLatlng;
});//dragEnd
});
First, when creating the marker, remember to pass the draggable option as true, like this:
var marker = L.marker(latLng, { draggable: true });
Now, check which drag event you want to attach a listener to and then call the redraw function of the polyline inside the callback, like this:
// var polyline defined somewhere
marker.on('drag', function (e) {
polyline.redraw();
});
If this doesn't work, please provide sample code so we can work around with it.
Edit
You also need to change the coordinates of the polyline, otherwise redraw will do nothing. Check out this answer on SO and see if it fits your needs.
Edit 2
You're using an array of polylines while the answer just uses one polyline which has the array of coordinates, so in your case you need to use two loops to accomplish the same task. You can make this faster and maybe use an object as a lookup table to get the right polyline for each marker, for example, like this:
var table = {};
// ...
table[marker] = polyline;
Then later you can get the polyline used for each marker. But anyway, here's what I think would work in your case the way it is in the sample (it was a little hard to understand but I hope it works for you).
I don't know where you are putting the second part of your sample (the event handlers) but I assume it's not inside the double loop that is creating the polylines, right? So this is what I came up with:
marker.on('dragstart', function (e) {
var markerLatLng = marker.getLatLng();
this.polylineLatLngs = [];
for (var i = 0; i < $scope.polycoords.length; i++) {
var polyline = $scope.polycoords[i];
var latLngs = polyline.getLatLngs()
for (var j = 0; j < latLngs.length; j++) {
if (markerLatLng.equals(latLngs[j])) {
this.polylineLatLngs.push([i, j]);
}
}
}
});
marker.on('drag', function (e) {
for (var i = 0; i < this.polylineLatLngs.length; i++) {
var polyline = $scope.polycoords[this.polylineLatLngs[i][0]];
var latLngs = polyline.getLatLngs();
var markerLatLng = marker.getLatLng();
latLngs.splice(this.polylineLatLngs[i][1], 1, markerLatLng);
polyline.setLatLngs(latLngs);
}
});
I am getting this type of behavior. Please let me know how I can solve this .
Thank you for your time.
This is the polyline created by getting data from db or by making the connection between panorama.
This Image when I start dragging the marker 2 I got the result like this
This image when I dragged the marker 3.
This type of result I am getting using the source code you provided above.

How do I get the bounding box of a mapboxgl.GeoJSONSource object?

I'm setting up a Mapbox GL JS map like this:
mapboxgl.accessToken = 'pk.my_token';
var cityBoundaries = new mapboxgl.GeoJSONSource({ data: 'http://domain.com/city_name.geojson' } );
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v8',
center: [cityLongitude,cityLatitude],
zoom: 13
});
Then I'm loading that GeoJSON data onto the map after it loads like this:
map.on('style.load', function(){
map.addSource('city', cityBoundaries);
map.addLayer({
'id': 'city',
'type': 'line',
'source': 'city',
'paint': {
'line-color': 'blue',
'line-width': 3
}
});
});
At this point, I have a map that's centered at the location I specified in new mapboxgl.Map, and it's at zoom level 13. So, only a piece of the GeoJSON data is visible on the map. I'd like to re-center and re-zoom the map so that the entire GeoJSON data is visible.
In Mapbox JS, I would do this by loading the GeoJSON data into a featureLayer and then fitting the map to its bounds with:
map.fitBounds(featureLayer.getBounds());
The fitBounds documentation for Mapbox GL JS indicates that it wants the bounds in the format of [[minLng, minLat], [maxLng, maxLat]].
Is there a way to determine the mix/max latitude & longitude values of this GeoJSON layer?
Based on the 'Obtaining a bounding box' section of this post, I've come up with this process...
map.on('style.load', function(){
$.getJSON('http://citystrides.dev/city_name.geojson', function(response){
var boundingBox = getBoundingBox(response);
var cityBoundary = new mapboxgl.GeoJSONSource({ data: response } );
map.addSource('city', cityBoundary);
map.addLayer({
'id': 'city',
'type': 'line',
'source': 'city',
'paint': {
'line-color': 'blue',
'line-width': 3
}
});
map.fitBounds([[boundingBox.xMin, boundingBox.yMin], [boundingBox.xMax, boundingBox.yMax]]);
})
});
function getBoundingBox(data) {
var bounds = {}, coords, point, latitude, longitude;
for (var i = 0; i < data.features.length; i++) {
coords = data.features[i].geometry.coordinates;
for (var j = 0; j < coords.length; j++) {
longitude = coords[j][0];
latitude = coords[j][1];
bounds.xMin = bounds.xMin < longitude ? bounds.xMin : longitude;
bounds.xMax = bounds.xMax > longitude ? bounds.xMax : longitude;
bounds.yMin = bounds.yMin < latitude ? bounds.yMin : latitude;
bounds.yMax = bounds.yMax > latitude ? bounds.yMax : latitude;
}
}
return bounds;
}
Here's a walkthrough of what the code is doing, for anyone out there who needs a detailed explanation:
map.on('style.load', function(){
When the map loads, let's do the stuff in this function.
$.getJSON('http://citystrides.dev/city_name.geojson', function(response){
Get the city's GeoJSON data. This is an asynchronous call, so we have to put the all the code that uses this data (the response) inside this function.
var boundingBox = getBoundingBox(response);
Get the bounding box of this GeoJSON data. This is calling the , function(){ that appears after the 'map on style load' block.
var cityBoundary = new mapboxgl.GeoJSONSource({ data: response } );
Build Mapbox's GeoJSON data.
map.addSource('city', cityBoundary);
Add the source to Mapbox.
map.addLayer({
Add the layer to Mapbox.
map.fitBounds([[boundingBox.xMin, boundingBox.yMin], [boundingBox.xMax, boundingBox.yMax]]);
Adjust the map to fix the GeoJSON data into view.
function getBoundingBox(data) {
This function iterates over the returned GeoJSON data, finding the minimum and maximum latitude and longitude values.
One thing to note in the getBoundingBox function is this line:
coords = data.features[i].geometry.coordinates;
In the original post, linked above, this line was written as coords = data.features[i].geometry.coordinates[0]; because their data for the list of coordinates was an array of arrays. My data isn't formatted that way, so I had to drop the [0]. If you try this code & it blows up, that might be the reason.
You can use the turf.js library. It has a bbox function:
const bbox = turf.bbox(foo);
https://turfjs.org/docs/#bbox
I use the turf-extent library, which is maintained by the Mapbox bunch anyhow. https://www.npmjs.com/package/turf-extent is the node module link.
In your code you simply import(ES6) or require as so:
ES6/Webpack: import extent from 'turf-extent';
Via script tag: `<script src='https://api.mapbox.com/mapbox.js/plugins/turf/v2.0.2/turf.min.js'></script>`
Then feed your response to the function, for example:
ES6/Webpack: let orgBbox = extent(response);
Normal: var orgBbox = turf.extent(geojson);
Then you can use the array values to set your map center:
center: [orgBbox[0], orgBbox[1]]
Or as you want, to fit bounds:
map.fitBounds(orgBbox, {padding: 20});
Here is an example using the turf.min.js in a regular html tag in case you are not using webpack or browser:
https://bl.ocks.org/danswick/83a8ddff7fb9193176a975a02a896792
Happy coding and mapping!
Based on James Chevalier's answer. For polygon/multipolygon tilesets that are assigend to a map in Mapbox Studio I am using this to get the bounding box:
getPolygonBoundingBox: function(feature) {
// bounds [xMin, yMin][xMax, yMax]
var bounds = [[], []];
var polygon;
var latitude;
var longitude;
for (var i = 0; i < feature.geometry.coordinates.length; i++) {
if (feature.geometry.coordinates.length === 1) {
// Polygon coordinates[0][nodes]
polygon = feature.geometry.coordinates[0];
} else {
// Polygon coordinates[poly][0][nodes]
polygon = feature.geometry.coordinates[i][0];
}
for (var j = 0; j < polygon.length; j++) {
longitude = polygon[j][0];
latitude = polygon[j][1];
bounds[0][0] = bounds[0][0] < longitude ? bounds[0][0] : longitude;
bounds[1][0] = bounds[1][0] > longitude ? bounds[1][0] : longitude;
bounds[0][1] = bounds[0][1] < latitude ? bounds[0][1] : latitude;
bounds[1][1] = bounds[1][1] > latitude ? bounds[1][1] : latitude;
}
}
return bounds;
}

OpenLayers - Add according popup text to marker array

I have a probably rather basic problem in OpenLayers, it would be really great if someone could help me out on this one.
I have an array of markers, which should each have a different popup box text. However, I fail in applying the according text to a marker. I tried to do this via another array for the content of the popup boxes. However, i couldn't relate the correct text to a marker. Here is my code:
var vs_locations = [
[13.045240, 47.8013271],
[13.145240, 47.8013271],
[13.245240, 47.8013271],
];
var popupContentHTML = [
"Text for marker with loc[0]",
"Text for marker with loc[1]",
"Text for marker with loc[2]"
];
function addVS(){
for (var i = 0; i < vs_locations.length;i++){
var loc = vs_locations[i];
var feature = new OpenLayers.Feature(volksschulen, new OpenLayers.LonLat(loc[0],loc[1],loc[2]).transform(proj4326,proj900913));
feature.closeBox = true;
feature.data.icon = new OpenLayers.Icon('coffeehouse.png');
feature.popupClass = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {
'autoSize': true,
});
marker = feature.createMarker();
volksschulen.addMarker(marker);
feature.data.popupContentHTML = ; //Here should be the text according to the marker
feature.data.overflow = "auto";
marker.events.register("mousedown", feature, markerClick);
feature.popup = feature.createPopup(feature.closeBox);
map.addPopup(feature.popup);
feature.popup.hide();
}
}
did you try:
feature.data.popupContentHTML = popupContentHTML[i];
assuming the length of your location array matches your text array, both in length anf position