Trying to create a 'lozenge' with google maps API - google-maps-api-3

I am current using google maps API to plot points on a map. I am looking for the best way to achieve a 'lozenge' type effect between points (see image).
Essentially the thickness of the lozenge will be determined dynamically.
The only way I can see to do this, is to draw 2 circles at the ends of my points, and create a rectangle in between them, with no borders. I don't like this idea, as it essentially adds 3 polygons to my map for each line I have, and means that I would have to then add 3 click events for each line (one on each polygon) to display data that I want to show, when clicking on the line.
Is there any better way to achieve this?

Just create 2 Polylines...
function initialize() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 3,
center: {lat: 20, lng: 0},
mapTypeId: 'terrain'
});
var flightPlanCoordinates = [
{lat: 20, lng: -20},
{lat: 20, lng: 20}
];
new google.maps.Polyline({
path: flightPlanCoordinates,
geodesic: true,
strokeColor: '#FF0000',
strokeOpacity: .3,
strokeWeight: 20,
map: map
});
new google.maps.Polyline({
path: flightPlanCoordinates,
geodesic: true,
strokeColor: '#000000',
strokeOpacity: 1.0,
strokeWeight: 1,
map: map
});
}
initialize();
#map-canvas {
height: 200px;
}
<div id="map-canvas"></div>
<script src="https://maps.googleapis.com/maps/api/js"></script>
Edit:
You mentioned in your comment you need the stroke to be fixed at a given distance from the center line.
You could still use that technique I think, as you can calculate the size in meters of a map pixel, based on the latitude and the zoom level, and as the size of the stroke is also in pixels, you should be able to recalculate the stroke each time you zoom-in/out. It won't be 100% precise since the stroke width must be an integer, but it should work.
It should work if the various Polyline points are more or less on the same latitude, because the resolution of a map with the Mercator projection is dependent on the latitude. So, for example if you have a big Polyline crossing from -70 to +70 latitude, it won't be accurate all the way.
This is the formula you can use to compute the size of 1 map pixel. You would need to replace map.getCenter().lat() by your Polyline center point.
// Calculate the width of 1 map pixel in meters
let scale = 156543.03392 * Math.cos(map.getCenter().lat() * Math.PI / 180) / Math.pow(2, map.getZoom());
Unfortunately, there is an issue with this approach. Although it's undocumented, a Polyline stroke width can't be bigger than 32px.
var map;
var polyBounds;
var polyStroke;
var polyWidth = 50; // Width in meters
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: {
lat: 20,
lng: 0
},
mapTypeId: 'terrain'
});
var flightPlanCoordinates = [{
lat: 20,
lng: -0.5
},
{
lat: 20,
lng: 0.5
}
];
polyStroke = new google.maps.Polyline({
path: flightPlanCoordinates,
geodesic: true,
strokeColor: '#FF0000',
strokeOpacity: .3,
strokeWeight: 0,
map: map
});
new google.maps.Polyline({
path: flightPlanCoordinates,
geodesic: true,
strokeColor: '#000000',
strokeOpacity: 1.0,
strokeWeight: 1,
map: map
});
polyBounds = new google.maps.LatLngBounds();
for (var i = 0; i < flightPlanCoordinates.length; i++) {
polyBounds.extend(flightPlanCoordinates[i]);
}
google.maps.event.addListenerOnce(map, 'idle', setStroke);
google.maps.event.addListener(map, 'zoom_changed', setStroke);
}
function setStroke() {
// Calculate the width of 1 map pixel in meters
let scale = 156543.03392 * Math.cos(polyBounds.getCenter().lat() * Math.PI / 180) / Math.pow(2, map.getZoom());
polyStroke.setOptions({
strokeWeight: Math.ceil(polyWidth / scale)
});
}
initialize();
#map-canvas {
height: 200px;
}
<div id="map-canvas"></div>
<script src="https://maps.googleapis.com/maps/api/js"></script>
So unless you find out that these values allow for your use case, this won't be a viable solution...
The solution you were pointed to in the comments (How to draw a polygon around a polyline in JavaScript?) is still probably your best option.

I actually discovered a much easier way to achieve this using the google maps API.
There are built in functions for getting a bearing of a point based on a start point...
So I created a function that takes the start and end points of a line, and then creates points around these based on the heading - then create a polygon using those points.
var pointCount = 12;
function setLozengePath(startPoint, endPoint) {
var sp = google.maps.geometry.spherical;
var heading = sp.computeHeading(startPoint, endPoint);
var points = [];
for (var i = 0; i <= pointCount; ++i) {
points.push(sp.computeOffset(endPoint, radius, heading + 90 - i * 15));
}
for (var i = 0; i <= pointCount; ++i) {
points.push(sp.computeOffset(startPoint, radius, heading - 90 - i * 15));
}
return points;
}

Related

Is it possible to have the editable attribute turned off in a polyline but still display the vertices as clickable circles?

I would like to display a polyline so that the vertices can not be moved, deleted or added, ie exactly like when the editable attribute is set to false, but the circles which are present when the editable attribute is set to true are still visible so that they can be clicked and a vertex number obtained.
So the polyline code could be:
newPoly = new google.maps.Polyline({
strokeColor: '#08088a',
strokeWeight: 2,
editable: false
});
Is this possible?
One option: process through the polyline, add circular markers to each vertex in the line with the vertex number in the marker's infowindow.
Related question: Google Maps V3 Polyline : make it editable without center point(s)
proof of concept fiddle
code snippet:
function initialize() {
var infowindow = new google.maps.InfoWindow();
var map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var polyCoord = [
new google.maps.LatLng(41.86, 8.73),
new google.maps.LatLng(41.88, 8.75),
new google.maps.LatLng(42, 8),
new google.maps.LatLng(43.5, 9)
];
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < polyCoord.length; i++) {
bounds.extend(polyCoord[i]);
var marker = new google.maps.Marker({
position: polyCoord[i],
title: '#0',
map: map,
icon: {
path: google.maps.SymbolPath.CIRCLE,
fillColor: 'white',
fillOpacity: 1,
scale: 3,
strokeColor: 'black',
strokeWeight: 1,
strokeOpacity: 1,
// anchor: new google.maps.Point(200, 200)
}
});
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infowindow.setContent("vertex #" + i + "<br>coord: (" + this.getPosition().toUrlValue(6) + ")");
infowindow.open(map, marker);
}
})(marker, i));
}
map.fitBounds(bounds);
// Polyline
var newPoly = new google.maps.Polyline({
strokeColor: '#08088a',
strokeWeight: 2,
editable: false,
path: polyCoord,
map: map
});
}
google.maps.event.addDomListener(window, "load", initialize);
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas" style="border: 2px solid #3872ac;"></div>

draw polygon but it draws a line on center

I draw polygon on my map but i get trouble because it draws a line on center if i formed a square or any shapes.I don't know what makes my polygon draw lines on center.
var map;
var count=0;
var polycolor = '#ED1B24';
var polyarray=[];
function initialize() {
var initial = new google.maps.LatLng(53.199246241276875, -105.76864242553711);
var mapOptions = {
zoom: 16,
center: initial,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.LARGE
},
mapTypeControl: false
};
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
google.maps.event.addListener(map, 'click', function(e) {
polyarray[count]= e.latLng;
addPolygon(polyarray);
count++;
});
}
function addPolygon(path){
var poly = new google.maps.Polygon({
path: path,
strokeColor: polycolor,
strokeOpacity: 1.0,
strokeWeight: 2
});
poly.setMap(map);
}
google.maps.event.addDomListener(window, 'load', initialize);
here is my jsfiddle
my demo
There is not a line down the center, you are drawing a new polygon for each click.
Make the polygon global, and update the path each time you add a point to it:
var poly = new google.maps.Polygon({
strokeColor: polycolor,
strokeOpacity: 1.0,
strokeWeight: 2
});
function addPolygon(path){
poly.setPath(path);
poly.setMap(map);
}
updated fiddle

How to catch if google maps infowindow if it overlays other?

I have a polyline on which i need to show each points coordinates, but the problem is that each info-window overlays other so i decided to draw only those info-windows which doesnt overlay others, but don't have any idea how to catch it...
Currently i have
window.onload = function() {
var bounds = new google.maps.LatLngBounds();
var coordinates = [];
map = new google.maps.Map(document.getElementById('map_canvas'), {mapTypeId: google.maps.MapTypeId.HYBRID});
for (i = 0, len = points.point.length; i < len; i++) {
var tmp_coor = new google.maps.LatLng(points.point[i].lat, points.point[i].lon);
coordinates.push(tmp_coor);
bounds.extend(tmp_coor);
var infowindow = new google.maps.InfoWindow({
content: points.point[i].lat + ', ' + points.point[i].lon,
maxWidth: 120
});
infowindow.setPosition(tmp_coor);
infowindow.open(map);
}
var polyline = new google.maps.Polyline({
path: coordinates,
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2,
editable: false
});
polyline.setMap(map);
map.fitBounds(bounds);
}
It creates
So maybe someone has any suggestions on this problem?

Is there a way to make overlay objects non-clickable?

I'm working on this project of mine that requires to define lat/lng elements by clicking and finally found a way to do so, but just discovered, that the already predefined elements interfere with the new overlay elements defining by the source below. So I looked and looked, and searched, and googled, but wasn't able to find any helpful info about that: is there a way to make the google maps overlays non-clickable?
I'm using a custom function to get the latitude and longitude of a click event and place a predefined circle overlay object. However if I have already predefined overlay elements, I cannot click on top of them to set a new overlay element. I.e. I'd need either to make them non-interactable or non-clickable, or just to set them on a different layer, so that they don't interfere with the click events for the new elements.
Here's the JS I use:
<script type="text/javascript">
var map;
var markersArray = []; //the array for the newly defined objects
function initMap()
{
var latlng = new google.maps.LatLng(41, 29);
var myOptions = {
zoom: 10,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
// add a click event handler to the map object
google.maps.event.addListener(map, "click", function(event)
{
// place a marker
placeMarker(event.latLng);
});
// I've predefined a couple of markers just to see how it works with already defined elements and discovered this interference that I mentioned above
var mar1 = new google.maps.LatLng(40.9653, 29.3705);
var marker1 = new google.maps.Circle({
center: mar1,
radius: 2500,
fillColor: "#FF0000",
strokeWeight: 0,
fillOpacity: 0.35,
map: map
});
var mar2 = new google.maps.LatLng(40.9664, 29.3252);
var marker2 = new google.maps.Circle({
center: mar2,
radius: 2500,
fillColor: "#FF0000",
strokeWeight: 0,
fillOpacity: 0.35,
map: map
});
marker1.setMap(map);
marker2.setMap(map);
}
function placeMarker(location) {
// first remove all new markers if there are any, so that we define one new at a time
deleteOverlays();
var new_marker = new google.maps.Circle({
center: location,
radius: 2500,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 0,
fillColor: "#FF0000",
fillOpacity: 0.35,
//position: location,
map: map
});
// add marker in markers array
markersArray.push(new_marker);
}
// Deletes all markers in the array by removing references to them
function deleteOverlays() {
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
markersArray.length = 0;
}
}
</script>
fiddle
set {clickable: false} in the CircleOptions.
var new_marker = new google.maps.Circle({
center: location,
radius: 2500,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 0,
fillColor: "#FF0000",
fillOpacity: 0.35,
clickable: false, // <=====================
map: map
});
Modified jsfiddle

The correct way to hide a polyline?

I´ve a function that show a polyline on a map, this part is working, now I want to implement a function that hides the polyline, but I can´t find my mistake, thanks in advance.
function cargaMapaCYL(mapa, varControl){
var limite = null;
limite = [
new google.maps.LatLng(42.49956716,-7.019005501),
new google.maps.LatLng(42.49947126,-7.029286373),
new google.maps.LatLng(42.50904062,-7.049299123),
new google.maps.LatLng(42.50722622,-7.069103626),
new google.maps.LatLng(42.50452387,-7.000150672),
new google.maps.LatLng(42.49348015,-6.983058917),
new google.maps.LatLng(42.49843269,-6.971666546),
new google.maps.LatLng(42.51765791,-6.956909023),
new google.maps.LatLng(42.52010069,-6.927429186),
new google.maps.LatLng(42.50992238,-6.914231493),
new google.maps.LatLng(42.50096695,-6.879679821),
new google.maps.LatLng(42.48775868,-6.857775832),
new google.maps.LatLng(43.23907504,-3.293216584)], "#000000", 5);
var contorno= new google.maps.Polyline({
path: limite,
strokeColor: "#000000",
strokeOpacity: 1.0,
strokeWeight: 2
});
if(varControl==true){
contorno.setMap(mapa);
}
if(varControl==false){
contorno.setMap(null);
}
}
You only need to create the Polyline once. Put it into a global var contorno = ... Then you can create a toggle function using the setVisible(boolean) method.
if(contorno.getVisible()){
contorno.setVisible(false);
else{
contorno.setVisible(true);
}
// or
contorno.getVisible() ? contorno.setVisible(false) : contorno.setVisible(true);
Blow is an example which hides the path after 3 seconds.
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
<div id="map"></div>
<script>
// This example creates a 2-pixel-wide red polyline showing the path of William
// Kingsford Smith's first trans-Pacific flight between Oakland, CA, and
// Brisbane, Australia.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 3,
center: {lat: 0, lng: -180},
mapTypeId: 'terrain'
});
var flightPlanCoordinates = [
{lat: 37.772, lng: -122.214},
{lat: 21.291, lng: -157.821},
{lat: -18.142, lng: 178.431},
{lat: -27.467, lng: 153.027}
];
var flightPath = new google.maps.Polyline({
path: flightPlanCoordinates,
geodesic: true,
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2
});
flightPath.setMap(map);
setTimeout(function() {
alert('hide path');
flightPath.setVisible(false);
}, 3000);
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
Everytime your function is called, it creates a new polyline. Which is either added to the map or not.
Persumably you want be able to call the function once with true to add the line, then again with false to remove it. At the moment, when you call it a second time, its creates a new line and doesn't add it to the map. It does not touch the original line, already added to the map.
One way is too keep the line in a global variable. Then you can refer to the exact same object between calls.
var contorno = null;
function cargaMapaCYL(mapa, varControl){
if (!contorno) {
var limite = [...], "#000000", 5);
contorno= new google.maps.Polyline({...});
}
if(varControl){
contorno.setMap(mapa);
} else {
contorno.setMap(null);
}
}

Resources