Google Maps Cluster Dropdown? - javascript

I'm using Google maps to place pins on the world, and I'm using markercluster.js to cluster the pins when they get too close. What I'm looking to do is make it so you can hover over a cluster of pins and a drop down will appear showing the titles of the pins in that area.
I haven't seen anything on the forums about this, so I thought maybe someone else might have run into this and found a solution already. Thanks for any help in advance!
My code is just the typical way to add pins to the Google maps API. But I'll list it here just in case.
var bounds = new google.maps.LatLngBounds();
var markers = [];
for (i = 0; i < locations.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(locations[i][1], locations[i][2]),
map: map
//icon: '/bin/images/people/' + locations[i][4] + '-1.jpg'
});
markers.push(marker);
bounds.extend(marker.position);
google.maps.event.addListener(marker, 'mouseover', (function(marker, i) {
return function() {
infowindow.setContent(locations[i][0]);
infowindow.open(map, marker)
}
})(marker, i))
}
var clusterStyles = [{
textColor: 'white',
url: 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m1.png',
height: 50,
width: 50
}, {
textColor: 'white',
url: 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m1.png',
height: 50,
width: 50
}, {
textColor: 'white',
url: 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m1.png',
height: 50,
width: 50
}];
var mcOptions = {
gridSize: 50,
styles: clusterStyles,
maxZoom: 15
};
var markerCluster = new MarkerClusterer(map, markers, mcOptions);
map.fitBounds(bounds);
var listener = google.maps.event.addListener(map, "idle", function() {
map.setZoom(3);
google.maps.event.removeListener(listener)
});

You could consider the following approach.
Modify ClusterIcon by introducing clustermouseover event that will be triggered on mouseover event:
//Note: the remaining code is omitted from this function
ClusterIcon.prototype.onAdd = function() {
this.div_ = document.createElement('DIV');
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(this.div_);
var that = this;
google.maps.event.addDomListener(this.div_, 'mouseover', function() {
that.triggerClusterMouseOver();
});
};
where
ClusterIcon.prototype.triggerClusterMouseOver = function () {
var markerClusterer = this.cluster_.getMarkerClusterer();
google.maps.event.trigger(markerClusterer, 'clustermouseover', this.cluster_);
};
Attach event handler for displaying the corresponding information. The following example demonstrates how to display the list of names:
google.maps.event.addListener(markerClusterer, 'clustermouseover', function(clusterer) {
var markers = clusterer.getMarkers();
markers.forEach(function(marker){
infowindow.content += '<div>' + marker.title + '</div>';
});
infowindow.setPosition(clusterer.getCenter());
infowindow.open(clusterer.getMap());
});
Example: Plunker

You could consider the following approach Also its work for me :
public void initilizeMap() {
googleMap = mFragment.getMap();
googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
googleMap.getUiSettings().setZoomControlsEnabled(true`enter code here`); // true to`enter code here`
googleMap.getUiSettings().setZoomGesturesEnabled(true);
googleMap.getUiSettings().setCompassEnabled(true);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
googleMap.getUiSettings().setRotateGesturesEnabled(true);
if (googleMap == null) {
Toast.makeText(getActivity(), "Sorry! unable to create maps",
Toast.LENGTH_SHORT).show();
}
mClusterManager = new ClusterManager<MyItem>(getActivity(), googleMap );
// googleMap.setInfoWindowAdapter(new CustomInfoWindowAdapter());
googleMap.setOnMapLoadedCallback(this);
googleMap.setMyLocationEnabled(true);
googleMap.setBuildingsEnabled(true);
googleMap.getUiSettings().setTiltGesturesEnabled(true);
MyItem offsetItem = new MyItem(Double.parseDouble(outletList.get(i).getMap_latitude()),
Double.parseDouble(outletList.get(i).getMap_longitude()), title , address);
mClusterManager.addItem(offsetItem);
googleMap.setInfoWindowAdapter(new CustomInfoWindowAdapter(offsetItem));
}
private class CustomInfoWindowAdapter implements InfoWindowAdapter {
Marker marker;
private View view;
private MyItem items;
public CustomInfoWindowAdapter(MyItem item) {
view = getActivity().getLayoutInflater().inflate(
R.layout.custom_info_window, null);
this.items = item;
}
#Override
public View getInfoContents(Marker marker) {
if (marker != null && marker.isInfoWindowShown()) {
marker.hideInfoWindow();
marker.showInfoWindow();
}
return null;
}
#Override
public View getInfoWindow(final Marker marker) {
this.marker = marker;
String url = null;
if (marker.getId() != null && markers != null && markers.size() > 0) {
if (markers.get(marker.getId()) != null
&& markers.get(marker.getId()) != null) {
url = markers.get(marker.getId());
}
}
final ImageView image = ((ImageView) view.findViewById(R.id.badge));
if (url != null && !url.equalsIgnoreCase("null")
&& !url.equalsIgnoreCase("")) {
imageLoader.displayImage(url, image, options,
new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri,
View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view,
loadedImage);
getInfoContents(marker);
}
});
} else {
image.setImageResource(R.drawable.ic_launcher);
}
final String title = items.getTitle();
Log.e(TAG, "TITLE : "+title);
final TextView titleUi = ((TextView) view.findViewById(R.id.title));
if (title != null) {
titleUi.setText(title);
} else {
titleUi.setText("");
}
final String address = items.getAddress();
final TextView snippetUi = ((TextView) view
.findViewById(R.id.snippet));
if (address != null) {
snippetUi.setText(address);
} else {
snippetUi.setText("");
}

Related

How to save result from Google Place Autocomplete

Firstly I would like to let you guys know that I have already checked many stack overflow Q&As, but I couldn't get the right solution.
I made a rails app by following the youtube.
https://www.youtube.com/watch?v=UtgwdLiJ5hA&t
It worked well including markerCluster which didn't cover in that youtube.
However, what I tried to add was that each user has his or her own search result(only the last one), and after hitting search button the page will be redirected to the same page with queries which have information about autocompleted place.
I succeeded in redirecting the same page with queries, but it was too hard to make the same map object as the one just after autocompleting.
The closest answer was as below, but it didn't work perfectly because AutocompleteService didn't return the first prediction as the place I wanted even though I put the exact address chosen just before redirecting.
How to set a default value for a Google places API auto complete textbox
The second trial was just copying some part of autocomplete object (bounds, location) and applying to the map object after redirecting. It seemed to work about only position, but the map display result has something wrong with boundary and the area seen.
The third trial was using place_id with the second trial, but I didn't think it would work.
I really wanted to insert the address text, select the address I chose before redirecting, and create autocomplete 'place_change' event AUTOMATICALLY as soon as the page was redirected. However, I have no idea how to do that.
Here is the main_map_controller.js (it is stimulus js)
import { Controller } from "stimulus"
export default class extends Controller {
// currentUrl is for redirecting to root_path in javascript
static targets = ["field", "map", "jsonMarkers", "currentUrl", "east", "north", "south", "west", "lat", "lng", "zoom"];
connect() {
if (typeof(google) != "undefined") {
this.initializeMap();
}
}
initializeMap() {
this._jason_locations = JSON.parse(this.jsonMarkersTarget.value);
this.map();
this.markerCluster();
this.autocomplete();
this.placeChanged();
// this.initialAutocomplete();
this.setPlace();
console.log('this.eastTarget.value:', this.eastTarget.value)
}
hasQuery() {
if (this.fieldTarget.value != "" && this.eastTarget.value != "" && this.northTarget.value != "" && this.southTarget.value != "" &&
this.westTarget.value != "" && this.latTarget.value != "" && this.lngTarget.value != "" && this.zoomTarget.value != ""
)
return true;
else
return false;
}
// Google map initialization
map() {
if (this._map == undefined) {
if (this.hasQuery())
{
this._map = new google.maps.Map(this.mapTarget, {
center: new google.maps.LatLng(
parseFloat(this.latTarget.value),
parseFloat(this.lngTarget.value)
),
zoom: 13
});
} else {
this._map = new google.maps.Map(this.mapTarget, {
center: new google.maps.LatLng(
0,
0
),
zoom: 13
});
}
// Try HTML5 geolocation
var cur_map = this._map;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
cur_map.setCenter({
lat: position.coords.latitude,
lng: position.coords.longitude
})
});
}
}
return this._map;
}
// markerCluster() make a group of markers
markerCluster() {
let current_map = this.map();
if (this._marker_cluster == undefined) {
var markers = this._jason_locations.map((location, i) => {
var marker = new google.maps.Marker({
position: {
lat: parseFloat(location["latitude"]),
lng: parseFloat(location["longitude"])
}
});
marker.addListener('click', () => {
let infoWindow = new google.maps.InfoWindow({
content: `<p>${location.address}</p>`
});
infoWindow.open(current_map, marker);
});
return marker;
});
this._marker_cluster = new MarkerClusterer(this.map(),
markers,
{imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'}
);
}
return this._markers_cluster;
}
// Autocomplete function. It suggests the full address. 'formatted_address' was added to use user's bad behavior instead of
// using placeChanged(), but 'formatted_address' saved was not 100% same as the result address of autocomplete, so I didtn' use it.
// I don't understand why???
autocomplete() {
if (this._autocomplete == undefined) {
this._autocomplete = new google.maps.places.Autocomplete(this.fieldTarget);
this._autocomplete.bindTo('bounds', this.map());
this._autocomplete.setFields(['address_components', 'geometry', 'icon', 'name', 'formatted_address', 'place_id']);
this._autocomplete.addListener('place_changed', this.placeChanged.bind(this));
}
return this._autocomplete;
}
// If user typed strange word after autocomplete done, we should not allow to search with that word.
placeChanged() {
this._place_changed = this.fieldTarget.value;
}
// Because AutoComplete cannot have initial place, I had to use another class, AutocompleteService.
initialAutocomplete() {
if (this.fieldTarget.value == undefined || this.fieldTarget.value == "")
return;
let autocompleteService = new google.maps.places.AutocompleteService();
let request = { input: this.fieldTarget.value };
autocompleteService.getPlacePredictions(request, (predictionsArr, placesServiceStatus) => {
console.log('predictionArr:', predictionsArr);
console.log('placesServiceStatus:', placesServiceStatus);
let placeRequest = { placeId: predictionsArr[0].place_id };
let placeService = new google.maps.places.PlacesService(this.map());
placeService.getDetails(placeRequest, (placeResult, placeServiceStatus) => {
console.log('placeResult:', placeResult)
console.log('placeServiceStatus:', placeServiceStatus);
this.setPlace(placeResult);
});
});
}
// setPlace(placeResult) {
setPlace() {
// let place = this.autocomplete().getPlace();
// let place = placeResult;
if (!this.hasQuery()) {
return;
}
console.log('this.eastTarget.value:', this.eastTarget.value)
console.log('this.northTarget.value:', this.northTarget.value)
console.log('this.southTarget.value:', this.southTarget.value)
console.log('this.westTarget.value:', this.westTarget.value)
// let bound = {
// east: parseFloat(this.eastTarget.value),
// north: parseFloat(this.northTarget.value),
// south: parseFloat(this.southTarget.value),
// west: parseFloat(this.westTarget.value)
// }
// console.log('bounds:', bound)
// // this.map().fitBounds(place.geometry.viewport);
// // this.map().setCenter(place.geometry.location);
// this.map().fitBounds(bound);
// let bounds = this.map().getBounds();
// console.log('bounds:', bounds)
let bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(parseFloat(this.southTarget.value), parseFloat(this.westTarget.value)),
new google.maps.LatLng(parseFloat(this.northTarget.value), parseFloat(this.eastTarget.value))
);
this.map().fitBounds(bounds);
this.map().setCenter({
lat: parseFloat(this.latTarget.value),
lng: parseFloat(this.lngTarget.value)
});
let zoom = this.map().getZoom();
console.log('zoom:', zoom)
let center = this.map().getCenter();
console.log('center:', center)
document.getElementById("search-area").innerHTML = `Near ${this.fieldTarget.value}`;
this._jason_locations.forEach( location => {
var position = {
lat: parseFloat(location["latitude"]),
lng: parseFloat(location["longitude"])
}
console.log('position:', position)
if (bounds.contains(position)) {
document.getElementById(location["id"]).classList.remove("d-none")
} else {
document.getElementById(location["id"]).classList.add("d-none")
}
});
// this.latitudeTarget.value = place.geometry.location.lat();
// this.longitudeTarget.value = place.geometry.location.lng();
}
reloadMap() {
let place = this.autocomplete().getPlace();
console.log(place)
// this.setPlace(place);
if (place == undefined || this.fieldTarget.value == "" || this._place_changed != this.fieldTarget.value || !place.geometry) {
window.alert("Address is invalid!");
return;
}
this.map().fitBounds(place.geometry.viewport);
this.map().setCenter(place.geometry.location);
console.log('place.geometry.viewport:', place.geometry.viewport)
console.log('place.geometry.location:', place.geometry.location)
let bounds = this.map().getBounds();
console.log('bounds:', bounds)
let zoom = this.map().getZoom();
console.log('zoom:', zoom)
console.log('place.place_id:', place.place_id)
// This code was redirect root_path with query, but there was a problem that map was reloaded twice, so removed it.
// If adding query is not a solution for having each user's recent search history, then what else would it be?
let jsonParams = { "address": this.fieldTarget.value, ...bounds.toJSON(), ...place.geometry.location.toJSON(), "zoom": zoom.toString() };
const params = new URLSearchParams(jsonParams);
console.log(params.toString());
// Redirect to /posts/?address=xxxxx
console.log('params:', `${this.currentUrlTarget.value}/?${params.toString()}`);
window.location.href = `${this.currentUrlTarget.value}/?${params.toString()}`;
console.log('window.location.href:', window.location.href)
}
// prohibit Enter key, only allow to hit the search button.
preventSubmit(e) {
if (e.key == "Enter") {
e.preventDefault();
}
}
}
Yay, I finally figured it out by getting a hint from the following stack overflow site.
Google Maps map.getBounds() immediately after a call to map.fitBounds
What I fixed was modifying the second trial.
I didn't pass over the result of fitBounds() of autocomplete but viewport bounds of autocomplete itself, and after redirecting I wrapped the all codes after fitBounds() into the 'bound_changed' event handler with the help of the above solution.
Here is my fixed code, and it worked well. (Sorry about unused code and comment. I want to leave it for the record)
import { Controller } from "stimulus"
export default class extends Controller {
// currentUrl is for redirecting to root_path in javascript
static targets = ["field", "map", "jsonMarkers", "currentUrl", "east", "north", "south", "west", "lat", "lng"];
connect() {
if (typeof(google) != "undefined") {
this.initializeMap();
}
}
initializeMap() {
this._jason_locations = JSON.parse(this.jsonMarkersTarget.value);
this.map();
this.markerCluster();
this.autocomplete();
this.placeChanged();
// this.initialAutocomplete();
this.setPlace();
console.log('this.eastTarget.value:', this.eastTarget.value)
}
hasQuery() {
if (this.fieldTarget.value != "" && this.eastTarget.value != "" && this.northTarget.value != "" && this.southTarget.value != "" &&
this.westTarget.value != "" && this.latTarget.value != "" && this.lngTarget.value != ""
)
return true;
else
return false;
}
// Google map initialization
map() {
if (this._map == undefined) {
if (this.hasQuery())
{
this._map = new google.maps.Map(this.mapTarget, {
center: new google.maps.LatLng(
parseFloat(this.latTarget.value),
parseFloat(this.lngTarget.value)
)
// zoom: parseInt(this.zoomTarget.value)
});
} else {
this._map = new google.maps.Map(this.mapTarget, {
center: new google.maps.LatLng(
0,
0
),
zoom: 13
});
}
// Try HTML5 geolocation
var cur_map = this._map;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
cur_map.setCenter({
lat: position.coords.latitude,
lng: position.coords.longitude
})
});
}
}
return this._map;
}
// markerCluster() make a group of markers
markerCluster() {
let current_map = this.map();
if (this._marker_cluster == undefined) {
var markers = this._jason_locations.map((location, i) => {
var marker = new google.maps.Marker({
position: {
lat: parseFloat(location["latitude"]),
lng: parseFloat(location["longitude"])
}
});
marker.addListener('click', () => {
let infoWindow = new google.maps.InfoWindow({
content: `<p>${location.address}</p>`
});
infoWindow.open(current_map, marker);
});
return marker;
});
this._marker_cluster = new MarkerClusterer(this.map(),
markers,
{imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'}
);
}
return this._markers_cluster;
}
// Autocomplete function. It suggests the full address. 'formatted_address' was added to use user's bad behavior instead of
// using placeChanged(), but 'formatted_address' saved was not 100% same as the result address of autocomplete, so I didtn' use it.
// I don't understand why???
autocomplete() {
if (this._autocomplete == undefined) {
this._autocomplete = new google.maps.places.Autocomplete(this.fieldTarget);
this._autocomplete.bindTo('bounds', this.map());
this._autocomplete.setFields(['address_components', 'geometry', 'icon', 'name']);
this._autocomplete.addListener('place_changed', this.placeChanged.bind(this));
}
return this._autocomplete;
}
// If user typed strange word after autocomplete done, we should not allow to search with that word.
placeChanged() {
this._place_changed = this.fieldTarget.value;
}
// Because AutoComplete cannot have initial place, I had to use another class, AutocompleteService.
initialAutocomplete() {
if (this.fieldTarget.value == undefined || this.fieldTarget.value == "")
return;
let autocompleteService = new google.maps.places.AutocompleteService();
let request = { input: this.fieldTarget.value };
autocompleteService.getPlacePredictions(request, (predictionsArr, placesServiceStatus) => {
console.log('predictionArr:', predictionsArr);
console.log('placesServiceStatus:', placesServiceStatus);
let placeRequest = { placeId: predictionsArr[0].place_id };
let placeService = new google.maps.places.PlacesService(this.map());
placeService.getDetails(placeRequest, (placeResult, placeServiceStatus) => {
console.log('placeResult:', placeResult)
console.log('placeServiceStatus:', placeServiceStatus);
this.setPlace(placeResult);
});
});
}
// setPlace(placeResult) {
setPlace() {
// let place = this.autocomplete().getPlace();
// let place = placeResult;
if (!this.hasQuery()) {
return;
}
console.log('this.eastTarget.value:', this.eastTarget.value)
console.log('this.northTarget.value:', this.northTarget.value)
console.log('this.southTarget.value:', this.southTarget.value)
console.log('this.westTarget.value:', this.westTarget.value)
// let bound = {
// east: parseFloat(this.eastTarget.value),
// north: parseFloat(this.northTarget.value),
// south: parseFloat(this.southTarget.value),
// west: parseFloat(this.westTarget.value)
// }
// console.log('bounds:', bound)
// // this.map().fitBounds(place.geometry.viewport);
// // this.map().setCenter(place.geometry.location);
// this.map().fitBounds(bound);
// let bounds = this.map().getBounds();
// console.log('bounds:', bounds)
let bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(parseFloat(this.southTarget.value), parseFloat(this.westTarget.value)),
new google.maps.LatLng(parseFloat(this.northTarget.value), parseFloat(this.eastTarget.value))
);
this.map().fitBounds(bounds);
google.maps.event.addListenerOnce(this.map(), 'bounds_changed', () => {
this.map().setCenter({
lat: parseFloat(this.latTarget.value),
lng: parseFloat(this.lngTarget.value)
});
bounds = this.map().getBounds();
console.log('bounds:', bounds)
let zoom = this.map().getZoom();
console.log('zoom:', zoom)
let center = this.map().getCenter();
console.log('center:', center)
document.getElementById("search-area").innerHTML = `Near ${this.fieldTarget.value}`;
this._jason_locations.forEach( location => {
var position = {
lat: parseFloat(location["latitude"]),
lng: parseFloat(location["longitude"])
}
console.log('position:', position)
if (bounds.contains(position)) {
document.getElementById(location["id"]).classList.remove("d-none")
} else {
document.getElementById(location["id"]).classList.add("d-none")
}
});
// this.latitudeTarget.value = place.geometry.location.lat();
// this.longitudeTarget.value = place.geometry.location.lng();
})
}
reloadMap() {
let place = this.autocomplete().getPlace();
console.log(place)
// this.setPlace(place);
if (place == undefined || this.fieldTarget.value == "" || this._place_changed != this.fieldTarget.value || !place.geometry) {
window.alert("Address is invalid!");
return;
}
// This code was redirect root_path with query, but there was a problem that map was reloaded twice, so removed it.
// If adding query is not a solution for having each user's recent search history, then what else would it be?
// let jsonParams = { "address": this.fieldTarget.value, ...bounds.toJSON(), ...place.geometry.location.toJSON(), "zoom": zoom.toString() };
let jsonParams = { "address": this.fieldTarget.value, ...place.geometry.viewport.toJSON(), ...place.geometry.location.toJSON()};
const params = new URLSearchParams(jsonParams);
// Redirect to /posts/?address=xxxxx
window.location.href = `${this.currentUrlTarget.value}/?${params.toString()}`;
}
// prohibit Enter key, only allow to hit the search button.
preventSubmit(e) {
if (e.key == "Enter") {
e.preventDefault();
}
}
}

How do I make line string in leaflet-realtime with marker on every point?

I want something like below image:
Don't know where to define linestring. It would be great if anyone can guide me. Moreover I want to change the color of linestring.
Here is my code:-
function createRealtimeLayer(url, container) {
return L.realtime(url, {
interval: 5 * 1000,
getFeatureId: function(f) {
return f.properties.id;
},
cache: true,
container: container,
onEachFeature(f, l) {
date = f.properties.date;
l.bindPopup(date);
l.on("mouseover", function () {
l.openPopup();
});
l.on("mouseout", function () {
l.closePopup();
});
}
});
}
realtime1 = createRealtimeLayer('getPosition').addTo(map),
realtime2 = createRealtimeLayer('getUserPositionHistory').addTo(map);
L.control.layers(null, {
'Current': realtime1,
'History': realtime2
}).addTo(map);
realtime1.once('update', function() {
map.fitBounds(realtime1.getBounds(), {maxZoom: 18});
});
To set the markers on every point add this to your onEachFeature:
if(layer instanceof L.Path){
l.getLatLngs().forEach(function(latlng){
L.marker(latlng).addTo(map);
})
}
if you want to colorize your line, you have to split up your line like that, but then append not the geojson layer to the map:
if(layer instanceof L.Path){
var lastlatlng = null;
layer.getLatLngs().forEach(function(latlng, i){
if(lastlatlng !== null){
L.polyline([lastlatlng,latlng],{color: getColor(i)}).addTo(map);
}
lastlatlng = latlng;
})
}
UPDATE
The Asker wants to create a Line by points from the geojson.
You can add latlngs from the points to a polyline with poly.addLatLng(latlng)
var oldcolor = null;
var polys = [];
function onEachFeature(feature, layer) {
if(feature.properties.color && oldcolor != feature.properties.color){
oldcolor = feature.properties.color;
var lastlatlng = [];
//This block gets the last latlng from the line before. so you have one line.
// If you want to seperate the lines by color, deleted this block
if( polys.length > 0){
var lastpoly = polys[polys.length-1];
if(lastpoly.getLatLngs() && lastpoly.getLatLngs().length > 0){
var lastlatlngs = lastpoly.getLatLngs();
lastlatlng = lastlatlngs[0][lastlatlngs[0].length-1];
}
}
//End of block
polys.push(L.polyline([lastlatlng],{color: oldcolor}).addTo(mymap));
}
var poly = polys[polys.length-1]; //get last line
poly.addLatLng(layer.getLatLng());
}
https://jsfiddle.net/falkedesign/pksy6o8v/

Google maps API V3 method fitBounds() using Prototypejs

I have a div as follows to display a google map:
#map {
width: 300px;
height: 300px;
border: 1px solid #DDD;
}
<div id="map"></div>
I want to display the map with a zoom level that fits the bounds of the above viewport.
When I code as follows it works fine:
var geocoder = new google.maps.Geocoder();
var map = new google.maps.Map($('#map')[0], {zoom: 10});
geocoder.geocode({ 'address': generatedAddress }, function (results, status) {
if (status == 'OK') {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
if (results[0].geometry.viewport)
map.fitBounds(results[0].geometry.viewport);
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
When I use typeahead-addresspicker.js to generate the map it zooms in too far?
I've narrowed it down to the following code. When you call the AddressPicker.prototype.updateMap function the boundsForLocation option on AddressPicker.prototype.initMap function should return this.map.fitBounds(response.geometry.viewport); When I debug I can see that it is hitting the following code inside the AddressPicker.prototype.updateBoundsForPlace function as expected:
if (response.geometry.viewport) {
console.log('test');
return this.map.fitBounds(response.geometry.viewport);
}
What I don't understand is how it gets wired back to the google.maps.Map - I'm not familiar with ptototypejs? So basically running through it, we initilize the map by calling initMap, then we call the updateMap function. Inside updateMap function we are calling the following snippet of code:
if (_this.map) {
if ((_ref = _this.mapOptions) != null) {
_ref.boundsForLocation(response);
}
}
which is suppose to set the bounds by calling the updateBoundsForPlace but the google maps options doesnt expose any property called boundsForLocation?
AddressPicker.prototype.initMap = function() {
var markerOptions, _ref, _ref1;
if ((_ref = this.options) != null ? (_ref1 = _ref.map) != null ? _ref1.gmap : void 0 : void 0) {
this.map = this.options.map.gmap;
} else {
this.mapOptions = $.extend({
zoom: 3,
center: new google.maps.LatLng(0, 0),
mapTypeId: google.maps.MapTypeId.ROADMAP,
boundsForLocation: this.updateBoundsForPlace
}, this.options.map);
this.map = new google.maps.Map($(this.mapOptions.id)[0], this.mapOptions);
}
this.lastResult = null;
markerOptions = $.extend({
draggable: true,
visible: false,
position: this.map.getCenter(),
map: this.map
}, this.options.marker || {});
this.marker = new google.maps.Marker(markerOptions);
if (markerOptions.draggable) {
return google.maps.event.addListener(this.marker, 'dragend', this.markerDragged);
}
};
AddressPicker.prototype.updateMap = function(event, place) {
if (this.options.placeDetails) {
return this.placeService.getDetails(place, (function(_this) {
return function(response) {
var _ref;
_this.lastResult = new AddressPickerResult(response);
if (_this.marker) {
_this.marker.setPosition(response.geometry.location);
_this.marker.setVisible(true);
}
if (_this.map) {
if ((_ref = _this.mapOptions) != null) {
_ref.boundsForLocation(response);
}
}
return $(_this).trigger('addresspicker:selected', _this.lastResult);
};
})(this));
} else {
return $(this).trigger('addresspicker:selected', place);
}
};
AddressPicker.prototype.updateBoundsForPlace = function(response) {
if (response.geometry.viewport) {
return this.map.fitBounds(response.geometry.viewport);
} else {
this.map.setCenter(response.geometry.location);
return this.map.setZoom(this.options.zoomForLocation);
}
};
Managed to fix by commenting out the following lines:
//if (response.geometry.viewport) {
// return this.map.fitBounds(response.geometry.viewport);
//} else {
this.map.setCenter(response.geometry.location);
return this.map.setZoom(this.options.zoomForLocation);
//}

Removing selected geojson feature with Google Maps JavaScript API

I'm using the Google Maps Javascript API to let users draw custom polygons with properties to be entered into a database. Before inserting them into the database though, they need to be able to delete selected shapes they've drawn.
This function isn't throwing any errors but it also isn't deleting the feature. What am I doing wrong?
var selectedshape;
function initMap() {
map = new google.maps.Map(document.getElementById('map2'), {
zoom: 1,
center: { lat: -1, lng: 1 }
});
function clearSelection() {
if (selectedShape) {
selectedShape = null;
}
}
function setSelection(shape) {
clearSelection();
selectedShape = shape;
}
map.data.addListener('click', function(event) {
map.controls[google.maps.ControlPosition.TOP_RIGHT].clear()
setSelection(event.feature);
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
map.data.revertStyle();
map.data.overrideStyle(event.feature, { strokeWeight: 8 });
selectID = event.feature.getProperty('uniqid')
selectID = event.feature.getProperty('uniqgeom')
$(".getSelectID").attr("id", selectID)
});
bounds = new google.maps.LatLngBounds();
map.data.addListener('addfeature', function(event) {
processPoints(event.feature.getGeometry(), bounds.extend, bounds);
map.setCenter(bounds.getCenter());
map.fitBounds(bounds);
var uniqid = "_" + Date.now();
feature_type = event.feature.getGeometry().getType()
if (feature_type == 'LineString') {
encoded_geom = event.feature.getProperty('uniqgeom') || google.maps.geometry.encoding.encodePath(event.feature.getGeometry().getArray());
} else {
encoded_geom = event.feature.getProperty('uniqgeom') || google.maps.geometry.encoding.encodePath(event.feature.getGeometry().getArray()[0].getArray())
}
event.feature.setProperty('encoded_geom', encoded_geom);
selectID = encoded_geom
$(".getSelectID").attr("id", selectID)
event.feature.setProperty('uniqid', uniqid);
});
function deleteSelectedShape() {
map.controls[google.maps.ControlPosition.TOP_RIGHT].clear()
if (selectedShape) {
map.data.forEach(function(feature) {
if (feature.getProperty('uniqid') == selectedShape.uniqid) {
map.data.remove(feature);
}
});
}
}
I believe the problem is a syntax error in
if (feature.getProperty('uniqid') == selectedShape.uniqid) {

Google maps api v3 - Map Toolbar implementation - settimeout for dblclick zoom

I use Map Toolbar implementation for javascript Google maps api v3:
http://nettique.free.fr/gmap/toolbar.html
It's working great! My only matter is that I wanna keep the dblclick event to zoom while creating a polygon, but now, if I double-click, I create a new marker and delete it right away.
With an older version of my code, I was using a timeout like explained here:
https://stackoverflow.com/a/8417447/1895428
but now, no matter where I put the setTimeout function, it makes no change. Does anybody knows where to put it?
I tried to modify the addPoint function in the js code (http://nettique.free.fr/gmap/lib/mapToolbar.js), but it didn't work:
addPoint : function(e, poly, index) { //alert(MapToolbar["shapeCounter"]);
update_timeout = setTimeout(function(){
var e = (typeof e.latLng != "undefined")? e.latLng : e,
image = new google.maps.MarkerImage('/image/marker-edition.png',
new google.maps.Size(9, 9),
new google.maps.Point(0, 0),
new google.maps.Point(5, 5)),
imageover = new google.maps.MarkerImage('/image/marker-edition-over.png',
new google.maps.Size(9, 9),
new google.maps.Point(0, 0),
new google.maps.Point(5, 5)),
path = poly.getPath(),
index = (typeof index != "undefined")? index : path.length,
markers = (poly.markers)? poly.markers : new google.maps.MVCArray,
marker = new google.maps.Marker({
position: e,
map: map[mapkey],
draggable: true,
icon: image
});
marker.index = index;
path.insertAt(index, e);
markers.insertAt(index, marker)
if(arguments[2]){
MapToolbar.reindex(markers);
} }, 200);
//click on a polymarker will delete it
google.maps.event.addListener(marker, 'click', function() {
marker.setMap(null);
markers.removeAt(marker.index);
path.removeAt(marker.index);
MapToolbar.reindex(markers);
if(markers.getLength() == 0){
MapToolbar.removeFeature(poly.id);
}
});
/*
google.maps.event.addListener(marker, 'dragstart', function() {
MapToolbar.currentlyDragging = true;
})
*/
google.maps.event.addListener(marker, 'position_changed', function() {
path.setAt(marker.index, marker.getPosition());
})
google.maps.event.addListener(marker, 'dragend', function() {
//MapToolbar.currentlyDragging = false;
path.setAt(marker.index, marker.getPosition());
var position = marker.getPosition(),
p;
//get previous point
if(typeof path.getAt(marker.index-1) != "undefined"){
var m1 = path.getAt(marker.index -1);
p = MapToolbar.getMidPoint(position, m1);
MapToolbar.addPoint(p, poly, marker.index);
}
// get next point
if(typeof path.getAt(marker.index+1) != "undefined"){
var m2 = path.getAt(marker.index+1);
p = MapToolbar.getMidPoint(position, m2);
MapToolbar.addPoint(p, poly, marker.index+1);
}
});
google.maps.event.addListener(marker, 'mouseover', function() {
this.setIcon(imageover);
});
google.maps.event.addListener(marker, 'mouseout', function() {
this.setIcon(image);
});
MapToolbar["isStarted"]++;
}
Not sure if this will work but following the stackoverflow example you have to change only listener code:
addPoint : function(e, poly, index) {
var e = (typeof e.latLng != "undefined")? e.latLng : e,
...
//click on a polymarker will delete it
var update_timeout = null;
google.maps.event.addListener(marker, 'click', function() {
update_timeout = setTimeout(function() {
marker.setMap(null);
markers.removeAt(marker.index);
path.removeAt(marker.index);
MapToolbar.reindex(markers);
if(markers.getLength() == 0){
MapToolbar.removeFeature(poly.id);
}
}, 200);
});
google.maps.event.addListener(marker, 'dblclick', function(event) {
clearTimeout(update_timeout);
});
...

Categories

Resources