|
@@ -0,0 +1,372 @@
|
|
|
|
+
|
|
|
|
+*风场类
|
|
|
|
+****/
|
|
|
|
+var Cesium = require("cesium/Cesium");
|
|
|
|
+var CanvasWindy = function (json,params) {
|
|
|
|
+
|
|
|
|
+ this.windData = json;
|
|
|
|
+
|
|
|
|
+ this.viewer = params.viewer;
|
|
|
|
+ this.canvas = params.canvas;
|
|
|
|
+ this.extent = params.extent || [];
|
|
|
|
+ this.canvasContext = params.canvas.getContext("2d");
|
|
|
|
+ this.canvasWidth = params.canvasWidth || 300;
|
|
|
|
+ this.canvasHeight = params.canvasHeight || 180;
|
|
|
|
+ this.speedRate = params.speedRate || 100;
|
|
|
|
+ this.particlesNumber = params.particlesNumber || 20000;
|
|
|
|
+ this.maxAge = params.maxAge || 120;
|
|
|
|
+ this.frameTime = 1000/(params.frameRate || 10);
|
|
|
|
+ this.color = params.color || '#ffffff';
|
|
|
|
+ this.lineWidth = params.lineWidth || 1;
|
|
|
|
+
|
|
|
|
+ this.initExtent = [];
|
|
|
|
+ this.calc_speedRate = [0,0];
|
|
|
|
+ this.windField = null;
|
|
|
|
+ this.particles = [];
|
|
|
|
+ this.animateFrame = null;
|
|
|
|
+ this.isdistory = false;
|
|
|
|
+ this._init();
|
|
|
|
+};
|
|
|
|
+CanvasWindy.prototype = {
|
|
|
|
+ constructor: CanvasWindy,
|
|
|
|
+ _init: function () {
|
|
|
|
+ var self = this;
|
|
|
|
+
|
|
|
|
+ this.windField = this.createField();
|
|
|
|
+ this.initExtent = [this.windField.west-180,this.windField.east-180,this.windField.south,this.windField.north];
|
|
|
|
+
|
|
|
|
+ if(this.extent.length!=0){
|
|
|
|
+ this.extent = [
|
|
|
|
+ Math.max(this.initExtent[0],this.extent[0]),
|
|
|
|
+ Math.min(this.initExtent[1],this.extent[1]),
|
|
|
|
+ Math.max(this.initExtent[2],this.extent[2]),
|
|
|
|
+ Math.min(this.initExtent[3],this.extent[3])
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._calcStep();
|
|
|
|
+
|
|
|
|
+ for (var i = 0; i < this.particlesNumber; i++) {
|
|
|
|
+ this.particles.push(this.randomParticle(new CanvasParticle()));
|
|
|
|
+ }
|
|
|
|
+ this.canvasContext.fillStyle = "rgba(0, 0, 0, 0.97)";
|
|
|
|
+ this.canvasContext.globalAlpha = 0.6;
|
|
|
|
+ this.animate();
|
|
|
|
+
|
|
|
|
+ var then = Date.now();
|
|
|
|
+ (function frame() {
|
|
|
|
+ if(!self.isdistory){
|
|
|
|
+ self.animateFrame = requestAnimationFrame(frame);
|
|
|
|
+ var now = Date.now();
|
|
|
|
+ var delta = now - then;
|
|
|
|
+ if (delta > self.frameTime) {
|
|
|
|
+ then = now - delta % self.frameTime;
|
|
|
|
+ self.animate();
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ self.removeLines();
|
|
|
|
+ }
|
|
|
|
+ })();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _calcStep:function(){
|
|
|
|
+ var isextent = (this.extent.length!=0);
|
|
|
|
+ var calcExtent = isextent?this.extent:this.initExtent;
|
|
|
|
+ var calcSpeed = this.speedRate;
|
|
|
|
+ this.calc_speedRate = [(calcExtent[1]-calcExtent[0])/calcSpeed,(calcExtent[3]-calcExtent[2])/calcSpeed];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ redraw:function(){
|
|
|
|
+ window.cancelAnimationFrame(this.animateFrame);
|
|
|
|
+ this.particles = [];
|
|
|
|
+ this._init();
|
|
|
|
+ },
|
|
|
|
+ createField: function () {
|
|
|
|
+ var data = this._parseWindJson();
|
|
|
|
+ return new CanvasWindField(data);
|
|
|
|
+ },
|
|
|
|
+ animate: function () {
|
|
|
|
+ var self = this,
|
|
|
|
+ field = self.windField;
|
|
|
|
+ var nextLng = null,
|
|
|
|
+ nextLat = null,
|
|
|
|
+ uv = null;
|
|
|
|
+ self.particles.forEach(function (particle) {
|
|
|
|
+ if (particle.age <= 0) {
|
|
|
|
+ self.randomParticle(particle);
|
|
|
|
+ }
|
|
|
|
+ if (particle.age > 0) {
|
|
|
|
+ var x = particle.x,
|
|
|
|
+ y = particle.y,
|
|
|
|
+ tlng = particle.tlng,
|
|
|
|
+ tlat = particle.tlat;
|
|
|
|
+ var gridpos = self._togrid(tlng,tlat);
|
|
|
|
+ var tx = gridpos[0];
|
|
|
|
+ var ty = gridpos[1];
|
|
|
|
+ if (!self.isInExtent(tlng,tlat)) {
|
|
|
|
+ particle.age = 0;
|
|
|
|
+ } else {
|
|
|
|
+ uv = field.getIn(tx, ty);
|
|
|
|
+ nextLng = tlng + self.calc_speedRate[0] * uv[0];
|
|
|
|
+ nextLat = tlat + self.calc_speedRate[1] * uv[1];
|
|
|
|
+ particle.lng = tlng;
|
|
|
|
+ particle.lat = tlat;
|
|
|
|
+ particle.x = tx;
|
|
|
|
+ particle.y = ty;
|
|
|
|
+ particle.tlng = nextLng;
|
|
|
|
+ particle.tlat = nextLat;
|
|
|
|
+ particle.age--;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ if (self.particles.length <= 0) this.removeLines();
|
|
|
|
+ self._drawLines();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ isInExtent:function(lng,lat){
|
|
|
|
+ var calcExtent = this.initExtent;
|
|
|
|
+ if((lng>=calcExtent[0] && lng<=calcExtent[1]) && (lat>=calcExtent[2] && lat<=calcExtent[3])) return true;
|
|
|
|
+ return false;
|
|
|
|
+ },
|
|
|
|
+ _resize:function(width,height){
|
|
|
|
+ this.canvasWidth = width;
|
|
|
|
+ this.canvasHeight = height;
|
|
|
|
+ },
|
|
|
|
+ _parseWindJson: function () {
|
|
|
|
+ var uComponent = null,
|
|
|
|
+ vComponent = null,
|
|
|
|
+ header = null;
|
|
|
|
+ this.windData.forEach(function (record) {
|
|
|
|
+ var type = record.header.parameterCategory + "," + record.header.parameterNumber;
|
|
|
|
+ switch (type) {
|
|
|
|
+ case "2,2":
|
|
|
|
+ uComponent = record['data'];
|
|
|
|
+ header = record['header'];
|
|
|
|
+ break;
|
|
|
|
+ case "2,3":
|
|
|
|
+ vComponent = record['data'];
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return {
|
|
|
|
+ header: header,
|
|
|
|
+ uComponent: uComponent,
|
|
|
|
+ vComponent: vComponent
|
|
|
|
+ };
|
|
|
|
+ },
|
|
|
|
+ removeLines: function () {
|
|
|
|
+ window.cancelAnimationFrame(this.animateFrame);
|
|
|
|
+ this.isdistory = true;
|
|
|
|
+ this.canvas.width = 1;
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _tomap: function (lng,lat,particle) {
|
|
|
|
+ var ct3 = Cesium.Cartesian3.fromDegrees(lng,lat,0);
|
|
|
|
+
|
|
|
|
+ var isVisible = new Cesium.EllipsoidalOccluder(Cesium.Ellipsoid.WGS84, this.viewer.camera.position).isPointVisible(ct3);
|
|
|
|
+ var pos = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, ct3);
|
|
|
|
+ if(!isVisible){
|
|
|
|
+ particle.age = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return pos?[pos.x,pos.y]:null;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _togrid: function (lng,lat) {
|
|
|
|
+ var field = this.windField;
|
|
|
|
+ var x = (lng-this.initExtent[0])/(this.initExtent[1]-this.initExtent[0])*(field.cols-1);
|
|
|
|
+ var y = (this.initExtent[3]-lat)/(this.initExtent[3]-this.initExtent[2])*(field.rows-1);
|
|
|
|
+ return [x,y];
|
|
|
|
+ },
|
|
|
|
+ _drawLines: function () {
|
|
|
|
+ var self = this;
|
|
|
|
+ var particles = this.particles;
|
|
|
|
+ this.canvasContext.lineWidth = self.lineWidth;
|
|
|
|
+
|
|
|
|
+ this.canvasContext.globalCompositeOperation = "destination-in";
|
|
|
|
+ this.canvasContext.fillRect(0,0,this.canvasWidth,this.canvasHeight);
|
|
|
|
+ this.canvasContext.globalCompositeOperation = "lighter";
|
|
|
|
+ this.canvasContext.globalAlpha = 0.9;
|
|
|
|
+ this.canvasContext.beginPath();
|
|
|
|
+ this.canvasContext.strokeStyle = this.color;
|
|
|
|
+ particles.forEach(function (particle) {
|
|
|
|
+ var movetopos = self._tomap(particle.lng, particle.lat,particle);
|
|
|
|
+ var linetopos = self._tomap(particle.tlng, particle.tlat,particle);
|
|
|
|
+
|
|
|
|
+ if(movetopos!=null && linetopos!=null){
|
|
|
|
+ self.canvasContext.moveTo(movetopos[0],movetopos[1]);
|
|
|
|
+ self.canvasContext.lineTo(linetopos[0],linetopos[1]);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ this.canvasContext.stroke();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ fRandomByfloat:function(under, over){
|
|
|
|
+ return under+Math.random()*(over-under);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ fRandomBy:function(under, over){
|
|
|
|
+ switch(arguments.length){
|
|
|
|
+ case 1: return parseInt(Math.random()*under+1);
|
|
|
|
+ case 2: return parseInt(Math.random()*(over-under+1) + under);
|
|
|
|
+ default: return 0;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ randomParticle: function (particle) {
|
|
|
|
+ var safe = 30,x=-1, y=-1,lng=null,lat=null;
|
|
|
|
+ var hasextent = this.extent.length!=0;
|
|
|
|
+ var calc_extent = hasextent?this.extent:this.initExtent;
|
|
|
|
+ do {
|
|
|
|
+ try{
|
|
|
|
+ if(hasextent){
|
|
|
|
+ var pos_x = this.fRandomBy(0,this.canvasWidth);
|
|
|
|
+ var pos_y = this.fRandomBy(0,this.canvasHeight);
|
|
|
|
+ var cartesian = this.viewer.camera.pickEllipsoid(new Cesium.Cartesian2(pos_x, pos_y), this.viewer.scene.globe.ellipsoid);
|
|
|
|
+ var cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
|
|
|
|
+ if(cartographic){
|
|
|
|
+
|
|
|
|
+ lng = Cesium.Math.toDegrees(cartographic.longitude);
|
|
|
|
+ lat = Cesium.Math.toDegrees(cartographic.latitude);
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ lng = this.fRandomByfloat(calc_extent[0],calc_extent[1]);
|
|
|
|
+ lat = this.fRandomByfloat(calc_extent[2],calc_extent[3]);
|
|
|
|
+ }
|
|
|
|
+ }catch(e){
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ if(lng){
|
|
|
|
+ var gridpos = this._togrid(lng,lat);
|
|
|
|
+ x = gridpos[0];
|
|
|
|
+ y = gridpos[1];
|
|
|
|
+ }
|
|
|
|
+ } while (this.windField.getIn(x, y)[2] <= 0 && safe++ < 30);
|
|
|
|
+ var field = this.windField;
|
|
|
|
+ var uv = field.getIn(x, y);
|
|
|
|
+ var nextLng = lng + this.calc_speedRate[0] * uv[0];
|
|
|
|
+ var nextLat = lat + this.calc_speedRate[1] * uv[1];
|
|
|
|
+ particle.lng = lng;
|
|
|
|
+ particle.lat = lat;
|
|
|
|
+ particle.x = x;
|
|
|
|
+ particle.y = y;
|
|
|
|
+ particle.tlng = nextLng;
|
|
|
|
+ particle.tlat = nextLat;
|
|
|
|
+ particle.speed = uv[2];
|
|
|
|
+ particle.age = Math.round(Math.random() * this.maxAge);
|
|
|
|
+ return particle;
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+*棋盘类
|
|
|
|
+*根据风场数据生产风场棋盘网格
|
|
|
|
+****/
|
|
|
|
+var CanvasWindField = function (obj) {
|
|
|
|
+ this.west = null;
|
|
|
|
+ this.east = null;
|
|
|
|
+ this.south = null;
|
|
|
|
+ this.north = null;
|
|
|
|
+ this.rows = null;
|
|
|
|
+ this.cols = null;
|
|
|
|
+ this.dx = null;
|
|
|
|
+ this.dy = null;
|
|
|
|
+ this.unit = null;
|
|
|
|
+ this.date = null;
|
|
|
|
+
|
|
|
|
+ this.grid = null;
|
|
|
|
+ this._init(obj);
|
|
|
|
+};
|
|
|
|
+CanvasWindField.prototype = {
|
|
|
|
+ constructor: CanvasWindField,
|
|
|
|
+ _init: function (obj) {
|
|
|
|
+ var header = obj.header,
|
|
|
|
+ uComponent = obj['uComponent'],
|
|
|
|
+ vComponent = obj['vComponent'];
|
|
|
|
+
|
|
|
|
+ this.west = +header['lo1'];
|
|
|
|
+ this.east = +header['lo2'];
|
|
|
|
+ this.south = +header['la2'];
|
|
|
|
+ this.north = +header['la1'];
|
|
|
|
+ this.rows = +header['ny'];
|
|
|
|
+ this.cols = +header['nx'];
|
|
|
|
+ this.dx = +header['dx'];
|
|
|
|
+ this.dy = +header['dy'];
|
|
|
|
+ this.unit = header['parameterUnit'];
|
|
|
|
+ this.date = header['refTime'];
|
|
|
|
+
|
|
|
|
+ this.grid = [];
|
|
|
|
+ var k = 0,
|
|
|
|
+ rows = null,
|
|
|
|
+ uv = null;
|
|
|
|
+ for (var j = 0; j < this.rows; j++) {
|
|
|
|
+ rows = [];
|
|
|
|
+ for (var i = 0; i < this.cols; i++, k++) {
|
|
|
|
+ uv = this._calcUV(uComponent[k], vComponent[k]);
|
|
|
|
+ rows.push(uv);
|
|
|
|
+ }
|
|
|
|
+ this.grid.push(rows);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ _calcUV: function (u, v) {
|
|
|
|
+ return [+u, +v, Math.sqrt(u * u + v * v)];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ _bilinearInterpolation: function (x, y, g00, g10, g01, g11) {
|
|
|
|
+ var rx = (1 - x);
|
|
|
|
+ var ry = (1 - y);
|
|
|
|
+ var a = rx * ry, b = x * ry, c = rx * y, d = x * y;
|
|
|
|
+ var u = g00[0] * a + g10[0] * b + g01[0] * c + g11[0] * d;
|
|
|
|
+ var v = g00[1] * a + g10[1] * b + g01[1] * c + g11[1] * d;
|
|
|
|
+ return this._calcUV(u, v);
|
|
|
|
+ },
|
|
|
|
+ getIn: function (x, y) {
|
|
|
|
+ if(x<0 || x>=359 || y>=180){
|
|
|
|
+ return [0,0,0];
|
|
|
|
+ }
|
|
|
|
+ var x0 = Math.floor(x),
|
|
|
|
+ y0 = Math.floor(y),
|
|
|
|
+ x1, y1;
|
|
|
|
+ if (x0 === x && y0 === y) return this.grid[y][x];
|
|
|
|
+
|
|
|
|
+ x1 = x0 + 1;
|
|
|
|
+ y1 = y0 + 1;
|
|
|
|
+
|
|
|
|
+ var g00 = this.getIn(x0, y0),
|
|
|
|
+ g10 = this.getIn(x1, y0),
|
|
|
|
+ g01 = this.getIn(x0, y1),
|
|
|
|
+ g11 = this.getIn(x1, y1);
|
|
|
|
+ var result = null;
|
|
|
|
+ try{
|
|
|
|
+ result = this._bilinearInterpolation(x - x0, y - y0, g00, g10, g01, g11);
|
|
|
|
+ }catch(e){
|
|
|
|
+ console.log(x,y);
|
|
|
|
+ }
|
|
|
|
+ return result;
|
|
|
|
+ },
|
|
|
|
+ isInBound: function (x, y) {
|
|
|
|
+ if ((x >= 0 && x < this.cols-1) && (y >= 0 && y < this.rows-1)) return true;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+*粒子对象
|
|
|
|
+****/
|
|
|
|
+var CanvasParticle = function () {
|
|
|
|
+ this.lng = null;
|
|
|
|
+ this.lat = null;
|
|
|
|
+ this.x = null;
|
|
|
|
+ this.y = null;
|
|
|
|
+ this.tlng = null;
|
|
|
|
+ this.tlat = null;
|
|
|
|
+ this.age = null;
|
|
|
|
+ this.speed = null;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export default CanvasWindy;
|