|
|
@@ -0,0 +1,1349 @@
|
|
|
+import { createSelector, OutputSelector } from 'reselect';
|
|
|
+import Global from './Common';
|
|
|
+import {
|
|
|
+ latLngDegreesToDecimal,
|
|
|
+ latLngDecimalToDegrees,
|
|
|
+ isObject,
|
|
|
+ isSafeString,
|
|
|
+ hasPoint,
|
|
|
+ isNumber
|
|
|
+} from './Utils'
|
|
|
+
|
|
|
+let LatLon = require('geodesy').LatLonSpherical;
|
|
|
+
|
|
|
+declare namespace LatLon {
|
|
|
+ export interface Spherical {
|
|
|
+ lat: number;
|
|
|
+ lon: number;
|
|
|
+ bearingTo: (point: LatLon.Spherical) => number;
|
|
|
+ destinationPoint: (distance: number, bearing: number, radius?: number) => LatLon.Spherical
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export interface CoordinateShort {
|
|
|
+ lat: number;
|
|
|
+ lng: number;
|
|
|
+}
|
|
|
+
|
|
|
+export interface CoordinateLong {
|
|
|
+ latitude: number;
|
|
|
+ longitude: number;
|
|
|
+}
|
|
|
+
|
|
|
+// Server
|
|
|
+export interface LatLngAddrServer extends CoordinateShort {
|
|
|
+ addr: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface LatLngPointServer extends LatLngAddrServer {
|
|
|
+ altitude: number;
|
|
|
+ unit: 0 | 1 | 2 | 3;
|
|
|
+}
|
|
|
+
|
|
|
+export interface StartPointServer extends CoordinateShort {
|
|
|
+ altitude: number;
|
|
|
+ unit: 0 | 1 | 2 | 3;
|
|
|
+}
|
|
|
+
|
|
|
+export interface AirRoutePoint extends CoordinateShort {
|
|
|
+ point_id: number;
|
|
|
+ point_name: string;
|
|
|
+ point_code: string;
|
|
|
+}
|
|
|
+
|
|
|
+// Passpoints
|
|
|
+export interface PassingPointNavServer extends CoordinateShort {
|
|
|
+ point_type: 1;
|
|
|
+ altitude: number;
|
|
|
+ unit: 0 | 1 | 2 | 3;
|
|
|
+
|
|
|
+ point_id: number;
|
|
|
+ point_code: string;
|
|
|
+ point_name: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface PassingPointAirlineServer {
|
|
|
+ point_type: 2;
|
|
|
+ altitude: number;
|
|
|
+ unit: 0 | 1 | 2 | 3;
|
|
|
+
|
|
|
+ airway?: number;
|
|
|
+ air_route_code: string;
|
|
|
+ points: Array<AirRoutePoint>;
|
|
|
+}
|
|
|
+
|
|
|
+export interface PassingPointNormalServer extends CoordinateShort{
|
|
|
+ point_type: 3;
|
|
|
+ altitude: number;
|
|
|
+ unit: 0 | 1 | 2 | 3;
|
|
|
+
|
|
|
+ point_name: string;
|
|
|
+}
|
|
|
+
|
|
|
+// Airspace
|
|
|
+export interface AirspaceInfoCircleServer {
|
|
|
+ airspace_name: string;
|
|
|
+ airspace_type: 1;
|
|
|
+ airspace_id: number;
|
|
|
+ note: string;
|
|
|
+
|
|
|
+ center_loc: string;
|
|
|
+ center_point_of_flying: CoordinateShort;
|
|
|
+ radius_of_flying: number;
|
|
|
+ altitude: number;
|
|
|
+ unit: number;
|
|
|
+}
|
|
|
+
|
|
|
+export interface AirspaceInfoLineServer {
|
|
|
+ airspace_name: string;
|
|
|
+ airspace_type: 2;
|
|
|
+ airspace_id: number;
|
|
|
+ note: string;
|
|
|
+
|
|
|
+ start_loc: string;
|
|
|
+ start_point: StartPointServer;
|
|
|
+ end_loc: string;
|
|
|
+ end_point: CoordinateShort;
|
|
|
+ passing_points: Array<PassingPointServer>;
|
|
|
+ airline_width?: number;
|
|
|
+}
|
|
|
+
|
|
|
+export interface AirspaceInfoPolygonServer {
|
|
|
+ airspace_name: string;
|
|
|
+ airspace_type: 3;
|
|
|
+ airspace_id: number;
|
|
|
+ note: string;
|
|
|
+
|
|
|
+ points: Array<LatLngAddrServer>;
|
|
|
+ altitude: number;
|
|
|
+ unit: number;
|
|
|
+}
|
|
|
+
|
|
|
+// Local
|
|
|
+export interface CoordinateShortString {
|
|
|
+ lat: string;
|
|
|
+ lng: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface LatLngAddrLocal extends CoordinateShortString {
|
|
|
+ addr: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface LatLngPointLocal extends LatLngAddrLocal{
|
|
|
+ height: string;
|
|
|
+ heightStandard: string;
|
|
|
+}
|
|
|
+
|
|
|
+// Passpoint
|
|
|
+export interface PassingPointNavLocal extends CoordinateShortString {
|
|
|
+ pointType: 1;
|
|
|
+ height: string;
|
|
|
+ heightStandard: string;
|
|
|
+
|
|
|
+ pointId: number;
|
|
|
+ pointCode: string;
|
|
|
+ pointName: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface PassingPointAirlineLocal {
|
|
|
+ pointType: 2;
|
|
|
+ height: string;
|
|
|
+ heightStandard: string;
|
|
|
+
|
|
|
+ airRouteId: number;
|
|
|
+ airlineCode: string;
|
|
|
+ airlines: Array<AirRoutePoint>;
|
|
|
+}
|
|
|
+
|
|
|
+export interface PassingPointNormalLocal extends CoordinateShortString {
|
|
|
+ pointType: 3;
|
|
|
+ height: string;
|
|
|
+ heightStandard: string;
|
|
|
+
|
|
|
+ addr: string;
|
|
|
+}
|
|
|
+
|
|
|
+// Airspace
|
|
|
+export interface AirspaceInfoCircleLocal {
|
|
|
+ airspaceType: 1;
|
|
|
+ airspaceId: number;
|
|
|
+ name: string;
|
|
|
+ note: string;
|
|
|
+
|
|
|
+ addr: string;
|
|
|
+ lat: string;
|
|
|
+ lng: string;
|
|
|
+ radius: number;
|
|
|
+ height: string;
|
|
|
+ heightStandard: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface AirspaceInfoLineLocal {
|
|
|
+ airspaceType: 2;
|
|
|
+ airspaceId: number;
|
|
|
+ name: string;
|
|
|
+ note: string;
|
|
|
+
|
|
|
+ dep: LatLngPointLocal;
|
|
|
+ arrive: LatLngAddrLocal;
|
|
|
+ passPoints: Array<PassingPointLocal>;
|
|
|
+ airlineWidth?: number;
|
|
|
+}
|
|
|
+
|
|
|
+export interface AirspaceInfoPolygonLocal {
|
|
|
+ airspaceType: 3;
|
|
|
+ airspaceId: number;
|
|
|
+ name: string;
|
|
|
+ note: string;
|
|
|
+
|
|
|
+ polygonPoints: Array<LatLngAddrLocal>;
|
|
|
+ height: string;
|
|
|
+ heightStandard: string;
|
|
|
+}
|
|
|
+
|
|
|
+declare type AirspaceInfoCircle = AirspaceInfoCircleLocal | AirspaceInfoCircleServer;
|
|
|
+declare type AirspaceInfoLine = AirspaceInfoLineLocal | AirspaceInfoLineServer;
|
|
|
+declare type AirspaceInfoPolygon = AirspaceInfoPolygonLocal | AirspaceInfoPolygonServer;
|
|
|
+
|
|
|
+declare type AirspaceInfoLocal = AirspaceInfoCircleLocal | AirspaceInfoLineLocal | AirspaceInfoPolygonLocal;
|
|
|
+declare type AirspaceInfoServer = AirspaceInfoCircleServer | AirspaceInfoLineServer | AirspaceInfoPolygonServer;
|
|
|
+declare type AirspaceInfo = AirspaceInfoLocal | AirspaceInfoServer
|
|
|
+
|
|
|
+declare type PassingPointNav = PassingPointNavLocal | PassingPointNavServer
|
|
|
+declare type PassingPointNormal = PassingPointNormalLocal | PassingPointNormalServer
|
|
|
+declare type PassingPointAirline = PassingPointAirlineLocal | PassingPointAirlineServer
|
|
|
+
|
|
|
+declare type PassingPointLocal = PassingPointNavLocal | PassingPointNormalLocal | PassingPointAirlineLocal
|
|
|
+declare type PassingPointServer = PassingPointNavServer | PassingPointNormalServer | PassingPointAirlineServer
|
|
|
+declare type PassingPoint = PassingPointLocal | PassingPointServer
|
|
|
+
|
|
|
+// shapes
|
|
|
+export type Coordinate = CoordinateLong | CoordinateShort
|
|
|
+export interface Circle {
|
|
|
+ coordinate: Coordinate;
|
|
|
+ radius: number;
|
|
|
+ lineWidth: number;
|
|
|
+ strokeColor: string;
|
|
|
+ fillColor: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface Marker {
|
|
|
+ coordinate: Coordinate;
|
|
|
+ imageName: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface Polygon {
|
|
|
+ lineWidth: number;
|
|
|
+ strokeColor: string;
|
|
|
+ fillColor: string;
|
|
|
+ coordinates: Coordinate[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface Line {
|
|
|
+ lineWidth: number;
|
|
|
+ strokeColor: string;
|
|
|
+ coordinates: Coordinate[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface CirclesAndMarkers {
|
|
|
+ circles: Circle[];
|
|
|
+ markers: Marker[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface LinesAndMarkes {
|
|
|
+ lines: Line[];
|
|
|
+ markers: Marker[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface LinesPolygonsAndMarkers {
|
|
|
+ polygons: Polygon[];
|
|
|
+ markers: Marker[];
|
|
|
+ lines: Line[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface PolygonsAndMarkers {
|
|
|
+ polygons: Polygon[];
|
|
|
+ markers: Marker[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface ShapeStyle {
|
|
|
+ imageName: string;
|
|
|
+ lineWidth: number;
|
|
|
+ strokeColor: string;
|
|
|
+ fillColor: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface ShapeStyles {
|
|
|
+ [styleName: string]: ShapeStyle
|
|
|
+}
|
|
|
+
|
|
|
+export function convertAirspaceInfoServerToLocal(airspaceInfo
|
|
|
+ : AirspaceInfoCircleServer | AirspaceInfoLineServer | AirspaceInfoPolygonServer)
|
|
|
+ : AirspaceInfoCircleLocal | AirspaceInfoLineLocal | AirspaceInfoPolygonLocal | null {
|
|
|
+
|
|
|
+ const airspaceType = airspaceInfo.airspace_type
|
|
|
+ if (airspaceType == Global.airspaceType.circle) {
|
|
|
+ const ai: AirspaceInfoCircleServer = <AirspaceInfoCircleServer> airspaceInfo
|
|
|
+ return <AirspaceInfoCircleLocal>{
|
|
|
+ airspaceType,
|
|
|
+ airspaceId: ai.airspace_id,
|
|
|
+ name: ai.airspace_name,
|
|
|
+ note: ai.note,
|
|
|
+ addr: ai.center_loc,
|
|
|
+ lat: latLngDecimalToDegrees(ai.center_point_of_flying.lat),
|
|
|
+ lng: latLngDecimalToDegrees(ai.center_point_of_flying.lng),
|
|
|
+ radius: ai.radius_of_flying,
|
|
|
+ height: ai.altitude + "",
|
|
|
+ heightStandard: Global.heightStandardsById.get(ai.unit)
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (airspaceType == Global.airspaceType.line) {
|
|
|
+ const ai: AirspaceInfoLineServer = <AirspaceInfoLineServer> airspaceInfo
|
|
|
+ let dep = {
|
|
|
+ addr: ai.start_loc,
|
|
|
+ lat: latLngDecimalToDegrees(ai.start_point.lat),
|
|
|
+ lng: latLngDecimalToDegrees(ai.start_point.lng),
|
|
|
+ height: ai.start_point.altitude + "",
|
|
|
+ heightStandard: Global.heightStandardsById.get(ai.start_point.unit)
|
|
|
+ }
|
|
|
+
|
|
|
+ let arrive = {
|
|
|
+ addr: ai.end_loc,
|
|
|
+ lat: latLngDecimalToDegrees(ai.end_point.lat),
|
|
|
+ lng: latLngDecimalToDegrees(ai.end_point.lng)
|
|
|
+ }
|
|
|
+
|
|
|
+ let passPoints: (PassingPointNavLocal | PassingPointAirlineLocal | PassingPointNormalLocal)[] = []
|
|
|
+ if(Array.isArray(ai.passing_points)) {
|
|
|
+ for(let obj of ai.passing_points) {
|
|
|
+ let point: PassingPointNavLocal | PassingPointAirlineLocal | PassingPointNormalLocal;
|
|
|
+
|
|
|
+ if (obj.point_type == Global.pointTypes.point) {
|
|
|
+ const pp = <PassingPointNormalServer>obj;
|
|
|
+ const lat = latLngDecimalToDegrees(pp.lat)
|
|
|
+ const lng = latLngDecimalToDegrees(pp.lng)
|
|
|
+
|
|
|
+ point = <PassingPointNormalLocal>{
|
|
|
+ pointType: pp.point_type,
|
|
|
+ addr: pp.point_name,
|
|
|
+ lat, lng
|
|
|
+ }
|
|
|
+ } else if (obj.point_type == Global.pointTypes.nav) {
|
|
|
+ const pp = <PassingPointNavServer>obj;
|
|
|
+ const lat = latLngDecimalToDegrees(pp.lat)
|
|
|
+ const lng = latLngDecimalToDegrees(pp.lng)
|
|
|
+
|
|
|
+ point = <PassingPointNavLocal>{
|
|
|
+ pointType: pp.point_type,
|
|
|
+ pointId: pp.point_id,
|
|
|
+ pointCode: pp.point_code,
|
|
|
+ pointName: pp.point_name,
|
|
|
+ lat, lng
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const pp = <PassingPointAirlineServer>obj;
|
|
|
+
|
|
|
+ point = <PassingPointAirlineLocal>{
|
|
|
+ pointType: pp.point_type,
|
|
|
+ airRouteId: pp.airway,
|
|
|
+ airlineCode: pp.air_route_code,
|
|
|
+ airlines: pp.points
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(isNumber(obj.altitude) && isNumber(obj.unit)) {
|
|
|
+ point.height = obj.altitude + "";
|
|
|
+ point.heightStandard = Global.heightStandardsById.get(obj.unit);
|
|
|
+ } else {
|
|
|
+ point.height = ""
|
|
|
+ point.heightStandard = [...Global.heightStandards.keys()][0];
|
|
|
+ }
|
|
|
+ passPoints.push(point)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return <AirspaceInfoLineLocal>{ ...{
|
|
|
+ airspaceType,
|
|
|
+ airspaceId: ai.airspace_id,
|
|
|
+ name: ai.airspace_name,
|
|
|
+ note: ai.note,
|
|
|
+ dep,
|
|
|
+ arrive,
|
|
|
+ passPoints
|
|
|
+ }, ...(ai.airline_width ? {airlineWidth: ai.airline_width} : {})}
|
|
|
+ }
|
|
|
+
|
|
|
+ if (airspaceType == Global.airspaceType.polygon) {
|
|
|
+ const ai = <AirspaceInfoPolygonServer>airspaceInfo;
|
|
|
+ if(Array.isArray(ai.points)) {
|
|
|
+ let polygonPoints = new Array();
|
|
|
+
|
|
|
+ let defaultPointName = 'A'
|
|
|
+ for(let obj of ai.points) {
|
|
|
+ let addr = obj.addr
|
|
|
+ if(!addr) {
|
|
|
+ addr = defaultPointName
|
|
|
+ defaultPointName = String.fromCharCode(defaultPointName.charCodeAt(0) + 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ polygonPoints.push({addr, lat: latLngDecimalToDegrees(obj.lat), lng: latLngDecimalToDegrees(obj.lng)})
|
|
|
+ }
|
|
|
+
|
|
|
+ return <AirspaceInfoPolygonLocal>{
|
|
|
+ airspaceType,
|
|
|
+ airspaceId: ai.airspace_id,
|
|
|
+ name: ai.airspace_name,
|
|
|
+ note: ai.note,
|
|
|
+ height: ai.altitude + '',
|
|
|
+ heightStandard: Global.heightStandardsById.get(ai.unit),
|
|
|
+ polygonPoints
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+}
|
|
|
+
|
|
|
+export function convertAirspaceInfoLocalToServer(airspace
|
|
|
+ : AirspaceInfoCircleLocal | AirspaceInfoLineLocal | AirspaceInfoPolygonLocal)
|
|
|
+ : AirspaceInfoCircleServer | AirspaceInfoLineServer | AirspaceInfoPolygonServer {
|
|
|
+ if (airspace.airspaceType == Global.airspaceType.circle) {
|
|
|
+ const ai = <AirspaceInfoCircleLocal> airspace;
|
|
|
+ const circle = <AirspaceInfoCircleServer>{
|
|
|
+ airspace_name: ai.name,
|
|
|
+ airspace_id: ai.airspaceId,
|
|
|
+ airspace_type: Global.airspaceType.circle,
|
|
|
+ note: isSafeString(ai.note) ? ai.note : null,
|
|
|
+ radius_of_flying: parseInt(ai.radius + ''),
|
|
|
+ center_point_of_flying: {
|
|
|
+ lng: latLngDegreesToDecimal(ai.lng),
|
|
|
+ lat: latLngDegreesToDecimal(ai.lat)
|
|
|
+ },
|
|
|
+ center_loc: ai.addr,
|
|
|
+ altitude: parseInt(ai.height),
|
|
|
+ unit: Global.heightStandards.get(ai.heightStandard)
|
|
|
+ };
|
|
|
+ return circle
|
|
|
+ } else if (airspace.airspaceType == Global.airspaceType.line) {
|
|
|
+ const ai = <AirspaceInfoLineLocal> airspace;
|
|
|
+ let line = <AirspaceInfoLineServer>{
|
|
|
+ airspace_name: ai.name,
|
|
|
+ airspace_id: ai.airspaceId,
|
|
|
+ airspace_type: Global.airspaceType.line,
|
|
|
+ note: ai.note,
|
|
|
+ start_loc: ai.dep.addr,
|
|
|
+ start_point: {
|
|
|
+ lng: latLngDegreesToDecimal(ai.dep.lng),
|
|
|
+ lat: latLngDegreesToDecimal(ai.dep.lat),
|
|
|
+ altitude: parseInt(ai.dep.height),
|
|
|
+ unit: Global.heightStandards.get(ai.dep.heightStandard)
|
|
|
+ },
|
|
|
+ end_loc: ai.arrive.addr,
|
|
|
+ end_point: {
|
|
|
+ lng: latLngDegreesToDecimal(ai.arrive.lng),
|
|
|
+ lat: latLngDegreesToDecimal(ai.arrive.lat)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(ai.airlineWidth) {
|
|
|
+ line['airline_width'] = ai.airlineWidth
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Array.isArray(ai.passPoints) && ai.passPoints.length > 0) {
|
|
|
+ let passing_points = [];
|
|
|
+
|
|
|
+ for (let obj of ai.passPoints) {
|
|
|
+ let retObj: PassingPointNormalServer | PassingPointNavServer | PassingPointAirlineServer;
|
|
|
+ if (obj.pointType == Global.pointTypes.point && hasPoint(<PassingPointNormalLocal>obj)) {
|
|
|
+ const pp = <PassingPointNormalLocal>obj;
|
|
|
+ retObj = <PassingPointNormalServer>{
|
|
|
+ point_type: Global.pointTypes.point,
|
|
|
+ point_name: pp.addr,
|
|
|
+ lat: latLngDegreesToDecimal(pp.lat),
|
|
|
+ lng: latLngDegreesToDecimal(pp.lng)
|
|
|
+ };
|
|
|
+ } else if (obj.pointType == Global.pointTypes.nav) {
|
|
|
+ const pp = <PassingPointNavLocal>obj;
|
|
|
+ retObj = <PassingPointNavServer>{
|
|
|
+ point_type: Global.pointTypes.nav,
|
|
|
+ point_name: pp.pointName,
|
|
|
+ point_id: pp.pointId,
|
|
|
+ point_code: pp.pointCode,
|
|
|
+ lat: latLngDegreesToDecimal(pp.lat),
|
|
|
+ lng: latLngDegreesToDecimal(pp.lng)
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ const pp = <PassingPointAirlineLocal>obj;
|
|
|
+ retObj = <PassingPointAirlineServer>{
|
|
|
+ point_type: Global.pointTypes.line,
|
|
|
+ airway: pp.airRouteId,
|
|
|
+ air_route_code: pp.airlineCode,
|
|
|
+ points: pp.airlines
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isSafeString(obj.height) && isSafeString(obj.heightStandard)) {
|
|
|
+ retObj.altitude = parseInt(obj.height);
|
|
|
+ // @ts-ignore
|
|
|
+ retObj.unit = Global.heightStandards.get(obj.heightStandard);
|
|
|
+ }
|
|
|
+ passing_points.push(retObj)
|
|
|
+ }
|
|
|
+
|
|
|
+ line.passing_points = passing_points;
|
|
|
+ }
|
|
|
+ return line;
|
|
|
+ } else {
|
|
|
+ const ai = <AirspaceInfoPolygonLocal> airspace;
|
|
|
+
|
|
|
+ let points = [];
|
|
|
+ for (let obj of ai.polygonPoints) {
|
|
|
+ points.push({ addr: obj.addr, lat: latLngDegreesToDecimal(obj.lat), lng: latLngDegreesToDecimal(obj.lng) })
|
|
|
+ }
|
|
|
+
|
|
|
+ const polygon = <AirspaceInfoPolygonServer>{
|
|
|
+ airspace_name: ai.name,
|
|
|
+ airspace_id: ai.airspaceId,
|
|
|
+ airspace_type: Global.airspaceType.polygon,
|
|
|
+ note: ai.note,
|
|
|
+ altitude: parseInt(ai.height),
|
|
|
+ unit: Global.heightStandards.get(ai.heightStandard),
|
|
|
+ points
|
|
|
+ };
|
|
|
+ return polygon;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getCircleRegions(circle: Circle): CoordinateShort[] {
|
|
|
+ let lat, lng;
|
|
|
+ if((circle.coordinate as any).latitude) {
|
|
|
+ let coord = <CoordinateLong> circle.coordinate;
|
|
|
+ lat = coord.latitude;
|
|
|
+ lng = coord.longitude;
|
|
|
+ } else {
|
|
|
+ let coord = <CoordinateShort> circle.coordinate;
|
|
|
+ lat = coord.lat;
|
|
|
+ lng = coord.lng;
|
|
|
+ }
|
|
|
+ let latlon = new LatLon(lat, lng)
|
|
|
+ let d1 = latlon.destinationPoint(circle.radius, 0)
|
|
|
+ let d2 = latlon.destinationPoint(circle.radius, 90)
|
|
|
+ let d3 = latlon.destinationPoint(circle.radius, 180)
|
|
|
+ let d4 = latlon.destinationPoint(circle.radius, 270)
|
|
|
+ return [{ lat: d1.lat, lng: d1.lon }, { lat: d2.lat, lng: d2.lon }, { lat: d3.lat, lng: d3.lon }, { lat: d4.lat, lng: d4.lon }]
|
|
|
+}
|
|
|
+
|
|
|
+function getDefaultStyle(): ShapeStyle {
|
|
|
+ let imageName = 'BA_oval'
|
|
|
+ let lineWidth = Global.amapLineWidth
|
|
|
+ let strokeColor = Global.amapStrokeColor
|
|
|
+ let fillColor = Global.amapFillColor
|
|
|
+
|
|
|
+ return {imageName, lineWidth, strokeColor, fillColor}
|
|
|
+}
|
|
|
+
|
|
|
+function getCirclesAndMarkers(airspaceInfos: AirspaceInfo[], setStyle: (styleName: string) => ShapeStyle | null, currentAirspaceIndex: number | undefined): CirclesAndMarkers {
|
|
|
+ let circles: Circle[] = [];
|
|
|
+ let markers: Marker[] = [];
|
|
|
+ if (!Array.isArray(airspaceInfos)) {
|
|
|
+ return {circles, markers};
|
|
|
+ }
|
|
|
+
|
|
|
+ let {imageName, lineWidth, strokeColor, fillColor} = getDefaultStyle()
|
|
|
+
|
|
|
+ //通过该方法获取样式
|
|
|
+ let circleStyle = setStyle('circle');
|
|
|
+ if(circleStyle) {
|
|
|
+ lineWidth = circleStyle.lineWidth
|
|
|
+ strokeColor = circleStyle.strokeColor
|
|
|
+ fillColor = circleStyle.fillColor
|
|
|
+ imageName = circleStyle.imageName
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < airspaceInfos.length; i++) {
|
|
|
+ let tmpCircle = airspaceInfos[i]
|
|
|
+ let airspaceTypeFix, radiusFix
|
|
|
+ if ((tmpCircle as any).airspaceType) {
|
|
|
+ airspaceTypeFix = 'airspaceType';
|
|
|
+ radiusFix = 'radius'
|
|
|
+ } else {
|
|
|
+ airspaceTypeFix = 'airspace_type';
|
|
|
+ radiusFix = 'radius_of_flying'
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((tmpCircle as any)[airspaceTypeFix] == Global.airspaceType.circle && currentAirspaceIndex != i) {
|
|
|
+ let coordinate = {latitude: 0, longitude: 0};
|
|
|
+ if ((tmpCircle as any).center_point_of_flying) {
|
|
|
+ let ai = <AirspaceInfoCircleServer>tmpCircle;
|
|
|
+ coordinate.latitude = ai.center_point_of_flying.lat;
|
|
|
+ coordinate.longitude = ai.center_point_of_flying.lng;
|
|
|
+ } else {
|
|
|
+ let ai = <AirspaceInfoCircleLocal>tmpCircle;
|
|
|
+ coordinate.latitude = latLngDegreesToDecimal(ai.lat);
|
|
|
+ coordinate.longitude = latLngDegreesToDecimal(ai.lng);
|
|
|
+ }
|
|
|
+
|
|
|
+ let radius = (tmpCircle as any)[radiusFix];
|
|
|
+
|
|
|
+ if(radius) {
|
|
|
+ let circle = <Circle>{lineWidth, strokeColor, fillColor, radius, coordinate}
|
|
|
+ circles.push(circle);
|
|
|
+ } else {
|
|
|
+ // 这里的经纬度必定是数值, 所以最后一个参数是 2
|
|
|
+ markers.push(addOvalPointConfig(coordinate.latitude, coordinate.longitude, imageName, 2));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return {circles, markers};
|
|
|
+}
|
|
|
+
|
|
|
+function getCircleAndMarkerSelector(airspaceInfos: () => AirspaceInfo[], setStyle: (styleName: string)=> ShapeStyle | null, currentAirspaceIndex: () => number | undefined) {
|
|
|
+ return createSelector(
|
|
|
+ airspaceInfos,
|
|
|
+ () => setStyle,
|
|
|
+ currentAirspaceIndex,
|
|
|
+ getCirclesAndMarkers
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function getLatLng(latlng: number | string, dataType: number): number {
|
|
|
+ if(dataType == 1) { // 驼峰模式,新建计划的时候的格式
|
|
|
+ return latLngDegreesToDecimal(<string>latlng)
|
|
|
+ } else {
|
|
|
+ return <number>latlng
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function drawLineConfig(lat: number | string, lng: number | string, dataType: number) {
|
|
|
+ return {
|
|
|
+ latitude: getLatLng(lat, dataType),
|
|
|
+ longitude: getLatLng(lng, dataType)
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function addOvalPointConfig(lat: number | string, lng: number | string, imageName: string, dataType: number) {
|
|
|
+ return {
|
|
|
+ coordinate: {
|
|
|
+ latitude: getLatLng(lat, dataType),
|
|
|
+ longitude: getLatLng(lng, dataType)
|
|
|
+ },
|
|
|
+ imageName: imageName
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function pointCompare(point1: AirRoutePoint | undefined, point2: AirRoutePoint | undefined) {
|
|
|
+ if (!point1 || !point2) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ const pointId1 = point1.point_id;
|
|
|
+ const pointId2 = point2.point_id;
|
|
|
+
|
|
|
+ if(pointId1 == pointId2) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ let [point1Lat, point1lng] = getFixedLatLng(point1)
|
|
|
+ let [point2lat, point2lng] = getFixedLatLng(point2)
|
|
|
+
|
|
|
+ if(myRound(point1Lat) == myRound(point2lat)
|
|
|
+ && myRound(point1lng) == myRound(point2lng)) {
|
|
|
+ return true
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function getCrossPoint(points1: AirRoutePoint[], points2: AirRoutePoint[]) {
|
|
|
+ for(let point1 of points1) {
|
|
|
+ for(let point2 of points2) {
|
|
|
+ if (pointCompare(point1, point2))
|
|
|
+ return point1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return undefined
|
|
|
+}
|
|
|
+
|
|
|
+function getAirwayPoints(airway: PassingPointAirline, pointBefore: PassingPoint, pointAfter: PassingPoint) {
|
|
|
+ let found = 0
|
|
|
+ let points = []
|
|
|
+
|
|
|
+ let pointTypeFix, pointsFix, airRoutePoint
|
|
|
+
|
|
|
+ if ('points' in airway) {
|
|
|
+ pointTypeFix = 'point_type';
|
|
|
+ pointsFix = 'points'
|
|
|
+ airRoutePoint = airway['points']
|
|
|
+ } else {
|
|
|
+ pointTypeFix = 'pointType'
|
|
|
+ pointsFix = 'airlines'
|
|
|
+ airRoutePoint = airway['airlines']
|
|
|
+ }
|
|
|
+
|
|
|
+ let crossPointBefore, crossPointAfter;
|
|
|
+ // 如果前后是其他航线,那么找到交叉点作为前后的点
|
|
|
+ if ( (pointBefore as any)[pointTypeFix] == Global.pointTypes.line ) {
|
|
|
+ crossPointBefore = getCrossPoint(airRoutePoint, <AirRoutePoint[]>(pointBefore as any)[pointsFix])
|
|
|
+ }
|
|
|
+
|
|
|
+ if((pointAfter as any)[pointTypeFix] == Global.pointTypes.line) {
|
|
|
+ crossPointAfter = getCrossPoint(airRoutePoint, <AirRoutePoint[]>(pointAfter as any)[pointsFix])
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let point of airRoutePoint) {
|
|
|
+ if (pointCompare(crossPointBefore, point) || pointCompare(crossPointAfter, point)) {
|
|
|
+ found++
|
|
|
+ points.push(Object.assign({}, point))
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if (found == 1) {
|
|
|
+ points.push(Object.assign({}, point))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(points.length > 0 && found == 2)) {
|
|
|
+ // 如果两个点不全在航线上面,那么画全部航线
|
|
|
+ points = airRoutePoint
|
|
|
+ }
|
|
|
+
|
|
|
+ return points;
|
|
|
+}
|
|
|
+
|
|
|
+function getLinesRouter(lineProps: AirspaceInfoLine, lineAndMarkerStyle: ShapeStyle | null) {
|
|
|
+ let coordinates: Coordinate[] = new Array();
|
|
|
+ let markers: Marker[] = new Array();
|
|
|
+ let lines: Line[] = []
|
|
|
+
|
|
|
+ let {imageName, lineWidth, strokeColor} = getDefaultStyle()
|
|
|
+
|
|
|
+ if (lineAndMarkerStyle) {
|
|
|
+ imageName = lineAndMarkerStyle.imageName
|
|
|
+ lineWidth = lineAndMarkerStyle.lineWidth
|
|
|
+ strokeColor = lineAndMarkerStyle.strokeColor
|
|
|
+ }
|
|
|
+
|
|
|
+ let startPoint, passPoints, endPoint, pointTypeFix, dataType, airlineWidth
|
|
|
+
|
|
|
+ if ((lineProps as any).start_point) {
|
|
|
+ let ll = <AirspaceInfoLineServer>lineProps;
|
|
|
+ dataType = 2 // 下划线模式
|
|
|
+
|
|
|
+ startPoint = ll['start_point']
|
|
|
+ passPoints = ll['passing_points']
|
|
|
+ endPoint = ll['end_point']
|
|
|
+ pointTypeFix = 'point_type';
|
|
|
+ airlineWidth = parseInt(ll['airline_width'] + '', 10)
|
|
|
+ } else {
|
|
|
+ let ll = <AirspaceInfoLineLocal>lineProps;
|
|
|
+ dataType = 1 // 驼峰模式
|
|
|
+
|
|
|
+ startPoint = ll['dep']
|
|
|
+ passPoints = ll['passPoints']
|
|
|
+ endPoint = ll['arrive']
|
|
|
+ pointTypeFix = 'pointType'
|
|
|
+ airlineWidth = parseInt(ll['airlineWidth'] + '', 10)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (startPoint) {
|
|
|
+ coordinates.push(drawLineConfig(startPoint.lat, startPoint.lng, dataType));
|
|
|
+ markers.push(addOvalPointConfig(startPoint.lat, startPoint.lng, imageName, dataType));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Array.isArray(passPoints)) {
|
|
|
+ for (let i = 0; i < passPoints.length; i++) {
|
|
|
+ let obj = passPoints[i]
|
|
|
+ if (!isObject(obj)) { // 所有的 points/airway 都必须是 obj
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let pointType = (obj as any)[pointTypeFix]
|
|
|
+ if ( pointType == Global.pointTypes.point
|
|
|
+ || pointType == Global.pointTypes.nav) {
|
|
|
+ let pp = <PassingPointNav | PassingPointNormal>obj
|
|
|
+ coordinates.push(drawLineConfig(pp.lat, pp.lng, dataType));
|
|
|
+ markers.push(addOvalPointConfig(pp.lat, pp.lng, imageName, dataType));
|
|
|
+ } else {
|
|
|
+ // 遇到一个航线,不需要和前前面的点连起来
|
|
|
+ let pp = <PassingPointAirline>obj
|
|
|
+ if (coordinates.length > 1) {
|
|
|
+ lines.push({lineWidth, strokeColor, coordinates})
|
|
|
+ }
|
|
|
+
|
|
|
+ coordinates = []
|
|
|
+
|
|
|
+ const pointBefore = i == 0 ? startPoint : passPoints[i - 1]
|
|
|
+ const pointAfter = i == passPoints.length - 1 ? (endPoint ? endPoint : passPoints[passPoints.length - 1]) : passPoints[i + 1]
|
|
|
+
|
|
|
+ lines.push({lineWidth, strokeColor, coordinates: getAirwayPoints(pp, <PassingPoint>pointBefore, <PassingPoint>pointAfter)})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (endPoint) {
|
|
|
+ coordinates.push(drawLineConfig(endPoint.lat, endPoint.lng, dataType));
|
|
|
+ markers.push(addOvalPointConfig(endPoint.lat, endPoint.lng, imageName, dataType));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (coordinates.length > 1) {
|
|
|
+ lines.push({lineWidth, strokeColor, coordinates});
|
|
|
+ }
|
|
|
+ if(airlineWidth > 0) {
|
|
|
+ // 有宽度的空域,需要线周围多画宽度的多边形
|
|
|
+ let polygons = processAirlineWidth(lines, airlineWidth)
|
|
|
+ return { lines, markers, polygons };
|
|
|
+ } else {
|
|
|
+ return { lines, markers, polygons: [] };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getFixedLatLng(point: {lat?: string | number, lng?: string | number, latitude?: string | number, longitude?: string | number}): [number, number] {
|
|
|
+ let pp = <any> point;
|
|
|
+ let lat = pp.latitude ? pp.latitude : pp.lat
|
|
|
+ let lng = pp.longitude ? pp.longitude : pp.lng
|
|
|
+
|
|
|
+ // 复制计划的数据,有的点是度数模式如 38°35′17″
|
|
|
+ if(isSafeString(lat)) {
|
|
|
+ lat = latLngDegreesToDecimal(<string>lat);
|
|
|
+ lng = latLngDegreesToDecimal(<string>lng);
|
|
|
+ }
|
|
|
+
|
|
|
+ return [lat, lng]
|
|
|
+}
|
|
|
+
|
|
|
+function processAirlineWidth(lines: Line[], airlineWidth: number) {
|
|
|
+ let polygons: Polygon[] = []
|
|
|
+ let {strokeColor, fillColor} = getDefaultStyle()
|
|
|
+
|
|
|
+ for(let line of lines) {
|
|
|
+ let points = line.coordinates
|
|
|
+
|
|
|
+ for(let i=0; i<points.length-1; i++) {
|
|
|
+ let [lat1, lng1] = getFixedLatLng(points[i])
|
|
|
+ let [lat2, lng2] = getFixedLatLng(points[i+1])
|
|
|
+ let point1: LatLon.Spherical = new LatLon(lat1, lng1)
|
|
|
+ let point2: LatLon.Spherical = new LatLon(lat2, lng2)
|
|
|
+
|
|
|
+ let coordinates = getCirclePoints(point1, point2, airlineWidth)
|
|
|
+ polygons.push({lineWidth: 1, strokeColor, fillColor, coordinates})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return polygons
|
|
|
+}
|
|
|
+
|
|
|
+function getCirclePoints(point1: LatLon.Spherical, point2: LatLon.Spherical, width: number) {
|
|
|
+ let percision = 10 // 半圆处理为多边形的时候,半圆上取几个点
|
|
|
+ let step = 180/percision
|
|
|
+ let bearing = (360 + point1.bearingTo(point2) - 90) % 360 // 取正值
|
|
|
+ let points = []
|
|
|
+ for(let diff = 0; diff <= 180; diff += step) {
|
|
|
+ let point = point2.destinationPoint(width, bearing + diff)
|
|
|
+ points.push({lat: point.lat, lng: point.lon})
|
|
|
+ }
|
|
|
+
|
|
|
+ for(let diff = 180; diff <= 360; diff += step) {
|
|
|
+ let point = point1.destinationPoint(width, bearing + diff)
|
|
|
+ points.push({lat: point.lat, lng: point.lon})
|
|
|
+ }
|
|
|
+ return points
|
|
|
+}
|
|
|
+
|
|
|
+function myRound(num: number, digits?: number) {
|
|
|
+ if(digits == null)
|
|
|
+ digits = 6 // 比较的精度,经纬度会被经过度分秒方式到浮点方式的转化
|
|
|
+
|
|
|
+ return Math.round(num * Math.pow(10, digits)) / Math.pow(10, digits)
|
|
|
+}
|
|
|
+
|
|
|
+function getLinesPolygonsAndMarkers(airspaceInfos: AirspaceInfo[], setStyle: FnSetStyle, currentAirspaceIndex: number | undefined): LinesPolygonsAndMarkers {
|
|
|
+ let retLines: Line[] = [];
|
|
|
+ let retMarkers: Marker[] = [];
|
|
|
+ let retPolygons: Polygon[] = [];
|
|
|
+ if (!Array.isArray(airspaceInfos)) {
|
|
|
+ return { lines: retLines, markers: retMarkers, polygons: retPolygons };
|
|
|
+ }
|
|
|
+ let lineStyle = setStyle('line');
|
|
|
+ for (let i = 0; i < airspaceInfos.length; i++) {
|
|
|
+ let tmpLine = airspaceInfos[i]
|
|
|
+ let airspaceTypeFix;
|
|
|
+ if ((tmpLine as any).airspaceType)
|
|
|
+ airspaceTypeFix = 'airspaceType';
|
|
|
+ else
|
|
|
+ airspaceTypeFix = 'airspace_type';
|
|
|
+ if ((tmpLine as any)[airspaceTypeFix] == Global.airspaceType.line && currentAirspaceIndex != i) {
|
|
|
+ let lineProps = <AirspaceInfoLine>tmpLine;
|
|
|
+ let { lines, markers, polygons } = getLinesRouter(lineProps, lineStyle);
|
|
|
+ retMarkers.push(...markers);
|
|
|
+ retLines.push(...lines);
|
|
|
+ retPolygons.push(...polygons)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return { lines: retLines, markers: retMarkers, polygons: retPolygons };
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function getLinePolygonsAndMarkerSelector(airspaceInfos: () => AirspaceInfo[], setStyle: FnSetStyle, currentAirspaceIndex: () => number | undefined) {
|
|
|
+ return createSelector(
|
|
|
+ airspaceInfos,
|
|
|
+ () => setStyle,
|
|
|
+ currentAirspaceIndex,
|
|
|
+ getLinesPolygonsAndMarkers
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function getPolygon(polygonProps: AirspaceInfoPolygon, polygonAndMarkerStyle: ShapeStyle | null) {
|
|
|
+ let coordinates: Coordinate[] = new Array();
|
|
|
+ let markers: Marker[] = new Array();
|
|
|
+
|
|
|
+ let {imageName, lineWidth, strokeColor, fillColor} = getDefaultStyle()
|
|
|
+
|
|
|
+ if (polygonAndMarkerStyle) {
|
|
|
+ imageName = polygonAndMarkerStyle.imageName
|
|
|
+ lineWidth = polygonAndMarkerStyle.lineWidth
|
|
|
+ strokeColor = polygonAndMarkerStyle.strokeColor
|
|
|
+ fillColor = polygonAndMarkerStyle.fillColor
|
|
|
+ }
|
|
|
+
|
|
|
+ let pointsFix, dataType;
|
|
|
+ if ((polygonProps as any).points) {
|
|
|
+ pointsFix = 'points';
|
|
|
+ dataType = 2
|
|
|
+ } else {
|
|
|
+ pointsFix = 'polygonPoints';
|
|
|
+ dataType = 1 // 驼峰模式
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Array.isArray((polygonProps as any)[pointsFix])) {
|
|
|
+ for (let obj of (polygonProps as any)[pointsFix]) {
|
|
|
+ if (!obj) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ coordinates.push(drawLineConfig(obj.lat, obj.lng, dataType));
|
|
|
+ markers.push(addOvalPointConfig(obj.lat, obj.lng, imageName, dataType));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let polygon = <Polygon>{lineWidth, strokeColor, fillColor, coordinates};
|
|
|
+
|
|
|
+ return { markers, polygon };
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function getPolygonsAndMarkers(airspaceInfos: AirspaceInfo[], setStyle: FnSetStyle, currentAirspaceIndex: number | undefined): PolygonsAndMarkers {
|
|
|
+ let markers: Marker[] = [];
|
|
|
+ let polygons: Polygon[] = [];
|
|
|
+ if (!Array.isArray(airspaceInfos)) {
|
|
|
+ return { markers, polygons };
|
|
|
+ }
|
|
|
+ let polygonAndMarkerStyle = setStyle('polygon');
|
|
|
+ for (let i = 0; i < airspaceInfos.length; i++) {
|
|
|
+ let polygon = <AirspaceInfoPolygon>airspaceInfos[i]
|
|
|
+ let airspaceTypeFix;
|
|
|
+ if ((polygon as any).airspaceType)
|
|
|
+ airspaceTypeFix = 'airspaceType';
|
|
|
+ else
|
|
|
+ airspaceTypeFix = 'airspace_type';
|
|
|
+
|
|
|
+ if ((polygon as any)[airspaceTypeFix] == Global.airspaceType.polygon && currentAirspaceIndex != i) {
|
|
|
+ let retObj = getPolygon(polygon, polygonAndMarkerStyle);
|
|
|
+ markers.push(...retObj.markers);
|
|
|
+ polygons.push(retObj.polygon);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return { markers, polygons };
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+function getPolygonAndMarkerSelector(airspaceInfos: () => AirspaceInfo[], setStyle: FnSetStyle, currentAirspaceIndex: () => number | undefined) {
|
|
|
+ return createSelector(
|
|
|
+ airspaceInfos,
|
|
|
+ () => setStyle,
|
|
|
+ currentAirspaceIndex,
|
|
|
+ getPolygonsAndMarkers
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function getMarkers(circlesAndMarkers: CirclesAndMarkers, polygonAndMarkers: PolygonsAndMarkers, lineAndMarkers: LinesAndMarkes) {
|
|
|
+ let markers: Marker[] = [];
|
|
|
+ if (circlesAndMarkers) {
|
|
|
+ markers = [...circlesAndMarkers.markers]
|
|
|
+ }
|
|
|
+ if (polygonAndMarkers) {
|
|
|
+ markers = [...markers, ...polygonAndMarkers.markers]
|
|
|
+ }
|
|
|
+ if (lineAndMarkers) {
|
|
|
+ markers = [...markers, ...lineAndMarkers.markers]
|
|
|
+ }
|
|
|
+ return markers
|
|
|
+}
|
|
|
+
|
|
|
+function getMarkerSelector(
|
|
|
+ circlesAndMarkers: OutputSelector<any, any, (...params: any[]) => CirclesAndMarkers>,
|
|
|
+ polygonAndMarkers: OutputSelector<any, any, (...params: any[]) => PolygonsAndMarkers>,
|
|
|
+ lineAndMarkers: OutputSelector<any, any, (...params: any[]) => LinesAndMarkes>) {
|
|
|
+ return createSelector(
|
|
|
+ circlesAndMarkers,
|
|
|
+ polygonAndMarkers,
|
|
|
+ lineAndMarkers,
|
|
|
+ getMarkers
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function getRegionPoints(circles: Circle[], lineAndMarkers: LinesAndMarkes, polygonAndMarkers: PolygonsAndMarkers) {
|
|
|
+ let regionPoints: Coordinate[] = new Array();
|
|
|
+
|
|
|
+ for (let i = 0; i < circles.length; i++) {
|
|
|
+ regionPoints.push(...getCircleRegions(circles[i]));
|
|
|
+ }
|
|
|
+
|
|
|
+ let lines = lineAndMarkers.lines;
|
|
|
+ for (let i = 0; i < lines.length; i++) {
|
|
|
+ regionPoints.push(...lines[i].coordinates);
|
|
|
+ }
|
|
|
+
|
|
|
+ let polygons = polygonAndMarkers.polygons;
|
|
|
+ for (let i = 0; i < polygons.length; i++) {
|
|
|
+ regionPoints.push(...polygons[i].coordinates);
|
|
|
+ }
|
|
|
+
|
|
|
+ return regionPoints;
|
|
|
+}
|
|
|
+
|
|
|
+function getRegionPointsSelector(
|
|
|
+ circles: OutputSelector<any, any, (...params: any[])=>Circle[]>,
|
|
|
+ lineAndMarkers: OutputSelector<any, any, (...params: any[])=>LinesAndMarkes>,
|
|
|
+ polygonAndMarkers: OutputSelector<any, any, (...params: any[])=>PolygonsAndMarkers>) {
|
|
|
+ return createSelector(
|
|
|
+ circles,
|
|
|
+ lineAndMarkers,
|
|
|
+ polygonAndMarkers,
|
|
|
+ getRegionPoints
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function getCircles(circlesAndMarkers: CirclesAndMarkers) {
|
|
|
+ return circlesAndMarkers.circles;
|
|
|
+}
|
|
|
+
|
|
|
+function getCircleSelector(circlesAndMarkers: OutputSelector<any, any, (...params: any[])=> CirclesAndMarkers>) {
|
|
|
+ return createSelector(
|
|
|
+ circlesAndMarkers,
|
|
|
+ getCircles
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function getLines(lineAndMarker: LinesAndMarkes) {
|
|
|
+ return lineAndMarker.lines;
|
|
|
+}
|
|
|
+
|
|
|
+function getLineSelector(lineAndMarker: OutputSelector<any, any, (...params: any[])=>LinesAndMarkes>) {
|
|
|
+ return createSelector(
|
|
|
+ lineAndMarker,
|
|
|
+ getLines
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function getPolygons(polygonAndMarkers: PolygonsAndMarkers, linePolygonsAndMarkers: LinesPolygonsAndMarkers) {
|
|
|
+ return [...polygonAndMarkers.polygons, ...linePolygonsAndMarkers.polygons];
|
|
|
+}
|
|
|
+
|
|
|
+function getPolygonSelector(
|
|
|
+ polygonAndMarkers: OutputSelector<any, any, (...params: any[]) => PolygonsAndMarkers>,
|
|
|
+ linePolygonsAndMarkers: OutputSelector<any, any, (...params: any[]) => LinesPolygonsAndMarkers>) {
|
|
|
+ return createSelector(
|
|
|
+ polygonAndMarkers,
|
|
|
+ linePolygonsAndMarkers,
|
|
|
+ getPolygons
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export type FnSetStyle = (styleName: string) => null | ShapeStyle
|
|
|
+
|
|
|
+let setStyle: (style: undefined | ShapeStyles) => FnSetStyle = (styles) => {
|
|
|
+ if (!styles)
|
|
|
+ return () => null
|
|
|
+ else
|
|
|
+ return (shapeName: string): ShapeStyle => styles[shapeName]
|
|
|
+}
|
|
|
+
|
|
|
+//获取selector
|
|
|
+export function getShapesSelector(airspaceInfos: () => AirspaceInfo[], style?: ShapeStyles, currentAirspaceIndex?: () => number | undefined) {
|
|
|
+ currentAirspaceIndex = currentAirspaceIndex ? currentAirspaceIndex : () => -1;
|
|
|
+ let circlesAndMarkers = getCircleAndMarkerSelector(airspaceInfos, setStyle(style), currentAirspaceIndex);
|
|
|
+ let circles = getCircleSelector(circlesAndMarkers);
|
|
|
+ let linePolygonsAndMarkers = getLinePolygonsAndMarkerSelector(airspaceInfos, setStyle(style), currentAirspaceIndex);
|
|
|
+ let lines = getLineSelector(linePolygonsAndMarkers);
|
|
|
+ let polygonAndMarkers = getPolygonAndMarkerSelector(airspaceInfos, setStyle(style), currentAirspaceIndex);
|
|
|
+ let polygons = getPolygonSelector(polygonAndMarkers, linePolygonsAndMarkers);
|
|
|
+ let markers = getMarkerSelector(circlesAndMarkers, polygonAndMarkers, linePolygonsAndMarkers);
|
|
|
+ let regionPoints = getRegionPointsSelector(circles, linePolygonsAndMarkers, polygonAndMarkers);
|
|
|
+ return {
|
|
|
+ markers,
|
|
|
+ circles,
|
|
|
+ lines,
|
|
|
+ polygons,
|
|
|
+ regionPoints
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//获取数组
|
|
|
+export function getShapes(airspaceInfos: AirspaceInfo[], style?: ShapeStyles, currentAirspaceIndex?: number) {
|
|
|
+ let {markers, polygons, circles, lines, regionPoints} =
|
|
|
+ getShapesSelector(()=>airspaceInfos, style, ()=>currentAirspaceIndex)
|
|
|
+ return {
|
|
|
+ markers: markers(airspaceInfos),
|
|
|
+ circles: circles(airspaceInfos),
|
|
|
+ lines: lines(airspaceInfos),
|
|
|
+ polygons: polygons(airspaceInfos),
|
|
|
+ regionPoints: regionPoints(airspaceInfos)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 总共 5 种格式, http://git.corp.brilliantaero.com/BA/Coco/issues/99#note_6358
|
|
|
+// 1. 全输出格式
|
|
|
+// 2. 简化格式
|
|
|
+// 3. 传真格式
|
|
|
+// 4. 用户端用的简化格式
|
|
|
+// 5. 极简格式
|
|
|
+
|
|
|
+function getHeight(height: number | string, unit: string | number, type: number) {
|
|
|
+ let shortNum
|
|
|
+ const num = +height; // to number
|
|
|
+
|
|
|
+ if(num >= 100) {
|
|
|
+ shortNum = parseInt(num/100 + '').toString()
|
|
|
+ if(shortNum.length <2) {
|
|
|
+ shortNum = '0' + shortNum
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let heightStandard = Global.heightStandardsById.get(unit)
|
|
|
+ if(!heightStandard) {
|
|
|
+ heightStandard = unit
|
|
|
+ }
|
|
|
+
|
|
|
+ // 这里统一使用数字判断
|
|
|
+ let standardUnit = Global.heightStandards.get(heightStandard)
|
|
|
+ let heightDesc
|
|
|
+ switch(standardUnit) {
|
|
|
+ case 1:
|
|
|
+ heightDesc = ['H*真', '真高*米']
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ heightDesc = ['H*标(含以下)', '标高*米(含以下)']
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ heightDesc = ['H*真(含以下)', '真高*米(含以下)']
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ heightDesc = ['H*标', '标高*米']
|
|
|
+ }
|
|
|
+ if(shortNum && (type == 1 || type == 2)) {
|
|
|
+ // H02真,H02真(含以下)
|
|
|
+ return heightDesc[0].replace('*', shortNum)
|
|
|
+ } else {
|
|
|
+ // 真高200米,真高200米(含以下)
|
|
|
+ return heightDesc[1].replace('*', height + '')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getAirspaceName(airspaceInfo: AirspaceInfo) {
|
|
|
+ if((airspaceInfo as any).airspace_name) {
|
|
|
+ const ai = <AirspaceInfoServer> airspaceInfo;
|
|
|
+ return ai.airspace_name
|
|
|
+ } else {
|
|
|
+ const ai = <AirspaceInfoLocal> airspaceInfo;
|
|
|
+ return ai.name
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export function circleContent(airspaceInfo: AirspaceInfoCircle, type=3) {
|
|
|
+ if(type == 5)
|
|
|
+ return getAirspaceName(airspaceInfo)
|
|
|
+
|
|
|
+ if('airspace_name' in airspaceInfo) {
|
|
|
+ const lat = latLngDecimalToDegrees(airspaceInfo.center_point_of_flying.lat);
|
|
|
+ const lng = latLngDecimalToDegrees(airspaceInfo.center_point_of_flying.lng);
|
|
|
+ let content = [];
|
|
|
+ let loc = `以${airspaceInfo.center_loc}`
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ loc += `(E${lng}, N${lat})`;
|
|
|
+ content.push(`${loc}为中心`)
|
|
|
+ content.push(`半径${airspaceInfo.radius_of_flying}米`);
|
|
|
+ content.push(getHeight(airspaceInfo.altitude, airspaceInfo.unit, type))
|
|
|
+ if (airspaceInfo.note)
|
|
|
+ content.push(`备注:${airspaceInfo.note}`)
|
|
|
+
|
|
|
+ return content.join(',');
|
|
|
+ } else {
|
|
|
+ let content = []
|
|
|
+ let loc = `以${airspaceInfo.addr}`
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ loc += `(E${airspaceInfo.lng}, N${airspaceInfo.lat})`;
|
|
|
+ content.push(`${loc}为中心`)
|
|
|
+ content.push(`半径${airspaceInfo.radius}米`);
|
|
|
+ content.push(getHeight(airspaceInfo.height, airspaceInfo.heightStandard, type))
|
|
|
+ if (airspaceInfo.note)
|
|
|
+ content.push(`备注:${airspaceInfo.note}`)
|
|
|
+ return content.join(',');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function flyingCenter(item: CoordinateShort | {} = {}): string{
|
|
|
+ if(item == {}){
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ const pp = <CoordinateShort> item;
|
|
|
+ return (
|
|
|
+ "(E" + latLngDecimalToDegrees(pp.lng) +
|
|
|
+ ', ' +
|
|
|
+ "N" + latLngDecimalToDegrees(pp.lat) + ")"
|
|
|
+ );
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+export function lineContent(airspaceInfo: AirspaceInfoLine, type=3): string {
|
|
|
+ if(type == 5)
|
|
|
+ return getAirspaceName(airspaceInfo)
|
|
|
+
|
|
|
+ if('airspace_name' in airspaceInfo) {
|
|
|
+ let content = [];
|
|
|
+ content.push(`${airspaceInfo.start_loc}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`${flyingCenter(airspaceInfo.start_point)}`)
|
|
|
+ content.push(` - `)
|
|
|
+ content.push(getHeight(airspaceInfo.start_point.altitude, airspaceInfo.start_point.unit, type))
|
|
|
+
|
|
|
+ const passing_points = airspaceInfo.passing_points;
|
|
|
+
|
|
|
+ if(Array.isArray(passing_points)) {
|
|
|
+ for(let i = 0; i < passing_points.length; i++) {
|
|
|
+ const obj = passing_points[i];
|
|
|
+ if (obj.point_type == Global.pointTypes.point) {
|
|
|
+ let pp = <PassingPointNormalServer>obj;
|
|
|
+ const lat = latLngDecimalToDegrees(pp.lat)
|
|
|
+ const lng = latLngDecimalToDegrees(pp.lng)
|
|
|
+ content.push(` - ${pp.point_name}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`(E${lng}, N${lat})`)
|
|
|
+ } else if (obj.point_type == Global.pointTypes.nav) {
|
|
|
+ let pp = <PassingPointNavServer>obj;
|
|
|
+ const lat = latLngDecimalToDegrees(pp.lat)
|
|
|
+ const lng = latLngDecimalToDegrees(pp.lng)
|
|
|
+ content.push(` - ${pp.point_code}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`(E${lng}, N${lat})`)
|
|
|
+ } else {
|
|
|
+ let pp = <PassingPointAirlineServer>obj;
|
|
|
+ content.push(` - ${pp.air_route_code}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ if(obj.altitude) {
|
|
|
+ content.push(` - ${getHeight(obj.altitude, obj.unit, type)}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ content.push(` - ${airspaceInfo.end_loc}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`${flyingCenter(airspaceInfo.end_point)}`)
|
|
|
+
|
|
|
+ if(isSafeString(airspaceInfo.airline_width)) {
|
|
|
+ content.push(`,宽度:${airspaceInfo.airline_width}米`)
|
|
|
+ }
|
|
|
+
|
|
|
+ if(isSafeString(airspaceInfo.note)) {
|
|
|
+ content.push(`,备注: ${airspaceInfo.note}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ let result = content.join("")
|
|
|
+ return result;
|
|
|
+ } else {
|
|
|
+ let content = [];
|
|
|
+ content.push(`${airspaceInfo.dep.addr}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`(E${airspaceInfo.dep.lng}, N${airspaceInfo.dep.lat})`)
|
|
|
+ content.push(` - ${getHeight(airspaceInfo.dep.height, airspaceInfo.dep.heightStandard, type)}`);
|
|
|
+
|
|
|
+ if (Array.isArray(airspaceInfo.passPoints)) {
|
|
|
+ let length = airspaceInfo.passPoints.length;
|
|
|
+ for (let i = 0; i < length; i++) {
|
|
|
+ let obj = airspaceInfo.passPoints[i];
|
|
|
+ if (obj.pointType == Global.pointTypes.point) {
|
|
|
+ let pp = <PassingPointNormalLocal>obj;
|
|
|
+ content.push(` - ${pp.addr}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`(E${pp.lng}, N${pp.lat})`);
|
|
|
+ } else if (obj.pointType == Global.pointTypes.nav) {
|
|
|
+ let pp = <PassingPointNavLocal>obj;
|
|
|
+ content.push(` - ${pp.pointCode}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`(E${pp.lng}, N${pp.lat})`);
|
|
|
+ } else {
|
|
|
+ let pp = <PassingPointAirlineLocal>obj;
|
|
|
+ content.push(` - ${pp.airlineCode}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(obj.height) {
|
|
|
+ content.push(` - ${getHeight(obj.height, obj.heightStandard, type)}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ content.push(` - ${airspaceInfo.arrive.addr}`)
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ content.push(`(E${airspaceInfo.arrive.lng}, N${airspaceInfo.arrive.lat})`);
|
|
|
+ if(airspaceInfo.airlineWidth) {
|
|
|
+ content.push(`,宽度:${airspaceInfo.airlineWidth}米`)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (airspaceInfo.note)
|
|
|
+ content.push(`,备注:${airspaceInfo.note}`)
|
|
|
+ return content.join('');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export function polygonContent(airspaceInfo: AirspaceInfoPolygon, type=3): string {
|
|
|
+ if(type == 5)
|
|
|
+ return getAirspaceName(airspaceInfo)
|
|
|
+
|
|
|
+ if('airspace_name' in airspaceInfo) {
|
|
|
+ let res = []
|
|
|
+ let points = airspaceInfo.points
|
|
|
+ for (let i = 0; i < points.length; i++) {
|
|
|
+ let c = `${points[i].addr ? points[i].addr : ''}`
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ c += `(E${latLngDecimalToDegrees(points[i].lng)}, N${latLngDecimalToDegrees(points[i].lat)})`;
|
|
|
+ res.push(c)
|
|
|
+ }
|
|
|
+
|
|
|
+ let content = [res.join('、')]
|
|
|
+ content.push(`${airspaceInfo.points.length}点连线范围内`)
|
|
|
+ content.push(`,${getHeight(airspaceInfo.altitude, airspaceInfo.unit, type)}`)
|
|
|
+ if(isSafeString(airspaceInfo.note)) {
|
|
|
+ content.push(`,备注:${airspaceInfo.note}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return content.join('');
|
|
|
+ } else {
|
|
|
+ let content = [];
|
|
|
+ let length = airspaceInfo.polygonPoints.length;
|
|
|
+ for (let i = 0; i < length; i++) {
|
|
|
+ let obj = airspaceInfo.polygonPoints[i];
|
|
|
+ let c = `${obj.addr ? obj.addr : ''}`
|
|
|
+ if(type == 1 || type == 3)
|
|
|
+ c += `(E${obj.lng}, N${obj.lat})`
|
|
|
+ content.push(c)
|
|
|
+ }
|
|
|
+ let cc = content.join('、') + `${length}点连线范围内`
|
|
|
+ cc += `,${getHeight(airspaceInfo.height, airspaceInfo.heightStandard, type)}`
|
|
|
+ if (airspaceInfo.note)
|
|
|
+ cc = `${content},备注:${airspaceInfo.note}`
|
|
|
+ return cc;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export {latLngDegreesToDecimal, latLngDecimalToDegrees}
|