I'm making a google maps where the user can have an unlimited number of different categories on the map. I want to cluster each category in a differently colored cluster.
So far I'm using the google maps symbol for the individual markers, and I want to create a symbol for the marker clusters as well.
From checking the reference of the marker clusterer plus. I see that it asks for image url - is there any way to use symbols instead?
thanks!
A symbol(in the meaning of the maps-API), basically is defined by a SVG-path.
The MarkerClusterer draws the Cluster-Icons via <img/>.
A image-src may also be a data-URI, so it's possible to draw such an symbol(SVG-document) as <img/> .
Simple implementation:
function initialize() {
var Symbol=function(id,width,height,fill){
var s={
heart: {
p:'M340.8,83C307,83,276,98.8,256,124.8c-20-26-51-41.8-84.8-41.8C112.1,83,64,131.3,64,190.7c0,27.9,10.6,54.4,29.9,74.6 L245.1,418l10.9,11l10.9-11l148.3-149.8c21-20.3,32.8-47.9,32.8-77.5C448,131.3,399.9,83,340.8,83L340.8,83z',
v:'0 0 512 512'
},
gear: {
p:'M462,280.72v-49.44l-46.414-16.48c-3.903-15.098-9.922-29.343-17.675-42.447l0.063-0.064l21.168-44.473l-34.96-34.96 l-44.471,21.167l-0.064,0.064c-13.104-7.753-27.352-13.772-42.447-17.673L280.72,50h-49.44L214.8,96.415 c-15.096,3.9-29.343,9.919-42.447,17.675l-0.064-0.066l-44.473-21.167l-34.96,34.96l21.167,44.473l0.066,0.064 c-7.755,13.104-13.774,27.352-17.675,42.447L50,231.28v49.44l46.415,16.48c3.9,15.096,9.921,29.343,17.675,42.447l-0.066,0.064 l-21.167,44.471l34.96,34.96l44.473-21.168l0.064-0.063c13.104,7.753,27.352,13.771,42.447,17.675L231.28,462h49.44l16.48-46.414 c15.096-3.903,29.343-9.922,42.447-17.675l0.064,0.063l44.471,21.168l34.96-34.96l-21.168-44.471l-0.063-0.064 c7.753-13.104,13.771-27.352,17.675-42.447L462,280.72z M256,338.4c-45.509,0-82.4-36.892-82.4-82.4c0-45.509,36.891-82.4,82.4-82.4 c45.509,0,82.4,36.891,82.4,82.4C338.4,301.509,301.509,338.4,256,338.4z',
v:'0 0 512 512'
},
vader: {
p:'M 454.5779,419.82295 328.03631,394.69439 282.01503,515.21933 210.30518,407.97233 92.539234,460.65437 117.66778,334.11278 -2.8571457,288.09151 104.38984,216.38165 51.707798,98.615703 178.2494,123.74425 224.27067,3.2193247 295.98052,110.46631 413.74648,57.784277 388.61793,184.32587 509.14286,230.34714 401.89587,302.057 z',
v:'0 0 512 512'
}
}
return ('data:image/svg+xml;base64,'+window.btoa('<svg xmlns="http://www.w3.org/2000/svg" height="'+height+'" viewBox="0 0 512 512" width="'+width+'" ><g><path fill="'+fill+'" d="'+s[id].p+'" /></g></svg>'));
}
var center = new google.maps.LatLng(37.4419, -122.1419);
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 1,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var markers = [];
for (var i = 0; i < 500; i++) {
var dataPhoto = data.photos[i];
var latLng = new google.maps.LatLng(dataPhoto.latitude,
dataPhoto.longitude);
var marker = new google.maps.Marker({
position: latLng
});
markers.push(marker);
}
var markerCluster = new MarkerClusterer(map, markers,{styles:[
{width:50,height:50,url:Symbol('heart',50,50,'red')},
{width:75,height:75,url:Symbol('gear',75,75,'green')},
{textColor:'tomato',textSize:'18',width:100,height:100,url:Symbol('vader',100,100,'blue')}
]});
}
google.maps.event.addDomListener(window, 'load', initialize);
html,body,#map{height:100%;margin:0;padding:0;}
<div id="map"></div>
<script src="http://maps.googleapis.com/maps/api/js?v=3"></script>
<script src="https://cdn.rawgit.com/mahnunchik/markerclustererplus/master/src/data.json"></script>
<script src="https://cdn.rawgit.com/mahnunchik/markerclustererplus/master/dist/markerclusterer.min.js"></script>
The properties of the symbols are:
p (the path)
v (the viewBox)
Note: window.btoa is not supported by IE<10 , you'll need to implement it on your own when you need to support older IEs
In the past I had to deal with this.
To solve it what I did was to extend the class Cluster to add a new Google Marker as an attribute. This marker, attached at the same position as the cluster object, is the one that will show the symbol.
Remember that to avoid the overlap of your symbol and the cluster image you will have to set the opacity to 0 in the style of the clusters.
Hope it works for you.
Related
I've been looking around for a solution to this problem, but i can't seem to find somthing that solves this. The closest i get is this thread. But this doesn't work.
What i'm trying to do is to run fitbounds based on a set of markers which works fine. But i would also like to center the map based on the users location (the bouncing marker in the plunk) and still keep all markers within the map view.
If i try to setCenter after fitBounds, some markers are outside of the map view.
Is there some nifty way of combining these two functions?
Here's the plunk illustrating the issue.
function initialize() {
var userCenter = new google.maps.LatLng(51.508742, -0.120850);
var userCenterMarker = new google.maps.Marker({
position: userCenter
});
userCenterMarker.setAnimation(google.maps.Animation.BOUNCE);
var mapProp = {
center: userCenter,
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("googleMap"), mapProp);
var bounds = new google.maps.LatLngBounds();
var markers = getMarkers();
$(markers).each(function() {
bounds.extend(this.position);
this.setMap(map);
});
userCenterMarker.setMap(map);
map.fitBounds(bounds);
setTimeout(function() {
map.setCenter(userCenter);
}, 1500);
}
Thanks!
Simple solution: Add your "user marker" to the bounds, do fitBounds, then decrement the resulting zoom by 1 and center on that marker.
bounds.extend(userCenterMarker.getPosition());
map.fitBounds(bounds);
google.maps.event.addListenerOnce(map,'bounds_changed', function() {
map.setZoom(map.getZoom()-1);
});
working fiddle
More complex solution: Center on the "user marker", check to see if the marker bounds is completely included in the map's current bounds (map.getBounds().contains(markerBounds)), if not, decrement the zoom level by 1.
The above answer didn't work for me. Here's what did:
contained = true;
map.fitBounds(bounds);
map.setCenter(center);
newbounds = map.getBounds();
for (i = 0; i < l; i ++) {
if (!newbounds.contains(markers[i].getPosition())) {
contained = false;
}
}
if (!contained) map.setZoom(map.getZoom() - 1);
I have a database of locations which I want to be able to print on a map. Ideally there should be one map with multiple pins for each location you have toggled on. So click a button for location X and it shows up on the map. Click the button for location Y and it shows up on the same map. Click X again and it hides from the map.
Currently I have it so I click on X and the map gets redrawn centered around point X.
Here is the HTML for each button:
<input type='button' data-lat='38.89864400' data-long='-77.05283400'
data-when='20 Aug at 2:00am' value='Location X' class='click' />
The jQuery I'm using is:
jQuery(document).ready(
function initialize() {
jQuery("input.click").click(function() {
showOnMap(jQuery(this).data('lat'), jQuery(this).data('long'), jQuery(this).data('when'));
});
}
);
function showOnMap(lat, long, message) {
var myLatlng = new google.maps.LatLng(lat, long);
var mapOptions = {
zoom: 13,
center: myLatlng
}
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: message
});
google.maps.event.addDomListener(window, 'load', showOnMap);
}
Is there an easy way to switch from what I have to what I want? I've searched for a while but no one seems to be asking this use case in a browser, just Android (which I'm not doing).
Thanks!
There is an example in the documentation on how to hide/show markers. In short, a marker is:
hidden by setting its map to null
showed by setting its map to map
To do so, you will need to access each marker individually. If you have a definite number of locations, it can be done by naming them with different names (eg var markerLocationX, var markerLocationY, etc). Otherwise, the markers need to be stored in an array.
Supposing you have a definite number of known locations to toggle the markers, your javascript code may look like this:
function toggleMarker(markerName) {
if (markerName.getMap() == null) {
markerName.setMap(map);
} else {
markerName.setMap(null);
}
}
Previous question here:
stack overflow with Google maps API (IE7 IE8)
I found the following question in the mean time: Google Maps API v3: Can I setZoom after fitBounds?
The solution there works just fine, when I have more than one marker on the map. However when I visit a subpage of groupbke.young.netaffinity.net eg. https://groupbke.young.netaffinity.net/hotels/ireland/dublin/dublin/young-testing-hotel-liege/specials/bed-and-breakfast
the map will only load if map.fitBounds() is called. On the other hand, even if the map is not loaded, it will throw a stack overflow error when I scroll above the map canvas.
Will throw a stack overflow anyway, if I try to use setZoom.
Any ideas?
var hoteldata = [
['Young Testing Hotel - Liège', 53.33932, -6.261427, '<div class="nearby-hotel"> <h1>Young Testing Hotel - Liège</h1> <div class="star-rating-1"></div><div class="clear"></div> <div class="nearby-hotel-image l"> <img src="http://groupbke.young.netaffinity.net/bookings/images/imagecache/3/0C9DBC143E18ED64059C1696A52D2941-60x60.jpg" border="1" class="imagetype1"/> </a> </div> <div class="nearby-hotel-description l"> <a class="nearby-hotel-desc" href="/hotels/ireland/dublin/dublin/young-testing-hotel-liege">Dublin\'s most luxurious design hotel is located in the heart of the city. Experience the best of both worlds when staying at this chic haven, to one side the tranquility and calm of St Stephen\'s Green and to the other, Grafton Street, Dublin\'s finest shopping avenue. From its central location, in amongst this buzzing vibrant city it is an easy stroll to explore the leading cultural, historical and leisure attractions. Or just step out to the chic shopping, high energy bars, fine dining restaurants and chattering Cafes.</a> Book Now </div> <div class="clear"></div> </div>', 4]
];
function initialize(mapid) {
var myOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false
};
var bounds = new google.maps.LatLngBounds();
var map = new google.maps.Map(document.getElementById(mapid), myOptions);
var infowindow = new google.maps.InfoWindow();
var markers = [];
for (i = 0; i < hoteldata.length; i++) {
var latLng = new google.maps.LatLng(hoteldata[i][1], hoteldata[i][2]);
bounds.extend(latLng);
var img = '/images/hotel-marker.png';
if (hoteldata[i][4] == 2) {
img = '/images/country-marker.png';
}
if (hoteldata[i][4] == 3) {
img = '/images/guesthouse-marker.png';
}
if (hoteldata[i][4] == 4) {
img = '/images/hotel-self-marker.png';
}
var marker = new google.maps.Marker({
position: latLng,
icon: img,
shadow: '/images/marker-shadow.png'
});
markers.push(marker);
bindInfoWindow(marker, map, infowindow, hoteldata[i][3]);
}
var clusterStyles = [
{
opt_textColor: 'white',
url: '/images/m3-blue.png',
height: 65,
width: 64
},
{
opt_textColor: 'white',
url: '/images/m3-green.png',
height: 65,
width: 64
},
{
opt_textColor: 'white',
url: '/images/m3-orange.png',
height: 65,
width: 64
}
];
var mcOptions = {
styles: clusterStyles,
maxZoom:14
};
if (markers.length>1){
var markerCluster = new MarkerClusterer(map, markers, mcOptions);
map.fitBounds(bounds);
google.maps.event.addListenerOnce(map, 'zoom_changed', function() {
var oldZoom = map.getZoom();
map.setZoom(oldZoom + (-7)); //Or whatever
});
} else if (markers.length == 1){
markers[0].setMap(map);
//google.maps.event.clearListeners(map, 'zoom_changed');
//google.maps.event.addListenerOnce(map, 'zoom_changed', function() {
// var oldZoom = map.getZoom();
// map.setZoom(oldZoom + (-7)); //Or whatever
// setTimeout('roomSetter(globalmap,globalzoom)',300);
//});
//google.maps.event.trigger(map,'zoom_changed');
//google.maps.event.clearListeners(map, 'zoom_changed');
//map.fitBounds(bounds);
//var oldZoom = map.getZoom();
//map.setCenter(bounds.getCenter());
//map.setZoom(oldZoom+(-7));
//map.setZoom(3);
//globalmap=map;
//globalzoom=map.getZoom()+(-7);
//setTimeout('zoomSetter(globalmap,globalzoom)',300);
}
}
var globalmap;
var globalzoom;
function zoomSetter(map, zoom){
//map.setZoom(zoom);
}
function bindInfoWindow(marker, map, infowindow, html) {
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(html);
infowindow.open(map, marker);
});
}
function initmaps() {
initialize('map_canvas');
initialize('map_thumb');
}
google.maps.event.addDomListener(window, 'load', initmaps);
I've set up 3 testpages to demonstrate the problem:
http://groupbke.young.netaffinity.net/maptest1.html
this has the setZoom() function and throws a stackoverflow error, even though this should be correct
http://groupbke.young.netaffinity.net/maptest2.html
this does nothing beyond adding the marker to the map. scroll zooming on the map still throws a stack error.
http://groupbke.young.netaffinity.net/maptest3.html
this has fitBound(), which in theory is not good, but works. can NOT adjust the zoom level after that, or it will throw a stackoverflow error. scroll zooming works.
What I was missing from the answer from my previous question was that an initial zoom level and center has to be set.
var myOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false,
zoom:22,
center: new google.maps.LatLng(50.820645,-0.137376)
};
Also had to change the event to 'idle' on the multi marker zooming adjustment.
if (markers.length>1){
var markerCluster = new MarkerClusterer(map, markers, mcOptions);
map.fitBounds(bounds);
google.maps.event.addListenerOnce(map, 'idle', function() {
var oldZoom = map.getZoom();
map.setZoom(oldZoom + (-7)); //Or whatever
});
}
Works like a charm after that.
When you have only one hotel, you create a LatLngBounds object containing only one point:
for (i = 0; i < hoteldata.length; i++) {
var latLng = new google.maps.LatLng(hoteldata[i][1], hoteldata[i][2]);
bounds.extend(latLng);
...}
but you then do a map.fitBounds(bounds) — this attempts an infinite zoom. While it's arguable that the API should be able to cope with that, it's equally arguable that it will attempt to do exactly what you tell it. It's possible that IE will behave differently to Firefox and other browsers.
Where you have commented out the fitBounds() in your quoted code in the question, that is present in your online page. Since that line only applies to instances where one marker is involved and the bounds object is a single point, I would replace it with a simple setZoom() instead.
map.setZoom(16);
I'm working on a property rental site. On the site I'd like to have a Google map, with all of the properties marked, and the local bus routes drawn, so that renters can see the proximity of the properties to the route.
I've achieved the first part of the problem; I've plotted the properties using markers. Now I need to add the bus route.
I've looked in to this and I can't quite work out the best way to achieve it. I looked at polylines and at using this tool, but the route is complex and would take hundreds of co-ordinates.
There is some kind of route api, as in this post but apparently it can only take 8 waypoints. Is that right?
Ideally I'd like to draw the map by selecting a start point, an end point, and dragging the route into place; and then somehow importing that route into what I have.
Here is the exact route that I want to import: http://www2.warwick.ac.uk/services/accommodation/landlords/12busroutes/.
My code to plot the properties is:
var siteRoot = "<?php bloginfo('stylesheet_directory');?>/";
var markers = [
<?php
$my_query = new WP_Query( 'post_type=properties' );
while ($my_query->have_posts()) : $my_query->the_post();
kdev_maps('list');
endwhile; // end of the loop.
?>
];
function googlemap() {
jQuery('#map_canvas').css({'height': '400px'});
// Create the map
// No need to specify zoom and center as we fit the map further down.
var map = new google.maps.Map(document.getElementById("map_canvas"), {
mapTypeId: google.maps.MapTypeId.ROADMAP,
streetViewControl: false
});
// Create the shared infowindow with two DIV placeholders
// One for a text string, the other for the StreetView panorama.
var content = document.createElement("div");
var title = document.createElement("div");
var boxText = document.createElement("div");
var myOptions = {
content: boxText
,disableAutoPan: false
,maxWidth: 0
,pixelOffset: new google.maps.Size(-117,-200)
,zIndex: null
,boxStyle: {
background: "url('"+siteRoot+"images/house-icon-flat.png') no-repeat"
,opacity: 1
,width: "240px"
,height: "190px"
}
,closeBoxMargin: "10px 0px 2px 2px"
,closeBoxURL: "http://kdev.langley.com/wp-content/themes/langley/images/close.png"
,infoBoxClearance: new google.maps.Size(1, 1)
,isHidden: false
,pane: "floatPane"
,enableEventPropagation: false
};
var infoWindow = new InfoBox(myOptions);
var MarkerImage = siteRoot+'images/house-web-marker.png';
// Create the markers
for (index in markers) addMarker(markers[index]);
function addMarker(data) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(data.lat, data.lng),
map: map,
title: data.title,
content: data.html,
icon: MarkerImage
});
google.maps.event.addListener(marker, "click", function() {
infoWindow.open(map, this);
title.innerHTML = marker.getTitle();
infoWindow.setContent(marker.content);
infoWindow.open(map, marker);
jQuery(".innerinfo").parent().css({'overflow':'hidden', 'margin-right':'10px'});
});
}
// Zoom and center the map to fit the markers
// This logic could be conbined with the marker creation.
// Just keeping it separate for code clarity.
var bounds = new google.maps.LatLngBounds();
for (index in markers) {
var data = markers[index];
bounds.extend(new google.maps.LatLng(data.lat, data.lng));
}
map.fitBounds(bounds);
var origcent = new google.maps.LatLng(map.getCenter());
// Handle the DOM ready event to create the StreetView panorama
// as it can only be created once the DIV inside the infowindow is loaded in the DOM.
closeInfoWindow = function() {
infoWindow.close();
};
google.maps.event.addListener(map, 'click', closeInfoWindow);
google.maps.event.addListener(infoWindow, 'closeclick', function()
{
centermap();
});
function centermap()
{
map.setCenter(map.fitBounds(bounds));
}
}
jQuery(window).resize(function() {
googlemap();
});
Any help is much appreciated.
Its worth looking at two things:
1) The GTFS format (https://developers.google.com/transit/gtfs/reference) this is a csv-based format that allows someone to cross reference transit times and routes, if you are lucky then the data will have been assembled for you for your transit authority :)
2) If you can pull in the coordinates then you can make a line feature as long as you want (within the bounds of browser capacity). This is done in much the same way as you pull in markers, except you pull the features you want into a polyline:
like this (from gmaps docs):
var flightPlanCoordinates = [
new google.maps.LatLng(37.772323, -122.214897),
new google.maps.LatLng(21.291982, -157.821856),
new google.maps.LatLng(-18.142599, 178.431),
new google.maps.LatLng(-27.46758, 153.027892)
];
var flightPath = new google.maps.Polyline({
path: flightPlanCoordinates,
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2
});
you will build your array from php, of course :)
hope that helps.
If you use free google maps you can use a start point, end point and 8 way point. In google maps api premiere you can use 23 way point for routing vehicles
I have a problem with googlemaps fitBounds functions.
for (var i = 0; i < countries.length; i++) {
var country = countries[i];
var latlng = new google.maps.LatLng(parseFloat(country.lat), parseFloat(country.lng));
mapBounds.extend(latlng);
}
map.fitBounds(mapBounds);
Some icons will be displayed outside the viewport / Visible area.
And idea?
Thanks in advance.
Consider the following example, which will generate 10 random points on the North East USA, and applies the fitBounds() method.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps LatLngBounds.extend() Demo</title>
<script src="http://maps.google.com/maps/api/js?sensor=false"
type="text/javascript"></script>
</head>
<body>
<div id="map" style="width: 400px; height: 300px;"></div>
<script type="text/javascript">
var map = new google.maps.Map(document.getElementById('map'), {
mapTypeId: google.maps.MapTypeId.TERRAIN
});
var markerBounds = new google.maps.LatLngBounds();
var randomPoint, i;
for (i = 0; i < 10; i++) {
// Generate 10 random points within North East USA
randomPoint = new google.maps.LatLng( 39.00 + (Math.random() - 0.5) * 20,
-77.00 + (Math.random() - 0.5) * 20);
// Draw a marker for each random point
new google.maps.Marker({
position: randomPoint,
map: map
});
// Extend markerBounds with each random point.
markerBounds.extend(randomPoint);
}
// At the end markerBounds will be the smallest bounding box to contain
// our 10 random points
// Finally we can call the Map.fitBounds() method to set the map to fit
// our markerBounds
map.fitBounds(markerBounds);
</script>
</body>
</html>
Refreshing this example many times, no marker ever goes outside the viewport. At most, sometimes a marker is slightly clipped from the top when hidden behind the controls:
It is also worth nothing that the fitBounds() always leaves a small margin between the LatLngBounds object and the viewport. This is clearly shown in the screenshots below, where the red bounding box represents the LatLngBounds which is passed to the fitBounds() method:
You may also be interested in checking out the following Stack Overflow posts on the topic:
fitbounds() in Google maps api V3 does not fit bounds
Google Maps v3 - Automating Zoom Level?
Show us a link to the patient. How many countries do you have in the countries array? The entire world? Are your bounds crossing the anti-meridian?
country.lat and country.lng are one point per country, and that's not enough to define the bounding box of the country. Is that some sort of "country centroid"?
If that's the case, and if you have markers east of the centroid of the easternmost country, or to the west of the centroid of the westermost country, those markers will, of course, fall outside the bounds that you're defining.
The map.fitBounds() method works fine. :-)
Marcelo.
Check if your google map object is shown properly in the area you have given.
it is possible that some part of your google map object is overflowed outside it's container and the markers are in that area, which they exists by you can't see them.
It seems that the result of fitBounds() is depending on the minZoom value.
For my use-case I managed to recenter the view to cover all the markers by using: map.setCenter(bounds.getCenter());
`
zoomToAllMarkers() {
if (!this.mapMarkers.length) {
return;
}
let bounds = new google.maps.LatLngBounds();
for (let i = 0; i < this.mapMarkers.length; i++) {
bounds.extend(this.mapMarkers[i].getPosition());
}
this.map.setCenter(bounds.getCenter());
}
`