我试图复制在以下所见的行为:
http://forevermore.net/articles/photo-zoom/
它允许平移和缩放照片,但它将平移限制在照片的边界。
以上示例使用google maps v2 code。
似乎我必须做以下事情:
google.maps.event.addListener(map, 'dragend', function()
{
//Get bounds and not allow dragging
});
(如此处所示:How do I limit panning in Google maps API V3?)
我的问题是:
如果无法使用通用解决方案,如何确定图像的正确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');
});
它工作正常,它只允许用户滚动到照片之外。
答案 0 :(得分:4)
说实话,我不认为使用谷歌地图真的是正确的方法。是的,你可能会破解它的工作,但这不是图书馆的意图。 (关于使用锤子将圆形螺钉装入三角形孔的事情。)
此外,您自己要同时使用Google的restrictive terms(您的网站必须公开)和新的pricing,这意味着超过25,000个网页浏览量/天将花费你 - 而你甚至没有使用地图。
相反,为什么不使用专为非常大的图像平铺缩放而设计的库? PanoJS3似乎符合要求。
PanoJS3 - 一个交互式JavaScript小部件,用于平移和缩放从较小的图块动态拼接在一起的全景图像。此小组件可用于查看比浏览器视口中的可用空间大得多的图像。示例包括全景图,地图或高分辨率文档扫描。
PanoJS3支持大多数流行平台上的原生导航:
- 个人电脑(使用鼠标滚动缩放,与谷歌地图相同)
- Macs(使用鼠标滚动或触摸面板进行2D平移)
- 带触摸界面的移动设备:iOS和Android(支持pintch缩放和平移手势)
- 手机和平板电脑(根据屏幕尺寸控制比例)
答案 1 :(得分:3)
我被JSFiddle焚烧删除了我的演示,所以我重新设计了解决方案,并使用SO的内置预览发布了下面的演示。但JSFiddle可以说更方便编辑,所以我也在那里添加了代码。 Demo in JSFiddle
原始解决方案将图像的坐标指定为+/- 50度,但我无法重现此行为。这个新代码使用+/- 85度。使用默认墨卡托投影的纬度和+/- 180经度。
我没有彻底测试新解决方案,因此请谨慎使用。我发现一个特别讨厌的错误是在边界检查中使用setCenter()
导致堆栈溢出。它通过将其替换为panTo()
来解决。我的主要观察结果是:
首先,解决方案是hacky。随着纬度的增加,它在屏幕上占据的空间也会增加。我所做的是在移动地图时重新计算地图边界限制之间的像素中点,而不是使用几何转换。为了使这个黑客工作,可接受的界限由地图的div的高度决定。
另一方面,经度表现正常。经度的诀窍在于它会重复,因此显示在此限制的标记和其他项目将被复制。我认为解决这个问题的方法是将经度坐标转换为远离此边界(如原始解决方案将经度转换为+/- 50度)。不幸的是,我现在无法重现这种坐标转换。
"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>
ORIGINAL 2012解决方案:
我结合了documentation's
的永远更多坐标系和moon's surface ImageMapTypes示例最初,演示从缩放0开始,以了解整个图像。放大后,平移将限制为矩形,其宽高比由(W)宽度和(H)八个文本框定义。对于此演示,只有这个比例W/H
或H/W
很重要。
我假设您的图像与上述两种图像类似,适合256x256图块,图像周围有“黑色边框”。此外,图像一直延伸到较长尺寸的瓷砖边缘。如果不是(但至少图像居中),则可以在latbound
和lngbound
变量中修改可视区域,这些变量对应于永久定义的坐标网格(-50,50) x (-50,50)
。 / p>
在演示中,当放大并且W&gt; H,纵横比水平较长:月球表面的整个宽度在中心周围可见,顶部/底部水平条将被阻挡。也就是说,在完整图像的顶部和底部的黑色陨石坑将无法在0以上的变焦处到达。使用黑色边框可视化实际图像时,某些“黑色区域”可能仍会在缩放1处显示,其面积减小随着缩放级别的增加。
放大时,H&gt; W,可到达区域垂直延伸。直接在整个表面中心上方和下方的黑暗陨石坑将是可到达的,但是左/右区域不是。在此演示中,宽高比由updateEdge
读取文本框改变;点击设置来电updateEdge
。
代码中的大部分工作都是为了防止移动到所需显示区域之外。当我测试时,永远更多的方法和“我如何限制平移”都是跳跃或导致错误,所以我想出了Range Limiting的修改版本,它通过测量屏幕宽度考虑了当前的缩放级别。高度:
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));
}
}
投影和图块提取代码的来源没有明显改变。