Creating Radar Map on Google Maps JavaScript API - javascript

I have a response data from a Radar Layer API like this:
{
"Date": "2020-04-18T04:00:05+03:00",
"Source": 2,
"Kml": [
{
"Polygons": [
{
"Polygon": [
{ "Cordinates": [25.8409, 51.6199] },
{ "Cordinates": [25.8341541, 51.619873] },
{ "Cordinates": [25.834177, 51.61238] },
{ "Cordinates": [25.8308582, 51.5936356] },
{ "Cordinates": [25.8275185, 51.5823822] }
....
....
]
}
],
"Color": "#47C247"
},
{
"Polygons": [
{
"Polygon": [
{ "Cordinates": [26.1740189, 50.5239372] },
{ "Cordinates": [26.1841354, 50.5238838] },
{ "Cordinates": [26.1909122, 50.53136] },
{ "Cordinates": [26.1977215, 50.5463562] }
....
....
]
}
],
"Color": "#47C247"
},
...
...
I want to create a Radar map using this data.
I tried to create polygons using each data and created a set interval function to loop through each polygon for 250ms so that it acts as an animation.
setInterval(() => {
deleteAllShape();
// console.log(data);
data.Kml.map((polygons) => {
const shape = polygons.Polygons.map((polygon) => {
const newMapData = [];
polygon.Polygon.map((obj) => {
const path = { lat: obj.Cordinates[0], lng:
obj.Cordinates[1] };
newMapData.push(path);
});
poly = new google.maps.Polygon({
paths: newMapData,
strokeColor: polygons.Color,
strokeOpacity: 0.8,
strokeWeight: 0,
fillColor: polygons.Color,
fillOpacity: 0.35,
draggable: false,
editable: false,
});
poly.setMap(map);
allPolygons.push(poly);
});
});
},250)
function deleteAllShape() {
poly = null;
for (let i = 0; i < allPolygons.length; i++) {
allPolygons[i].setMap(null);
}
}
This is working to an extend. But the problem is the map and the browser slows down and hangs up after creating some polygons.
When I researched on several radar maps (eg: windy.com) :-
I found that they are rendering images on the map. My question is how to create images using above data and create a radar map?

This may not answer your question, as I'm unclear what you're trying to do with images instead of polygons. However, it might speed things up.
Currently you loop over allPolygons every 250ms, preventing all previous polygons from appearing on the map.
Then you draw a new polygon, and add it into allPolygons, so it gets removed on the next iteration in 250ms. That's all fine.
However, as the number of polygons increase, you'll be increasing the size of that for loop each time:
for (let i = 0; i < allPolygons.length; i++) {
So it'll get progressively slower as you draw more polygons. You don't say how many polygons you're adding, but I'd guess it's a lot.
Instead, all you need to do is hide the most recently created polygon. All the previous ones in allPolygons will already have been hidden, so you don't need to call setMap(null) on every polygon, as it's just the most recent one that's not already set to null.
Maybe something like:
function deleteAllShape() {
allPolygons[allPolygons.length - 1].setMap(null);
}
Alternatively, just this, if you don't need allPolygons for anything else, save you having to store them in that array.
poly.setMap(null);
Also, instead of creating poly then calling poly.setMap(map);, just do
poly = new google.maps.Polygon({
paths: newMapData,
strokeColor: polygons.Color,
strokeOpacity: 0.8,
strokeWeight: 0,
fillColor: polygons.Color,
fillOpacity: 0.35,
draggable: false,
editable: false,
map
});

Related

Update polygon leaflet realtime

I am trying to display a polygon from an external geojson file, the data loads but does not update the polygon in real time.
The polygon is added but color is not updated after interval when level changes.
Heres is my code:
L.realtime({
url: 'js/areas.json',
crossOrigin: true,
type: 'json'
}, {
interval: 60 * 1000,
onEachFeature: function (feature, latlng) {
var level = feature.properties.level;
if (level == 0) {
var polygon = L.polygon(latlng._latlngs, {
color: '#51F03B',
opacity: 0.3,
fillOpacity: 0.1
}).addTo(map);
} else if (level == 1) {
var polygon = L.polygon(latlng._latlngs, {
color: '#F43B19',
opacity: 0.3,
fillOpacity: 0.1
}).addTo(map);
}
return polygon;
},
updateFeature: function (feature, oldLayer, newLayer) {
var level = feature.properties.level;
if (!oldLayer) {
return;
}
if (level== 0) {
oldLayer.setStyle({color: '#51F03B'});
} else if (level == 1) {
oldLayer.setStyle({color: '#F43B19'});
}
return oldLayer;
}
});
If i don´t return oldLayer, the polygon color changes but don´t remove the previous polygon.
geoJson file:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"level": 0,
"id": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-75.360297, 6.071571],
[-76.005083, 6.063846],
[-76.051694, 6.511708],
[-75.298149, 6.573451]
]]
}
}]
}
I show markers and more in this way but I don't know if being polygons is different.
The way I worked with "real-time" with polygon was cleaning the previous polygon and creating a new one. With that in mind, you will need to keep track of the layers that you have created (like in an array), a method to clear that layer (or clear all layers, there's a leaflet method for that) and a method to set a timeOut to call an update method.
I say "real-time" because currently, I keep asking back-end for an update using a timeOut function.
first, when you received the geojson draw the polygon, add it to your map and call the setTimeout with your update method.
second, you will need a method to remove the old layer, something like this:
const resetPolygonArray = polygonId => {
myPolygon = polygonArray.filter(polygon => {
if (polygon.id != polygonId) {
return myPolygon
} else {
map_machiney.removeLayer(myPolygon.geojson)
}
})
}
even though you can use that array to store the polygon and the marker related to it, like this structure:
polygonArray.push({
id: polygonId,
geojson: geojson,
marker: marker
})

Google Maps Api: cannot click on clickable polygon behind datalayer

Hi I am using google maps api(JavaScript) to build an interactive world map. It went really well until I ran into this problem. I am using polygons to show to outline of a country. These polygons trigger a modal showing information about the country when clicked on. This worked until I started to use "Data Layer: Earthquake data". Instead of using earthquake data I use sales information of the company I work at. So if a large share of our customers are from the Netherlands then the datalayer assigned to the Netherlands will be very large. The problem is that because of the datalayers the countries are no longer clickable. I can not click "through" the datalayer. Is there a possibility that I can trigger the event behind the datalayer?
This code displays the datalayers:
map.data.loadGeoJson('./data/test.json');
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0
}
})
});
map.data.addListener('mouseover', function(event) {
map.data.overrideStyle(event.feature, {
title: 'Hello, World!'
});
});
map.data.addListener('mouseout', function(event) {
map.data.revertStyle();
});
function eqfeed_callback(data) {
map.data.addGeoJson(data);
}
This code displays the polygons:
function drawMap(data) {
var rows = data['rows'];
for (var i in rows) {
if (rows[i][0] != 'Antarctica') {
var newCoordinates = [];
var geometries = rows[i][1]['geometries'];
if (geometries) {
for (var j in geometries) {
newCoordinates.push(constructNewCoordinates(geometries[j]));
}
} else {
newCoordinates = constructNewCoordinates(rows[i][1]['geometry']);
}
var country = new google.maps.Polygon({
paths: newCoordinates,
strokeColor: 'transparent',
strokeOpacity: 1,
strokeWeight: 0.3,
fillColor: '#cd0000',
fillOpacity: 0,
name: rows[i][0]
});
google.maps.event.addListener(country, 'mouseover', function() {
this.setOptions({
fillOpacity: 0.3
});
});
google.maps.event.addListener(country, 'mouseout', function() {
this.setOptions({
fillOpacity: 0
});
});
google.maps.event.addListener(country, 'click', function() {
var countryName = this.name;
var code = convert(countryName); // Calls a function that converts the name of the country to its official ISO 3166-1 alpha-2 code.
var modal = document.querySelector('.modal');
var instance = M.Modal.init(modal);
instance.open();
});
country.setMap(map);
}
}
If read in the documentation that changing the zIndex won't work because "Markers are always displayed in front of line-strings and polygons."
Is there a way to click on a polygon behind a datalayer?
EDIT
I tried to give the polygon a higher zIndex and I made the datalayer not clickable
map.data.loadGeoJson('./data/test.json');
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0,
clickAble: false,
zIndex: 50
}
})
});
function eqfeed_callback(data) {
map.data.addGeoJson(data);
}
function drawMap(data) {
var rows = data['rows'];
for (var i in rows) {
if (rows[i][0] != 'Antarctica') {
var newCoordinates = [];
var geometries = rows[i][1]['geometries'];
if (geometries) {
for (var j in geometries) {
newCoordinates.push(constructNewCoordinates(geometries[j]));
}
} else {
newCoordinates = constructNewCoordinates(rows[i][1]['geometry']);
}
var country = new google.maps.Polygon({
paths: newCoordinates,
strokeColor: 'transparent',
strokeOpacity: 1,
strokeWeight: 0.3,
fillColor: '#cd0000',
fillOpacity: 0,
name: rows[i][0],
zIndex: 100
});
google.maps.event.addListener(country, 'mouseover', function() {
this.setOptions({
fillOpacity: 0.3
});
});
google.maps.event.addListener(country, 'mouseout', function() {
this.setOptions({
fillOpacity: 0
});
});
google.maps.event.addListener(country, 'click', function() {
var countryName = this.name;
var code = convert(countryName); // Calls a function that converts the name of the country to its official ISO 3166-1 alpha-2 code.
var modal = document.querySelector('.modal');
var instance = M.Modal.init(modal);
instance.open();
});
country.setMap(map);
}
}
//console.log(map);
//test(map)
}
EDIT
Apparently the datalayer wasn't the problem, but the icon was. That is why it didn't work when I did this:
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0,
clickable: false
}
})
});
The correct way to do it is this:
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0
},
clickable: false
})
});
You basically have 2 options here:
Set the zIndex of your Polygons to a higher number than the data layer. Your Polygons will be clickable but obviously will appear above the data layer, which might not be what you want.
Set the clickable property of the data layer to false so that you can click elements that are below. This will work if you don't need to react to clicks on the data layer...
Option 2 example code:
map.data.setStyle({
clickable: false
});
Edit: Full working example below, using option 2. As you can see the Polygon is below the data layer but you can still click it.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: {
lat: -28,
lng: 137
}
});
var polygon = new google.maps.Polygon({
strokeOpacity: 0,
strokeWeight: 0,
fillColor: '#00FF00',
fillOpacity: .6,
paths: [
new google.maps.LatLng(-26, 139),
new google.maps.LatLng(-23, 130),
new google.maps.LatLng(-35, 130),
new google.maps.LatLng(-26, 139)
],
map: map
});
polygon.addListener('click', function() {
console.log('clicked on polygon');
});
// Load GeoJSON
map.data.loadGeoJson('https://storage.googleapis.com/mapsdevsite/json/google.json');
// Set style
map.data.setStyle({
fillColor: '#fff',
fillOpacity: 1,
clickable: false
});
}
#map {
height: 200px;
}
<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
<div id="map"></div>
I have found that after, setting the z-order, the maps api does not reliably send clicks to polygon feature in the top layer when there are many polygons.
I had one data layer of regions where each feature is a precinct boundary. When you click on one feature, it loads another data layer on top. The top layer consists of polygons inside the region with a higher z-order, representing house title boundaries within that region.
After the houses are loaded, clicking on a house should send the click to the house polygon, not the region. But this sometimes failed - especially if there are many houses.
To resolve the issue, after clicking on a region feature, I set that feature to be non clickable. Then the clicks always propagate to the correct house feature. You can still click on other features of the lower layer, just not the selected one. This solution should work if your data and presentation follows a similar pattern.
/* private utility is only called by this.hideOnlyMatchingFeaturesFromLayer() */
_overrideStyleOnFeature(feature, layer, key, value, overrideStyle, defaultStyle) {
if (feature.getProperty(key) === value) {
if (this.map) {
layer.overrideStyle(feature, overrideStyle);
}
} else {
if (this.map) {
layer.overrideStyle(feature, defaultStyle);
}
}
}
/* Apply an overrideStyle style to features in a data layer that match key==value
* All non-matching features will have the default style applied.
* Otherwise all features except the matching feature is hidden!
* Examples:
* overrideStyle = { clickable: false,strokeWeight: 3}
* defaultStyle = { clickable: true,strokeWeight: 1}
*/
overrideStyleOnMatchingFeaturesInLayer(layer, key, value, overrideStyle, defaultStyle) {
layer.forEach((feature) => {
if (Array.isArray(feature)) {
feature.forEach((f) => {
_overrideStyleOnFeature(f, layer, key, value, overrideStyle, defaultStyle);
});
} else {
_overrideStyleOnFeature(feature, layer, key, value, overrideStyle, defaultStyle);
}
});
}
/* example usage */
overrideStyleOnMatchingFeaturesInLayer(
theRegionsDataLayer,
'PROP_NAME',
propValue,
{ clickable: false, strokeWeight: 3},
{ clickable: true, strokeWeight: 1}
);

Not able to delete selected polygon in ui-gmap-google-map

I am able to draw multiple polygon by using Google Draw manager. Now I am not able to select specific polygon from multiple polygon and delete and edit it. Also not able to get new array after edit or delete.
My demo.js code is as follows :
$scope.map = {
center: { latitude: 19.997454, longitude: 73.789803 },
zoom: 10,
//mapTypeId: google.maps.MapTypeId.ROADMAP,
//radius: 15000,
stroke: {
color: '#08B21F',
weight: 2,
opacity: 1
},
fill: {
color: '#08B21F',
opacity: 0.5
},
geodesic: true, // optional: defaults to false
draggable: false, // optional: defaults to false
clickable: false, // optional: defaults to true
editable: false, // optional: defaults to false
visible: true, // optional: defaults to true
control: {},
refresh: "refreshMap",
options: { scrollwheel: true },
Polygon: {
visible: true,
editable: true,
draggable: true,
geodesic: true,
stroke: {
weight: 3,
color: 'red'
}
},
source: {
id: 'source',
coords: {
'latitude': 19.9989551,
'longitude': 73.75095599999997
},
options: {
draggable: false,
icon: 'assets/img/person.png'
}
},
isDrawingModeEnabled: true
};
$scope.drawingManagerOptions = {
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
//google.maps.drawing.OverlayType.CIRCLE,
google.maps.drawing.OverlayType.POLYGON,
]
},
circleOptions: {
fillColor: '#BCDCF9',
fillOpacity:0.5,
strokeWeight: 2,
clickable: false,
editable: true,
zIndex: 1
},
polygonOptions: {
fillColor: '#BCDCF9',
strokeColor: '#57ACF9',
fillOpacity: 0.5,
strokeWeight: 2,
clickable: false,
editable: true,
zIndex: 1
}
};
var coords = [];
var polygon;
$scope.eventHandler = {
polygoncomplete: function (drawingManager, eventName, scope, args) {
polygon = args[0];
var path = polygon.getPath();
for (var i = 0 ; i < path.length ; i++) {
coords.push({
latitude: path.getAt(i).lat(),
longitude: path.getAt(i).lng()
});
}
},
};
$scope.removeShape = function () {
google.maps.event.clearListeners(polygon, 'click');
google.maps.event.clearListeners(polygon, 'drag_handler_name');
polygon.setMap(null);
}
And My HTML code is as follows :
<ui-gmap-google-map center="map.center" zoom="map.zoom" options="map.options" control="map.control">
<ui-gmap-marker coords="map.source.coords"
options="map.source.options"
idkey="map.source.id">
</ui-gmap-marker>
<ui-gmap-drawing-manager options="drawingManagerOptions" control="drawingManagerControl" events="eventHandler"></ui-gmap-drawing-manager>
</ui-gmap-google-map>
You can find polygon image for reference:
Now I want to select one of polygon from following image and want to delete or update it.
Some help will be really appreciable.
By the ui-google-map plugin's drawing manager doc, you could get the google.maps.drawing.DrawingManager object by the control attributes (putting there an object)
<ui-gmap-drawing-manager control="drawingManagerControl" options="drawingManagerOptions"></ui-gmap-drawing-manager>
and
$scope.drawingManagerControl = {};
//Now get the drawingManager object
var drawingManager = $scope.drawingManagerControl.getDrawingManager();
Having this object is the main work.
Now you can look on everything you need. For your case you need the overlaycomplete events, it will listen for every time you have drawn a shape (=> polygon , circle, polyline)
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
var newShape = e.overlay;
});
newShape is the new shape drawn, polygon in your case, so you can use it like a Polygon object and can use all you need in this reference.
Now I want to select one of polygon from following image and want to
delete or update it.
For it, we'll distinct the polygon selected, by assigning it in a global variable: eg var selectedShape;
And now, Add a click event listener for this drawn polygon and update it as the selectedShape, and now to delete or update, you can use the selectedShape variable.
var selectedShape;
... ...
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
var newShape = e.overlay;
google.maps.event.addListener(newShape, 'click', function() {
selectedShape = newShape;
});
});
Finally you can delete the selected shape by setting his map to null selectedShape.setMap(null); and update the shape by setting it editable to true shape.setEditable(true);
And finally to make these click event possible you need to add clickable options to true for all shape.
PS: Use the IsReady Service to have map ready before working on it
Working plunker: https://embed.plnkr.co/qfjkT2lOu2vkATisGbw7/
Update:
But how to get all co-ordinates of multiple polygon after edit or
draw.
you already have this in your script, in polygonecomplete ($scope.eventHandler). Now you can add it in overlaycomplete events listener, and for everytime you updated the shape (see code bellow)
But challenge is how to identify which polygon is edited on the
map and how to update that specific polygon from my array
You can push in an array for each shape created and could manage it:
...
var allShapes = []; //the array contains all shape, to save in end
....
//get path coords: I use your code there
function getShapeCoords(shape) {
var path = shape.getPath();
var coords = [];
for (var i = 0; i < path.length; i++) {
coords.push({
latitude: path.getAt(i).lat(),
longitude: path.getAt(i).lng()
});
}
return coords;
}
....
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
var newShape = e.overlay;
google.maps.event.addListener(newShape, 'click', function() {
selectedShape = newShape;
});
...
// get coordinate of the polygon
var shapeCoords = getShapeCoords(newShape);
// pushing this shape to allShapes array
allShapes.push(newShape);
});
in the delete function you can delete id by the index of the selectedShape
//delete selected shape
function deleteSelectedShape() {
if (!selectedShape) {
alert("There are no shape selected");
return;
}
var index = allShapes.indexOf(selectedShape);
allShapes.splice(index, 1);
selectedShape.setMap(null);
}
Now you have the allShapes array, and in the end you can loop it then get for each coordinates and save in your db.
I updated the plunker and added some debug log do show you.
This snipet from github could help:
https://github.com/beekay-/gmaps-samples-v3/blob/master/drawing/drawing-tools.html

google maps draw a line between two points

I am getting the latitude/longitude from the DB. I am unable to draw a line between two distances. Here is my code
var auto_refresh = setInterval(
function () {
$.get('http://developer.allsecure.me/Location/longlat', function (data) {
map_canvas.setCenter(new google.maps.LatLng(data.startlat, data.startlong));
clearMarkers();
setMarker(map_canvas, 'center', new google.maps.LatLng(data.startlat, data.startlong), '', '/img/device.png', '', '', true);
var line = new google.maps.Polyline({
path: [new google.maps.LatLng(data.startlat, data.startlong), new google.maps.LatLng(data.endlat, data.endlong)],
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 10,
map: map
});
}, 'json');
}, 1000);
I don't know why it isn't adding the polylines between the two distances.
As the comment above was the actual solution
When you define the line you use map: map shouldn't this be map: map_canvas?

change color programmatically for FusionTablesLayer

I'm reading an array of polygons onto a Google Map using kml in a fusion table. I have an array of 4 colors, and I'd like to programmatically color the polygons one of those 4 colors, depending on the values in another array.
Somehow, the map only colors 4 polygons at a time, even when I specify that there are only 4 styles. How can I color all 130 polygons?
Here is my code:
function setInitialStyles() {
layer = new google.maps.FusionTablesLayer({
map : map,
query : {
select : "geometry",
from : "1gwSN6n_00uZ7YuAP7g4FiUiilybqDRlRmWJrpvA"
}
});
var options = {
styles : [
{
polygonOptions:
{
fillColor: "#ffffff",
strokeColor: "#bcbcbc",
fillOpacity: ".75"
}
}
]
};
var styles = [];
var style1 = candColor[0];
var style2 = candColor[1];
var style3 = candColor[2];
var style4 = candColor[3];
for (var i=0;i<countyCodes.length; i++) {
var c = countyCodes[i];
var whereClause = "'COUSUBFP' = " + c;
var myStyle;
if (countyColors[i] == "#0D58A6" ) { myStyle = style1; }
if (countyColors[i] == "#981400" ) { myStyle = style2; }
if (countyColors[i] == "#E3D132" ) { myStyle = style3; }
if (countyColors[i] == "#007F37" ) { myStyle = style4; }
options.styles.push({
where: whereClause,
polygonOptions: {
fillColor: myStyle
}
});
}
layer.setOptions(options);
}
You can't. Currently FusionTablesLayer is limited to one styled layer, which may have up to five applied styles. See the documentation about the limitation of FusionTablesLayer.
You can define general styling rules (like WHERE clauses) that are applied to all of your polygons. But again: you can only define 5 such rules.
layer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1gwSN6n_00uZ7YuAP7g4FiUiilybqDRlRmWJrpvA'
},
styles: [{
polygonOptions: {
fillColor: "#ffffff",
strokeColor: "#bcbcbc",
fillOpacity: ".75"
}
}, {
where: "population < 1000",
polygonOptions: {
fillColor: "#0000FF"
}
}, {
where: "population > 10000",
polygonOptions: {
fillOpacity: 1.0
}
}]
});
layer.setMap(map);
Your array of styles can only be 5 elements long as I mentioned in the last question you asked on this
This approach (using the Fusion Tables API v1, currently matching on name, not COUSUBFP, as your original table didn't include that column) might work for you, but it is rendering the polygons as native Google Maps API v3 objects, so there may be performance issues.

Categories

Resources