How to automatically scatter identical markers over a certain area - javascript

I'm currently building a webpage that shows customer reviews on a Google Maps. There is one issue and that is that there are already over 1200 reviews that needs to be shown on the map but those reviews only have a city attached to them so when I load all of those reviews in the map than a lot of them will share the exact same coordinates.
I am looking for a way to scatter identical markers within a certain radius. So lets say pick every single marker on the map and move them al 1% in a random direction to create distance between them.
I don't really mind how this will be done, be it by javascript or PHP, duriong the placement of the markers or beforehand with an algorithm that sets new coordinates one.

May be something like this
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 5,
center: { lat: -25.363, lng: 131.044 }
});
var originalMarker = new google.maps.Marker({
position: { lat: -25.363, lng: 131.044 },
map: map,
title: ''
});
google.maps.event.addListenerOnce(map, 'idle', function () {
var circle = new google.maps.Circle({
map: map,
radius: 1000 * 1000, //in metres
fillColor: '#AA0000'
});
circle.bindTo('center', originalMarker, 'position');
drawMarkersInCircle(circle, 200);
});
}
function drawMarkersInCircle(circle, count) {
var map = circle.getMap();
var proj = map.getProjection();
var centerPoint = proj.fromLatLngToPoint(circle.getCenter());
var radius = Math.abs(proj.fromLatLngToPoint(circle.getBounds().getNorthEast()).x - centerPoint.x);
for (var i = 0; i < count; i++) {
var point = createRandomPointInCircle(centerPoint, radius);
var pos = proj.fromPointToLatLng(point);
//console.log(point);
var marker = new google.maps.Marker({
position: pos,
map: map,
title: ''
});
}
}
function createRandomPointInCircle(centerPoint, radius) {
var angle = Math.random() * Math.PI * 2;
var x = (Math.cos(angle) * getRandomArbitrary(0, radius)) + centerPoint.x;
var y = (Math.sin(angle) * getRandomArbitrary(0, radius)) + centerPoint.y;
return new google.maps.Point(x, y);
}
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
google.maps.event.addDomListener(window, 'load', initMap);
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
<script src="http://maps.googleapis.com/maps/api/js?key=&sensor=false"></script>
<div id="map"></div>
The example demonstrates how to draw a markers randomly inside a circle.

Related

Google Maps Api draw my polygon (rectangle) as parallelogram

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>

Google Maps API 3 - Show All Markers on Screen, but Keep Centerpoint

This is very similar to this question. I would like to ensure that all markers are shown at the current zoom level. However, I would also like to choose the center point beforehand (current location of user). If circles are markers, and the square is my intended centerpoint, in the images below, the linked solution would create the first (left, top) image. I would like the second (right, bottom) image.
You can create a LatLngBounds object and extend it with each of your markers coordinates. Then get your bounds object north east and south west coordinates and check whether theses coordinates are contained within the current map bounds. If not, zoom out and try again.
Most of the below code is to generate random markers within certain bounds. The real interesting parts are where I call bounds.extend(position) and the fitAllBounds function.
var map, bounds;
function initialize() {
var southWest = new google.maps.LatLng(40, -70);
var northEast = new google.maps.LatLng(35, -80);
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();
var center = new google.maps.LatLng(40, -70);
map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 12,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
// Add center marker
new google.maps.Marker({
position: center,
label: 'C',
map: map
});
// Create the bounds object
bounds = new google.maps.LatLngBounds();
// Create random markers
for (var i = 0; i < 20; i++) {
// Calculate a random position
var position = new google.maps.LatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random());
var marker = new google.maps.Marker({
position: position,
map: map
});
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
map.setZoom(5);
map.setCenter(marker.position);
}
})(marker, i));
// Extend the bounds with the last marker position
bounds.extend(position);
}
// Fit all bounds once, when the map is ready
google.maps.event.addListenerOnce(map, 'idle', function() {
fitAllBounds(bounds);
});
}
function fitAllBounds(b) {
// Get north east and south west markers bounds coordinates
var ne = b.getNorthEast();
var sw = b.getSouthWest();
// Get the current map bounds
var mapBounds = map.getBounds();
// Check if map bounds contains both north east and south west points
if (mapBounds.contains(ne) && mapBounds.contains(sw)) {
// Everything fits
return;
} else {
var mapZoom = map.getZoom();
if (mapZoom > 0) {
// Zoom out
map.setZoom(mapZoom - 1);
// Try again
fitAllBounds(b);
}
}
}
initialize();
#map-canvas {
height: 200px;
width: 200px;
}
<div id="map-canvas"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
Here it is also on JSFiddle:
JSFiddle demo

Google Maps API fitbounds moving map to left or right

I have an application where I save center latitude, longitude and the distance between the center and the North East corner of the map, in this way we could draw the map with the same bounds we saved.
The first issue I faced when I was drawing the map with the stored data was that using fitbounds() a margin is added to the bounds I pass, this added an extra zoom level, with it the map looks very different to the original version of the map. To fix it I recalculate the bounds with the solution #6 from : https://code.google.com/p/gmaps-api-issues/issues/detail?id=3117
I use map.fitbounds(new bounds) with the result of recalculating the bounds due to the margin added by the fitbounds method.
But now I noticed that when I draw a map with the values stored in database, the map is being moved to the right or left randomly, this is more notorius on low zoom levels. I hope you can give an idea why this is happening or how can I fix it. Thank you.
You can write your custom function to work around a margin. Please look at the implementation of myFitBounds() function.
code snippet:
var map;
function myFitBounds(myMap, bounds) {
myMap.fitBounds(bounds);
var overlayHelper = new google.maps.OverlayView();
overlayHelper.draw = function () {
if (!this.ready) {
var projection = this.getProjection(),
zoom = getExtraZoom(projection, bounds, myMap.getBounds());
if (zoom > 0) {
myMap.setZoom(myMap.getZoom() + zoom);
}
this.ready = true;
google.maps.event.trigger(this, 'ready');
}
};
overlayHelper.setMap(myMap);
}
// LatLngBounds b1, b2 -> zoom increment
function getExtraZoom(projection, expectedBounds, actualBounds) {
var expectedSize = getSizeInPixels(projection, expectedBounds),
actualSize = getSizeInPixels(projection, actualBounds);
if (Math.floor(expectedSize.x) == 0 || Math.floor(expectedSize.y) == 0) {
return 0;
}
var qx = actualSize.x / expectedSize.x;
var qy = actualSize.y / expectedSize.y;
var min = Math.min(qx, qy);
if (min < 1) {
return 0;
}
return Math.floor(Math.log(min) / Math.log(2) /* = log2(min) */);
}
// LatLngBounds bnds -> height and width as a Point
function getSizeInPixels(projection, bounds) {
var sw = projection.fromLatLngToContainerPixel(bounds.getSouthWest());
var ne = projection.fromLatLngToContainerPixel(bounds.getNorthEast());
return new google.maps.Point(Math.abs(sw.y - ne.y), Math.abs(sw.x - ne.x));
}
function initialize() {
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
panControl: false,
zoomControl: false,
mapTypeControl: false,
scaleControl: false,
streetViewControl: false,
overviewMapControl: false
};
map = new google.maps.Map(document.getElementById('map-canvas'),mapOptions);
bounds = new google.maps.LatLngBounds();
var extendPoint1 = new google.maps.LatLng(35.491823,6.626860999999963);
var extendPoint2 = new google.maps.LatLng(47.09192,18.520579999999995);
new google.maps.Marker({position: extendPoint1,map: map});
new google.maps.Marker({position: extendPoint2,map: map});
bounds.extend(extendPoint1);
bounds.extend(extendPoint2);
myFitBounds(map,bounds);
//map.fitBounds(bounds);
}
html,
body,
#map-canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<div id="map-canvas"></div>
<script async defer src="https://maps.googleapis.com/maps/api/js?v=3&libraries=places&callback=initialize"></script>

Circle fade out over time on Google Map [duplicate]

This question already has an answer here:
How do I fade out a circle in a Google Map, x seconds after I've added it to the map?
(1 answer)
Closed 8 years ago.
I am attempting to fade circles out with a setInterval on fillOpacity. However a console log shows the opacity changing but the appearance of the circle does not seem to change. Is there a different set function required to do this?
http://jsfiddle.net/faaxeskg/5/
setInterval(function() {
marker.fillOpacity -= .01;
console.log(marker.fillOpacity);
}, 200);
code snippet:
var map = null;
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(-25.363882, 131.044922)
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
// Add 500 markers to the map at random locations
var southWest = new google.maps.LatLng(-31.203405, 125.244141);
var northEast = new google.maps.LatLng(-25.363882, 131.044922);
var bounds = new google.maps.LatLngBounds(southWest, northEast);
map.fitBounds(bounds);
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();
setInterval(function() {
var position = new google.maps.LatLng(
southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
var populationOptions = {
strokeOpacity: 0,
fillColor: '#FF0000',
fillOpacity: 0.65,
map: map,
center: position,
radius: 40000
};
var marker = new google.maps.Circle(populationOptions);
setInterval(function() {
marker.fillOpacity -= .01;
console.log(marker.fillOpacity);
}, 200);
setTimeout(function() {
marker.setMap(null);
delete marker;
}, 30000);
}, 2000);
}
google.maps.event.addDomListener(window, 'load', initialize);
html,
body,
#map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry"></script>
<div id="map-canvas" style="float:left;width:100%;height:100%;"></div>
You need to associate the "opacity change" to the marker, you can do that with function closure (a createCircleMarker function).
Don't use undocumented properties. Use the documented methods.
marker.set("fillOpacity, marker.get("fillOpacity")-0.01);
working code snippet:
var map = null;
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(-25.363882, 131.044922)
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
// Add 500 markers to the map at random locations
var southWest = new google.maps.LatLng(-31.203405, 125.244141);
var northEast = new google.maps.LatLng(-25.363882, 131.044922);
var bounds = new google.maps.LatLngBounds(southWest, northEast);
map.fitBounds(bounds);
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();
setInterval(function() {
var position = new google.maps.LatLng(
southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
var populationOptions = {
strokeOpacity: 0,
fillColor: '#FF0000',
fillOpacity: 0.65,
map: map,
center: position,
radius: 40000
};
createCircleMarker(populationOptions);
}, 2000);
}
function createCircleMarker(populationOptions) {
var marker = new google.maps.Circle(populationOptions);
setInterval(function() {
marker.set("fillOpacity",marker.get("fillOpacity")-0.05);
console.log(marker.fillOpacity);
}, 200);
setTimeout(function() {
marker.setMap(null);
delete marker;
}, 30000);
}
google.maps.event.addDomListener(window, 'load', initialize);
html,
body,
#map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=geometry"></script>
<div id="map-canvas" style="float:left;width:100%;height:100%;"></div>

Horizontal repeating Google Maps Marker on ImageMapType

<!DOCTYPE html>
<html>
<head>
<title>Image map types</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script>
var moonTypeOptions = {
getTileUrl: function(coord, zoom) {
var bound = Math.pow(2, zoom);
return 'full-out' +
'/' + zoom + '/' + coord.x + '/' +
(bound - coord.y - 1) + '.png';
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 6,
minZoom: 1,
radius: 1738000,
name: 'Moon'
};
var moonMapType = new google.maps.ImageMapType(moonTypeOptions);
function initialize() {
var myLatlng = new google.maps.LatLng(0, 0);
var mapOptions = {
center: myLatlng,
zoom: 1,
streetViewControl: false,
mapTypeControlOptions: {
mapTypeIds: ['moon']
}
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
map.mapTypes.set('moon', moonMapType);
map.setMapTypeId('moon');
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: 'Hello World!'
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
I have above code to render a custom large 16834 * 16834 image of 19 mb which is broken into tiles using gdal2tiles and gdal_translate. In short, I have all the corresponding tiles.
The above code is working perfectly fine rendering image and different zoom levels. However, when I add marker, it is displayed multiple times at lower zoom level. I would like the marker not repeat itself horizontally.
Is there any way to avoid horizontal repeating markers? Currently, I'm using Leaflet.js which doesn't repeat marker horizontally as Google Maps library.
I want to use Google maps because of its stability and popularity.
I'm using Ubuntu 14.04 as OS.
set the optimized-option of the markers to false
For those who has still this problem, have a look at my solution.
1- Set the maps zoom to (2) and add marker positions (lat,long) i.e
var minZoomLevel = 2;
map.setZoom(minZoomLevel);
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < result.length; i++){
var latlng = new google.maps.LatLng(result[i].Lat, result[i].Lng);
bounds.extend(latlng);
});
2- Attach a event listener on zoom changed i.e
google.maps.event.addListener(map, 'zoom_changed', function() {
if (map.getZoom() < minZoomLevel) map.setZoom(minZoomLevel);
});
3- Attach a center changed listener (This done the trick) i.e
google.maps.event.addListener(map, 'center_changed', function()
{
checkBounds(bounds);
}
function checkBounds(allowedBounds) {
if(allowedBounds.contains(map.getCenter())) {
return;
}
var mapCenter = map.getCenter();
var X = mapCenter.lng();
var Y = mapCenter.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
Every time you change the center, it will check your points and restrict map to certain area . Setting zoom will show only one world tile, and check bound will restrict the horizontal scrolling, thats how your markers will show only one time in map, set zoom according to your condition that fits in !!

Categories

Resources