Programming

Google Maps API V3-정확히 같은 지점에 여러 마커

procodes 2020. 8. 3. 21:05
반응형

Google Maps API V3-정확히 같은 지점에 여러 마커


이것에 조금 붙어 있습니다. JSON을 통해 지리 좌표 목록을 검색하여 Google지도에 표시합니다. 정확히 같은 지점에 두 개 이상의 마커가있는 경우를 제외하고는 모두 잘 작동합니다. API는 1 개의 마커 (상단 마커) 만 표시합니다. 이것은 내가 생각하는 것만 큼 공평하지만 어떻게 든 그들을 표시하는 방법을 찾고 싶습니다.

나는 구글을 검색하고 몇 가지 해결책을 찾았지만 대부분 API의 V2에 대한 것 같거나 그다지 좋지 않은 것 같습니다. 이상적으로는 일종의 그룹 마커를 클릭 한 다음 마커가 모두있는 지점 주위에 클러스터 된 마커를 표시하는 솔루션을 원합니다.

누구 든지이 문제가 있거나 비슷한 문제가 있으며 해결책을 공유해야합니까?


OverlappingMarkerSpiderfier를 살펴보십시오 .
데모 페이지가 있지만 정확히 같은 지점에있는 마커는 표시하지 않으며 서로 매우 가까운 마커 만 표시합니다.

그러나 정확히 같은 지점에 마커가있는 실제 사례는 http://www.ejw.de/ejw-vor-ort/에서 볼 수 있습니다 (지도를 아래로 스크롤하여 몇 가지 마커를 클릭하여 거미 효과를 확인하십시오) ).

그것은 당신의 문제에 대한 완벽한 해결책 인 것 같습니다.


마커가 같은 건물에있는 경우 마커를 오프셋하는 것은 실제 해결책이 아닙니다. 원하는 것은 markerclusterer.js를 다음과 같이 수정하는 것입니다.

  1. MarkerClusterer 클래스에 프로토 타입 클릭 메소드를 다음과 같이 추가하십시오. 나중에 map initialize () 함수에서이를 재정의합니다.

    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    
  2. ClusterIcon 클래스에서 clusterclick 트리거 후에 다음 코드를 추가하십시오.

    // Trigger the clusterclick event.
    google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);
    
    var zoom = this.map_.getZoom();
    var maxZoom = markerClusterer.getMaxZoom();
    // if we have reached the maxZoom and there is more than 1 marker in this cluster
    // use our onClick method to popup a list of options
    if (zoom >= maxZoom && this.cluster_.markers_.length > 1) {
       return markerClusterer.onClickZoom(this);
    }
    
  3. 그런 다음 initialize () 함수에서 맵을 초기화하고 MarkerClusterer 객체를 선언합니다.

    markerCluster = new MarkerClusterer(map, markers);
    // onClickZoom OVERRIDE
    markerCluster.onClickZoom = function() { return multiChoice(markerCluster); }
    

    multiChoice ()는 InfoWindow를 선택할 수있는 옵션 목록과 함께 팝업하는 (아직 작성되지 않은) 함수입니다. markerClusterer 객체는 해당 클러스터에 몇 개의 마커가 있는지 결정하기 위해 필요하므로 함수에 전달됩니다. 예를 들면 다음과 같습니다.

    function multiChoice(mc) {
         var cluster = mc.clusters_;
         // if more than 1 point shares the same lat/long
         // the size of the cluster array will be 1 AND
         // the number of markers in the cluster will be > 1
         // REMEMBER: maxZoom was already reached and we can't zoom in anymore
         if (cluster.length == 1 && cluster[0].markers_.length > 1)
         {
              var markers = cluster[0].markers_;
              for (var i=0; i < markers.length; i++)
              {
                  // you'll probably want to generate your list of options here...
              }
    
              return false;
         }
    
         return true;
    }
    

나는 이것을 jQuery와 함께 사용했고 작업을 수행합니다.

var map;
var markers = [];
var infoWindow;

function initialize() {
    var center = new google.maps.LatLng(-29.6833300, 152.9333300);

    var mapOptions = {
        zoom: 5,
        center: center,
        panControl: false,
        zoomControl: false,
        mapTypeControl: false,
        scaleControl: false,
        streetViewControl: false,
        overviewMapControl: false,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }


    map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

    $.getJSON('jsonbackend.php', function(data) {
        infoWindow = new google.maps.InfoWindow();

        $.each(data, function(key, val) {
            if(val['LATITUDE']!='' && val['LONGITUDE']!='')
            {                
                // Set the coordonates of the new point
                var latLng = new google.maps.LatLng(val['LATITUDE'],val['LONGITUDE']);

                //Check Markers array for duplicate position and offset a little
                if(markers.length != 0) {
                    for (i=0; i < markers.length; i++) {
                        var existingMarker = markers[i];
                        var pos = existingMarker.getPosition();
                        if (latLng.equals(pos)) {
                            var a = 360.0 / markers.length;
                            var newLat = pos.lat() + -.00004 * Math.cos((+a*i) / 180 * Math.PI);  //x
                            var newLng = pos.lng() + -.00004 * Math.sin((+a*i) / 180 * Math.PI);  //Y
                            var latLng = new google.maps.LatLng(newLat,newLng);
                        }
                    }
                }

                // Initialize the new marker
                var marker = new google.maps.Marker({map: map, position: latLng, title: val['TITLE']});

                // The HTML that is shown in the window of each item (when the icon it's clicked)
                var html = "<div id='iwcontent'><h3>"+val['TITLE']+"</h3>"+
                "<strong>Address: </strong>"+val['ADDRESS']+", "+val['SUBURB']+", "+val['STATE']+", "+val['POSTCODE']+"<br>"+
                "</div>";

                // Binds the infoWindow to the point
                bindInfoWindow(marker, map, infoWindow, html);

                // Add the marker to the array
                markers.push(marker);
            }
        });

        // Make a cluster with the markers from the array
        var markerCluster = new MarkerClusterer(map, markers, { zoomOnClick: true, maxZoom: 15, gridSize: 20 });
    });
}

function markerOpen(markerid) {
    map.setZoom(22);
    map.panTo(markers[markerid].getPosition());
    google.maps.event.trigger(markers[markerid],'click');
    switchView('map');
}

google.maps.event.addDomListener(window, 'load', initialize);

Chaoley의 답변을 확장하여 좌표가 정확히 동일한 위치 (개체 lnglat속성 포함) 목록을 제공하여 원래 위치에서 약간 멀리 이동 시키는 기능을 구현했습니다 (객체 수정). 그런 다음 중심점 주위에 멋진 원을 형성합니다.

위도 (52deg North)에 대해 0.0003 도의 원 반경이 가장 효과적이며 킬로미터로 변환 할 때 위도와 경도의 차이를 보완해야한다는 것을 알았습니다. 여기에서 위도에 대한 대략적인 전환을 확인할 수 있습니다 .

var correctLocList = function (loclist) {
    var lng_radius = 0.0003,         // degrees of longitude separation
        lat_to_lng = 111.23 / 71.7,  // lat to long proportion in Warsaw
        angle = 0.5,                 // starting angle, in radians
        loclen = loclist.length,
        step = 2 * Math.PI / loclen,
        i,
        loc,
        lat_radius = lng_radius / lat_to_lng;
    for (i = 0; i < loclen; ++i) {
        loc = loclist[i];
        loc.lng = loc.lng + (Math.cos(angle) * lng_radius);
        loc.lat = loc.lat + (Math.sin(angle) * lat_radius);
        angle += step;
    }
};

@Ignatius의 가장 훌륭한 답변은 MarkerClustererPlus v2.0.7에서 작동하도록 업데이트되었습니다.

  1. MarkerClusterer 클래스에 프로토 타입 클릭 메소드를 다음과 같이 추가하십시오. 나중에 map initialize () 함수에서이를 재정의합니다.

    // BEGIN MODIFICATION (around line 715)
    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    // END MODIFICATION
    
  2. ClusterIcon 클래스에서 클릭 / 클러스터 클릭 트리거 후에 다음 코드를 추가하십시오.

    // EXISTING CODE (around line 143)
    google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
    google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name
    
    // BEGIN MODIFICATION
    var zoom = mc.getMap().getZoom();
    // Trying to pull this dynamically made the more zoomed in clusters not render
    // when then kind of made this useless. -NNC @ BNB
    // var maxZoom = mc.getMaxZoom();
    var maxZoom = 15;
    // if we have reached the maxZoom and there is more than 1 marker in this cluster
    // use our onClick method to popup a list of options
    if (zoom >= maxZoom && cClusterIcon.cluster_.markers_.length > 1) {
        return mc.onClick(cClusterIcon);
    }
    // END MODIFICATION
    
  3. 그런 다음 initialize () 함수에서 맵을 초기화하고 MarkerClusterer 객체를 선언합니다.

    markerCluster = new MarkerClusterer(map, markers);
    // onClick OVERRIDE
    markerCluster.onClick = function(clickedClusterIcon) { 
      return multiChoice(clickedClusterIcon.cluster_); 
    }
    

    multiChoice ()는 InfoWindow를 선택할 수있는 옵션 목록과 함께 팝업하는 (아직 작성되지 않은) 함수입니다. markerClusterer 객체는 해당 클러스터에 몇 개의 마커가 있는지 결정하기 위해 필요하므로 함수에 전달됩니다. 예를 들면 다음과 같습니다.

    function multiChoice(clickedCluster) {
      if (clickedCluster.getMarkers().length > 1)
      {
        // var markers = clickedCluster.getMarkers();
        // do something creative!
        return false;
      }
      return true;
    };
    

위의 답변은 더 우아하지만 실제로 실제로 정말 훌륭하게 작동하는 빠르고 더러운 방법을 발견했습니다. www.buildinglit.com 에서 실제로 확인할 수 있습니다.

내가 한 것은 내 genxml.php 페이지에 위도 및 경도에 임의의 오프셋을 추가하는 것이므로 맵으로 마커를 만들 때마다 오프셋으로 매번 약간 다른 결과를 반환합니다. 이것은 해킹처럼 들리지만 실제로는 마커가 겹치는 경우지도에서 클릭 할 수 있도록 임의의 방향으로 약간의 너지를 움직일 수 있습니다. 그것은 실제로 실제로 잘 작동합니다. 누가 그 복잡성을 다루고 어디서나 봄을 원하기 때문에 스파이더 방법보다 더 좋습니다. 마커를 선택하기 만하면됩니다. Nudging은 무작위로 완벽하게 작동합니다.

다음은 내 php_genxml.php에서 while 문 반복 노드 생성의 예입니다.

while ($row = @mysql_fetch_assoc($result)){ $offset = rand(0,1000)/10000000;
$offset2 = rand(0, 1000)/10000000;
$node = $dom->createElement("marker");
$newnode = $parnode->appendChild($node);
$newnode->setAttribute("name", $row['name']);
$newnode->setAttribute("address", $row['address']);
$newnode->setAttribute("lat", $row['lat'] + $offset);
$newnode->setAttribute("lng", $row['lng'] + $offset2);
$newnode->setAttribute("distance", $row['distance']);
$newnode->setAttribute("type", $row['type']);
$newnode->setAttribute("date", $row['date']);
$newnode->setAttribute("service", $row['service']);
$newnode->setAttribute("cost", $row['cost']);
$newnode->setAttribute("company", $company);

위도 및 경도 아래에 + 오프셋이 있습니다. 위의 두 변수에서. 마커를 거의 움직이지 않을 정도로 작은 소수를 얻으려면 무작위로 0,1000을 10000000으로 나누어야했습니다. 필요에 따라보다 정확한 변수를 얻으려면 해당 변수를 사용하십시오.


동일한 건물에 여러 서비스가있는 상황에서는 실제 지점에서 반경으로 마커를 약간 (예 : .001도) 오프셋 할 수 있습니다. 이것은 또한 좋은 시각 효과를 만들어 내야합니다.


V3 용 Marker Clusterer확인하십시오. 이 라이브러리는 근처 지점을 그룹 마커로 클러스터합니다. 클러스터를 클릭하면 맵이 확대됩니다. 그래도 확대했을 때 같은 지점에 마커와 동일한 문제가 있다고 생각합니다.


이것은 이번에는 JavaScript를 사용하는 Matthew Fox가 제안한 것과 유사한 임시 간결한 '빠르고 더러운'솔루션입니다.

자바 스크립트에서 당신은 모두 상쇄 작은 임의 추가하고 긴 모든 위치의 위도를 상쇄 할 수 예를

myLocation[i].Latitude+ = (Math.random() / 25000)

( 25000으로 나누면 충분한 분리가 가능하지만 특정 주소와 같은 정확한 위치에서 마커를 크게 이동 시키지는 않습니다.)

이렇게하면 서로 상쇄하는 작업이 상당히 잘 이루어 지지만, 확대 한 후에 만 ​​가능합니다. 축소해도 위치에 대한 여러 옵션이 여전히 명확하지 않습니다.


MarkerClustererPlus와 함께 작동하도록 업데이트되었습니다.

  google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
  google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name

  // BEGIN MODIFICATION
  var zoom = mc.getMap().getZoom();
  // Trying to pull this dynamically made the more zoomed in clusters not render
  // when then kind of made this useless. -NNC @ BNB
  // var maxZoom = mc.getMaxZoom();
  var maxZoom = 15;
  // if we have reached the maxZoom and there is more than 1 marker in this cluster
  // use our onClick method to popup a list of options
  if (zoom >= maxZoom && cClusterIcon.cluster_.markers_.length > 1) {
    var markers = cClusterIcon.cluster_.markers_;
    var a = 360.0 / markers.length;
    for (var i=0; i < markers.length; i++)
    {
        var pos = markers[i].getPosition();
        var newLat = pos.lat() + -.00004 * Math.cos((+a*i) / 180 * Math.PI);  // x
        var newLng = pos.lng() + -.00004 * Math.sin((+a*i) / 180 * Math.PI);  // Y
        var finalLatLng = new google.maps.LatLng(newLat,newLng);
        markers[i].setPosition(finalLatLng);
        markers[i].setMap(cClusterIcon.cluster_.map_);
    }
    cClusterIcon.hide();
    return ;
  }
  // END MODIFICATION

나는 간단한 해결책을 좋아하므로 여기에 내 것이 있습니다. lib를 수정하는 대신 관리하기가 더 어려워집니다. 당신은 단순히 이와 같은 이벤트를 볼 수 있습니다

google.maps.event.addListener(mc, "clusterclick", onClusterClick);

그럼 당신은 그것을 관리 할 수 ​​있습니다

function onClusterClick(cluster){
    var ms = cluster.getMarkers();

즉, 부트 스트랩을 사용하여 목록이있는 패널을 표시했습니다. 나는 "붐비는"장소에 거미를 쓰는 것보다 훨씬 더 편하고 사용하기 쉽다고 생각합니다. (클러스터 기회를 사용하는 경우 스파이더가되면 충돌이 발생합니다). 줌도 확인할 수 있습니다.

btw. 방금 전단지를 찾았고 훨씬 잘 작동하는 것 같습니다. 클러스터 및 spiderfy는 매우 유동적으로 작동합니다 http://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.10000.html 오픈 소스입니다.


이것을 확인하십시오 : https://github.com/plank/MarkerClusterer

이것은 동일한 위치에 여러 개의 마커가있을 때 클러스터 마커에 infoWindow를 갖도록 수정 된 MarkerCluster입니다.

작동 방식을 확인할 수 있습니다 : http://culturedays.ca/en/2013-activities


위에 주어진 답변을 확장하여 맵 객체를 초기화 할 때 maxZoom 옵션을 설정하십시오.


오프셋을주는 것은 사용자가 최대로 확대 할 때 마커를 멀리 만듭니다. 그래서 나는 그것을 달성 할 수있는 방법을 찾았습니다. 이것은 올바른 방법은 아니지만 매우 잘 작동했습니다.

// This code is in swift
for loop markers
{
//create marker
let mapMarker = GMSMarker()
mapMarker.groundAnchor = CGPosition(0.5, 0.5)
mapMarker.position = //set the CLLocation
//instead of setting marker.icon set the iconView
let image:UIIMage = UIIMage:init(named:"filename")
let imageView:UIImageView = UIImageView.init(frame:rect(0,0, ((image.width/2 * markerIndex) + image.width), image.height))
imageView.contentMode = .Right
imageView.image = image
mapMarker.iconView = imageView
mapMarker.map = mapView
}

상단에 표시하려는 마커 아이콘이 표시되도록 마커의 zIndex를 설정하십시오. 그렇지 않으면 자동 스와핑과 같은 마커에 애니메이션이 적용됩니다. 사용자가 마커를 탭하면 zIndex 스왑을 사용하여 zIndex를 처리하여 마커를 맨 위에 놓습니다.


그것으로 도망 치는 방법 .. [Swift]

    var clusterArray = [String]()
    var pinOffSet : Double = 0
    var pinLat = yourLat
    var pinLong = yourLong
    var location = pinLat + pinLong

새로운 마커를 만들려고합니까? clusterArray오프셋 확인 및 조작

 if(!clusterArray.contains(location)){
        clusterArray.append(location)
    } else {

        pinOffSet += 1
        let offWithIt = 0.00025 // reasonable offset with zoomLvl(14-16)
        switch pinOffSet {
        case 1 : pinLong = pinLong + offWithIt ; pinLat = pinLat + offWithIt
        case 2 : pinLong = pinLong + offWithIt ; pinLat = pinLat - offWithIt
        case 3 : pinLong = pinLong - offWithIt ; pinLat = pinLat - offWithIt
        case 4 : pinLong = pinLong - offWithIt ; pinLat = pinLat + offWithIt
        default : print(1)
        }


    }

결과

enter image description here


Matthew Fox의 교활한 천재적 답변에 덧붙여 마커 객체를 설정할 때 각 위도 및 경도에 작은 임의의 오프셋을 추가했습니다. 예를 들면 다음과 같습니다.

new LatLng(getLat()+getMarkerOffset(), getLng()+getMarkerOffset()),

private static double getMarkerOffset(){
    //add tiny random offset to keep markers from dropping on top of themselves
    double offset =Math.random()/4000;
    boolean isEven = ((int)(offset *400000)) %2 ==0;
    if (isEven) return  offset;
    else        return -offset;
}

Extending answers above, when you got joined strings, not added/subtracted position (e.g. "37.12340-0.00069"), convert your original lat/longitude to floats, e.g. using parseFloat(), then add or subtract corrections.

참고URL : https://stackoverflow.com/questions/3548920/google-maps-api-v3-multiple-markers-on-exact-same-spot

반응형