化石原创文章,转载请注明来源并保留原文链接


gCoord 地理坐标系转换工具,支持WGS84/GCJ02/BD09等常用坐标系互转 。做国内项目的时候比较方便。

https://github.com/hujiulong/gcoord

最简单的方式是直接引用:

<script src="https://unpkg.com/gcoord/dist/gcoord.js"></script>

想作为服务器本地的:

npm install gcoord --save

然后在npm安装gcoord的目录的node_modules\gcoord\dist下,拷贝gcoord.js到项目中,然后通过本地路径引用即可。譬如:

<script type="text/javascript" src="./3rdParty/gcoord.js"></script>

化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


项目中,倾斜摄影模型从osgb而来,使用3dtile工具转化成了3d tiles格式供Cesium使用。然后服务器会给出一定的坐标。倾斜摄影模型的坐标和服务器给出的坐标都是基于wgs84的。

如果使用google地图,只使用卫星地图( http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}&s=Gali )的话,那么倾斜摄影和服务器给的坐标,都能和卫星地图完美配合。无需做任何事情。

然而地图上使用的时候必须看到道路标识,所以给到Cesium的tiles路径就变成了: http://mt1.google.cn/vt/lyrs=y&hl=zh-CN&x={x}&y={y}&z={z}&gl=cn。

可以看出来,请求的api中,参数lyrs从s变成了y,这个决定了从google服务器上请求下来的瓦片图,带有道路合成。参数也多了一个gl=cn,没有这个参数的话,会发现请求到的瓦片图,道路和卫星图是不匹配的,也就是道路有偏差。

所以瓦片请求路径就成了带gl=cn的那个。

不过使用这个瓦片图,其实google是为了符合中国规范,对图做了偏移。所以,所有wgs84的坐标信息(原来的),在这份地图上看起来都有了错位。因而我们需要解决两个问题:

1、tileset在该地图上的位置改动

2、服务器给到wgs84坐标后,我们得换算成现在地图上的坐标

下面是相关的方式:

1、tileset

所涉项目tileset数量很少,所以采用手工方式改动经纬度,看位置信息。最后记录下合适的经纬度。最后配合下面的代码,使tileset在加载准备好以后,自动放置到最后的位置。

var params = {
    tx: 120.7521311337607,    //longitude
    ty: 31.19968924358735,    //latitude
    tz: 0.1,                  //height
    rx: 0,                    //degree
    ry: 0,                    //degree
    rz: 0                     //degree
};
	
function update3dtilesMaxtrix(tileset, params) {
    // rotation maxtrix construct, first we get matrix3, then matrix4 from matrix3
    var mx = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(params.rx));
    var my = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(params.ry));
    var mz = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(params.rz));
    var rotationX = Cesium.Matrix4.fromRotationTranslation(mx);
    var rotationY = Cesium.Matrix4.fromRotationTranslation(my);
    var rotationZ = Cesium.Matrix4.fromRotationTranslation(mz);

    // translation matrix4
    var position = Cesium.Cartesian3.fromDegrees(params.tx, params.ty, params.tz);
    var m = Cesium.Transforms.eastNorthUpToFixedFrame(position);

    // concate translation and all rotations
    Cesium.Matrix4.multiply(m, rotationX, m);
    Cesium.Matrix4.multiply(m, rotationY, m);
    Cesium.Matrix4.multiply(m, rotationZ, m);

    // update tileset's matrix4
    tileset._root.transform = m;
}

viewer.scene.primitives.add(tileset).readyPromise.then(function(t) {
	update3dtilesMaxtrix(t, params);
});

viewer.zoomTo(tileset);

2、服务器给的wgs84坐标信息

使用gcoord库,把每个wgs84坐标转化成gcj02坐标即可。示例代码:

var result = gcoord.transform(
  [116.403988, 39.914266],    // 经纬度坐标
  gcoord.WGS84,               // 当前坐标系
  gcoord.GCJ02                // 目标坐标系
);

化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


Cesium的viewer.zoomTo()可以控制当前相机快速定位物体(zoomTo方法的第一个参数代表)。实际使用中,由于物体的大小,我们需要修改或者调整默认的距离和角度。

这个需求可以通过给zoomTo方法传入第二个参数实现。举例:

globalViewer.zoomTo(entity, new globalCesium.HeadingPitchRange(0, Cesium.Math.toRadians(-50), 500));

第二个参数是一个HeadingPitchRange()类型,原型如下:

new Cesium.HeadingPitchRange(heading, pitch, range) 

第一个参数是绕Y轴转动的弧度,第二个是绕X轴的弧度,第三个是离目标中心的距离。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


目前发现的是在scene类中,有preUpdate、postUpdate、preRender、postRender几个Event是每帧会呼叫到的。分别对应逻辑更新前、逻辑更新后。渲染前、渲染后几个阶段。

这个可以在Cesium.Scene的文档里查到。

如果渲染发生错误,会有Event renderError发出。

注意这里提到的东西都是Event形式,因而写法如下:

viewer.scene.preUpdate.addEventListener(function(scene, time) {
	console.log("preUpdate called.");
	console.log(time);
});

viewer.scene.postUpdate.addEventListener(function(scene, time) {
	console.log("postUpdate called.");
});

viewer.scene.postRender.addEventListener(function(scene, time) {
	console.log("postRender called.");
});

viewer.scene.preRender.addEventListener(function(scene, time) {
	console.log("preRender called.");
});

化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


一般我们在Cesium中取得当前鼠标点击的位置,比如左键点击,会使用如下类似的方法:

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(click) {
	// TO things as you need
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

这个方法的click信息,是经过Cesium过滤、包装过的,内容是一个Cartision2的实例,表示的是基于Cesium控制的Canvas的坐标。canvas左上角为(0,0),右下角(width,height)。

如果一个页面是由Cesium的canvas和其他的网页内容共同组成的,我们可能往往需要原始的点击事件,因为原始的事件里面包含了更多信息(甚至是可用的方法),可以让我们深度控制。

在我试用的方法这里(一个Cesiuim应用中,点击3D模型,弹出一个网页写的上下文菜单,需要知道各种信息,比如pageX等),分几个要点:

1、Cesium的setInputAction回调中只负责告知点击的状态,比如选中了一个Entity;

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(click) {
	console.log("this from cesuim");
	// parameter click contains Cartision2, based on canvas coordinate
	var pickedObject = viewer.scene.pick(click.position);
	if(Cesium.defined(pickedObject)) {
		// this is an entity
		if(pickedObject.node != null) {
			var entityId = pickedObject.id._id;
			var en = viewer.entities.getById(entityId);

			var cartographic = Cesium.Cartographic.fromCartesian(en.position._value);
			var longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(10);
			var latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(10);
			
			objectSelected = true;
		}
	}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

2、侦听document的click信息,发现对应的状态则执行真正的动作;

document.addEventListener("click", function(e) {
	console.log("click event receive");
	if (objectSelected) {
		showInfoPanel();
	}
});

3、使用Cesium的viewer.clock.onTick.addEventListener作帧循环,每帧把状态重置;

viewer.clock.onTick.addEventListener(function(clock) {
	objectSelected = false;
});

注意:
在Cesium回调中置状态,document回调中真正做事情的顺序是因为Cesium的回调先于document触发。


化石原创文章,转载请注明来源并保留原文链接