openlayers是开源地图,地图瓦片清晰度和信息都没有商业地图那么完善,在此基础上,套上谷歌地图这一层皮就完美了!
墨卡托投影
OpenLayers 采用的是墨卡托投影,Google Maps也是墨卡托投影,那么在openlayers上显示的坐标和google应该可以是同一种坐标,设备上传的是gps经纬度,首先要把gps的位置显示在ol上,必须转换为ol的坐标系
// 广州珠江帝景坐标,源gps经纬度var lonLat = [113.3219361305, 23.1067781880];// 转换为ol坐标var coord = ol.proj.transform(lonLat, 'EPSG:4326', 'EPSG:3857');// 或者, 下面默认转3857坐标var coord = ol.proj.fromLonLat(lonLat);
谷歌地图Tile
在ol中,声明瓦片的方法很简单
var osmLayer = new ol.layer.Tile({ source: new ol.source.OSM()})
开源地图允许使用第三方瓦片,例如google map,百度地图,下面介绍google map的瓦片
谷歌国际化地图瓦片
var osmLayer = new ol.layer.Tile({ source: new ol.source.TileImage({url: 'http://maps.google.com/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m3!1e0!2sm!3i375060738!3m9!2spl!3sUS!5e18!12m1!1e47!12m3!1e37!2m1!1ssmartmaps!4e0'})})
谷歌地域化地图瓦片
这种瓦片可以在不同国家显示不同语言,很强大
var osmLayer = new ol.layer.Tile({ source: new ol.source.OSM({ url: 'http://mt{0-3}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attributions: [ new ol.Attribution({ html: '© Google' }), new ol.Attribution({ html: '<a href="https://developers.google.com/maps/terms">Terms of Use.</a>' }) ] })})
完美套上谷歌地图瓦片后,测试了一下,在中国同样的坐标系,在ol和google瓦片是有偏差的,美国,日本,韩国,香港,台湾暂时没发现这问题
美国华盛顿的坐标,上面是套的google地图,下面是ol地图
在中国,坐标明显有了偏移,红色的位置才是正确的
那么需要针对中国坐标稍作调整,可以看出,偏移量不是很大,那么在中国区域每次定位时候,只需要把偏移差给计算进去就行了
在调整坐标时候,发现坐标x和y偏移量有固定的偏差,x偏差约600,y偏差约-300,转换方法如下,after是正确的坐标
var before = ol.proj.transform(lonLat, 'EPSG:4326', 'EPSG:3857');var offsetX = 600; var offsetY = -300;var after = [before[0] + offsetX, before[1] + offsetY];
最后展示的结果
中国地图纠正
上面解决了偏移问题,那么现在面临了另一个问题,如何判断GPS的经纬度是在中国区域呢?
调用google api,需翻墙,有访问限制
https://maps.googleapis.com/maps/api/geocode/json?latlng=11.2742848,75.8013801
$.get("https://maps.googleapis.com/maps/api/geocode/json?latlng=23.1082829028,113.3215552569", function(location) { console.log(location); var countryL = location.results[0].address_components[4].long_name; var countryS = location.results[0].address_components[4].short_name console.log(countryS) // CN})
缺点:不同经纬度返回数组不确定,这样获取对应的国家代码也烦
调用 geoNames api,需翻墙,也有访问限制
http://ws.geonames.org/countryCodeJSON?lat=23.1067781880&lng=113.3219361305&username=kobe
$.get("http://ws.geonames.org/countryCodeJSON?lat=23.1067781880&lng=113.3219361305&username=kobe", function(location) { var countryCode = location.countryCode; // CN})
返回结果很简单,比google api方便处理
{"languages":"zh-CN,yue,wuu,dta,ug,za","distance":"0","countryCode":"CN","countryName":"China"}
turf库
turf这个库也是很牛逼,开发地图利器,坐标处理很强大,判断点是否在区域内
var linestring1 = turf.linestring([ [4.9020, 52.3667], [4.9030, 52.3667], [4.9040, 52.3667], [4.9050, 52.3667]]);var pt1 = turf.point([4.9040, 52.3667]);// 在区域内则返回对象,不在返回undefinedvar intersection = turf.intersect(pt1, linestring1);
openlayers识别
综合上面的限制,觉得解决方法还不是很完美,想到了一个办法,通过地图坐标,绘制中国地图多边形,再判断坐标点是否在多边形内。
绘制多边形
在官方例子中发现了一个例子,很强大,把各个国家的范围区分出来了,对应的国家的geojson坐标地址,可以这里入口,把中国的区域坐标给取出来,绘制中国区域代码
var countrySource = new ol.source.Vector({ url: './china.geojson', format: new ol.format.GeoJSON()});var countryLayer = new ol.layer.Vector({ source: countrySource})// 设置名字,容易获取countryLayer.set("name", "chinaLayer");var map = new ol.Map({ target: 'map', layers: [ osmLayer, countryLayer ], view: new ol.View({ center: [0, 0], zoom: 2 })});
判断点是否在区域内
获取区域的多边形,然后使用intersectsCoordinate接口即可
map.getLayers().forEach(function(layer) { if (layer.get("name") == 'chinaLayer') { var source = layer.getSource(); // source是url加载,必須异步判断 source.on("change", function(evt) { var src = evt.target; if(src.getState() === 'ready'){ var features = source.getFeatures(); console.log(features[0].getGeometry()) var guangzhouLonLat = [113.3215552569, 23.1082829028]; var guangzhouPoint = ol.proj.fromLonLat(guangzhouLonLat, 'EPSG:3857'); var inChina = features[0].getGeometry().intersectsCoordinate(guangzhouPoint); console.log(inChina); } }) }})
上面方法是有点麻烦,而且速度不是很快,可以在不需要绘制到地图上来判断吗?又想到一个好办法,直接在新建feature去获取对应的多边形区域来判断,很好很强大
$.get("./countries.geojson", function (data) { var lonLat = [113.3215552569, 23.1082829028]; // 華盛頓 // lonLat = [-77.0368707000, 38.9071923000]; var feature = (new ol.format.GeoJSON()).readFeatures(data); var guangzhouLonLat = [113.3215552569, 23.1082829028]; var inChina = feature[0].getGeometry().intersectsCoordinate(guangzhouLonLat); console.log(inChina) // true})
参考资料
Finding country name from geolocation
check-if-a-point-falls-within-a-multipolygon-with-python
how-to-check-for-geometry-intersection-point-in-polygon-using-openlayers3