openlayers4.0嵌套谷歌地图

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]);

// 在区域内则返回对象,不在返回undefined
var 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