Big data, artificial intelligence, industrial Internet of things, 5G have or are subtly changing our lives. In the era of rapid development of information technology, who can grasp the core of data, use effective methods to do data mining and data analysis, from the data to find trends, who can achieve accurate control, real-time analysis, targeted, so as to obtain faster, more stable, more long-term development. In the aviation field, the information of airport, flight and route is crucial data. This paper will introduce the example of global route developed by using JavaScript, HTML5, GIS and other technologies based on HT platform.

Interface to preview

– the main interface

– Aircraft and aircraft shadow animation

Code implementation

– Scene construction

The scene of this example consists of 3D and 2D scenes, which are respectively constructed by THE 3D and 2D editor of HT. The editing tool is developed based on HTML5 technology and is easy to get started. Moreover, many primitive types are predefined, and users can quickly and visually build various 3D/2D scenes without coding. The 3D scene effect is as follows:

The 2D panel mainly includes the left route table, the right real-time storm data table and the information panel at the bottom. The left route table shows the route information of different continents. The continents can be switched by the left button at the bottom. Right measure storm information is simulated and updated in real time; The bottom information bar contains the continent button and route details. Panel screenshot:

– Calculation of route origin and airport location

The airport and route source data for the example comes from the open source website OpenFlights.org. After getting the raw data, we first performed preliminary processing on the airport and airline data and saved them as JSON files. The format of airport data after processing is as follows. The information corresponding to each field is latitude, longitude, altitude, airport abbreviation, continent, country, region and airport name in turn.

[[-9.443380356,147.2200012, 146,"POM"."OC"."PG"."PG-NCD"."Port Moresby"],
[63.98500061,-22.60560036, 171,"KEF"."EU"."IS"."IS-2"."Reykjavík"], [36.001741, 117.63201, 0,"CN-0083"."AS"."CN"."CN-U-A".""],... ]Copy the code

The format of the processed airline data fragment is as follows. Take the first information as an example, the starting airport of the airline is MIA, Can be arrived at the airport, including [” 3201: PUJ “, “24: MSY”, “24: MVD”, “24: NAS,” “24: ORF”, “24: PHL”, “24: inhibits PTP”, “24: PTY”, “24: RIC”, “24: SAL,” “24: SAN”, “24: SDQ.” “24: the SFO”, “1299: the AMS”].

{"MIA": ["3201:PUJ"."24:MSY"."24:MVD"."24:NAS"."24:ORF"."24:PHL"."24:PTP"."24:PTY"."24:RIC"."24:SAL"."24:SAN"."24:SDQ"."24:SFO"."1299:AMS"]."HKG": ["3021:SIN"."1683:MNL"."2994:ICN"."15999:PVG"."24:JFK"."24:LAX"."24:NRT"."24:SFO"."330:YVR"."218:KIX"."576:KUL"."1680:SGN"."328:POM"]."SJU": ["3029:SXM"."3029:TPA"],... }Copy the code

Through the analysis of airport and airline data after processing, we can see that airport location is the basis of airline generation. The airport data after processing already has the longitude and latitude information of the airport, so the key point of the problem is how to convert the longitude and latitude into spherical coordinates, the conversion code is as follows:

// Convert longitude and latitude to sphere position getSpherePos(radius, longitude, latitude) {let ang1 = Math.PI * (longitude - 90) / 180;
    let ang2 = Math.PI * latitude / 180;
    let x, y, z;
    let s_r = radius;
    x = s_r * Math.sin(ang1) * Math.cos(ang2);
    y = s_r * Math.cos(ang1) * Math.cos(ang2);
    z = s_r * Math.sin(ang2);
    return [x, y, z];
}Copy the code

The spherical coordinates of each airport are calculated and the coordinate information is saved in the global array with other existing airport information.

– Route generation

The hT. Polyline type is used to generate routes. This type supports 3d space point description and extends from 2d plane curve to 3d space curve with segments parameter. In this example, according to the starting point and ending point of the route, vector operation is used to construct the middle control point, and Bessel curve is generated to render the route. After the course is created and added to the DataModel (via the add function), call the setHost(host) function to attach it to earth so that the course changes as the earth moves or rotates. The following code implementation creates a route:

@param {Object} end createEdge(start, end) {@param {Object} endlet edge;
    let distance = ht.Default.getDistance(start.point, end.point);
    let ratio = distance / this.radius;
    let v1 = new ht.Math.Vector3(start.point);
    let v2 = new ht.Math.Vector3(end.point);
    let v3 = v1.clone().add(v2).setLength(distance / 2);
    letv4 = v3.clone().add(v2); v3.add(v1); edge = new ht.Polyline(); Edge. setPoints([{x: start.point[0], y: start.point[2], e: start.point[1]}, {x: v3.x, y: start.point[1]}) v3.z, e: v3.y }, { x: v4.x, y: v4.z, e: v4.y }, { x: end.point[0], y: end.point[2], e: end.point[1] }, ]); edge.setSegments([1, 4]); this.dm3d.add(edge); edge.setHost(this.earth); }Copy the code

The difficulty of this part lies in how to construct intermediate control points according to the starting and ending positions of the route to generate Bezier curves. The following diagram illustrates the calculation of vectors and the variation of each vector variable in the code.

All routes can be drawn and generated by calling createEdge(start, end) function of creating routes for cyclic processing of all routes data. As shown in the figure:

– 2D/3D interactive line drawing

In the second picture of the article, there is a yellow line. The starting point of this line corresponds to the route selected in the table, and the end point corresponds to the route in 3D space. When you click on a route in the table, how do you create a line that spans 2D and 3D space? In this example, the idea is to get the position coordinate p3 of 3D space, call g3d.toViewPosition to get the two-dimensional screen coordinate P, and then call g2d.getLogicalPoint to get the 2D coordinate, which is the position of the end point. Here is the code implementation to get the destination position:

// Get the end of the positioning line -- the position corresponding to the selected route in the 3D spheregetLineEnd() {
    letP3 = this.g3d.getLineOffset(this.selectededge, this.g3d.getLineLength(this.selectededge) * 0.5);let p = g3d.toViewPosition([p3.point.x, p3.point.y, p3.point.z]);
    p = this.g2d.getLogicalPoint(p);
    this.endPoint = p;
}Copy the code

The starting position of the line is coded as follows, calculating the abscissa and ordinate of the starting point respectively.

// Get the position of the route tablegetLineStart() {
    let offset = this.table.a('ht.translateY');
    let lineStartPoint = {};
    let height = this.table.getHeight();
    let origY = this.table.p().y - height / 2 + this.table.a('ht.headHeight') + this.table.a("ht.rowHeight") / 2;
    lineStartPoint.x = this.table.p().x + this.table.getWidth() / 2;
    lineStartPoint.y = origY + this.rowIndex * this.table.a("ht.rowHeight") + offset;
    this.startPoint = lineStartPoint;
}Copy the code

– Aircraft, aircraft shadow animation and light source movement

When you select a flight path in the table or double click on a flight path on earth, the plane will fly along the flight path with a light source moving above the plane and a shadow moving below it. This section uses the built-in HT startAni function to start the animation. In the startAni function, the action function must be provided to implement property changes during animation; FinishFunc is the function that is called after the animation ends. A simple animation example is as follows:

ht.Default.startAnim({
    frames: 60,
    interval: 16,
    finishFunc: function() {
        console.log('finish');
    },
    action: function(t) { console.log(t); }});Copy the code

The following is the action function in this Demo, which completes the movement of the plane, the light source and the shadow of the plane in the animation process, and the attitude adjustment and rotation of the plane.

action: function (v, t) {
    letoffset = that.g3d.getLineOffset(that.selectedEdge, length * v); / / the offsetletp1 = offset.point; / / 3 d coordinateslettangent = offset.tangent; // The tangent directionlet direction = new ht.Math.Vector3(tangent);
    letvp1 = new ht.Math.Vector3(p1); Direction. MultiplyScalar (0.1); direction.add(vp1); direction.setLength(direction.length() + 2); vp1.setLength(vp1.length() + 2); that.airPlane.p3(vp1.x, vp1.y, vp1.z); that.airPlane.setRotationMode('yxz');
    that.airPlane.lookAtX([0, 0, 0], 'bottom');
    that.airPlane.lookAtX([direction.x, direction.y, direction.z], 'front');

    lightP = new ht.Math.Vector3(p1);
    lightP.setLength(that.radius * 2);
    that.spotLight.p3(lightP.x, lightP.y, lightP.z);

    direction.setLength(that.radius);
    lightP.setLength(that.radius);
    that.planeShadow.p3(lightP.x, lightP.y, lightP.z);
    that.planeShadow.setRotationMode('yxz');
    that.planeShadow.lookAtX([0, 0, 0], 'back');
    that.planeShadow.lookAtX([direction.x, direction.y, direction.z], 'right');
}Copy the code

– Satellite animation

In this example, the satellite revolves around the Earth in an elliptical orbit, and the Logo and halo revolve around the satellite. Parametric equations are used to calculate elliptic orbits. Assuming that the length of the semi-major axis and semi-minor axis of the ellipse is a and B, the semi-major axis and semi-minor axis are used as the inner and outer tangent circles of the ellipse respectively. It can be seen from the following figure that any point A on the ellipse has the same y-coordinate as point A1 on the tangent circle and the same y-coordinate as point A2 on the tangent circle, so the coordinates of point A can be described as (A * cosθ, b * sinθ), where θ is the central Angle of the tangent circle or the tangent circle of the ellipse.

The Logo and halo are rotated using 3D rotation functions. For details, please refer to the 3D rotation functions section of the HT 3D manual. The code implementation of satellite animation is as follows:

// Satellite and Logo rotationstartSat() {
    let dm = this.dm3d;
    leta = 1226; // Semi-major axis of ellipseletb = 698; // Ellipse semi-short axislet x, y, z;
    y = 0;
    letsat_ang = 0; // Satellite initial Angleletlogo_ang = 0; // The initial Angle of the LogosetInterval(() => { sat_ang = sat_ang + this.satelliteSpeed; Logo_ang = logo_ang + 0.01x = a * math. cos(-sat_ang); // z = b * math.sin (-sat_ang); // Y = x * math.sin (math.pi * 16/180); Math.pi * 16/180; math.pi * 16/180; // This.sat.p3 (x, y, z); this.logo.setRotationY(logo_ang); this.logo.setRotationZ(28 / 180 * Math.PI); this.logo.setRotationMode('yzx');

        this.sat_p.setRotationY(logo_ang);
        this.sat_p.setRotationZ(-35 / 180 * Math.PI);
        this.sat_p.setRotationMode('yzx');
    }, 16.7);
}Copy the code

– Storm animation

The storm animation uses the setInterval() method to repeatedly call the storm animation section to simulate the movement of the storm, as the storm grows larger and smaller. The idea is to set two flags to judge whether the storm is getting bigger or smaller. When the storm is getting bigger, the length of the storm in the x, Y and Z axes will be continuously increased, and the setSize3d function will be used to assign the value. The length of the storm in x, Y, and Z axis is continuously reduced and assigned by setSize3d function. The storm movement code is implemented as follows:

// Storm animationstartStorm() {
    let s_ang = 0;
    let s_ang2 = 0;
    let s_x, s_y, s_z;
    letS_r = 380.07;setInterval(() => {s_ang = s_ang + 0.002; S_ang2 = s_ang2 + 0.002; s_x = s_r * Math.sin(s_ang) * Math.cos(s_ang2); s_z = s_r * Math.cos(s_ang) * Math.cos(s_ang2); s_y = s_r * Math.sin(s_ang2); this.storm.p3(s_x, s_y, s_z); this.storm.lookAtX([0, 0, 0],'bottom');
        this.storm.setRotationMode('yzx');
        this.storm.setRotationY(s_ang * 20);
    }, 60);
}Copy the code

Performance optimization

In order to bring better user experience, this example has also carried out a series of optimization, so that the operation of the instance is more smooth and beautiful.

– Batch display route

There are 2,486 routes in this example, and if you display them all at once on Earth, with various styles, it will not only load very slowly, but it will probably crash because of too much memory. Therefore, this example adopts batch loading route to improve system performance. The implementation idea is to set a style named display_flag to control whether the route is displayed or not during the initial loading, and then update the route every 30 seconds (in this Demo). The relevant codes are as follows:

this.maxDisplayCount = 300; This. MAX_DISPLAY_COUNT = 6; Edge.s ({// when creating a route'display_flag': parseInt(Math.random() * 10) % this.MAX_DISPLAY_COUNT,
});

start() {
    this.edgeTimer = setInterval(() => {
        this.edges.forEach((val) => {
            let showFlag = this.checkStormDistance(val);
            showFlag = showFlag && (val.s('display_flag') == this.displayFlag);
            val.s('3d.visible', showFlag)
        });
        this.displayCount++;
        if(this.displayCount > this.maxDisplayCount) { this.displayFlag = (this.displayFlag + 1) % this.MAX_DISPLAY_COUNT; this.displayCount = 0; }}, 100); }Copy the code

– Polyline resolution dynamically changes

HT realizes the curve by differentiating segments. The parameter shape3d.resolution is used to control the number of differentiating segments of the curve. This parameter determines the accuracy of 3D graphics. In this Demo, shape3d.resolution is set to 60 to prevent aircraft jitter. However, after such setting, performance will be greatly affected. Therefore, dynamic resolution adjustment is adopted to dynamically adjust according to whether the route is selected to improve performance. Here’s the code. . Also need to call in updateResolution g3d invalidateCachedGeometry (data) to reset the geometry, update methods see “Polyline cache and updating methods” section.

Resolution updateResolution(isRestore) {if(! This.selectededge) {// No route is selectedreturn;
    }
    let res, thickness;
    let len = this.g3d.getLineLength(this.selectedEdge);
    if(isRestore) {// Need to restore default value res = 30; Thickness = 0.7; }else {
        res = len / 200 * 30;
        if (res < 60) {
            res = 60;
        }
        thickness = 5;
    }
    this.selectedEdge.s('shape3d.resolution', res);
    this.selectedEdge.setThickness(thickness);
}Copy the code

– Polyline cache and update method

As mentioned earlier, there are 2486 routes created in this Demo, each of which is a 3D curve of the HT. polyLine type. To improve performance, set the attribute Geometry. Cache to true when creating a route. In the subsequent polyLine properties (such as points, segments, width) changes, using g3d. InvalidateCachedGeometry (data) to reset the geometry.

// Set edge.s({'geometry.cache': true}); // This. SelectedEdge property changes, and the geometry is reset.let ui = g3d.getData3dUI(this.selectedEdge);
ui.shapeModel = ui.info = null;
this.g3d.invalidateData(this.selectedEdge);Copy the code

– Added a cube to the center of the active continent to assist positioning

Add a cube in the center of the valid continent to assist positioning. When clicking the continent button, use the flyTo() function to adjust the Angle of view of the sphere.

– 2D/3D interactive line drawing calls setTimeout

When a 2D/3D positioning line is displayed behind the panel, the positioning line needs to be recalculated and drawn every time the user moves the interface. Considering that the mobile interface triggers this event very frequently, if it responds every time, the application will become very busy and slow. Events can even be lost, such as when the user has stopped moving and the line is not drawn in place. Therefore, setTimout is used to ensure that the shortest interval of updates is 50ms and eliminate unnecessary updates. Of course, this interval can be adjusted according to the actual situation to reduce the sense of visual dullness.

this.updateTimer = setTimeout(() => {
    this.updateTimer = null;
    if(this.selectededge == null) {// No route is selectedreturn; } this.getLineEnd(); // Calculate the end of 2D/3D positioning line this.updateLine(true); }, 50);Copy the code

With 2D and 3D scenes, according to the ideas and logic introduced in the paper, animation generation, airline data loading, airline visualization, aircraft situation visualization and real-time display of storm data can be completed. The whole process is infinitely fun.

Based on aviation big data, on the basis of data analysis and visualization mentioned in this example, more application scenarios can be mined, such as flight operation data visualization, real-time display of aircraft data, flight history data analysis, emergency route scheduling, etc. If you want to learn more industrial Internet 2 d, 3 d visualization application cases, can reference here more www.hightopo.com/blog/1103.h… .