Вопрос по – Google Maps v3 ImageMapType Предотвращение переноса

3

Я пытаюсь повторить поведение, замеченное в:

http://forevermore.net/articles/photo-zoom/

Это позволяет панорамирование и масштабирование фотографии, но это ограничивает панорамирование до границ фотографии.

Пример выше с использованием кода Google Maps v2.

Кажется, мне нужно сделать следующее:

google.maps.event.addListener(map, 'dragend', function() 
{
    //Get bounds and not allow dragging

});

(Как видно здесь:Как ограничить панорамирование в Google Maps API V3?)

Моя проблема:

  • Images that will be panned/zoomed are dynamic in size, I want a generic solution (if possible)

Если невозможно найти общее решение, как определить правильные границы LatLon для изображения?

Вот что у меня так далеко:

var customTypeOptions = {
  getTileUrl: function(coord, zoom) {
        return "img/earth_tiles/tile-" + zoom + "-" + coord.x + "-" + coord.y + ".jpg";
  },
  tileSize: new google.maps.Size(256, 256),
  maxZoom: 6,
  minZoom: 0,
  name: "Custom Map Type"
};

var customMapType = new google.maps.ImageMapType(customTypeOptions);


jQuery(document).ready(function(){
  var myLatlng = new google.maps.LatLng(0, 0);
  var myOptions = {
    center: myLatlng,
    zoom: 3,
     disableDefaultUI: true,
     zoomControl: true
  };

  var map = new google.maps.Map(document.getElementById("map"), myOptions);
    map.mapTypes.set('custom', customMapType);
    map.setMapTypeId('custom');
});

Он отлично работает, он просто позволяет пользователю прокручивать за пределами фотографии.

спасибо за фрагмент кода, но вы также можете разместить ссылку на свою текущую рабочую страницу? Трудно без проблем снимать без реальных изображений Suvi Vignarajah

Ваш Ответ

2   ответа
4

Честно говоря, я не думаю, что использование Google Maps действительно правильный подход. Да, вы, вероятно, можете взломать его на работу, но это не совсем то, для чего предназначена библиотека. (Что-то вроде использования молотка для установки круглого винта в треугольное отверстие.)

Кроме того, вы подчиняетесь как Googleограничительные условия (твой сайтmust быть публичным) и их новыеценообразование, что означает более25,000 просмотров страниц / день будет стоить вам & # x2014; а такжеyou're not even using the maps.

Вместо этого, почему бы не использовать библиотеку, предназначенную для мозаичного масштабирования очень больших изображений?PanoJS3 кажется, отвечает всем требованиям.

PanoJS3 - An interactive JavaScript widget for panning and zooming a panoramic image stitched together dynamically from smaller tiles. This widget can be used for viewing images that are much larger than the available space in the browser viewport. Examples include panoramas, maps or high resolution document scans.

PanoJS3 supports native navigation on most popular platforms:

  • PCs (zooming using mouse scroll, same as Google Maps)
  • Macs (2D panning with the mouse scroll or touch panels)
  • Mobile devices with touch interfaces: iOS and Android (supports pintch to zoom and pan gestures)
  • Phones and tablets (scales controls according to the screen size)
Я пробовал Pano JS, у него нет возможности ставить точки на Mac, которые можно нажимать. Chris Muench
Да, Pano не поддерживает это (в вашем вопросе не упоминалось, что вам нужна эта функциональность). Конечно, я все же был бы склонен добавить поддержку PanoJS для кликабельных точек по сравнению со взломом Карт Google.
@ Крис, вы также можете попробовать OpenLayers.
спасибо @josh за ссылку на буклет! Я "пропустил" что ваша точка намеренно :) Функциональность карты хороша, так как каждый имеет функцию полигона и маркера. что нужно ОП.
@ Томас - эй. & GT; 720 КБminified JS. OpenLayers старый, медленный и раздутый.Leaflet (80kB) - намного лучший выбор для карт. В любом случае, я думаю, что вы упустили мою точку зрения: вы взламываете библиотеку карт для отображения изображения, а не используете библиотекуdesigned для отображения негабаритных изображений.
3

Я сгорел от JSFiddle, удалив мою демонстрацию, поэтому я переработал решение, и я публикую демо ниже со встроенным предварительным просмотром SO. Но JSFiddle, возможно, удобнее редактировать, поэтому я тоже добавил туда код.Демо в JSFiddle

Исходное решение назначает координаты изображения +/- 50 градусов, но я не могу воспроизвести это поведение. Этот новый код использует +/- 85 градусов. широта и +/- 180 долготы с проекцией Меркатора по умолчанию.

Я не проверил новое решение полностью, поэтому используйте его с осторожностью. Особенно неприятная ошибка, которую я обнаружил, заключалась в том, что при использованииsetCenter() проверка внутри границ привела к переполнению стека. Это было решено путем замены наpanTo(), Мои основные наблюдения:

  1. First, the solution is hacky. As the latitude increases, so does the space it takes up on the screen. What I do is re-compute the pixel midpoint between the limits of the map bounds when the map is moved, rather than using a geometrical conversion. To make this hack work, the acceptable bounds are dictated by the map's div's height.

  2. On the other hand, longitude behaves normally. The trick with longitude is that it repeats, so markers and other items showing up at this limit will be duplicated. I think the way around this problem is to transform the longitude coordinates far from this boundary (as in the original solution transforming longitudes to +/- 50 degrees). Unfortunately I'm unable right now to reproduce this coordinate transformation.

"use strict";

// observations
//
// map does wrap around at longitudes +/-180; however, tile display can be
// manipulated to only show up once.
//
// markers placed around longiudes +/-180 will show up twice. Not sure how to
// prevent this.

var divHeight = document.getElementById("map-canvas").clientHeight;

var TILE_SIZE = 256;

var map;
var allowedBounds;

var bounds;
var sw;
var ne;
var width;
var height;

// https://developers.google.com/maps/documentation/javascript/examples/map-coordinates

function degreesToRadians(deg) {
  return deg * (Math.PI / 180);
}

function radiansToDegrees(rad) {
  return rad / (Math.PI / 180);
}

function bound(value, opt_min, opt_max) {
  if (opt_min != null) value = Math.max(value, opt_min);
  if (opt_max != null) value = Math.min(value, opt_max);
  return value;
}

function fromLatLngToPoint(latLng, map) {
  var point = new google.maps.Point(0, 0);
  var origin = new google.maps.Point(TILE_SIZE/2, TILE_SIZE/2);

  var pixelsPerLonDegree_ = TILE_SIZE / 360;
  var pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);

  point.x = origin.x + latLng.lng() * pixelsPerLonDegree_;

  // Truncating to 0.9999 effectively limits latitude to 89.189. This is
  // about a third of a tile past the edge of the world tile.
  var siny = bound(Math.sin(degreesToRadians(latLng.lat())), -0.9999,
                   0.9999);
  point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) *
    -pixelsPerLonRadian_;
  return point;
}

function fromPointToLatLng(point) {
  // value from 0 to 256
  var pixelOrigin_ = new google.maps.Point(TILE_SIZE / 2,
                                           TILE_SIZE / 2);
  var origin = new google.maps.Point(TILE_SIZE/2, TILE_SIZE/2);

  var pixelsPerLonDegree_ = TILE_SIZE / 360;
  var pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);

  var origin = pixelOrigin_;
  var lng = (point.x - origin.x) / pixelsPerLonDegree_;
  var latRadians = (point.y - origin.y) / -pixelsPerLonRadian_;
  var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) -
                             Math.PI / 2);
  return new google.maps.LatLng(lat, lng);
};

function midpointLat() {
  var tileFactor = 1 << map.getZoom();
  var midpointFromTop = divHeight / tileFactor / 2;
  return fromPointToLatLng(new google.maps.Point(0, midpointFromTop)).lat();
}

function addMarker(lat, lng) {
  new google.maps.Marker({
    position: new google.maps.LatLng(lat, lng),
  }).setMap(map);
}

function addIcon(lat, lng, url) {
  new google.maps.Marker({
    position: new google.maps.LatLng(lat, lng),
    icon: url,
  }).setMap(map);
}

function updateEdge() {
  bounds = map.getBounds();
  
  sw = bounds.getSouthWest();
  ne = bounds.getNorthEast();

  var swLng = sw.lng();
  var swLat = sw.lat();

  var neLng = ne.lng();
  var neLat = ne.lat();
  
  if (swLng > neLng) {
    swLng -= 360;
  } 
  width = neLng - swLng;
  
  var left = Math.min(-180+(width/2),-0.000001);
  var right = Math.max(180-(width/2),0.000001);
  
  var divCenterLat = fromPointToLatLng(new google.maps.Point(0, divHeight)).lat();
  var currentZoom = map.getZoom();

  var top = midpointLat();
  var bottom = -midpointLat();
  
  allowedBounds = new google.maps.LatLngBounds(
    new google.maps.LatLng(bottom,left),
    new google.maps.LatLng(top,right));

}

function boxIn() {
  if (allowedBounds.contains(map.getCenter())) {
    return;
  } else {
    var mapCenter = map.getCenter();
    var X = mapCenter.lng();
    var Y = mapCenter.lat();

    var AmaxX = allowedBounds.getNorthEast().lng();
    var AmaxY = allowedBounds.getNorthEast().lat();
    var AminX = allowedBounds.getSouthWest().lng();
    var AminY = allowedBounds.getSouthWest().lat();

    if (X < AminX) {
      X = AminX;
    }
    if (X > AmaxX) {
      X = AmaxX;
    }
    if (Y < AminY) {
      Y = AminY;
    }
    if (Y > AmaxY) {
      Y = AmaxY;
    }

    map.panTo(new google.maps.LatLng(Y, X));
  }
}

var moonTypeOptions = {
  getTileUrl: function(coord, zoom) {
    var normalizedCoord = getNormalizedCoord(coord, zoom);
    if (!normalizedCoord) {
      return null;
    }
    var bound = Math.pow(2, zoom);
    return 'http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' +
      
    '/' + zoom + '/' + normalizedCoord.x + '/' +  
      (bound - normalizedCoord.y - 1) + '.jpg';
  },
  tileSize: new google.maps.Size(256, 256),
  maxZoom: 9,
  minZoom: 0,
  radius: 100,
  name: 'Moon'
};

var moonMapType = new google.maps.ImageMapType(moonTypeOptions);


// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
  var y = coord.y;
  var x = coord.x;

  // tile range in one direction range is dependent on zoom level
  // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
  var tileRange = 1 << zoom;

  // don't repeat across y-axis (vertically)
  if (y < 0 || y >= tileRange) {
    return null;
  }

  if (x < 0 || x >= tileRange) {
    // ORIGINAL LINE to repeat across x-axis
    // x = (x % tileRange + tileRange) % tileRange;

    // in reality, do not want repeated tiles
    return null;
  }

  return {
    x: x,
    y: y
  };
}

function initialize() {
  var myLatlng = new google.maps.LatLng(0, 0);
  var mapOptions = {
    center: myLatlng,
    zoom: 1,
    // streetViewControl: false,
    disableDefaultUI: true,
  };

  map = new google.maps.Map(document.getElementById('map-canvas'),
                            mapOptions);
  map.mapTypes.set('moon', moonMapType);
  map.setMapTypeId('moon');


  google.maps.event.addListener(map, 'tilesloaded', function() {
    updateEdge();
  });
  
  google.maps.event.addListener(map, 'zoom_changed', function() {
    updateEdge();
    boxIn();
  });

  google.maps.event.addListener(map, 'center_changed', function() {
    boxIn();
  });

  google.maps.event.addListener(map, 'click', function(e) {
    console.log("map clicked at: " + e.latLng.lat() + "," + e.latLng.lng());
  });

  updateEdge();

  addIcon(0, 0, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=O|00FF00|000000");

  addIcon(85.1, 179, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=TR|00FF00|000000");

  addIcon(-85.1, -179, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=BL|00FF00|000000");

  addIcon(20.1, 9, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=2|00FF00|000000");
  addIcon(40.1, 9, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=4|00FF00|000000");
  addIcon(60.1, 9, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=6|00FF00|000000");
  addIcon(80.1, 9, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=8|00FF00|000000");
  addIcon(85.1, 9, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=8|00FF00|000000");
  addIcon(-85.1, 9, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=8|00FF00|000000");

  addIcon(60.1, -179, "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=Y|00FF00|000000");
}

google.maps.event.addDomListener(window, 'load', initialize);
<!DOCTYPE html>
<html>
  <head>
    <title>Image map types</title>
    <style>
      html, body, #map-canvas {
      height: 450px;
      width: 450px;
        margin: 0px;
        padding: 0px;
      }
    </style>
    <script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
  </head>
  <body>
    <div id="map-canvas"></div>

    <script src="moon.js"></script>
  </body>
</html>

ОРИГИНАЛ 2012 РЕШЕНИЕ:

Я объединил вечную систему координат идокументация & APOS; s Пример ImageMapTypes дляповерхность луны

Первоначально, демонстрация начинается с увеличения 0, чтобы дать представление обо всем изображении. После увеличения панорамирование будет ограничено прямоугольником с соотношением сторон, определяемым (W) idth и (H) восемью текстовыми полями. Для этой демонстрации только это соотношениеW/H, или жеH/W это важно.

Я предполагаю, что ваши изображения будут похожи на оба вышеперечисленных, умещаются в ячейки размером 256x256 и имеют «черную рамку»; вокруг изображения. Кроме того, изображение простирается вплоть до плиток & apos; край более длинного измерения. Если нет (но, по крайней мере, изображение центрировано), видимую область можно изменить вlatbound а такжеlngbound переменные, которые соответствуют координатной сетке(-50,50) x (-50,50) определено во веки веков.

В демоверсии, когда увеличено и W & gt; H, соотношение сторон длиннее по горизонтали: вся ширина лунной поверхности видна вокруг центра, и верхние / нижние горизонтальные полосы будут заблокированы. То есть темные кратеры вверху и внизу полного изображения не будут доступны при увеличении, превышающем 0. Визуализация реального изображения с черными полями, некоторые из «черной области» может по-прежнему отображаться при увеличении 1, площадь которого уменьшается с увеличением уровня масштабирования.

Когда увеличено и H & gt; W, достижимая область простирается вертикально. Темные кратеры непосредственно над и под центром всей поверхности будут доступны, но левые / правые области - нет. В этой демонстрации соотношение сторон изменяется наupdateEdge чтение текстовых полей; нажав на кнопку «Установить вызовы»updateEdge.

Большая часть усилий в коде состояла в том, чтобы предотвратить перемещение за пределы желаемой области отображения. И навсегда, и метод "Как ограничить панорамирование"? были скачками или вызывали ошибки при тестировании, поэтому я придумал модифицированную версиюОграничение диапазона который учитывает текущий уровень масштабирования путем измерения ширины и высоты экрана:

  function updateEdge() {
    imageWidth = parseInt(document.getElementById("imgWidth").value);
    imageHeight = parseInt(document.getElementById("imgHeight").value);
    if(imageWidth > imageHeight) {
      widthPercent = 100;
      heightPercent = imageHeight / imageWidth * 100;
    }
    else {
      heightPercent = 100;
      widthPercent = imageWidth / imageHeight * 100;
    }

    latbound = heightPercent/2.0;
    lngbound = widthPercent/2.0;

    var bounds = map.getBounds();
    var sw = bounds.getSouthWest();
    var ne = bounds.getNorthEast();
    var width = ne.lng() - sw.lng();
    var height = ne.lat() - sw.lat();

    var bottom = Math.min(-latbound+(height/2),-0.000001);
    var left = Math.min(-lngbound+(width/2),-0.000001);
    var top = Math.max(latbound-(height/2),0.000001);
    var right = Math.max(lngbound-(width/2),0.000001);

    allowedBounds = new google.maps.LatLngBounds(
      new google.maps.LatLng(bottom,left),
      new google.maps.LatLng(top,right));
  }


google.maps.event.addListener(map, 'tilesloaded', function() {
    updateEdge();
});
google.maps.event.addListener(map, 'zoom_changed', function() {
    updateEdge();
    boxIn();
});

google.maps.event.addListener(map, 'center_changed', function() {
    boxIn();
});

function boxIn() {
    if (allowedBounds.contains(map.getCenter())) {
        return;
    }
    else {
        var mapCenter = map.getCenter();
        var X = mapCenter.lng();
        var Y = mapCenter.lat();

        var AmaxX = allowedBounds.getNorthEast().lng();
        var AmaxY = allowedBounds.getNorthEast().lat();
        var AminX = allowedBounds.getSouthWest().lng();
        var AminY = allowedBounds.getSouthWest().lat();

        if (X < AminX) {
            X = AminX;
        }
        if (X > AmaxX) {
            X = AmaxX;
        }
        if (Y < AminY) {
            Y = AminY;
        }
        if (Y > AmaxY) {
            Y = AmaxY;
        }

        map.setCenter(new google.maps.LatLng(Y, X));
    }
}

Код проекции и выборки плитки существенно не отличается от своих источников.

демо не найдено 404 :(
@agungyuliaji Я переработал решение, но оно не совпадает с оригиналом.

Похожие вопросы