问题描述
我负责维护英格兰西南部步行俱乐部的网站,该网站显示了即将进行的步行活动列表。每个步行条目都有一个用于步行开始的操作系统网格参考,以及一个步行者可以输入到其卫星导航设备的英国邮政编码,以帮助他们开车到步行的起点。我目前使用 Nearby UK 的 API 获取每次步行的起点网格参考的最近邮政编码,这也为我提供了从我的网格参考到邮政编码中心的罗盘方位和距离,在一个国家地区可以是从步行开始一英里或更远。根据这些,我计算出邮政编码中心的网格参考,然后我可以将这两个点显示为操作系统地图上的标记 - 到目前为止,一切都很好。
最近完成了从 OS Open Space 到 OS Data Hub 的迁移,我想知道我是否还可以使用 OS Data Hub 为我提供距网格参考最近的邮政编码,以及从一个到另一个的方位角和距离,或者邮政编码中心的网格参考,而不需要为此使用 Nearby API。
大约一个月前,我向 Ordnance Survey 的客户成功团队询问了这个问题,但他们还没有提供任何帮助。我还尝试了各种使用英国邮政编码数据库的方法,该数据库列出了每个英国邮政编码及其东坐标和北坐标,但在整个列表中搜索最近的坐标,使用毕达哥拉斯计算到步行起点,需要几分钟。这可能是因为我必须使用我们的 walks 数据库进行搜索,该数据库是用 Visual Basic 编写的,但那是另一回事了。
任何关于如何从 OS Data Hub 获取最近的邮政编码及其位置的指针,对于给定的网格参考,都非常受欢迎。
解决方法
例如,您可以找到最近的邮政编码,只要它的中心距离不超过 1 公里
https://api.os.uk/search/names/v1/nearest?point=440200,458300&radius=1000&fq=LOCAL_TYPE:Postcode&key=yourkey
超过 1 公里,您需要在原点周围的外圈中进一步搜索
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Basic Map</title>
<link rel="stylesheet" href="https://labs.os.uk/public/os-api-branding/v0.2.0/os-api-branding.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id="map"></div>
<script src="https://labs.os.uk/public/os-api-branding/v0.2.0/os-api-branding.js"></script>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<script>
var apiKey = 'ufArYa1UUPHJcOYbiJDaKkA7Fb4oCkEs';
var serviceUrl = 'https://api.os.uk/maps/raster/v1/zxy';
// Setup the EPSG:27700 (British National Grid) projection.
proj4.defs("EPSG:27700","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs");
ol.proj.proj4.register(proj4);
var tilegrid = new ol.tilegrid.TileGrid({
resolutions: [ 896.0,448.0,224.0,112.0,56.0,28.0,14.0,7.0,3.5,1.75 ],origin: [ -238375.0,1376256.0 ]
});
var gridReference = new ol.Feature();
gridReference.setStyle(
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,fill: new ol.style.Fill({
color: 'red'
})
}),text: new ol.style.Text({
font: 'bold 14px sans-serif',offsetY: -6,textBaseline: 'bottom'
})
})
);
var postcode = new ol.Feature();
postcode.setStyle(
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,fill: new ol.style.Fill({
color: 'blue'
})
}),textBaseline: 'bottom'
})
})
);
// Initialize the map object.
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: serviceUrl + '/Road_27700/{z}/{x}/{y}.png?key=' + apiKey,projection: 'EPSG:27700',tileGrid: tilegrid
})
}),new ol.layer.Vector({
source: new ol.source.Vector({
features: [gridReference,postcode]
})
})
],target: 'map',view: new ol.View({
projection: 'EPSG:27700',extent: [ -238375.0,0.0,900000.0,1376256.0 ],resolutions: tilegrid.getResolutions(),minZoom: 0,maxZoom: 9,center: [ 337297,503695 ],zoom: 7
})
});
map.on('singleclick',function(evt) {
gridReference.setGeometry(new ol.geom.Point(evt.coordinate));
postcode.setGeometry(undefined);
var x = (Math.round(evt.coordinate[0]/10)/10) + 10000;
var y = (Math.round(evt.coordinate[1]/10)/10) + 5000;
var a1y = (4 - (Math.floor(y/5000)%5))*5;
var a2y = (4 - (Math.floor(y/1000)%5))*5;
var y1 = Math.floor(y/100)%10;
var y2 = Math.floor(y/10)%10;
var y3 = Math.floor(y)%10;
a1y += (Math.floor(x/5000)%5);
a2y += (Math.floor(x/1000)%5);
var x1 = Math.floor(x/100)%10;
var x2 = Math.floor(x/10)%10;
var x3 = Math.floor(x)%10;
var grid500km = String.fromCharCode(a1y + Math.floor((a1y+17)/25) + "A".charCodeAt(0));
var grid100km = grid500km + String.fromCharCode(a2y + Math.floor((a2y+17)/25) + "A".charCodeAt(0));
var gridText = grid100km + x1 + x2 + x3 + y1 + y2 + y3;
gridReference.getStyle().getText().setText(gridText);
var minDistSq = Infinity;
var postcodeCoord,postcodeText;
var radius = 0;
tryPoints([evt.coordinate]);
function tryPoints(coordinates) {
var promises = [];
coordinates.forEach(function(coordinate) {
promises.push(
fetch(
'https://api.os.uk/search/names/v1/nearest?point=' +
coordinate[0].toFixed(2) + ',' + coordinate[1].toFixed(2) +
'&radius=1000&fq=LOCAL_TYPE:Postcode&key=' + apiKey
).then(function(response) {
return response.json();
})
);
});
Promise.all(promises).then(function(results) {
results.forEach(function(result) {
if (result.results && result.results.length > 0) {
var entry = result.results[0]['GAZETTEER_ENTRY'];
var dx = entry['GEOMETRY_X'] - evt.coordinate[0];
var dy = entry['GEOMETRY_Y'] - evt.coordinate[1];
var distSq = dx * dx + dy * dy;
if (distSq < minDistSq) {
minDistSq = distSq;
postcodeCoord = [entry['GEOMETRY_X'],entry['GEOMETRY_Y']];
postcodeText = entry['NAME1'];
}
}
});
if (postcodeCoord) {
postcode.setGeometry(new ol.geom.Point(postcodeCoord));
postcode.getStyle().getText().setText(postcodeText);
} else if (radius < 4) {
radius++;
var outerCircle = ol.geom.Polygon.fromCircle(new ol.geom.Circle(evt.coordinate,radius * 1000),16 * radius);
tryPoints(outerCircle.getCoordinates()[0].slice(0,-1));
}
});
}
});
</script>
</body>
</html>