So versetzen Sie den Mittelpunkt in Google Maps API V3

78

Ich habe eine Google-Karte mit einem halbtransparenten Bereich, der einen Teil des Gebiets abdeckt. Ich möchte den Mittelpunkt der Karte anpassen, um den Teil der Karte zu berücksichtigen, der teilweise verdeckt ist. Siehe das Bild unten. Idealerweise ist der Mittelpunkt der Karte der Ort, an dem das Fadenkreuz und der Stift platziert sind.

Ich hoffe das ergibt Sinn.

Der Grund ist einfach: Wenn Sie zoomen, muss die Karte über dem Fadenkreuz zentriert werden und nicht bei 50% bis 50%. Außerdem werde ich Markierungen auf der Karte zeichnen und sie nacheinander durchlaufen. Wenn die Karte auf sie zentriert ist, müssen sie sich auch an der versetzten Position befinden.

Danke im Voraus!

Modell der Karte, die ich baue

werden
quelle
Sie müssen Ihre Frage klären. Was bedeutet "den teilweise verdeckten Teil der Karte berücksichtigen"? Und was bedeutet "anstatt bei 50% 50%"? Sie möchten helfen, können aber nicht herausfinden, was Sie erreichen möchten.
Sean Mickey
Hallo, können Sie bitte die URL des Arbeitsbeispiels angeben, da ich Hilfe beim Erstellen einer beweglichen Tricklinie zusammen mit der Routenlinie benötige
SP Singh

Antworten:

83

Dies ist nicht besonders schwierig, wenn Sie die entsprechende vorherige Antwort gefunden haben .

Sie müssen die Mitte der Karte in ihre Weltkoordinaten konvertieren, herausfinden, wo die Karte zentriert werden muss, um die scheinbare Mitte an die gewünschte Stelle zu bringen, und die Karte mithilfe der realen Mitte neu zentrieren .

Die API zentriert die Karte immer in der Mitte des Ansichtsfensters. Sie müssen also vorsichtig sein, wenn Sie sie verwenden, map.getCenter()da sie das reale Zentrum und nicht das scheinbare Zentrum zurückgibt. Ich nehme an, es wäre möglich, die API zu überladen, so dass ihre getCenter()und setCenter()Methoden ersetzt werden, aber das habe ich nicht getan.

Code unten. Beispiel online . Im Beispiel wird durch Klicken auf die Schaltfläche die Mitte der Karte (dort befindet sich eine Straßenkreuzung) um 100 Pixel nach unten und um 200 Pixel nach links verschoben.

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);

}
Andrew Leach
quelle
Ist es möglich, diesen Code beim Laden der Seite auszulösen, sodass die Karte beim ersten Anzeigen als Versatz angezeigt wird?
Darren Cook
@DarrenCook: Natürlich. Verwenden Sie dies einfach anstelle von map.setCenter(). Alles, was dieser Code tut, ist neu zu berechnen, was die Karte tun soll, und dann macht es sein eigenes map.setCenter().
Andrew Leach
Danke Andrew, also habe ich in meinem Code addMarkerFromAdress (latLng, title) genommen und durch die Funktion offsetCenter (latlng, offsetx, offsety) ersetzt, und alles funktioniert immer noch - aber wie bekomme ich den Wert für offsety (243px) in die Funktion?
Darren Cook
27
Bessere Verwendungmap.panBy(250, 0);
Alexander Shvetsov
2
Ich habe festgestellt, dass die nwVariable von diesem Skript nie verwendet wird und zumindest in meinem Anwendungsfall ohne Probleme entfernt werden kann. Ich habe das aber nicht alleine versucht, da ich bereits eine Reihe anderer Methoden habe, die die Grenzen setzen und so weiter.
Hellatan
70

Schauen Sie sich auch die Funktion panBy (x: Nummer, y: Nummer) für das Kartenobjekt an.

In der Dokumentation wird Folgendes über die Funktion erwähnt:

Ändert die Mitte der Karte um den angegebenen Abstand in Pixel. Wenn der Abstand kleiner als die Breite und Höhe der Karte ist, wird der Übergang reibungslos animiert. Beachten Sie, dass das Kartenkoordinatensystem von West nach Ost (für x-Werte) und von Nord nach Süd (für y-Werte) zunimmt.

Verwenden Sie es einfach so:

mapsObject.panBy(200, 100)
Kah Tang
quelle
7
Kennt jemand eine Möglichkeit, die Animation für diese Methode immer zu deaktivieren?
Ash
Dies muss die Antwort sein. Es hat perfekt und so einfach funktioniert. Vielen Dank.
Developer107
10

Hier ist eine einfachere Methode, die beim reaktionsschnellen Design möglicherweise nützlicher ist, da Sie Prozentsätze anstelle von Pixeln verwenden können. Keine Weltkoordinaten, keine LatLngs to Points!

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);
brycewjohnson
quelle
Versuchte dies auf InfoBox und es funktioniert! (obwohl das pixelOffset noch repariert werden muss)
ab
1
Gute Antwort sowohl für die Einfachheit der Lösung als auch für das reaktionsschnelle Design
Daniel Tonon
1
Ich erhalte einen nicht erfassten Typfehler: Die Eigenschaft 'toSpan' von undefined
Mike Kormendy kann
können wir das newCenter lat lang bekommen?
Herr Tomar
1
Dies würde nicht funktionieren, wenn der Zoom im selben Vorgang geändert werden muss. Außerdem würde es nicht genau funktionieren, wenn sich das (neue) Zielzentrum geografisch weit vom aktuellen Zentrum entfernt befindet. Selbst bei gleicher Zoomstufe stellen 0,25 der Bildschirmbreite einen anderen Abstand am Äquator dar als beispielsweise in Norwegen, beispielsweise aufgrund der verwendeten Mercator-Projektion. Das Projizieren der endgültigen Koordinaten ist robuster.
Pscl
8

Habe gerade eine andere einfachste Lösung gefunden. Wenn Sie die fitBounds-Methode verwenden, können Sie ihr optionales zweites Argument übergeben. Dieses Argument ist Auffüllen, das beim Anpassen von Grenzen berücksichtigt wird.

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

// OR use Number:
map.fitBounds(bounds, 20)

Weiterführende Literatur: offizielle Dokumente .

Nickensoul
quelle
Dies ist perfekt!
Monchisan
6

Andrews ist die Antwort. In meinem Fall gab map.getBounds () jedoch immer wieder undefiniert zurück. Ich habe das Warten auf das Ereignis bounds_changed behoben und dann die Funktion aufgerufen, um die Mitte zu versetzen. Wie so:

var center_moved = false;
google.maps.event.addListener(map, 'bounds_changed', function() {
  if(!center_moved){
    offsetCenter(map.getCenter(), 250, -200);
    center_moved = true; 
  }
});
Nahuel
quelle
1
Diese Antwort in Kombination mit (nach) Andrews Antwort war die Lösung für mich. Danke Nahuel!
Mike Kormendy
5

Alte Frage, ich weiß. Aber wie wäre es mit einer CSS-zentrierteren Methode?

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

Was ich getan habe war:

  • Wickeln Sie die Karte ein und überlagern Sie sie mit overflow: hidden

  • Überlagert die Überlagerung mit position: absolute

  • Erweitern Sie die scheinbare Breite der Karte um die Überlagerungsbreite (plus Auffüllen und Versatz), indem Sie ein Negativ festlegen margin-left.

  • Um dann https://www.google.com/permissions/geoguidelines/attr-guide.html einzuhalten, positionierten Sie die Widgets und Zuordnungen div.

Auf diese Weise liegt die Mitte der Karte in einer Linie mit der Mitte des gewünschten Bereichs. Das js ist nur eine Standardkarte js.

Das Neupositionieren der Symbole für Street View ist eine Übung für den Leser :)


Wenn Sie die Überlagerung links möchten, ändern Sie einfach Zeile 24 margin-leftzu margin-rightund Zeile 32 rightzu left.

Mardoxx
quelle
Ich mag diesen Ansatz wirklich. Hält das Angular / Javascript schön sauber.
Verschiedene Künstler
Danke :) Dies muss natürlich aktualisiert werden, wenn Google seinen Stil ändert. Ich stelle dem CSS Kommentare vor, die die Attributionen und Steuerelemente usw. verschieben. Dies sollte eine Vorstellung davon geben, wie ich es gemacht habe, wenn es geändert werden muss. Zusätzliches CSS muss erstellt werden, um die Straßenansicht zu sortieren.
Mardoxx
3

Nach ausgiebiger Suche konnte ich keinen Weg finden, dies auch mit Zoom zu tun. Zum Glück hat ein kluger Kerl es herausgefunden. Hier gibt es auch eine Geige

'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%;
}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>

<section>
  <figure>
    <gmap id="gmap"></gmap>
    <figcaption id="overlay">
      <h4>Tile Overlay</h4>
      <p>To be avoided by the map!</p>
    </figcaption>
  </figure>
</section>
<button onclick="zoom(true)">zoom in</button>
<button onclick="zoom(false)">zoom out</button>
<button onclick="setBounds()">set bounds</button>

Chris
quelle
1

Ein weiterer Ansatz zum Versetzen einer Route oder einer Gruppe von Markierungen finden Sie hier:

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

Es wird weiterhin die fromLatLngToPoint()in der Antwort von @Andrew Leach beschriebene Methode verwendet .

MrUpsidown
quelle
1
Dies ist komplexer als die akzeptierte Antwort IMO.
Ian Lunn