Come compensare il punto centrale in Google Maps API V3

Ho una mappa di Google con un pannello semi trasparente che copre una parte dell’area. Vorrei regolare il punto centrale della mappa per tenere conto della porzione di mappa parzialmente oscurata. Guarda l’immagine qui sotto. Idealmente, dove sono posizionati il ​​mirino e il mirino, sarebbe il punto centrale della mappa.

Spero che abbia un senso.

Il motivo è semplice: quando lo zoom è necessario centrare la mappa sul mirino anziché al 50% 50%. Inoltre, traccerò i marcatori sulla mappa e li attraverserò in sequenza. Quando la mappa si concentra su di loro, devono anche essere nella posizione di offset.

Grazie in anticipo!

Mockup della mappa che sto costruendo

Questo non è particolarmente difficile una volta trovata la risposta precedente pertinente .

Devi convertire il centro della mappa nelle sue coordinate mondiali, trovare dove la mappa deve essere centrata per mettere il centro apparente dove vuoi e ricentrare la mappa usando il centro reale .

L’API map.getCenter() sempre la mappa al centro del viewport, quindi è necessario fare attenzione se si utilizza map.getCenter() poiché restituirà il centro reale, non il centro apparente. Suppongo che sarebbe ansible sovraccaricare l’API in modo che i suoi getCenter() e setCenter() vengano sostituiti, ma non l’ho fatto.

Codice sotto. Esempio online . Nell’esempio, facendo clic sul pulsante si sposta il centro della mappa (c’è un incrocio stradale lì) di 100 px e 200 px di sinistra.

 function offsetCenter(latlng, offsetx, offsety) { // latlng is the apparent centre-point // offsetx is the distance you want that point to move to the right, in pixels // offsety is the distance you want that point to move upwards, in pixels // offset can be negative // offsetx and offsety are both optional var scale = Math.pow(2, map.getZoom()); var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latlng); var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0); var worldCoordinateNewCenter = new google.maps.Point( worldCoordinateCenter.x - pixelOffset.x, worldCoordinateCenter.y + pixelOffset.y ); var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter); map.setCenter(newCenter); } 

Dai anche un’occhiata alla funzione panBy (x: number, y: number) sull’object maps.

La documentazione menziona questo sulla funzione:

Cambia il centro della mappa in base alla distanza specificata in pixel. Se la distanza è inferiore sia alla larghezza che all’altezza della mappa, la transizione verrà animata in modo uniforms. Si noti che il sistema di coordinate della mappa aumenta da ovest a est (per i valori x) e da nord a sud (per i valori y).

Basta usarlo in questo modo:

 mapsObject.panBy(200, 100) 

Ecco un metodo più semplice che potrebbe essere più utile nella progettazione retriggers poiché è ansible utilizzare percentuali anziché pixel. Nessuna coordinata del mondo, nessuna LatLng ai punti!

 var center; // a latLng var offsetX = 0.25; // move center one quarter map width left var offsetY = 0.25; // move center one quarter map height down var span = map.getBounds().toSpan(); // a latLng - # of deg map spans var newCenter = { lat: center.lat() + span.lat()*offsetY, lng: center.lng() + span.lng()*offsetX }; map.panTo(newCenter); // or map.setCenter(newCenter); 

Ecco un esempio di soluzione del problema utilizzando il metodo panBy() dell’API delle mappe: http://jsfiddle.net/upsidown/2wej9smf/

Andrew’s è la risposta. Tuttavia, nel mio caso, map.getBounds () continuava a tornare indefinito. L’ho risolto in attesa dell’evento bounds_changed e poi ho chiamato la funzione per bilanciare il centro. Così:

 var center_moved = false; google.maps.event.addListener(map, 'bounds_changed', function() { if(!center_moved){ offsetCenter(map.getCenter(), 250, -200); center_moved = true; } }); 

Vecchia domanda, lo so. Ma che ne dici di un modo più incentrato sui CSS?

http://codepen.io/eddyblair/pen/VjpNQQ

Quello che ho fatto è stato:

  • Avvolgi la mappa e la sovrapposizione in un contenitore con overflow: hidden

  • Sovrapposta la sovrapposizione con la position: absolute

  • Estesa larghezza apparente della mappa per la larghezza di sovrapposizione (più eventuali padding e offset) impostando un margin-left negativo margin-left .

  • Quindi per rispettare https://www.google.com/permissions/geoguidelines/attr-guide.html posizionati i widget e le div attribuzioni.

In questo modo il centro della mappa si trova in linea con il centro dell’area desiderata. Il js è solo la mappa standard js.

Il riposizionamento delle icone per la vista stradale è un esercizio per il lettore 🙂


Se vuoi la sovrapposizione a sinistra, modifica la linea 24 margin-left a margin-left a margin-right e orizzontale a right .

Dopo una lunga ricerca non sono riuscito a trovare un modo per farlo che includesse anche lo zoom. Per fortuna un tipo intelligente l’ ha capito. C’è anche un violino qui

 'use strict'; const TILE_SIZE = { height: 256, width: 256 }; // google World tile size, as of v3.22 const ZOOM_MAX = 21; // max google maps zoom level, as of v3.22 const BUFFER = 15; // edge buffer for fitting markers within viewport bounds const mapOptions = { zoom: 14, center: { lat: 34.075328, lng: -118.330432 }, options: { mapTypeControl: false } }; const markers = []; const mapDimensions = {}; const mapOffset = { x: 0, y: 0 }; const mapEl = document.getElementById('gmap'); const overlayEl = document.getElementById('overlay'); const gmap = new google.maps.Map(mapEl, mapOptions); const updateMapDimensions = () => { mapDimensions.height = mapEl.offsetHeight; mapDimensions.width = mapEl.offsetWidth; }; const getBoundsZoomLevel = (bounds, dimensions) => { const latRadian = lat => { let sin = Math.sin(lat * Math.PI / 180); let radX2 = Math.log((1 + sin) / (1 - sin)) / 2; return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; }; const zoom = (mapPx, worldPx, fraction) => { return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2); }; const ne = bounds.getNorthEast(); const sw = bounds.getSouthWest(); const latFraction = (latRadian(ne.lat()) - latRadian(sw.lat())) / Math.PI; const lngDiff = ne.lng() - sw.lng(); const lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; const latZoom = zoom(dimensions.height, TILE_SIZE.height, latFraction); const lngZoom = zoom(dimensions.width, TILE_SIZE.width, lngFraction); return Math.min(latZoom, lngZoom, ZOOM_MAX); }; const getBounds = locations => { let northeastLat; let northeastLong; let southwestLat; let southwestLong; locations.forEach(function(location) { if (!northeastLat) { northeastLat = southwestLat = location.lat; southwestLong = northeastLong = location.lng; return; } if (location.lat > northeastLat) northeastLat = location.lat; else if (location.lat < southwestLat) southwestLat = location.lat; if (location.lng < northeastLong) northeastLong = location.lng; else if (location.lng > southwestLong) southwestLong = location.lng; }); const northeast = new google.maps.LatLng(northeastLat, northeastLong); const southwest = new google.maps.LatLng(southwestLat, southwestLong); const bounds = new google.maps.LatLngBounds(); bounds.extend(northeast); bounds.extend(southwest); return bounds; }; const zoomWithOffset = shouldZoom => { const currentzoom = gmap.getZoom(); const newzoom = shouldZoom ? currentzoom + 1 : currentzoom - 1; const offset = { x: shouldZoom ? -mapOffset.x / 4 : mapOffset.x / 2, y: shouldZoom ? -mapOffset.y / 4 : mapOffset.y / 2 }; const newCenter = offsetLatLng(gmap.getCenter(), offset.x, offset.y); if (shouldZoom) { gmap.setZoom(newzoom); gmap.panTo(newCenter); } else { gmap.setCenter(newCenter); gmap.setZoom(newzoom); } }; const setMapBounds = locations => { updateMapDimensions(); const bounds = getBounds(locations); const dimensions = { width: mapDimensions.width - mapOffset.x - BUFFER * 2, height: mapDimensions.height - mapOffset.y - BUFFER * 2 }; const zoomLevel = getBoundsZoomLevel(bounds, dimensions); gmap.setZoom(zoomLevel); setOffsetCenter(bounds.getCenter()); }; const offsetLatLng = (latlng, offsetX, offsetY) => { offsetX = offsetX || 0; offsetY = offsetY || 0; const scale = Math.pow(2, gmap.getZoom()); const point = gmap.getProjection().fromLatLngToPoint(latlng); const pixelOffset = new google.maps.Point((offsetX / scale), (offsetY / scale)); const newPoint = new google.maps.Point( point.x - pixelOffset.x, point.y + pixelOffset.y ); return gmap.getProjection().fromPointToLatLng(newPoint); }; const setOffsetCenter = latlng => { const newCenterLatLng = offsetLatLng(latlng, mapOffset.x / 2, mapOffset.y / 2); gmap.panTo(newCenterLatLng); }; const locations = [{ name: 'Wilshire Country Club', lat: 34.077796, lng: -118.331151 }, { name: '301 N Rossmore Ave', lat: 34.077146, lng: -118.327805 }, { name: '5920 Beverly Blvd', lat: 34.070281, lng: -118.331831 }]; locations.forEach(function(location) { let marker = new google.maps.Marker({ position: new google.maps.LatLng(location.lat, location.lng), title: location.name }) marker.setMap(gmap); markers.push(marker); }); mapOffset.x = overlayEl.offsetWidth; document.zoom = bool => zoomWithOffset(bool); document.setBounds = () => setMapBounds(locations); 
 section { height: 180px; margin-bottom: 15px; font-family: sans-serif; color: grey; } figure { position: relative; margin: 0; width: 100%; height: 100%; } figcaption { position: absolute; left: 15px; top: 15px; width: 120px; padding: 15px; background: white; box-shadow: 0 2px 5px rgba(0, 0, 0, .3); } gmap { display: block; height: 100%; } 
  

Tile Overlay

To be avoided by the map!

Ho appena trovato un’altra soluzione più semplice. Nel caso tu stia utilizzando il metodo fitBounds, puoi passare il secondo argomento opzionale al suo. Questo argomento è il riempimento, che verrà considerato durante l’adattamento dei limiti.

 // pass single side: map.fitBounds(bounds, { left: 1000 }) // OR use Number: map.fitBounds(bounds, 20) 

Ulteriori letture: documenti ufficiali .

Un altro approccio quando si tratta di compensare una rotta o un gruppo di marcatori può essere trovato qui:

https://stackoverflow.com/a/26192440/1238965

Usa ancora il metodo fromLatLngToPoint() descritto nella risposta @Andrew Leach.