Adding Polygon data to an OSM map on ThingsBoard - widget

I'm trying to integrate Polygons data into an OpenStreetMap on a ThingsBoard map widget.
I have the data saves as a GeoJSON file and I also converted it to a list of polygons (another list) made of [LAT, long] values.
Not sure how to add the file into the map and there isn't a choice to use my own OpenStreetMap map/link.
I tried inserting the following lines into the "onInit" function of the widget but can't make it load successfully.
Here is the code I added to the 'controllerScript' attribute on the widget's JSON.
self.onInit = function() {
\n\tself.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);
\n\tself.ctx.map.location.polygon = self.ctx.map.createPolygon(
ListOfLatLnGPolygons, self.ctx.map.location.settings, self.ctx.map.location,
function (event)
\n{
\n\tself.ctx.map.callbacks.onLocationClick(self.ctx.map.location);
\n\tself.ctx.map.locationPolygonClick(event, self.ctx.map.location);
\n}, self.ctx.map.location.dsIndex);
\n\tself.ctx.map.polygons.push(self.ctx.map.location.polygon);
\n}
\nself.onDataUpdated = function() {
\n\tself.ctx.map.update();
\n}
\n
\nself.onResize = function() {
\n\tself.ctx.map.resize();
\n}
\n
\nself.getSettingsSchema = function() {
\n\treturn TbMapWidgetV2.settingsSchema('openstreet-map');
\n}
\n
\nself.getDataKeySettingsSchema = function() {
\n\treturn TbMapWidgetV2.dataKeySettingsSchema('openstreet-map');
\n}
\n
\nself.actionSources = function() {
\n\treturn TbMapWidgetV2.actionSources();
\n}
\n
\nself.onDestroy = function() {
\n}
\n

Edited the code and made it work as follows on the onInit() function.
self.onInit = function() {
self.ctx.map = new TbMapWidgetV2('openstreet-map',
false, self.ctx);
var tbMap = self.ctx.map;
var coordinates = [[lat1,long1],[lat2,long2],[lat2,long3]];
// I manually entered the coordinates
var latLangs = [];
self.ctx.map.configureLocationsSettings();
self.ctx.settings.showPolygon = true;
self.ctx.settings.polygonColor = "#FE7569";
self.ctx.settings.polygonStrokeColor = "#000000";
coordinates.forEach(function(coord) {
latLangs.push(tbMap.map.createLatLng(
coord[1], coord[0]));
})
tbMap.map.createPolygon(latLangs, self.ctx.settings,
location, true, null);
tbMap.update();
}
I still don't know why, but this answer only shows the polygon on the map, but it is not clickable in order to use it with a "on polygon click" action.

Related

How to reset coordinates into an Ajax call after having initialized a map?

I inserted a map on my webpage by using the Leaflet library. What I want to do is to show a map zoomed on a specific region according to which city the user types into a text field.
I firstly initialized my map on my JS file:
function initMaps(){
map = L.map('leaflet').setView([0, 0], 13);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
'attribution': 'Map data © OpenStreetMap contributors'
}).addTo(map);
}
My javascript code also has an Ajax call.
What I want to do now is to reset the coordinates on the Ajax call.
I wrote the following:
var readCoordinates = function(){
$.ajax({
url: "https://nominatim.openstreetmap.org/search?q=" + encodeURIComponent($("#inlineFormInputCitta").val()) + "+Italy&format=geocodejson",
dataType: "json",
success: function (data) {
setTimeout(function () {
for (let i = 0; i < data.features.length; i++) {
let coordinate = data.features[i].geometry.coordinates;
console.log(coordinate);
map.setView(coordinate, 13);
console.log("ajax and for loop have been activated");
console.log(coordinate.geometry.coordinates);
};
$("#ristoranti").prop("disabled", false);
}, 1000);
}
});
};
The API I'm referring to in the URL is the following: https://nominatim.openstreetmap.org/search?q=Roma%20Italy&format=geocodejson
What I did is trying to reset the coordinates here: map.setView(coordinate, 13);
after having cycled the elements in the JSON object, see the following:
for (let i = 0; i < data.features.length; i++) {
let coordinate = data.features[i].geometry.coordinates;
I may display several coordinates in the console, see the following:
That's because in the JSON file I get through the API request there are several:
The result of this is the following map, which isn't zoomed anywhere:
Which coordinates should I take in order to display that specific region?
EDIT - - -
I changed the code because I'm trying to get a specific subobject, i.e. the one in the screen below (which has "type" = "city"):
The new snippet is the one below, where I add an if statement:
var readCoordinates = function(){
$.ajax({
url: "https://nominatim.openstreetmap.org/search?q=" + encodeURIComponent($("#inlineFormInputCitta").val()) + "+Italy&format=geocodejson",
dataType: "json",
success: function (data) {
setTimeout(function() {
for (let i = 0; i < data.features.length; i++) {
debugger;
let type = data.features[i].properties.geocoding.type;
if( $(type).val() === "city") {
let coordinate = data.features[i].geometry.coordinates;
let lng = coordinate[0];
let lat = coordinate[1];
map.setView([lng, lat], 13);
console.log("ajax and for loop have been activated");
console.log(coordinate);}
};
$("#ristoranti").prop("disabled", false);
}, 1000);
}
});
};
I'm doing the debugger and get many undefined values:
I would do something like that:
if (typeof data.features[0] !== 'undefined') {
let coordinate = data.features[0].geometry.coordinates;
var latlng = L.latLng(coordinate.reverse());
map.flyTo(latlng, 12)
}
Be sure to have something in your array
Get data from the first item since it should be the correct one in most case
Create a latlng with those coordinates. Be careful, sometime you need to reverse the array to have the correct position.
Use flyTo to have a smooth transition to your coordinates. 12 is the zoom level
You don't need to loop over the data since you need only one position. You can replace the for with that.
You're having two problems here:
The response from the Nominatim API is returning several search results, each of them in one GeoJSON Feature inside the response FeatureCollection. It's up to you to choose which search result you want to focus in the map (the first?), or let the user do so.
You're not aware that GeoJSON uses longitude-latitude (or x-y) coordinates, whereas Leaflet uses latitude-longitude (or y-x)

marker.setposition with label

I have the program below that reads aircraft coordinates from a txt file and places a number of markers on Google Maps. Every 2 sec it reads the txt file again and pushes the markers to the new aircraft locations. This works fine with the original markers "moving" to the new positions. What I would like to do is add a label to the marker to show the height of each aircraft. To do this I need to generate the label after the txt file has been read but when I do it doesn't delete the old markers but adds new markers.
The portion of code below is working fine but without a variable labels. The relevant code is where var eplanezero is created. If I move this line of code anywhere within the setInterval(function (){ the eplanezero.setPosition will not function properly. I have tried dozens of various but nothing seems to work. Any thoughts appreciated. Note that this is only a portion of the code.
moveMarker(map, playerIcon, enemyIcon);
}
function moveMarker(map, playerIcon, enemyIcon)
{
var eplanezero = new google.maps.Marker({map: map, icon: enemyIcon, label: "1"});
setInterval(function ()
{
$.post("MISSION_ADMIN_radar.txt", function(dataenemy, status)
{
var latlnge = JSON.stringify(dataenemy);
latlnge = latlnge.replace(/"/g,"");
latlnge = latlnge.replace(/[\\r\\n]/g,"");
CoordsEnemy = latlnge.split(";");
var army = parseFloat(CoordsEnemy[0].substring(0));
if (army == 2)//Own army = 1, Enemy = 2
{
var commaPos = CoordsEnemy[0].indexOf(',');
var hyphenPos = CoordsEnemy[0].indexOf('+');
var lat0 = parseFloat(CoordsEnemy[0].substring(4, commaPos));
var long0 = parseFloat(CoordsEnemy[0].substring(commaPos + 1, CoordsEnemy[0].length));
}
eplanezero.setPosition(new google.maps.LatLng(lat0, long0));
});
}, 2000);
The altitude is obtained from the same array that holds the lat and long. However, to place the variable Alt into the marker, I have to create variable eplanezero in the setInterval function so that it updates it every 2 secs. When I do this, it will not move the marker but adds a new marker, leaving the original marker in the old position.
The revised code is as follows:
function moveMarker(map, playerIcon, enemyIcon)
{
//var eplanezero = new google.maps.Marker({map: map, icon: enemyIcon, label: "1"});
var eplanezero = new google.maps.Marker({map: map, icon: enemyIcon, label: Alt});
setInterval(function ()
{
$.post("MISSION_ADMIN_radar.txt", function(dataenemy, status)
{
var latlnge = JSON.stringify(dataenemy);
latlnge = latlnge.replace(/"/g,"");
latlnge = latlnge.replace(/[\\r\\n]/g,"");
CoordsEnemy = latlnge.split(";");
var army = parseFloat(CoordsEnemy[0].substring(0));
if (army == 2)//Own army = 1, Enemy = 2
{
var commaPos = CoordsEnemy[0].indexOf(',');
var hyphenPos = CoordsEnemy[0].indexOf('+');
var Alt0 = parseFloat(CoordsEnemy[0].substring(hyphenPos + 1 , CoordsEnemy[0].length));
var lat0 = parseFloat(CoordsEnemy[0].substring(4, commaPos));
var long0 = parseFloat(CoordsEnemy[0].substring(commaPos + 1, CoordsEnemy[0].length));
var eplanezero = new google.maps.Marker({map: map, icon: enemyIcon, label: Alt});
}
eplanezero.setPosition(new google.maps.LatLng(lat0, long0));
});
}, 2000);
.setPosition must be a method that looks in the array and checks for previous coordinates. if the array doesn't contain any coordinates it places a new marker at the new coordinates, If the array does contain coordinates it "moves"the marker to the new location. Establishing the array within the loop deletes any previous array and the coordinates and therefore Google Map correctly places a new marker. Only by creating the array outside the loop will the method work correctly. Unfortunately this means that the label or the icon cannot be changed once the array is created.

Google Maps Data Layer, Click on geometry programmatically

I have a google map where I load geometry data to the data layer using geoJSON. I bind to the following event in the data layer.
map.data.addListener('click', function (event) { console.log(event);});
How can I trigger this event manually? I know I can trigger the click event on a marker manually, but it is triggered through google.maps.event.
That should be done with google.maps.event.trigger.
Try this (not sure):
// invoke a click
google.maps.event.trigger(map.data, 'click');
While Emmanuel's answer is technically correct, I would like to expand on it because I struggled for 2-3 hours to get this working.
In my code, I have a predefined click event function which was using feature object.
Here is my click event where I have used feature object:
transLayerData.addListener('click', function (event) {
var lfeature = event.feature;
var html = lfeature.getProperty('popupInfo');
infowindow.setContent(html);
infowindow.setPosition(event.latLng);
infowindow.setOptions({pixelOffset: new google.maps.Size(0,-34)});
infowindow.open(myMap);
});
If you are using a feature object in your predefined data layer click function (like above) , it is important that you pass the feature object and you create an event object with feature object in it.
Here is my code which gets the lat and long out of feature object and creates an event object:
var featureGeometry = feature.getGeometry();
var lsType = featureGeometry.getType();
var isLineData = false;
var lsType = featureGeometry.getType();
if ((lsType == 'LineString') || (lsType == 'MultiLineString') || (lsType == 'LinearRing') || (lsType == 'Polygon') | (lsType == 'MultiPolygon')) {
isLineData = true;
}
var featurePosition;
if (isLineData) {
// will center the map on the first vertex of the first LineString
var tmp = featureGeometry.getAt(0);
featurePosition = featureGeometry.getAt(0);
// following will set line's storke weight to 10
feature.setProperty('strokeWeight', 10);
} else{
featurePosition = featureGeometry.get();
}
myMap.setZoom(10);
myMap.setCenter(featurePosition);
var llat = featurePosition.lat();
var llong = featurePosition.lng();
// Creating event with the feature object is important. Especially if you have your very own feature click method defined
var lEvent = {
stop: null,
latLng: new google.maps.LatLng(llat, llong),
feature: feature,
}
// transLayerData is your data layer object.
// This is how I have defined my translayer object:
// transLayerData = new google.maps.Data({ map: myMap });
if (!isLineData) {
google.maps.event.trigger(transLayerData, 'click', lEvent);
} else {
google.maps.event.trigger(transLayerData, 'mouseover', lEvent);
}
}

How to save a completed polygon points leaflet.draw to mysql table

I would like to use leaflet.draw to create outlines of regions. I have managed to get this working ok: https://www.mapbox.com/mapbox.js/example/v1.0.0/leaflet-draw/
Now I'd like to save the data for each polygon to a mysql table. Am a little stuck on how I would go about exporting the data and the format I should be doing it in.
If possible I'd like to pull the data back into a mapbox/leaflet map in the future so guess something like geojson would be good.
So you could use draw:created to capture the layer, convert it to geojson then stringify it to save in your database. I've only done this once and it was dirty but worked.
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
var shape = layer.toGeoJSON()
var shape_for_db = JSON.stringify(shape);
});
If you want to collect the coordinates, you can do it this way:
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
drawnItems.addLayer(layer);
var shapes = getShapes(drawnItems);
// Process them any way you want and save to DB
...
});
var getShapes = function(drawnItems) {
var shapes = [];
drawnItems.eachLayer(function(layer) {
// Note: Rectangle extends Polygon. Polygon extends Polyline.
// Therefore, all of them are instances of Polyline
if (layer instanceof L.Polyline) {
shapes.push(layer.getLatLngs())
}
if (layer instanceof L.Circle) {
shapes.push([layer.getLatLng()])
}
if (layer instanceof L.Marker) {
shapes.push([layer.getLatLng()]);
}
});
return shapes;
};
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
var shape = layer.toGeoJSON()
var shape_for_db = JSON.stringify(shape);
});
// restore
L.geoJSON(JSON.parse(shape_for_db)).addTo(mymap);
#Michael Evans method should work if you want to use GeoJSON.
If you want to save LatLngs points for each shape you could do something like this:
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
var latLngs;
if (type === 'circle') {
latLngs = layer.getLatLng();
}
else
latLngs = layer.getLatLngs(); // Returns an array of the points in the path.
// process latLngs as you see fit and then save
}
Don't forget the radius of the circle
if (layer instanceof L.Circle) {
shapes.push([layer.getLatLng()],layer.getRadius())
}
PS that statement may not get the proper formatting but you see the point. (Or rather the radius as well as the point ;-)
Get shares as associative array + circle radius
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'marker') {
layer.bindPopup('Call Point!');
}
drawnItems.addLayer(layer);
var shapes = getShapes(drawnItems);
console.log("shapes",shapes);
});
var getShapes = function (drawnItems) {
var shapes = [];
shapes["polyline"] = [];
shapes["circle"] = [];
shapes["marker"] = [];
drawnItems.eachLayer(function (layer) {
// Note: Rectangle extends Polygon. Polygon extends Polyline.
// Therefore, all of them are instances of Polyline
if (layer instanceof L.Polyline) {
shapes["polyline"].push(layer.getLatLngs())
}
if (layer instanceof L.Circle) {
shapes["circle"].push([layer.getLatLng()])
}
if (layer instanceof L.Marker) {
shapes["marker"].push([layer.getLatLng()],layer.getRadius());
}
});
return shapes;
};
For me it worked this:
map.on(L.Draw.Event.CREATED, function (e) {
map.addLayer(e.layer);
var points = e.layer.getLatLngs();
puncte1=points.join(',');
puncte1=puncte1.toString();
//puncte1 = puncte1.replace(/[{}]/g, '');
puncte1=points.join(',').match(/([\d\.]+)/g).join(',')
//this is the field where u want to add the coordinates
$('#geo').val(puncte1);
});
For me it worked this:
after get coordinates send to php file with ajax then save to db
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Set the title to show on the polygon button
L.drawLocal.draw.toolbar.buttons.polygon = 'Draw a polygon!';
var drawControl = new L.Control.Draw({
position: 'topright',
draw: {
polyline: true,
polygon: true,
circle: true,
marker: true
},
edit: {
featureGroup: drawnItems,
remove: true
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'marker') {
layer.bindPopup('');
}
drawnItems.addLayer(layer);
shape_for_db = layer.getLatLngs();
SEND TO PHP FILE enter code hereWITH AJAX
var form_data = new FormData();
form_data.append("shape_for_db",shape_for_db);
form_data.append("name", $('#nameCordinate').val());
$.ajax({
url: 'assets/map_create.php', // point to server-side PHP script
dataType: 'text', // what to expect back from the PHP script, if anything
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'post',
success: function (php_script_response) {
var tmp = php_script_response.split(',');
alert(tmp );
}
});
});
map.on(L.Draw.Event.EDITED, function (e) {
var layers = e.layers;
var countOfEditedLayers = 0;
layers.eachLayer(function (layer) {
countOfEditedLayers++;
});
console.log("Edited " + countOfEditedLayers + " layers");
});
L.DomUtil.get('changeColor').onclick = function () {
drawControl.setDrawingOptions({rectangle: {shapeOptions: {color: '#004a80'}}});
};

HTML Canvas - Updating the canvas periodically

I have a canvas which I use to draw a plot periodically. I have a function that receives data periodically, and it parses the data and plots it on the canvas. For the plotting I use Chart.js.
But I am not able to update the plot periodically. I have confirmed that the data are received correctly and parsed, but the plot is not updating. It updates when I click the page, or if I minimize the browser and maximize it again. The plot would briefly appear and the next time update is called, the plot dissapears.
Here is my code. I am using Firefox.
function start ()
{
// create a new websocket and connect
window.ws = new wsImpl('ws://localhost:8181/consoleappsample', 'my-protocol');
// when data is comming from the server, this metod is called
ws.onmessage = function (evt)
{
ParseIncomingData(evt.data);
};
// when the connection is established, this method is called
ws.onopen = function ()
{
inc.innerHTML = 'Connected<br/>';
textPanel.style.background = "#00FF00";
};
// when the connection is closed, this method is called
ws.onclose = function ()
{
inc.innerHTML = 'Connection closed<br/>';
textPanel.style.background = "#FF0000";
}
var periodicFuncID = setInterval( function() { ws.send(1); }, 2000);
}
function ParseIncomingData(data)
{
var splitContents = data.split(',');
var inc = document.getElementById('incomming');
var xaxis = new Array();
var yaxis = new Array();
yaxis = splitContents;
var dataType = yaxis.shift();
var data;
for(var i=1; i<=yaxis.length; i++)
{
xaxis.push(i);
}
data =
{
labels : xaxis,
datasets : [
{
//fillColor : "rgba(135,206,250,0.5)",
fillColor : "rgba(0,0,0,0.4)",
strokeColor : "rgba(220,220,220,1)",
pointColor : "rgba(255,165,0,1)",
pointStrokeColor : "#585858 ",
data : yaxis
}
]
}
var canvas= document.getElementById('Plot');
var ctx = canvas.getContext("2d");
var myLine = new Chart(ctx).Line(data);
}
window.onload = start;
Most of the code is unrelated to my problem but I just wanted to know if there is something wrong in the way I handle.
Thank you.