I'm resizing the symbol this way:
google.maps.event.addListener(map_object, 'zoom_changed', (function(arrow) { return function() {
var zoom = map_object.getZoom();
var scale = 2.48676567E-2 * Math.exp(0.143700035 * zoom);
arrow.icons[0].icon.scale = scale;
}})(arrow));
When I inspect the arrow object, the scale property changes properly on zoom_changed, but it's not changing its size in the map.
Is there something I'm forgetting to do?
UPDATE
The arrow object is defined as follow:
var sym = {
path: "M0 15 L30 0 L60 15",
rotation: angle,
scale: 0.25,
strokeWeight: 3,
};
var arrow = new google.maps.Polyline({
path: [{lat: lat, lng: lng},{lat: lat2, lng: lng}],
geodesic: true,
strokeColor: color,
strokeOpacity: 0.9,
icons: [{
icon: sym,
offset: '100%'
}]
});
Just for future references.
I solved my problem using the setOptions method from Polyline class, instead of setting the scale property.
My code becomes to this:
google.maps.event.addListener(map_object, 'zoom_changed', (function(arrow) { return function() {
var zoom = map_object.getZoom();
var scale = 2.48676567E-2 * Math.exp(0.143700035 * zoom);
var icon = arrow.icons[0].icon;
icon.scale = scale;
arrow.setOptions({icons: [{icon: icon, offset: '100%'}]});
}})(arrow));
Now, the arrow size is changing on map zoom in the right way.
Related
I can't manage with drawing rectangle between two cities. I've searched everywhere on the Internet and can't find out why my polygon is drawn on Google Maps as parallelogram even so on 2d plane (not earth plane) this rectangle is drawn properly.
What I noticed is that the curvature sides of parallelogram depends on where cities are placed on map. If two cities are placed vis-a-vis then my function draw rectangle successfully. But If they are placed diagonally then my function draw parallelogram. The result should be rotated rectangle with height as distance between two cities and width as kilometers that user chooses.
Here is my function that should draw rectangle between two cities. As args we need to give position of first city ($x1 is lat, $y1 is lng), position of second city and as third arg a radius in kilometers ($l1) from center point of rectangle.
function getPolygon($x1,$y1,$x2,$y2,$l1){
var $l1 = $l1*0.010526; //approx kilometers
var $distanceV = [($x2 - $x1), ($y2 - $y1)];
var $vlen = Math.sqrt(Math.pow($distanceV[0], 2) +
Math.pow($distanceV[1],2));
if($vlen == 0)
return [[0,0],[0,0],[0,0],[0,0]];
var $l2 = $vlen;
var $normalized = [($distanceV[0] / $vlen), ($distanceV[1] / $vlen)];
var $rotated = [(-1 * $normalized[1]), ($normalized[0])];
var $p1 = [($x1 - $rotated[0] * $l1 / 2), ($y1 - $rotated[1] * $l1 / 2)];
var $p2 = [($p1[0] + $rotated[0] * $l1), ($p1[1] + $rotated[1] * $l1)];
var $p3 = [($p1[0] + $normalized[0] * $l2), ($p1[1] + $normalized[1] * $l2)];
var $p4 = [($p3[0] + $rotated[0] * $l1), ($p3[1] + $rotated[1] * $l1)];
var $points = [
{lat: $p1[0], lng: $p1[1]},
{lat: $p3[0], lng: $p3[1]},
{lat: $p4[0], lng: $p4[1]},
{lat: $p2[0], lng: $p2[1]},
{lat: $p1[0], lng: $p1[1]}
];
return $points;
}
Then I draw it on Google Maps like this:
new google.maps.Polygon({
paths: getPolygon(first_city_lat, first_city_lng, second_city_lat, second_city_lng, 30),
strokeColor: '#FF0000',
strokeOpacity: 0.5,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.05
});
Here is an example should be rectangle between Birmingham and Oxford: JSFiddle
Additionally I'm sure that kilometers converter is not exact and it again depends how cities are placed.
The earth is curved. To get a polygon that appears rectangular on the curved sphere, you need to use calculations that take the projection of the map into account.
The Google Maps Javascript API v3 has a spherical geometry library that can be used to compute the desired points.
function getPolygon($x1,$y1,$x2,$y2,$l1){
var points = [];
var city1 = new google.maps.LatLng($x1, $y1);
var city2 = new google.maps.LatLng($x2, $y2);
var heading = google.maps.geometry.spherical.computeHeading(city1, city2);
points.push(google.maps.geometry.spherical.computeOffset(city1, $l1/2*1000, heading+90));
points.push(google.maps.geometry.spherical.computeOffset(city1, $l1/2*1000, heading-90));
points.push(google.maps.geometry.spherical.computeOffset(city2, $l1/2*1000, heading-90));
points.push(google.maps.geometry.spherical.computeOffset(city2, $l1/2*1000, heading+90));
points.push(points[0]);
return points;
}
proof of concept fiddle
code snippet:
var map;
google.maps.event.addDomListener(window, "load", function() {
var map = new google.maps.Map(document.getElementById("map_div"), {
center: new google.maps.LatLng(52.489471, -1.898575),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var trace = new google.maps.Polygon({
paths: getPolygon(52.489471, -1.898575, 51.752022, -1.257677, 30),
strokeColor: '#FF0000',
strokeOpacity: 0.5,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.05,
map: map
});
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < trace.getPath().getLength(); i++) {
bounds.extend(trace.getPath().getAt(i));
}
map.fitBounds(bounds);
function getPolygon($x1, $y1, $x2, $y2, $l1) {
var points = [];
var city1 = new google.maps.LatLng($x1, $y1);
var city2 = new google.maps.LatLng($x2, $y2);
var heading = google.maps.geometry.spherical.computeHeading(city1, city2);
points.push(google.maps.geometry.spherical.computeOffset(city1, $l1 / 2 * 1000, heading + 90));
points.push(google.maps.geometry.spherical.computeOffset(city1, $l1 / 2 * 1000, heading - 90));
points.push(google.maps.geometry.spherical.computeOffset(city2, $l1 / 2 * 1000, heading - 90));
points.push(google.maps.geometry.spherical.computeOffset(city2, $l1 / 2 * 1000, heading + 90));
points.push(points[0]);
return points;
}
});
html,
body {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px;
}
#map_div {
height: 95%;
}
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
<div id="map_div"></div>
In an e-commerce system I'm building, I want to use google maps api to show the origins of the purchases. I want the markers to be shaped as a circle, and I want that circle's size to be determined by the number of purchases made from that particular city. Let's say there were 100 orders from NYC and 200 from Boston, The Boston's circle will be twice the size.
How can I do that?
Your marker icon can be a symbol (SVG path) so you can scale it to your convenience.
The following example upscales the symbol each time you add one to the map. You can easily reuse that to your use case.
var map;
var polyLine;
var polyOptions;
var iconSize = 0.5;
function initialize() {
var mapOptions = {
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: new google.maps.LatLng(0,0)
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
google.maps.event.addListener(map, 'click', function(event) {
addPoint(event);
});
}
function addPoint(event) {
var icon = {
path: "M-20,0a20,20 0 1,0 40,0a20,20 0 1,0 -40,0",
fillColor: '#FF0000',
fillOpacity: .6,
anchor: new google.maps.Point(0,0),
strokeWeight: 0,
scale: iconSize
}
var marker = new google.maps.Marker({
position: event.latLng,
map: map,
draggable: false,
icon: icon,
zIndex : -20
});
map.panTo(event.latLng);
iconSize += .1;
}
initialize();
JSFiddle demo
Created some functionality in google v3 api where users can drop a pin. Once they drop a pin an editable circle is created with the pin as the center. They can expand the circle and see the distance which pops up in an infowindow. The issue is that it is difficult to get to the nearest mile or half mile. I will try to get it to the nearest mile by dragging the circle and it gets to like 10.23 miles...
Does anyone have recommendation about this?
http://jsfiddle.net/wa8s0dya/
//create circle
function createCircle() {
if ((circle != null) && circle.setMap) {
circle.setMap(null);
circle = null;
}
var options = {
strokeColor: '#0099FF',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#0099FF',
fillOpacity: 0.35,
map: map,
center: marker.getPosition(),
editable: true,
radius: 500
};
// Add the circle for this city to the map.
circle = new google.maps.Circle(options);
google.maps.event.addListener(circle, 'radius_changed', function () {
removeInfoWindow();
popUpPinInfo(marker, circle.radius, map);
});
google.maps.event.addListener(circle, 'circlecomplete', function () {
removeInfoWindow();
popUpPinInfo(marker, circle.radius, map);
});
}
One mile is 1609.34 meters (the circle's radius is defined in meters).
To make the circle size in increments of 1 mile, you need to make the radius (the +8m causes it to not get rounded down to .99 miles):
circle.setRadius(circle.getRadius() - (circle.getRadius() % 1609.34)+8);
Whenever the radius changes:
google.maps.event.addListener(circle, 'radius_changed', function () {
if ((circle.getRadius() % 1609.34) > 160) {
circle.setRadius(circle.getRadius() - (circle.getRadius() % 1609.34)+8) // meters per mile
}
removeInfoWindow();
popUpPinInfo(marker, circle.radius, map);
});
google.maps.event.addListener(circle, 'circlecomplete', function () {
circle.setRadius(circle.getRadius() - (circle.getRadius() % 1609.34)+8) // meters per mile
removeInfoWindow();
popUpPinInfo(marker, circle.radius, map);
});
updated fiddle
I'm trying to figure out of it's possible to detect when two Google Maps circles (around markers) intersect or bump into each other.
What I want to accomplish is, if two circles intersect, I want to raise an event. I'm not sure if this is possible though.
Calculate the distance between the centers of the circles, if it is less than the sum of the radius of the two circles, they intersect.
proof of concept fiddle
(based off of the code in Larry Dukek's answer, but using native Google Maps Javascript API v3 functions from the geometry library)
code snippet:
let map;
function initMap() {
// Create the map.
map = new google.maps.Map(document.getElementById('map'), {
center: {
lat: 41.081301,
lng: -98.214219
},
zoom: 25
});
var c0 = new google.maps.Circle({
strokeColor: '#0000FF',
strokeOpacity: 1,
strokeWeight: 1,
fillColor: '#0000FF',
fillOpacity: 0.2,
map: map,
center: {
lat: 41.082953,
lng: -98.215285
},
radius: 200
});
var c1 = new google.maps.Circle({
strokeColor: '#FF0000',
strokeOpacity: 1,
strokeWeight: 1,
fillColor: '#FF0000',
fillOpacity: 0.2,
map: map,
center: {
lat: 41.081070,
lng: -98.214027
},
radius: 34.692866520
});
console.log("c1 & c0 hasIntersections returns:" + hasIntersections(c1, c0));
var c2 = new google.maps.Circle({
strokeColor: '#00FF00',
strokeOpacity: 1,
strokeWeight: 1,
fillColor: '#00FF00',
fillOpacity: 0.2,
map: map,
center: {
lat: 41.083313,
lng: -98.211635
},
radius: 34.692866520
});
console.log("c2 & c0 hasIntersections returns:" + hasIntersections(c2, c0));
}
function hasIntersections(circle0, circle1) {
var center0 = circle0.getCenter();
var center1 = circle1.getCenter();
var maxDist = circle0.getRadius() + circle1.getRadius();
var actualDist = google.maps.geometry.spherical.computeDistanceBetween(center0, center1);
return maxDist >= actualDist;
}
/* 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;
}
<!DOCTYPE html>
<html>
<head>
<title>Circles</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="map"></div>
<!-- Async script executes immediately and must be after any DOM elements used in callback. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=geometry&v=weekly&channel=2" async></script>
</body>
</html>
Here is some JavaScript that will detect if two circles intersect
var e = Math; // shortcut for the mathematical function
var D2R = e.PI/180.0; // value used for converting degrees to radians
Number.prototype.toRadians = function() {
return this * D2R;
};
function distance(lat0,lng0,lat1,lng1){
// convert degrees to radians
var rlat0 = lat0.toRadians();
var rlng0 = lng0.toRadians();
var rlat1 = lat1.toRadians();
var rlng1 = lng1.toRadians();
// calculate the differences for both latitude and longitude (the deltas)
var Δlat=(rlat1-rlat0);
var Δlng=(rlng1-rlng0);
// calculate the great use haversine formula to calculate great-circle distance between two points
var a = e.pow(e.sin(Δlat/2),2) + e.pow(e.sin(Δlng/2),2)*e.cos(rlat0)*e.cos(rlat1);
var c = 2*e.asin(e.sqrt(a));
var d = c * 6378137; // multiply by the radius of the great-circle (average radius of the earth in meters)
return d;
}
function hasIntersections(circle0,circle1){
var center0 = circle0.getCenter();
var center1 = circle1.getCenter();
var maxDist = circle0.getRadius()+circle1.getRadius();
var actualDist = distance(center0.lat(),center0.lng(),center1.lat(),center1.lng());
return maxDist>=actualDist;
}
Just call hasIntersections with the references to your circles. Here is an example that showing two circles almost touching (returning false) and if you change the zero to a one in c1 they will touch (returning true).
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 41.081301, lng: -98.214219},
zoom: 25
});
var c0 = new google.maps.Circle({
strokeOpacity: .1,
strokeWeight: 1,
fillColor: '#0000FF',
fillOpacity: .2,
map: map,
center: {lat:41.082953, lng: -98.215285},
radius: 200
});
var c1 =new google.maps.Circle({
strokeOpacity: .1,
strokeWeight: 1,
fillColor: '#FF0000',
fillOpacity: .2,
map: map,
center: {lat:41.081070, lng: -98.214027},
radius: 34.692866520
});
console.log(hasIntersections(c1,c0));
Another frustrating issue I have with Jvectormap, I wish to focus on a Marker on page/map load via lngLat, how would I do this? Ideally it would be good to say focus on this marker or focus on latlng. I will only be displaying 1 marker per map but I won't know the x/y just the lngLat or possibly countrycode. There might be an easier way altogether to do this so suggestions would be welcome. Thanks for your help in advanced
var markers = [ {latLng: [47.774099, -52.793427], name: "loc 1", label: "This blahblah"}]
$(function(){
$('#map1').vectorMap({
map: 'world_mill_en',
scale: ['#C8EEFF', '#0071A4'],
normalizeFunction: 'polynomial',
hoverOpacity: 0.7,
hoverColor: false,
markerStyle: {
initial: {
fill: '#F8E23B',
stroke: '#383f47'
}
},
backgroundColor: '#383f47',
markers: markers,
focusOn:{
latLng: [47.774099, -52.793427],
scale: 5
},
onMarkerLabelShow: function(event, label, index) {
label.html( ''+markers[index].label+'');
}
});
});
I needed the same thing as you and came across your unanswered question. This is the code I wrote (slash copied, pasted & modified from jVectorMap source) to solve the problem for myself. I hope you and others find it helpful.
Simply pass the scale, longitude, and latitude to setFocusLatLng.
I may attempt to get this or something similar accepted into the jVectorMap project on GitHub, so there may be a better way to do this later.
Disclaimer: Points on the edge of the map will not be centered. They should be in the view, though.
EDIT: As requested, here is the whole thing on jsfiddle:
http://jsfiddle.net/BryanTheScott/rs7H5/
EDIT: Also added the rest of the JS below:
$(function(){
var smallMap = $('#my_map_container').vectorMap({
map: 'world_mill_en',
zoomOnScroll: true,
zoomMax:5,
zoomMin:5,
regionStyle: {
initial: {
fill: '#222222',
"fill-opacity": 1,
stroke: '#444444',
"stroke-width": 1,
"stroke-opacity": 0.7
},
hover: {
"fill-opacity": 0.8,
fill: '#333333'
}
},
markerStyle: {
initial: {
fill: "#000000",
"stroke": "#7FC556",
"stroke-width": 2,
r: 3
}
},
markers: [[37.770172,-122.422771]]
});
var mapObj = $('#my_map_container').vectorMap('get', 'mapObject');
mapObj.setFocusLatLng = function(scale, lat, lng){
var point,
proj = jvm.WorldMap.maps[this.params.map].projection,
centralMeridian = proj.centralMeridian,
width = this.width - this.baseTransX * 2 * this.baseScale,
height = this.height - this.baseTransY * 2 * this.baseScale,
inset,
bbox,
scaleFactor = this.scale / this.baseScale,
centerX,
centerY;
if (lng < (-180 + centralMeridian)) {
lng += 360;
}
point = jvm.Proj[proj.type](lat, lng, centralMeridian);
inset = this.getInsetForPoint(point.x, point.y);
if (inset) {
bbox = inset.bbox;
centerX = (point.x - bbox[0].x) / (bbox[1].x - bbox[0].x);
centerY = (point.y - bbox[0].y) / (bbox[1].y - bbox[0].y);
this.setFocus(scale, centerX, centerY);
}
}
mapObj.setFocusLatLng(5, 37.770172,-122.422771);
});
Very late to the party here, but I needed a way to center on a marker even after the map had initially loaded, and here is what I came up with:
var yourMapHere = $(yourMapObject).vectorMap('get', 'mapObject');
var point = yourMapHere.latLngToPoint(yourMarkerLat, yourMarkerLng);
var x = yourMapHere.transX - point.x / yourMapHere.scale;
var y = yourMapHere.transY - point.y / yourMapHere.scale;
yourMapHere.setScale(yourScaleValueHere, x, y, true, true);