自定义地图图块叠加问题
Custom Map Tile Overlay Issues
我需要在 Google 地图提供的标准卫星图像上显示几百到一千张高分辨率航空照片。这些图像在地理上是分散的,所以我决定将一个图块服务器实现为一个通用 asp.net 处理程序(*.ashx 文件)。我将根据 Google 开发者网站上显示的地图来描述我的问题,网址如下 URL:
https://developers.google.com/maps/documentation/javascript/examples/maptype-overlay
一切正常 more-or-less,但我遇到以下两个问题:
1) 选择 "Satellite" 地图类型后,将鼠标悬停在该按钮上会生成一个带有名为 "Labels" 复选框的下拉菜单。我怎样才能在标题为 "Aerial Photographs" 的下拉菜单中添加另一个复选框来切换我的叠加层 on/off?我是否需要 hard-code JQuery 利用 Google 地图实现细节的黑客攻击,或者我可以通过 API?
来实现吗?
2) 我的 *.ashx 处理程序 return 要么是图像,要么是状态 204(无内容)(如果指定的图块不存在)。问题是 204 结果没有被缓存,所以每次我缩小并返回到同一位置时,我的服务器都会为客户端应该已经知道不存在的所有图块获取 re-hit。我没有看到它记录了瓷砖服务器应该 return 为这样的 "empty" 瓷砖,以便客户端可以缓存结果。如果没有特定位置的地图图块,我应该 return 怎么办?
谢谢。
鉴于这个问题没有得到回应,很明显稀疏切片服务器是一种不常见的做法。以下是这两个问题的解决方案(尽管可能很老套):
1) 如何在 "Satellite" 下拉菜单中添加一个复选框来切换我的地图图层?不幸的是,没有支持的方法来做到这一点,所以我想出了以下令人难以置信的骇人听闻的代码:
// Create a function to select the "Labels" checkbox
var getLabelButton = function() {
return $('.gm-style-mtc:last > div:last > div:last');
};
// Clone the "Labels" checkbox used to show/hide the hybrid map overlay
var labelBtn = getLabelButton();
var labelClone = labelBtn.clone();
// Change the display and hover text for the new button
labelClone.prop('title', Localizer.GetString('CustomImagery.Description'));
labelClone.find('label').html(Localizer.GetString('CustomImagery.Name'));
// Highlight the button when the client hovers the mouse over it
var checkbox = labelClone.children('span');
var prevBackColor = labelClone.css('background-color');
var prevBorderColor = checkbox .css('border-color');
labelClone.hover(function() {
labelClone.css('background-color', '#EBEBEB');
checkbox .css('border-color' , '#666');
}, function() {
labelClone.css('background-color', prevBackColor);
checkbox .css('border-color' , prevBorderColor);
});
// Set the checkmark image source to be the correct value, instead of transparent
var checkmark = checkbox .children('div');
var checkmarkImg = checkmark.children('img');
checkmarkImg.attr('src', 'https://maps.gstatic.com/mapfiles/mv/imgs8.png');
// Attach the new checkbox after the Labels checkbox
labelBtn.after(labelClone);
// Create a method to determine if the selected map type supports custom imagery
var mapTypesSupportingCustomImagery = [
google.maps.MapTypeId.SATELLITE,
google.maps.MapTypeId.HYBRID
];
var isImagerySupportedOnSelectedMapType = function() {
var mapTypeId = googleMap.getMapTypeId();
return (0 <= mapTypesSupportingCustomImagery.indexOf(mapTypeId));
};
// Show the checkmark and imagery if the initial map type supports it
if (isImagerySupportedOnSelectedMapType()) {
checkmark.css('display', '');
googleMap.overlayMapTypes.push(tileServer);
}
// Show/hide the checkmark and imagery when the user clicks on the checkbox
labelClone.on('click', function() {
var showImagery = (checkmark.css('display') === 'none');
if (showImagery) {
checkmark.css('display', '');
googleMap.overlayMapTypes.push(tileServer);
} else {
checkmark.css('display', 'none');
var tileServerIndex = googleMap.overlayMapTypes.indexOf(tileServer);
googleMap.overlayMapTypes.removeAt(tileServerIndex);
}
});
// Create a function that returns whether the custom imagery should be displayed
var displayCustomImagery = function() {
return (isImagerySupportedOnSelectedMapType() && checkmark.css('display') != 'none');
};
// Add an event listener to add the tile server when displaying satellite view
google.maps.event.addListener(googleMap, 'maptypeid_changed', function() {
var tileServerIndex = googleMap.overlayMapTypes.indexOf(tileServer);
if (displayCustomImagery()) {
if (tileServerIndex < 0) {
googleMap.overlayMapTypes.push(tileServer);
}
} else if (0 <= tileServerIndex) {
googleMap.overlayMapTypes.removeAt(tileServerIndex);
}
});
上面代码中,googleMap是地图对象,tileServer是我实现的一个google.maps.ImageMapType对象。
2) 我应该return 什么来表示一个空的瓷砖?
我对这个问题的解决方案更加清晰。我只是列出了服务器上所有图块的文件名,这些文件名是所请求图块的 Morton 编号的 base-4 编码。然后我将此列表作为从字符串到布尔值(始终为真)的字典发送给客户端。客户端在发出请求之前只需检查服务器是否包含地图图块,因此服务器不必担心要 return 什么(我将其保留为 return 如果出现 204 错误提出了无效的请求)。 javascript在getTileUrl方法中获取tile名称如下:
function(coord, zoom) {
// Return null if the zoom level is not supported
// NOTE: This should not be necessary, but minZoom and
// maxZoom parameters are ignored for image map types
if (zoom < minZoom || maxZoom < zoom) {
return null;
}
// Get the name of the map tile being requested
var tileName = '';
var y = coord.y << 1;
for (var shift = zoom - 1; 0 <= shift; --shift) {
var digit = (coord.x >>> shift) & 1;
digit |= ( y >>> shift) & 2;
tileName += digit;
}
// Return if the map tile being requested does not exist
if (!mapTiles[tileName]) {
return null;
}
// Return the url to the tile server
...
}
我需要在 Google 地图提供的标准卫星图像上显示几百到一千张高分辨率航空照片。这些图像在地理上是分散的,所以我决定将一个图块服务器实现为一个通用 asp.net 处理程序(*.ashx 文件)。我将根据 Google 开发者网站上显示的地图来描述我的问题,网址如下 URL:
https://developers.google.com/maps/documentation/javascript/examples/maptype-overlay
一切正常 more-or-less,但我遇到以下两个问题:
1) 选择 "Satellite" 地图类型后,将鼠标悬停在该按钮上会生成一个带有名为 "Labels" 复选框的下拉菜单。我怎样才能在标题为 "Aerial Photographs" 的下拉菜单中添加另一个复选框来切换我的叠加层 on/off?我是否需要 hard-code JQuery 利用 Google 地图实现细节的黑客攻击,或者我可以通过 API?
来实现吗?2) 我的 *.ashx 处理程序 return 要么是图像,要么是状态 204(无内容)(如果指定的图块不存在)。问题是 204 结果没有被缓存,所以每次我缩小并返回到同一位置时,我的服务器都会为客户端应该已经知道不存在的所有图块获取 re-hit。我没有看到它记录了瓷砖服务器应该 return 为这样的 "empty" 瓷砖,以便客户端可以缓存结果。如果没有特定位置的地图图块,我应该 return 怎么办?
谢谢。
鉴于这个问题没有得到回应,很明显稀疏切片服务器是一种不常见的做法。以下是这两个问题的解决方案(尽管可能很老套):
1) 如何在 "Satellite" 下拉菜单中添加一个复选框来切换我的地图图层?不幸的是,没有支持的方法来做到这一点,所以我想出了以下令人难以置信的骇人听闻的代码:
// Create a function to select the "Labels" checkbox
var getLabelButton = function() {
return $('.gm-style-mtc:last > div:last > div:last');
};
// Clone the "Labels" checkbox used to show/hide the hybrid map overlay
var labelBtn = getLabelButton();
var labelClone = labelBtn.clone();
// Change the display and hover text for the new button
labelClone.prop('title', Localizer.GetString('CustomImagery.Description'));
labelClone.find('label').html(Localizer.GetString('CustomImagery.Name'));
// Highlight the button when the client hovers the mouse over it
var checkbox = labelClone.children('span');
var prevBackColor = labelClone.css('background-color');
var prevBorderColor = checkbox .css('border-color');
labelClone.hover(function() {
labelClone.css('background-color', '#EBEBEB');
checkbox .css('border-color' , '#666');
}, function() {
labelClone.css('background-color', prevBackColor);
checkbox .css('border-color' , prevBorderColor);
});
// Set the checkmark image source to be the correct value, instead of transparent
var checkmark = checkbox .children('div');
var checkmarkImg = checkmark.children('img');
checkmarkImg.attr('src', 'https://maps.gstatic.com/mapfiles/mv/imgs8.png');
// Attach the new checkbox after the Labels checkbox
labelBtn.after(labelClone);
// Create a method to determine if the selected map type supports custom imagery
var mapTypesSupportingCustomImagery = [
google.maps.MapTypeId.SATELLITE,
google.maps.MapTypeId.HYBRID
];
var isImagerySupportedOnSelectedMapType = function() {
var mapTypeId = googleMap.getMapTypeId();
return (0 <= mapTypesSupportingCustomImagery.indexOf(mapTypeId));
};
// Show the checkmark and imagery if the initial map type supports it
if (isImagerySupportedOnSelectedMapType()) {
checkmark.css('display', '');
googleMap.overlayMapTypes.push(tileServer);
}
// Show/hide the checkmark and imagery when the user clicks on the checkbox
labelClone.on('click', function() {
var showImagery = (checkmark.css('display') === 'none');
if (showImagery) {
checkmark.css('display', '');
googleMap.overlayMapTypes.push(tileServer);
} else {
checkmark.css('display', 'none');
var tileServerIndex = googleMap.overlayMapTypes.indexOf(tileServer);
googleMap.overlayMapTypes.removeAt(tileServerIndex);
}
});
// Create a function that returns whether the custom imagery should be displayed
var displayCustomImagery = function() {
return (isImagerySupportedOnSelectedMapType() && checkmark.css('display') != 'none');
};
// Add an event listener to add the tile server when displaying satellite view
google.maps.event.addListener(googleMap, 'maptypeid_changed', function() {
var tileServerIndex = googleMap.overlayMapTypes.indexOf(tileServer);
if (displayCustomImagery()) {
if (tileServerIndex < 0) {
googleMap.overlayMapTypes.push(tileServer);
}
} else if (0 <= tileServerIndex) {
googleMap.overlayMapTypes.removeAt(tileServerIndex);
}
});
上面代码中,googleMap是地图对象,tileServer是我实现的一个google.maps.ImageMapType对象。
2) 我应该return 什么来表示一个空的瓷砖?
我对这个问题的解决方案更加清晰。我只是列出了服务器上所有图块的文件名,这些文件名是所请求图块的 Morton 编号的 base-4 编码。然后我将此列表作为从字符串到布尔值(始终为真)的字典发送给客户端。客户端在发出请求之前只需检查服务器是否包含地图图块,因此服务器不必担心要 return 什么(我将其保留为 return 如果出现 204 错误提出了无效的请求)。 javascript在getTileUrl方法中获取tile名称如下:
function(coord, zoom) {
// Return null if the zoom level is not supported
// NOTE: This should not be necessary, but minZoom and
// maxZoom parameters are ignored for image map types
if (zoom < minZoom || maxZoom < zoom) {
return null;
}
// Get the name of the map tile being requested
var tileName = '';
var y = coord.y << 1;
for (var shift = zoom - 1; 0 <= shift; --shift) {
var digit = (coord.x >>> shift) & 1;
digit |= ( y >>> shift) & 2;
tileName += digit;
}
// Return if the map tile being requested does not exist
if (!mapTiles[tileName]) {
return null;
}
// Return the url to the tile server
...
}