Currently, it appears that if I have say a modestly sized Google Maps display port (300px by 300px) with FullscreenControl enabled, and I center that small map view over a specific area, like... France, for instance... And then I hit the full screen button to expand the display to the edges of my screen (1920px by 1080px), France gets tucked wayyyyy up in the top-left corner of my screen.
Basically, the top-left of the original 300px x 300px display moves to the top-left of my screen, and rest of the world map extends from that corner at the original zoom level.
Is there any way to basically just set it up so that the full screen display opens up having the same center point as the original display, and vice versa when the full screen mode gets closed?
Does toggling the full screen button trigger an event or anything that I can hook a setCenter to?
I compose a code a bit strange but it works perfectly:
<script>
var map;
var center = -1;
var isFullScreen = false;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 8,
center: { lat: -34.397, lng: 150.644 }
});
center = { lat: map.getCenter().lat(), lng: map.getCenter().lng() };
google.maps.event.addDomListener(map, 'bounds_changed', onBoundsChanged);
}
function onBoundsChanged() {
var isFullHeight = $(map.getDiv()).children().eq(0).height() == window.innerHeight;
var isFullWidth = $(map.getDiv()).children().eq(0).width() == window.innerWidth;
if (isFullHeight && isFullWidth && !isFullScreen) {
isFullScreen = true;
map.setCenter(center);
console.log('FULL');
} else if (!isFullWidth||!isFullHeight){
if (isFullScreen) {
map.setCenter(center);
}
isFullScreen = false;
console.log('NOT-FULL');
}
center = { lat: map.getCenter().lat(), lng: map.getCenter().lng() };
}
</script>
I use the bounds_changed event.
If I detect that the map is in full screen I set the map in the center that I note in the precedent event and I set a boolean to true. If the map is not in full screen, I check if the boolean is true, it mean that in the precedent event the map was in full screen, so I recenter the map and next I check if the boolean is false. At the end of the event, I keep the center in a variable fro the next event.
All this are not very clear...
You may consult this post in Stack Overflow.
Notice: This code does not work if you display a single map in all the page of your web app. I do not find a way to remove this bug. Your suggestions are very appreciated, thanks.
Also notice: My code is inspired of the precedent answer. However you can find the same code here. Please notice that it is not a duplicate answer. I add my own part of code in it.
Tell me if you have some questions or comments.
Try this
/** To detect Map Full Screen event */
google.maps.event.addListener( map, 'bounds_changed', onBoundsChanged ); //Event listener map bound change.
var isMapFullScreen = false;
var defaultLocation = {
lat: 27.94,
lng: -82.45
};
function onBoundsChanged() {
if(!isMapFullScreen){
var isFullHeight = $(map.getDiv()).children().eq(0).height() == window.innerHeight;
var isFullWidth= $(map.getDiv()).children().eq(0).width() == window.innerWidth;
if (isFullHeight && isFullWidth) {
isMapFullScreen = true;
myMarker.setPosition(defaultLocation);
map.setCenter(defaultLocation);
console.log('FULL');
} else {
isMapFullScreen = false;
console.log('NOT-FULL');
}
}
}
Related
I saw an the website of google that we could add/remove the zoomControl base on the map size.
But the page doesn't explain how to do this.
My objective would be to remove the zoom control on smartphone, so under 576px I would like the zoom control to be hidden.
I'm working with angular but do not wish to subscribe to the screen size, but would like to let google do that on his self.
Google map doesn't accept such variable, but it's mentioned that the control will be hidden at 200px width, and to disable this you should explicitly mention it.
So if you wish to do that, you'll have to go with a little bit of javascript.
First check the screen width of device then set zoomControl property based on that. For example:-
function initMap(): void {
let zoomControl = true;
if(window.innerWidth < 576) zoomControl = false;
const map = new google.maps.Map(
document.getElementById("map") as HTMLElement,
{
zoom: 4,
center: { lat: -33, lng: 151 },
zoomControl: zoomControl,
scaleControl: true,
}
);
}
declare global {
interface Window {
initMap: () => void;
}
}
window.initMap = initMap;
im working on a PWA which retrieves the users location via geolocation-API, places a pin to the current location of the user and allows him to drag the pin to a certain location.
this works perfectly in chrome but when testing it in safari, the map only displays the whole world on
the map and doesnt allow zooming further than a few pinches + it doesnt place a marker.
// configure pin
function addDraggableMarker(map, behavior) {
// create icon
var svgMarkup = /* some random svg here */;
var icon = new H.map.Icon(svgMarkup);
// position the pin
var coords = {
lat: lat,
lng: lng
};
// initialize pin
window.marker = new H.map.Marker(
coords, {
icon: icon
}, {
volatility: true
}
);
// make pin draggable
marker.draggable = true;
map.addObject(marker);
// disable the default draggability of the underlying map
map.addEventListener('dragstart', function(ev) {
var target = ev.target,
pointer = ev.currentPointer;
if (target instanceof H.map.Marker) {
var targetPosition = map.geoToScreen(target.getGeometry());
target.offset = new H.math.Point(pointer.viewportX - targetPosition.x, pointer.viewportY - targetPosition.y);
behavior.disable();
}
}, false);
// re-enable the default draggability of the underlying map when dragging has completed
map.addEventListener('dragend', function(ev) {
var target = ev.target;
if (target instanceof H.map.Marker) {
behavior.enable();
window.markerMoved = target.getGeometry();
}
}, false);
// Listen to the drag event and move the position of the marker as necessary
map.addEventListener('drag', function(ev) {
var target = ev.target,
pointer = ev.currentPointer;
if (target instanceof H.map.Marker) {
target.setGeometry(map.screenToGeo(pointer.viewportX - target.offset.x, pointer.viewportY - target.offset.y));
}
}, false);
}
// click on the button which displays the map
$("#addLocation").on('click', function() {
// 1. connect to API
window.platform = new H.service.Platform({
'apikey': 'MY_API_KEY'
});
window.defaultLayers = platform.createDefaultLayers();
// 2. initialize map
window.map = new H.Map(document.getElementById('map'),
defaultLayers.vector.normal.map, {
center: {
lat: lat,
lng: lng
},
zoom: 16,
pixelRatio: window.devicePixelRatio || 1
});
// stretch map to size of container
window.addEventListener('resize', () => map.getViewPort().resize());
// 3. make map interactive
//Behavior implements default interactions for pan/zoom (also on mobile touch environments)
window.behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// 4. add map UI
window.ui = H.ui.UI.createDefault(map, defaultLayers, "de-DE");
// Add the click event listener.
addDraggableMarker(map, behavior);
});
I copied your code into jsfiddle example and everything works as expected in Safari browser. After clicking the button "AddLocation" the map is displayed at correct location + zoom with marker at the center.
There is one small issue with your code when the Marker is created. volatility property should be next to the icon property:
new H.map.Marker(
coords, {
icon: icon,
volatility: true
}
);
I am using marker cluster group.
using that I am able to show two markers.
when you click number two which is in red color you will see two markers.
after that when I click one marker I need to zoom in two levels to see the location
I wrote a marker click for that and in that i added zoom and tried to use fitbounds too, but its not zooming in.
we used mapcenter then also it did not work `
can you tell me how to fix it.
providing my code snippet and sandbox below.
https://codesandbox.io/s/20756jrz8p
MarkerClick = e => {
console.log("e----->", e);
this.setState({
viewport: { center: [20, 6], zoom: 7 }
});
//this.refs.mymap.leafletElement.setZoom(8);
//let bounds = this.refs.mymap.leafletElement.fitBounds();
//console.log("bounds----->", bounds);
console.log(
"after setting state zoomlevel bounds showCard--->",
this.state.zoom
);
// this.setState({ zoom: 18 });
//this.setState({ zoomLevel: 7 });
};
The Zoom value you have used in MarkerClick() is lower than the current zoom value (zoom = 8). Hence you are not getting proper zoom. Use Zoom = 14 or 16 and a different set of co-ordinates instead of [20,6].
For Example:
MarkerClick = e => {
...
this.setState({
viewport: { center: [43.39528702235596, 6.294845731267186], zoom: 16 }
});
...
}
UPDATE: I have a static solution working right now, but I'd still like to see if it can be improved upon. All of the code is the same aside from the on click event for switching views.
$(document).on('click', '.mobile-toggle a', function (e) {
e.preventDefault();
if (!$(this).hasClass("active")) {
var target = $(this).attr("data-target");
$("#results > div, .mobile-toggle a").removeClass("active");
$(this).addClass("active");
$("#" + target).addClass("active");
}
var center = dmap.getCenter();
google.maps.event.trigger(dmap, 'resize');
dmap.setCenter(center);
dmap.setZoom(12);
});
This gets the map centered properly, which is good. The zoom is also fine, but it doesn't always fit the route. Sometimes the route is too big to fit, and other times the map should probably be zoomed in a little bit more. Is there any way to determine the zoom value that should be used based on the route? I'm fairly certain this is generally supposed to happen on its own, but that doesn't seem to be the case this time around.
Original post below.
I've read a bunch of questions/answers about this, but none of the provided answers seem to do what I'm looking for. I'll try to explain this the best I can.
I'm currently writing a store locator. On desktop, everything looks fine. Mobile is where I run into difficulty because of some changing views (showing/hiding divs). From what I've read, it looks like a lot of people run into a problem where the map is created in a hidden div and then it's displayed incorrectly (e.g. the map only occupies the top left area of the container) when the div is shown.
The layout of the store locator is as follows - after you search for a location, you see a list view of all the results. When you click the 'map view' tab up top, you see a Google maps view with all of the nearby stores on it. This works fine. If you select a store and click 'get directions' from the list view, you see a list view of the directions to get to that store. Likewise, if you select a store and click 'get directions' while in map view, you see a map of the directions. That works fine in both scenarios.
The issue is when I am in a list view, click to get directions, and then switch over from the list view of directions to the map view. The map gets drawn with the correct route and it fills the div just like it should - however, the route is in the top left of the map, and the map itself is zoomed way out. For example, if the route is in the Philadelphia area, the map is so zoomed out that its center is generally around Bermuda. And it's roughly the same spot in Bermuda every time.
Here's the relevant code for the button press between list and map views.
$(document).on('click', '.mobile-toggle a', function (e) {
e.preventDefault();
if (!(this).hasClass("active")) {
var target = $(this).attr("data-target");
$("#results > div, .mobile-toggle a").removeClass("active");
$(this).addClass("active");
$("#" + target).addClass("active");
}
google.maps.event.trigger(dmap, 'resize');
}
dmap is a global variable containing the directions map, and the map itself has these two listeners assigned to it when it is created.
google.maps.event.addListener(dmap, 'idle', function () {
google.maps.event.trigger(dmap, 'resize');
dmapCenter = dmap.getCenter();
});
google.maps.event.addDomListener(window, 'resize', function () {
dmap.setCenter(dmapCenter);
});
This redraws the map and makes sure the center stays the same while the window is resized, but the map itself still isn't focused on the route from location A to location B. I feel like the solution can't be too far from what I've already tried (based on what I've read), but I can't seem to get this working.
Sorry about the wall of text. If there's any other code you think would help potentially answer the question, please let me know. Thanks!
EDIT: As requested, here's the full code that draws the map.
function calcRoute(start, dest) {
var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
directionsDisplay = new google.maps.DirectionsRenderer();
var mapOptions = {
zoom: 12,
center: new google.maps.LatLng(lat, lng)
};
var map = new google.maps.Map(document.getElementById('directions-map'), mapOptions);
directionsDisplay.setMap(map);
directionsDisplay.setPanel(document.getElementById('directions'));
var request = {
origin: start,
destination: dest,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
dmap = map;
dmapCenter = map.getCenter();
google.maps.event.addListener(map, 'idle', function () {
google.maps.event.trigger(map, 'resize');
dmapCenter = map.getCenter();
});
google.maps.event.addDomListener(window, 'resize', function () {
map.setCenter(dmapCenter);
});
}
lat and lng are global variables with the latitude and longitude of the search location.
The workflow you are using (IMHO) seems a little odd to me (IMHO), mainly of your choice to initialize a map instance every time you calculate the directions.
I don't know how much this will help since I haven't been able to test it on a mobile device, but below is code to make a google map, render directions between two points, and maintain the map center after the map is resized (test resize by running snippet in full page than resizing the browser window).
var DMAP,
DMAP_RENDERER,
DIRECTIONS_SERVICE;
/*
Run only once when your page loads to ready global components
for any future direction calls.
*/
function initializeDirectionsFeature(){
//set up directions map
var dmapOptions = {
zoom: 4,
center: new google.maps.LatLng(38.8282, -98.5795) //USA center
};
DMAP = new google.maps.Map( $("#map").get(0), dmapOptions);
//set up renderer for directions map
var rendererOptions = {
map: DMAP,
panel: $("#directions").get(0)
};
DMAP_RENDERER = new google.maps.DirectionsRenderer(rendererOptions);
//Initialize the directions service
DIRECTIONS_SERVICE = new google.maps.DirectionsService();
//Trigger map redraw when dom element is resized
google.maps.event.addDomListener(window, 'resize', function () {
google.maps.event.trigger(DMAP, 'resize');
});
//Preserve map perspective when after resize
google.maps.event.addListener(DMAP, 'resize', function () {
var center = DMAP.getCenter();
google.maps.event.addListenerOnce(DMAP, 'center_changed', function () {
DMAP.setCenter( center );
});
});
}
/*
Gets and renders the directions between params.
Params 'from' and 'to' can be either LatLng or
a String that will be geocoded. Param 'renderer'
is the `google.maps.DirectionsRenderer` to use.
*/
function calcDirections(from, to, renderer){
var request = {
origin: from,
destination: to,
travelMode: google.maps.TravelMode.DRIVING
};
DIRECTIONS_SERVICE.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
renderer.setDirections(response);
}
});
}
//for Snippet, actuall run google maps initialize function
initializeDirectionsFeature();
//For Snippet example
$("#query").submit(function(e){
e.preventDefault();
calcDirections(e.target.from.value, e.target.to.value, DMAP_RENDERER)
});
/* just for Snippet, gmap element just needs a height */
html, body {
position: relative;
width: 98%;
height: 98%;
min-height: 500px;
}
#map, #directions {
width: 100%;
height: 40%;
}
.inline-block {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js">
</script>
<form id="query">
<div class="inline-block">
<label for="from">From:</label>
<input id="from" name="from" value="New York, NY"/>
</div>
<div class="inline-block">
<label for="to">To:</label>
<input id="to" name="to" value="Philadelphia, PA"/>
</div>
<button type="submit">Go</button>
</form>
<div id="map"></div>
<div id="directions">Directions:</div>
I'm working on optimizing a site for mobile. We have a Location page that includes info about a location and a map of the location via the Google Maps API. (v2 - I know it's deprecated but I haven't justified the time to upgrade, "if it ain't broke..") I want to use a single column layout with basic information followed by the map followed by more information.
Now when I use my finger to scroll down the mobile page on an iPhone, once I get to the map, the page scrolling is overridden and the map starts panning. The only way for me to scroll farther down the page is to put my finger above or below the map, assuming such space is available. If I disable map dragging, then when I start scrolling down and get to the map it doesn't pan but the page doesn't scroll either. I would like to treat the map as a static image that I can scroll past, but still allow the zoom buttons and allow the map to be redrawn with directions through a select field I have coded, so a literal static image is not a solution.
I found this post that required similar functionality, but it's using v3. I think all I need to do is "add touch events to the map container," but I'm not familiar with that part of javascript, and what I have below does not allow normal scrolling. Do I need to bite the bullet on v3, or do I have a bug on adding touch events that has a simple javascript correction to do what I want?
function initialize() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map_canvas"));
geocoder = new GClientGeocoder();
}
}
function showAddress(address, zoom) {
//clipped... this part works fine
}
//These three lines create a map that my finger pans
initialize();
showAddress("[clipped.. street, zip code]");
map.addControl(new GSmallZoomControl3D());
//This stops the map pan but still prevents normal page finger scrolling
map.disableDragging();
//If done right could this allow normal page finger scrolling??
var dragFlag = false;
map.addEventListener("touchstart", function(e){
dragFlag = true;
start = (events == "touch") ? e.touches[0].pageY : e.clientY;
},true);
map.addEventListener("touchend", function(){
dragFlag = false;
}, true);
map.addEventListener("touchmove",function(
if ( !dragFlag ) return;
end = (events == "touch") ? e.touches[0].pageY : e.clientY;
window.scrollBy( 0,( start - end ) );
}, true);
I have also tried replacing map.addEventListener with document.getElementById("map_canvas").addEventListener or document.addEventListener to no avail.
I solved it by upgrading to v3 and then detecting a basic javascript error in my use of the code from the solution linked above. The key was
start = (events == "touch") ? e.touches[0].pageY : e.clientY;
The user must have been setting the events variable somewhere outside the presented code, since it looks like the matching assignment is for touch events and the else assignment is for key events. But since I didn't have an events variable it was defaulting to the wrong assignment. I simply changed mine to start = e.touches[0].pageY (and did the same for the touchend event) and now everything works.
However, I switched back to v2 to see if it would work with that javascript error corrected, and it did not. So it looks like I did not waste any time upgrading to v3, neither in figuring out this specific solution nor in setting myself up for future compatibility.
In conclusion, if you want to embed Google Maps on a mobile page and be able to scroll past it, you need to use API v3, disable dragging, and add touch events. I made a few minor tweaks to my code as well, presented here for any who may benefit in the future:
function initialize()
{
geocoder = new google.maps.Geocoder();
var myOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}
function showAddress(address, zoom)
{
if (geocoder)
{
geocoder.geocode( { 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
map.setOptions( { zoom: zoom });
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
}
});
}
}
initialize();
showAddress("'.$geocode_address.'");
map.setOptions( { draggable: false });
var dragFlag = false;
var start = 0, end = 0;
function thisTouchStart(e)
{
dragFlag = true;
start = e.touches[0].pageY;
}
function thisTouchEnd()
{
dragFlag = false;
}
function thisTouchMove(e)
{
if ( !dragFlag ) return;
end = e.touches[0].pageY;
window.scrollBy( 0,( start - end ) );
}
document.getElementById("map_canvas").addEventListener("touchstart", thisTouchStart, true);
document.getElementById("map_canvas").addEventListener("touchend", thisTouchEnd, true);
document.getElementById("map_canvas").addEventListener("touchmove", thisTouchMove, true);