In my rails application I have implemented a google maps using the polygon drawing tool. I have been able to add coordinates and save these to my database successfully.
The problem i'm having is when a user wants to edit and save any changes made to the polygon shape. How do i implement this function? My best guess is to use a conditional to see if the database has any saved coordinates, if so, load them in a listener?
HTML
<div style='width: 100%;'>
<%= hidden_field_tag(:map_coords, value = nil, html_options = {id: 'propertyCoordinates'}) %>
Javascript
function initMap() {
var map = new google.maps.Map(document.getElementById("map"), {
center: { lat: -40.6892, lng: 74.0445 },
zoom: 8,
mapTypeId: google.maps.MapTypeId.HYBRID,
});
var polyOptions = {
strokeWeight: 0,
fillOpacity: 0.45,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
};
// loads databased saved coordinates
var propertyCoords = [<%= #property.coordinates %>];
var points = [];
for (var i = 0; i < propertyCoords.length; i++) {
points.push({
lat: propertyCoords[i][0],
lng: propertyCoords[i][1]
});
}
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ["polygon"]
},
polylineOptions: {
editable: true,
draggable: true
},
rectangleOptions: polyOptions,
circleOptions: polyOptions,
polygonOptions: polyOptions,
map: map
});
if (typeof points !== 'undefined') {
// My guess is to use a conditional statement to check if the map has any coordinates saved?
} else {
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
if (e.type !== google.maps.drawing.OverlayType.MARKER) {
// Switch back to non-drawing mode after drawing a shape.
drawingManager.setDrawingMode(null);
// Add an event listener that selects the newly-drawn shape when the user
// mouses down on it.
var newShape = e.overlay;
newShape.type = e.type;
google.maps.event.addListener(newShape, 'click', function (e) {
if (e.vertex !== undefined) {
if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
var path = newShape.getPaths().getAt(e.path);
path.removeAt(e.vertex);
if (path.length < 3) {
newShape.setMap(null);
}
}
}
setSelection(newShape);
});
}
var coords = e.overlay.getPath().getArray();
document.getElementById("propertyCoordinates").value = coords;
});
}
} // END function initMap()
If I understand correctly, what you're looking for is the polygon editing functionality. My stackblitz example goes something like this:
Draw a polygon if you already have user-saved coordinates. Then fit the map bounds to the poly's bounds. For this you'll probably need a getBounds polyfill.
Make the polygon editable so you can listen to its points' changes. Check the function enableCoordinatesChangedEvent.
Listen to the changes & extract the new polygon points. Look for function extractPolygonPoints.
Then proceed with your business logic.
FYI you'll need to put your own API key at the bottom of the stackblitz index.html. Look for YOUR_KEY.
Related
I am trying to alert user while drawing polygon over Google Maps with pre rendered Polygons using geoJson. Whenever polygon is drawn over existing Polygons, user should get alert. I got an example but it works for the polygons on the same layer (Fiddle Example). My Code is hosted here. Refer the image below that I need:
JS Code as below:
var drawingManager;
function initialize() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 12,
center: new google.maps.LatLng(28.631162, 77.213313),
mapTypeId: google.maps.MapTypeId.ROADMAP,
disableDefaultUI: true,
zoomControl: true
});
var polyOptions = {
fillColor: '#0099FF',
fillOpacity: 0.7,
strokeColor: '#AA2143',
strokeWeight: 2,
editable: true
};
// Creates a drawing manager attached to the map that allows the user to draw Polygons
map.data.loadGeoJson('near.json')
drawingManager = new google.maps.drawing.DrawingManager({
drawingControlOptions: {
drawingModes: [
google.maps.drawing.OverlayType.POLYGON
]
},
polygonOptions: polyOptions,
map: map
});
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
alert("Polygon Completed. Here show message if overlapped");
});
}
HTML as below
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Create Boundary</title>
<style>
#map,
html,
body {
padding: 0;
margin: 0;
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/1.1.2/jsts.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=API-KEY" async defer></script>
<script src="scripts/map.js"></script>
</div>
</body>
</html>
One option would be to parse the polygons from the GeoJSON to "normal" google.maps.Polygon objects, then preload those to your all_overlays array:
map.data.addListener('addfeature', function(e) {
if (e.feature.getGeometry().getType() == "Polygon") {
// simplifying assumption, depends on data
// just check first linear ring
var poly = new google.maps.Polygon({
path: e.feature.getGeometry().getAt(0).getArray(),
fillColor: '#0099FF',
fillOpacity: 0.7,
strokeColor: '#AA2143',
strokeWeight: 2,
map: map
});
all_overlays.push(poly);
}
});
proof of concept fiddle
code snippet:
var drawingManager;
var selectedShape;
var all_overlays = [];
var gmarkers = Array();
var polygons = Array();
function setSelection(shape) {
clearSelection();
selectedShape = shape;
shape.setEditable(true);
}
function clearSelection() {
if (selectedShape) {
selectedShape.setEditable(false);
selectedShape = null;
}
}
function initialize() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: new google.maps.LatLng(33.619003, -83.867405),
mapTypeId: google.maps.MapTypeId.ROADMAP,
disableDefaultUI: true,
zoomControl: true
});
// zoom to show all the features
var bounds = new google.maps.LatLngBounds();
map.data.addListener('addfeature', function(e) {
processPoints(e.feature.getGeometry(), bounds.extend, bounds);
map.fitBounds(bounds);
if (e.feature.getGeometry().getType() == "Polygon") {
// simplifying assumption, depends on data
// just check first linear ring
var poly = new google.maps.Polygon({
path: e.feature.getGeometry().getAt(0).getArray(),
fillColor: '#0099FF',
fillOpacity: 0.7,
strokeColor: '#AA2143',
strokeWeight: 2,
map: map
});
all_overlays.push(poly);
}
});
map.data.loadGeoJson("https://raw.githubusercontent.com/datameet/Municipal_Spatial_Data/master/Delhi/Delhi_Wards.geojson");
map.data.setMap(null);
var polyOptions = {
fillColor: '#0099FF',
fillOpacity: 0.7,
strokeColor: '#AA2143',
strokeWeight: 2,
editable: true
};
// Creates a drawing manager attached to the map that allows the user to draw Polygons
drawingManager = new google.maps.drawing.DrawingManager({
drawingControlOptions: {
drawingModes: [
google.maps.drawing.OverlayType.POLYGON
]
},
polygonOptions: polyOptions,
map: map
});
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
calcIntersection(e.overlay, all_overlays);
all_overlays.push(e.overlay);
});
}
function calcIntersection(newOverlay, allOverlays) {
//ensure the polygon contains enought vertices
if (newOverlay.getPath().getLength() < 3) {
alert("Not enought vertices. Draw a polygon that contains at least 3 vertices.");
return;
}
var geometryFactory = new jsts.geom.GeometryFactory();
var newPolygon = createJstsPolygon(geometryFactory, newOverlay);
//iterate existing polygons and find if a new polygon intersects any of them
var result = allOverlays.filter(function(currentOverlay) {
var curPolygon = createJstsPolygon(geometryFactory, currentOverlay);
var intersection = newPolygon.intersection(curPolygon);
return intersection.isEmpty() == false;
});
//if new polygon intersects any of exiting ones, draw it with green color
if (result.length > 0) {
newOverlay.setOptions({
strokeWeight: 2.0,
fillColor: 'green'
});
}
}
function createJstsPolygon(geometryFactory, overlay) {
var path = overlay.getPath();
var coordinates = path.getArray().map(function name(coord) {
return new jsts.geom.Coordinate(coord.lat(), coord.lng());
});
coordinates.push(coordinates[0]);
var shell = geometryFactory.createLinearRing(coordinates);
return geometryFactory.createPolygon(shell);
}
google.maps.event.addDomListener(window, 'load', initialize);
function processPoints(geometry, callback, thisArg) {
if (geometry instanceof google.maps.LatLng) {
callback.call(thisArg, geometry);
} else if (geometry instanceof google.maps.Data.Point) {
callback.call(thisArg, geometry.get());
} else {
geometry.getArray().forEach(function(g) {
processPoints(g, callback, thisArg);
});
}
}
#map,
html,
body {
padding: 0;
margin: 0;
height: 100%;
}
<script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/1.1.2/jsts.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=drawing&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map"></div>
I've seen this tool which let you draw a line on gmaps and it generates the js code for you
So the JS is:
var myCoordinates = [
new google.maps.LatLng(48.955410,10.034749),
new google.maps.LatLng(59.648652,29.898030)
];
var polyOptions = {
path: myCoordinates,
strokeColor: "#FF0000",
strokeOpacity: 1,
strokeWeight: 3
}
var it = new google.maps.Polyline(polyOptions);
it.setMap(map);
What I would like to do is to start the line from a pin I receive and not a pin I set when I click as per that tool and then I would to draw a infobox at the end of that line (so not where it starts form the pin).
What I am aiming for is to draw a line form a starting point and have an infobox such as per this image below, see the lines on the map
Therefore I can pass the coords here:
new google.maps.LatLng(48.955410,10.034749),
new google.maps.LatLng(59.648652,29.898030)
But how would I target the end of the line and place text there?
With this answer I can define a start and end, but how to draw a box at the end point?
I think you can do it using a marker at the end point of the line, then attaching, for example an infoWindow, at the end and finally hiding the marker.
function initMap() {
var coordinates = {
lat: 40.785845,
lng: -74.020496
};
var coordinates2 = {
lat: 40.805845,
lng: -74.130496
};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: coordinates,
scrollwheel: false
});
var marker = new google.maps.Marker({
position: coordinates,
map: map
});
var infoMarker = new google.maps.Marker({
position: coordinates2,
map: map
});
var infowWindow = new google.maps.InfoWindow();
var line = new google.maps.Polyline({
path: [
marker.position,
infoMarker.position
],
strokeColor: "#FF0000",
strokeOpacity: 1,
strokeWeight: 3
});
line.setMap(map);
infowWindow.setContent("<b>Hello world!</b>");
infowWindow.open(map, infoMarker);
infoMarker.setVisible(false);
}
google.maps.event.addDomListener(window, "load", initMap);
Check it working on this jsfiddle
I'm working on an application centered around Google Maps and for one of the features the user will input a series of clicks in order to create a polygon. I would like to do something with the information after the clicks but I don't know how to accomplish this without setTimeout() which wouldn't work due to the variable amount of time it could take the users to create the polygon.
Currently my code is structured as such (with help from one of the answers on the question Draw polygon using mouse on google maps.
let polygons = [];
$('.set-polygon').click(function () {
let isClosed = false;
let poly = new google.maps.Polyline({
map: map,
path: [],
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2
});
google.maps.event.addListener(map, 'click', function (e) {
if (isClosed) {
return;
}
let markerIndex = poly.getPath().length;
let isFirstMarker = markerIndex === 0;
let marker = new google.maps.Marker({
map: map,
position: e.latLng,
draggable: true
});
if (isFirstMarker) {
google.maps.event.addListener(marker, 'click', function () {
if (isClosed) {
return;
}
let path = poly.getPath();
poly.setMap(null);
poly = new google.maps.Polygon({
map: map,
path: path,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
});
isClosed = true;
});
}
google.maps.event.addListener(marker, 'drag', function (e) {
poly.getPath().setAt(markerIndex, e.latLng);
});
poly.getPath().push(e.latLng);
let params = {
name: "Polygon1",
poly: poly
};
polygons.push(params);
console.log(polygons);
});
});
I have a google maps based program that can calculate the area based on user input. Here the Demo JSFiddle
The HTML should be like this
<div class="google-maps" id="map" style="height: 400px; position: relative; border: 1px solid #CCC;"></div>
<p>Length (red line):
<span id="span-length">0</span> mt - Area (grey area): <span id="span-area">0</span> mt2</p>
And the Javascript
var map;
// Create a meausure object to store our markers, MVCArrays, lines and polygons
var measure = {
mvcLine: new google.maps.MVCArray(),
mvcPolygon: new google.maps.MVCArray(),
mvcMarkers: new google.maps.MVCArray(),
line: null,
polygon: null
};
// When the document is ready, create the map and handle clicks on it
jQuery(document).ready(function() {
map = new google.maps.Map(document.getElementById("map"), {
zoom: 15,
center: new google.maps.LatLng(39.57592, -105.01476),
mapTypeId: google.maps.MapTypeId.ROADMAP,
draggableCursor: "crosshair" // Make the map cursor a crosshair so the user thinks they should click something
});
google.maps.event.addListener(map, "click", function(evt) {
// When the map is clicked, pass the LatLng obect to the measureAdd function
measureAdd(evt.latLng);
});
});
function measureAdd(latLng) {
// Add a draggable marker to the map where the user clicked
var marker = new google.maps.Marker({
map: map,
position: latLng,
draggable: true,
raiseOnDrag: false,
title: "Drag me to change shape",
icon: new google.maps.MarkerImage("/images/demos/markers/measure-vertex.png", new google.maps.Size(9, 9), new google.maps.Point(0, 0), new google.maps.Point(5, 5))
});
// Add this LatLng to our line and polygon MVCArrays
// Objects added to these MVCArrays automatically update the line and polygon shapes on the map
measure.mvcLine.push(latLng);
measure.mvcPolygon.push(latLng);
// Push this marker to an MVCArray
// This way later we can loop through the array and remove them when measuring is done
measure.mvcMarkers.push(marker);
// Get the index position of the LatLng we just pushed into the MVCArray
// We'll need this later to update the MVCArray if the user moves the measure vertexes
var latLngIndex = measure.mvcLine.getLength() - 1;
// When the user mouses over the measure vertex markers, change shape and color to make it obvious they can be moved
google.maps.event.addListener(marker, "mouseover", function() {
marker.setIcon(new google.maps.MarkerImage("/images/demos/markers/measure-vertex-hover.png", new google.maps.Size(15, 15), new google.maps.Point(0, 0), new google.maps.Point(8, 8)));
});
// Change back to the default marker when the user mouses out
google.maps.event.addListener(marker, "mouseout", function() {
marker.setIcon(new google.maps.MarkerImage("/images/demos/markers/measure-vertex.png", new google.maps.Size(9, 9), new google.maps.Point(0, 0), new google.maps.Point(5, 5)));
});
// When the measure vertex markers are dragged, update the geometry of the line and polygon by resetting the
// LatLng at this position
google.maps.event.addListener(marker, "drag", function(evt) {
measure.mvcLine.setAt(latLngIndex, evt.latLng);
measure.mvcPolygon.setAt(latLngIndex, evt.latLng);
});
// When dragging has ended and there is more than one vertex, measure length, area.
google.maps.event.addListener(marker, "dragend", function() {
if (measure.mvcLine.getLength() > 1) {
measureCalc();
}
});
// If there is more than one vertex on the line
if (measure.mvcLine.getLength() > 1) {
// If the line hasn't been created yet
if (!measure.line) {
// Create the line (google.maps.Polyline)
measure.line = new google.maps.Polyline({
map: map,
clickable: false,
strokeColor: "#FF0000",
strokeOpacity: 1,
strokeWeight: 3,
path:measure. mvcLine
});
}
// If there is more than two vertexes for a polygon
if (measure.mvcPolygon.getLength() > 2) {
// If the polygon hasn't been created yet
if (!measure.polygon) {
// Create the polygon (google.maps.Polygon)
measure.polygon = new google.maps.Polygon({
clickable: false,
map: map,
fillOpacity: 0.25,
strokeOpacity: 0,
paths: measure.mvcPolygon
});
}
}
}
// If there's more than one vertex, measure length, area.
if (measure.mvcLine.getLength() > 1) {
measureCalc();
}
}
function measureCalc() {
// Use the Google Maps geometry library to measure the length of the line
var length = google.maps.geometry.spherical.computeLength(measure.line.getPath());
jQuery("#span-length").text(length.toFixed(1))
// If we have a polygon (>2 vertexes inthe mvcPolygon MVCArray)
if (measure.mvcPolygon.getLength() > 2) {
// Use the Google Maps geometry library tomeasure the area of the polygon
var area = google.maps.geometry.spherical.computeArea(measure.polygon.getPath());
jQuery("#span-area").text(area.toFixed(1));
}
}
function measureReset() {
// If we have a polygon or a line, remove them from the map and set null
if (measure.polygon) {
measure.polygon.setMap(null);
measure.polygon = null;
}
if (measure.line) {
measure.line.setMap(null);
measure.line = null
}
// Empty the mvcLine and mvcPolygon MVCArrays
measure.mvcLine.clear();
measure.mvcPolygon.clear();
// Loop through the markers MVCArray and remove each from the map, then empty it
measure.mvcMarkers.forEach(function(elem, index) {
elem.setMap(null);
});
measure.mvcMarkers.clear();
jQuery("#span-length,#span-area").text(0);
}
I'm trying get the mid point (centroid) and return the Long Lat value. It's kinda like this JSFiddle. The problem is I'm trying to get the mid point (centroid) but alywas getting an error. It's return like this :
I am appreciate if anyone can help :D
Thanks
You can declare LatLngBounds() object first, then extend your bound objects for each marker:
var bounds = new google.maps.LatLngBounds();
var loc = measure.polygon.getPath().b;
for (i = 0; i < loc.length; i++) {
bounds.extend(new google.maps.LatLng(loc[i].lat(), loc[i].lng()));
}
var marker = new google.maps.Marker({
position: bounds.getCenter(),
map: map
});
https://jsfiddle.net/xvbLr993/13/
Using the Google Maps API Drawing Manager, I want to collect the location of each point in the polygon that is drawn by a user.
I know there is a getPath() function, but I'm not sure where I use it.
This is all the code I have so far:
var map; var drawingManager;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(45.13536, -100.762952),
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
drawingControl: false,
polygonOptions: {
fillColor: "#000000",
fillOpacity: .6,
strokeWeight: 1,
strokeColor: "#666666",
clickable: false,
editable: true,
zIndex: 1
}
});
drawingManager.setMap(map);
google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
drawingManager.setDrawingMode(null);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
So from this code, how can I use the getPath() function to show the coordinates that make up the Polygon that is drawn?
use it inside the polygoncomplete-callback.
example:
google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
drawingManager.setDrawingMode(null);
var arr=[];
polygon.getPath().forEach(function(latLng){arr.push(latLng.toString());})
alert(arr.join(',\n'));
});
it iterates over the path(using the forEach-method of a google.maps.MVCArray) and populates another array with the string-representations of the LatLng's