Эх сурвалжийг харах

改成 typescript 代码,增加测试用例

wd 7 жил өмнө
parent
commit
fc399cbe9e
13 өөрчлөгдсөн 2150 нэмэгдсэн , 1066 устгасан
  1. 2 1
      .eslintrc
  2. 0 32
      Common.js
  3. 31 0
      Common.ts
  4. 24 33
      Utils.ts
  5. 30 0
      __tests__/convertAirspaceInfo-test.ts
  6. 44 0
      __tests__/shapes-test.ts
  7. 80 0
      __tests__/utils-test.ts
  8. 166 0
      __tests__/vars.ts
  9. 0 801
      index.js
  10. 1349 0
      index.ts
  11. 35 8
      package.json
  12. 36 0
      tsconfig.json
  13. 353 191
      yarn.lock

+ 2 - 1
.eslintrc

@@ -1,7 +1,8 @@
 {
     "parser": "babel-eslint",
     "env": {
-        "es6": true
+        "es6": true,
+        "jest": true
     },
     "parserOptions": {
         "ecmaVersion": 6,

+ 0 - 32
Common.js

@@ -1,32 +0,0 @@
-let Global = {
-    airspaceType: {
-        circle: 1,
-        line: 2,
-        polygon: 3
-    },
-
-    pointTypes: {
-        nav: 1,        // 导航点
-        line: 2,        // 航线
-        point: 3        // 普通航路点
-    }
-
-}
-
-Global.amapLineWidth = 3;
-Global.amapStrokeColor = "#58A8F5";
-
-Global.amapFillColor = "#58A8F511";
-
-Global.heightStandards = new Map([
-    ["米/标高", 0],
-    ["米/真高", 1],
-    ["米(含以下)/标高", 2],
-    ["米(含以下)/真高", 3]
-]);
-Global.heightStandardsById = new Map();
-for (let [k, v] of Global.heightStandards) {
-    Global.heightStandardsById.set(v, k);
-}
-
-module.exports = Global;

+ 31 - 0
Common.ts

@@ -0,0 +1,31 @@
+export default class Global {
+    static airspaceType = {
+        circle: 1,
+        line: 2,
+        polygon: 3
+    };
+
+    static pointTypes = {
+        nav: 1,        // 导航点
+        line: 2,        // 航线
+        point: 3        // 普通航路点
+    };
+
+    static amapLineWidth = 3;
+    static amapStrokeColor = "#58A8F5";
+
+    static amapFillColor = "#58A8F511";
+
+    static heightStandards = new Map([
+        ["米/标高", 0],
+        ["米/真高", 1],
+        ["米(含以下)/标高", 2],
+        ["米(含以下)/真高", 3]
+    ]);
+    static heightStandardsById = new Map();
+
+}
+
+for (let [k, v] of Global.heightStandards) {
+    Global.heightStandardsById.set(v, k);
+}

+ 24 - 33
Utils.js → Utils.ts

@@ -1,5 +1,3 @@
-
-
 /*
     205.395583333332 = 205°23'44.1"
     1,直接读取"度":205
@@ -7,7 +5,7 @@
     3,(23.734999999920-23)*60=44.099999995200 得到"秒":44.1
     return string,
 */
-function latLngDecimalToDegrees(decimal) {
+export function latLngDecimalToDegrees(decimal: number): string {
     let absDecimal = Math.abs(decimal);
     let isNegative = Math.abs(decimal) != decimal;
     let d = Math.floor(absDecimal); //度
@@ -16,10 +14,10 @@ function latLngDecimalToDegrees(decimal) {
     if(s == 60) {s = 0; m++}
     if(m == 60) {m = 0; d++}
     //d = ('000'+d).slice(-3);                   // left-pad with leading zeros
-    m = ('00'+m).slice(-2);                    // left-pad with leading zeros
-    s = ('00'+s).slice(-2);
+    const mm = ('00'+m).slice(-2);                    // left-pad with leading zeros
+    const ss = ('00'+s).slice(-2);
     //if (s<10) s = '0' + s;                     // left-pad with leading zeros (note may include decimals)
-    return (isNegative ? "-" : "") + d + '°' + m + "′" + s + '″';
+    return (isNegative ? "-" : "") + d + '°' + mm + "′" + ss + '″';
 }
 
 /*
@@ -27,46 +25,45 @@ function latLngDecimalToDegrees(decimal) {
     例:57°55'56.6" =57+55/60+56.6/3600=57.9323888888888
     return Float or NaN
 */
-function latLngDegreesToDecimal(degreesStr) {
+export function latLngDegreesToDecimal(degreesStr: string): number {
     let degreesArr = degreesStr.split("°");
-    let degrees = degreesArr[0];
+    let degrees = parseInt(degreesArr[0]);
     let isNegative = Math.abs(degrees) != degrees;
     if (degreesArr.length == 1) {
-        return parseInt(degrees);
+        return degrees;
+    } else if(!isSafeString(degreesArr[1])) {
+        return degrees;
     }
 
     let minutesArr = degreesArr[1].split("′");
-    let minutes = minutesArr[0];
+    let minutes = parseInt(minutesArr[0]);
+    let min = parseFloat((isNegative ? "-" : "") + (Math.abs(degrees) + Math.abs(minutes) / 60));
     if (minutesArr.length == 1) {
-        return parseFloat((isNegative ? "-" : "") + (Math.abs(degrees) + Math.abs(minutes) / 60));
+        return min;
+    } else if(!isSafeString(minutesArr[1])) {
+        return min;
     }
 
     let secondsStr = minutesArr[1];
     let secondsArr = secondsStr.split('″');
-    let seconds = secondsArr[0];
+    let seconds = parseFloat(secondsArr[0]);
 
     return parseFloat((isNegative ? "-" : "") + (Math.abs(degrees) + Math.abs(minutes) / 60 + Math.abs(seconds) / 3600));
 }
 
-function isObject(arg) {
+export function isObject(arg: any): boolean {
     return typeof arg === 'object' && arg !== null;
 }
 
-function isUndefined(arg) {
+export function isUndefined(arg: any): boolean {
     return arg === void 0;
 }
 
-function isString(arg) {
+export function isString(arg: any): boolean {
     return typeof arg === 'string';
 }
 
-function hasLine(obj) {
-    return (isObject(obj)
-        && isSafeString(obj.lat)
-        && isSafeString(obj.lng));
-}
-
-function isSafeString(arg) {
+export function isSafeString(arg: any): boolean {
     if (isUndefined(arg)) {
         return false;
     }
@@ -82,18 +79,12 @@ function isSafeString(arg) {
     return true;
 }
 
-function hasPoint(obj) {
+export function isNumber(arg: any): boolean {
+    return typeof arg === 'number';
+}
+
+export function hasPoint(obj: any): boolean {
     return (isObject(obj)
         && isSafeString(obj.lat)
         && isSafeString(obj.lng));
 }
-module.exports = {
-    latLngDecimalToDegrees,
-    latLngDegreesToDecimal,
-    isObject,
-    isUndefined,
-    isString,
-    isSafeString,
-    hasLine,
-    hasPoint
-}

+ 30 - 0
__tests__/convertAirspaceInfo-test.ts

@@ -0,0 +1,30 @@
+import {convertAirspaceInfoLocalToServer, convertAirspaceInfoServerToLocal} from '../index';
+import * as vars from './vars';
+
+let amap = require('../index');
+import Global from '../Common'
+
+test('圆形空域 本地格式 <-> 远端格式', () => {
+    expect(convertAirspaceInfoServerToLocal(vars.circleServer)).toEqual(vars.circleLocal)
+    expect(convertAirspaceInfoLocalToServer(vars.circleLocal)).toEqual(vars.circleServer)
+});
+
+test('线型空域 带宽度 本地格式 <-> 远端格式', () => {
+    expect(convertAirspaceInfoServerToLocal(vars.lineServer1)).toEqual(vars.lineLocal1)
+    expect(convertAirspaceInfoLocalToServer(vars.lineLocal1)).toEqual(vars.lineServer1)
+});
+
+test('多边形空域 本地格式 <-> 远端格式', () => {
+    expect(convertAirspaceInfoServerToLocal(vars.polygonServer)).toEqual(vars.polygonLocal)
+    expect(convertAirspaceInfoLocalToServer(vars.polygonLocal)).toEqual(vars.polygonServer)
+});
+
+test('线型空域 不带宽度 本地格式 <-> 远端格式', () => {
+    expect(convertAirspaceInfoServerToLocal(vars.lineServer2)).toEqual(vars.lineLocal2)
+    expect(convertAirspaceInfoLocalToServer(vars.lineLocal2)).toEqual(vars.lineServer2)
+});
+
+test('线型空域 无 PassingPoints 本地格式 <-> 远端格式', () => {
+    expect(convertAirspaceInfoServerToLocal(vars.lineServer3)).toEqual(vars.lineLocal3)
+    expect(convertAirspaceInfoLocalToServer(vars.lineLocal3)).toEqual(vars.lineServer3)
+});

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 44 - 0
__tests__/shapes-test.ts


+ 80 - 0
__tests__/utils-test.ts

@@ -0,0 +1,80 @@
+import {
+    latLngDegreesToDecimal,
+    latLngDecimalToDegrees,
+    isObject,
+    isSafeString,
+    hasPoint,
+    isNumber,
+    isUndefined
+} from '../Utils'
+
+let tests1: [number, string][] = [
+    [205.395583333332, '205°23′44″'],
+    [0, '0°00′00″'],
+    [-100, '-100°00′00″'],
+    [180, '180°00′00″'],
+    [360, '360°00′00″'],
+    [900, '900°00′00″']
+]
+
+for(let t of tests1) {
+    test(`latLng 数值 -> 度分秒: ${t[0]} -> ${t[1]}`, () => {
+        expect(latLngDecimalToDegrees(t[0])).toBe(t[1]);
+    })
+}
+
+let tests2: [number, string][] = [
+    [205.39555555555555, '205°23′44″'],
+    [0, '0°00′00″'],
+    [-100, '-100°00′00″'],
+    [180, '180°00′00″'],
+    [360, '360°00′00″'],
+    [900, '900°00′00″'],
+    [900, '900°'],
+    [900, '900°00′']
+]
+for(let t of tests2) {
+    test(`latLng 度分秒 -> 数值: ${t[1]} -> ${t[0]}`, () => {
+        expect(latLngDegreesToDecimal(t[1])).toBe(t[0]);
+    })
+}
+
+let input: any[] = [undefined, true, {}, [], 1, 0, null, Object.create({}), '', '111', NaN, Infinity]
+
+let tests3: boolean[] = [false, false, true, true, false, false, false, true, false, false, false, false]
+let tests4: boolean[] = [true, false, false, false, false, false, false, false, false, false, false, false]
+let tests5: boolean[] = [false, false, false, false, false, false, false, false, false, true, false, false]
+let tests6: boolean[] = [false, false, false, false, true, true, false, false, false, false, true, true]
+
+for(let i=0; i<input.length; i++) {
+    let ii = input[i];
+    test(`isObject: ${ii} = ${tests3[i]}`, () => {
+        expect(isObject(ii)).toBe(tests3[i]);
+    })
+
+    test(`isUndefined: ${ii} = ${tests4[i]}`, () => {
+        expect(isUndefined(ii)).toBe(tests4[i]);
+    })
+
+    test(`isSafeString: ${ii} = ${tests5[i]}`, () => {
+        expect(isSafeString(ii)).toBe(tests5[i]);
+    })
+
+    test(`isNumber: ${ii} = ${tests6[i]}`, () => {
+        expect(isNumber(ii)).toBe(tests6[i]);
+    })
+}
+
+let tests7: [any, boolean][] = [
+    [{'lat': '111.11', 'lng': '222.22'}, true],
+    [{'lat': 111.11, 'lng': 222.22}, false],
+    [{'lat': 111.11, 'lng': '222.22'}, false],
+    [{'lat': 111.11}, false],
+    [{}, false]
+]
+
+for(let t of tests7) {
+    test(`hasPoint: ${t[0]} = ${t[1]}`, () => {
+        expect(hasPoint(t[0])).toBe(t[1])
+    })
+}

+ 166 - 0
__tests__/vars.ts

@@ -0,0 +1,166 @@
+import * as AirspaceInfo from '../index';
+
+export const circleServer: AirspaceInfo.AirspaceInfoCircleServer = {
+    airspace_name: "test",
+    airspace_type: 1,
+    airspace_id: 123,
+    note: "notes note",
+
+    center_loc: "loc name",
+    center_point_of_flying: {
+        lat: 123.4,
+        lng: 40.3
+    },
+    radius_of_flying: 123,
+    altitude: 1000,
+    unit: 1
+}
+
+export const circleLocal = <AirspaceInfo.AirspaceInfoCircleLocal>{"addr": "loc name", "airspaceId": 123, "airspaceType": 1, "height": "1000", "heightStandard": "米/真高", "lat": "123°24′00″", "lng": "40°18′00″", "name": "test", "note": "notes note", "radius": 123}
+
+export const polygonServer: AirspaceInfo.AirspaceInfoPolygonServer = {
+    airspace_name: 'test',
+    airspace_type: 3,
+    airspace_id: 123,
+    note: 'notes',
+    points: [{
+        addr: 'test',
+        lat: 123.4,
+        lng: 40.3
+    }],
+    altitude: 123,
+    unit: 1
+}
+
+export const polygonLocal: AirspaceInfo.AirspaceInfoPolygonLocal = {"airspaceId": 123, "airspaceType": 3, "height": "123", "heightStandard": "米/真高", "name": "test", "note": "notes", "polygonPoints": [{"addr": "test", "lat": "123°24′00″", "lng": "40°18′00″"}]}
+
+export const lineServer1: AirspaceInfo.AirspaceInfoLineServer = {
+    airspace_name: "test",
+    airspace_type: 2,
+    airspace_id: 123,
+    note: "notes note",
+
+    start_loc: "start loc",
+    start_point: {
+        lat: 123.4,
+        lng: 40.3,
+        altitude: 111,
+        unit: 2
+
+    },
+    end_loc: "end loc",
+    end_point: {
+        lat: 123.4,
+        lng: 40.3
+    },
+    passing_points: [{
+        point_type: 3,
+        altitude: 1000,
+        unit: 0,
+        point_name: "test point",
+        lat: 123.4,
+        lng: 40.3
+    },{
+        point_type: 2,
+        altitude: 1111,
+        unit: 1,
+        airway: 123,
+        air_route_code: "B334",
+        points: [{
+            point_id: 123,
+            point_name: 'test point',
+            point_code: 'UAV22',
+            lat: 123.4,
+            lng: 40.3
+        }]
+    },{
+        point_type: 1,
+        altitude: 12222,
+        unit: 3,
+        point_id: 123,
+        point_code: '123',
+        point_name: 'test point',
+        lat: 123.4,
+        lng: 40.3
+    }],
+    airline_width: 12
+}
+
+export const lineLocal1: AirspaceInfo.AirspaceInfoLineLocal = {"airlineWidth": 12, "airspaceId": 123, "airspaceType": 2, "arrive": {"addr": "end loc", "lat": "123°24′00″", "lng": "40°18′00″"}, "dep": {"addr": "start loc", "height": "111", "heightStandard": "米(含以下)/标高", "lat": "123°24′00″", "lng": "40°18′00″"}, "name": "test", "note": "notes note", "passPoints": [{"addr": "test point", "height": "1000", "heightStandard": "米/标高", "lat": "123°24′00″", "lng": "40°18′00″", "pointType": 3}, {"airRouteId": 123, "airlineCode": "B334", "airlines": [{"lat": 123.4, "lng": 40.3, "point_code": "UAV22", "point_id": 123, "point_name": "test point"}], "height": "1111", "heightStandard": "米/真高", "pointType": 2}, {"height": "12222", "heightStandard": "米(含以下)/真高", "lat": "123°24′00″", "lng": "40°18′00″", "pointCode": "123", "pointId": 123, "pointName": "test point", "pointType": 1}]}
+
+export const lineServer2: AirspaceInfo.AirspaceInfoLineServer = {
+    airspace_name: "test",
+    airspace_type: 2,
+    airspace_id: 123,
+    note: "notes note",
+
+    start_loc: "start loc",
+    start_point: {
+        lat: 123.4,
+        lng: 40.3,
+        altitude: 111,
+        unit: 2
+
+    },
+    end_loc: "end loc",
+    end_point: {
+        lat: 123.4,
+        lng: 40.3
+    },
+    passing_points: [{
+        point_type: 3,
+        altitude: 1000,
+        unit: 0,
+        point_name: "test point",
+        lat: 123.4,
+        lng: 40.3
+    },{
+        point_type: 2,
+        altitude: 1111,
+        unit: 1,
+        airway: 123,
+        air_route_code: "B334",
+        points: [{
+            point_id: 123,
+            point_name: 'test point',
+            point_code: 'UAV22',
+            lat: 123.4,
+            lng: 40.3
+        }]
+    },{
+        point_type: 1,
+        altitude: 12222,
+        unit: 3,
+        point_id: 123,
+        point_code: '123',
+        point_name: 'test point',
+        lat: 123.4,
+        lng: 40.3
+    }]
+}
+
+export const lineLocal2: AirspaceInfo.AirspaceInfoLineLocal = {"airspaceId": 123, "airspaceType": 2, "arrive": {"addr": "end loc", "lat": "123°24′00″", "lng": "40°18′00″"}, "dep": {"addr": "start loc", "height": "111", "heightStandard": "米(含以下)/标高", "lat": "123°24′00″", "lng": "40°18′00″"}, "name": "test", "note": "notes note", "passPoints": [{"addr": "test point", "height": "1000", "heightStandard": "米/标高", "lat": "123°24′00″", "lng": "40°18′00″", "pointType": 3}, {"airRouteId": 123, "airlineCode": "B334", "airlines": [{"lat": 123.4, "lng": 40.3, "point_code": "UAV22", "point_id": 123, "point_name": "test point"}], "height": "1111", "heightStandard": "米/真高", "pointType": 2}, {"height": "12222", "heightStandard": "米(含以下)/真高", "lat": "123°24′00″", "lng": "40°18′00″", "pointCode": "123", "pointId": 123, "pointName": "test point", "pointType": 1}]}
+
+export const lineServer3: AirspaceInfo.AirspaceInfoLineServer = {
+    airspace_name: "test",
+    airspace_type: 2,
+    airspace_id: 123,
+    note: "notes note",
+
+    start_loc: "start loc",
+    start_point: {
+        lat: 123.4,
+        lng: 40.3,
+        altitude: 111,
+        unit: 2
+
+    },
+    end_loc: "end loc",
+    end_point: {
+        lat: 123.4,
+        lng: 40.3
+    },
+    passing_points: []
+}
+
+export const lineLocal3: AirspaceInfo.AirspaceInfoLineLocal = {"airspaceId": 123, "airspaceType": 2, "arrive": {"addr": "end loc", "lat": "123°24′00″", "lng": "40°18′00″"}, "dep": {"addr": "start loc", "height": "111", "heightStandard": "米(含以下)/标高", "lat": "123°24′00″", "lng": "40°18′00″"}, "name": "test", "note": "notes note", "passPoints": []}

+ 0 - 801
index.js

@@ -1,801 +0,0 @@
-import { createSelector } from 'reselect';
-import Global from './Common';
-import {
-    latLngDegreesToDecimal,
-    latLngDecimalToDegrees,
-    isObject,
-    isSafeString
-} from './Utils'
-let LatLon = require('geodesy').LatLonSpherical;
-
-function getCircleRegions(circle) {
-    let latlon = new LatLon(circle.coordinate.latitude, circle.coordinate.longitude)
-    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() {
-    let imageName = 'BA_oval'
-    let lineWidth = Global.amapLineWidth
-    let strokeColor = Global.amapStrokeColor
-    let fillColor = Global.amapFillColor
-
-    return {imageName, lineWidth, strokeColor, fillColor}
-}
-
-function getCirclesAndMarkers(airspaceInfos, setStyle, currentAirspaceIndex) {
-    let circles = [];
-    let markers = [];
-    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, dataType
-        if (tmpCircle.airspaceType) {
-            airspaceTypeFix = 'airspaceType';
-            radiusFix = 'radius'
-            dataType = 1
-        } else {
-            airspaceTypeFix = 'airspace_type';
-            radiusFix = 'radius_of_flying'
-            dataType = 2
-        }
-
-        if (tmpCircle[airspaceTypeFix] == Global.airspaceType.circle && currentAirspaceIndex != i) {
-            let coordinate = {};
-            if (tmpCircle.center_point_of_flying) {
-                coordinate.latitude = tmpCircle.center_point_of_flying.lat;
-                coordinate.longitude = tmpCircle.center_point_of_flying.lng;
-            } else {
-                coordinate.latitude = latLngDegreesToDecimal(tmpCircle.lat);
-                coordinate.longitude = latLngDegreesToDecimal(tmpCircle.lng);
-            }
-
-            let radius = tmpCircle[radiusFix];
-
-            if(radius) {
-                let circle = {lineWidth, strokeColor, fillColor, radius, coordinate}
-                circles.push(circle);
-            } else {
-                markers.push(addOvalPointConfig(coordinate.latitude, coordinate.longitude, imageName, dataType));
-            }
-        }
-    }
-
-    return {circles, markers};
-}
-
-function getCircleAndMarkerSelector(airspaceInfos, setStyle, currentAirspaceIndex) {
-    return createSelector(
-        airspaceInfos,
-        () => setStyle,
-        currentAirspaceIndex,
-        getCirclesAndMarkers
-    );
-}
-
-function getLatLng(latlng, dataType) {
-    if(dataType == 1) { // 驼峰模式,新建计划的时候的格式
-        return latLngDegreesToDecimal(latlng)
-    } else {
-        return latlng
-    }
-}
-
-function drawLineConfig(lat, lng, dataType) {
-    return {
-        latitude: getLatLng(lat, dataType),
-        longitude: getLatLng(lng, dataType)
-    };
-}
-
-function addOvalPointConfig(lat, lng, imageName, dataType) {
-    return {
-        coordinate: {
-            latitude: getLatLng(lat, dataType),
-            longitude: getLatLng(lng, dataType)
-        },
-        imageName: imageName
-    };
-}
-
-function pointCompare(point1, point2) {
-    const pointId1 = point1.point_id || point1.pointId;
-    const pointId2 = point2.point_id || point2.pointId;
-
-    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, points2) {
-    for(let point1 of points1) {
-        for(let point2 of points2) {
-            if (pointCompare(point1, point2))
-                return point1
-        }
-    }
-}
-
-function getAirwayPoints(airway, pointBefore, pointAfter) {
-    let found = 0
-    let points = []
-
-    let pointTypeFix, pointsFix
-
-    if ('points' in airway) {
-        pointTypeFix = 'point_type';
-        pointsFix = 'points'
-    } else {
-        pointTypeFix = 'pointType'
-        pointsFix = 'airlines'
-    }
-
-    // 如果前后是其他航线,那么找到交叉点作为前后的点
-    if ( pointBefore[pointTypeFix] == Global.pointTypes.line ) {
-        pointBefore = getCrossPoint(airway[pointsFix], pointBefore[pointsFix])
-    }
-
-    if(pointAfter[pointTypeFix] == Global.pointTypes.line) {
-        pointAfter = getCrossPoint(airway[pointsFix], pointAfter[pointsFix])
-    }
-
-    for (let point of airway[pointsFix]) {
-        if (pointCompare(pointBefore, point) || pointCompare(pointAfter, point)) {
-            found++
-            points.push(Object.assign({}, point))
-            continue
-        }
-
-        if (found == 1) {
-            points.push(Object.assign({}, point))
-        }
-    }
-
-    if (!(points.length > 0 && found == 2)) {
-        // 如果两个点不全在航线上面,那么画全部航线
-        points = airway[pointsFix]
-    }
-
-    return points;
-}
-
-
-
-function getLinesRouter(lineProps, lineAndMarkerStyle) {
-    let coordinates = new Array();
-    let markers = new Array();
-    let lines = []
-
-    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.start_point) {
-        dataType = 2 // 下划线模式
-
-        startPoint = lineProps['start_point']
-        passPoints = lineProps['passing_points']
-        endPoint = lineProps['end_point']
-        pointTypeFix = 'point_type';
-        airlineWidth = parseInt(lineProps['airline_width'], 10)
-    } else {
-        dataType = 1 // 驼峰模式
-
-        startPoint = lineProps['dep']
-        passPoints = lineProps['passPoints']
-        endPoint = lineProps['arrive']
-        pointTypeFix = 'pointType'
-        airlineWidth = parseInt(lineProps['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[pointTypeFix]
-            if ( pointType == Global.pointTypes.point
-                || pointType == Global.pointTypes.nav) {
-                coordinates.push(drawLineConfig(obj.lat, obj.lng, dataType));
-                markers.push(addOvalPointConfig(obj.lat, obj.lng, imageName, dataType));
-            } else {
-                // 遇到一个航线,不需要和前前面的点连起来
-                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(obj, pointBefore, 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) {
-    let lat = point.latitude ? point.latitude : point.lat
-    let lng = point.longitude ? point.longitude : point.lng
-
-    // 复制计划的数据,有的点是度数模式如 38°35′17″
-    if(isSafeString(lat)) {
-        lat = latLngDegreesToDecimal(lat);
-        lng = latLngDegreesToDecimal(lng);
-    }
-
-    return [lat, lng]
-}
-
-function processAirlineWidth(lines, airlineWidth) {
-    let polygons = []
-    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 = new LatLon(lat1, lng1)
-            let point2 = new LatLon(lat2, lng2)
-
-            let coordinates = getCirclePoints(point1, point2, airlineWidth)
-            polygons.push({lineWidth: 1, strokeColor, fillColor, coordinates})
-        }
-    }
-    return polygons
-}
-
-function getCirclePoints(point1, point2, width) {
-    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, digits) {
-    if(digits == null)
-        digits = 6 // 比较的精度,经纬度会被经过度分秒方式到浮点方式的转化
-
-    return Math.round(num * Math.pow(10, digits)) / Math.pow(10, digits)
-}
-
-function getLinesPolygonsAndMarkers(airspaceInfos, setStyle, currentAirspaceIndex) {
-    let retLines = [];
-    let retMarkers = [];
-    let retPolygons = [];
-    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.airspaceType)
-            airspaceTypeFix = 'airspaceType';
-        else
-            airspaceTypeFix = 'airspace_type';
-        if (tmpLine[airspaceTypeFix] == Global.airspaceType.line && currentAirspaceIndex != i) {
-            let { lines, markers, polygons } = getLinesRouter(tmpLine, lineStyle);
-            retMarkers.push(...markers);
-            retLines.push(...lines);
-            retPolygons.push(...polygons)
-        }
-    }
-
-    return { lines: retLines, markers: retMarkers, polygons: retPolygons };
-}
-
-
-function getLinePolygonsAndMarkerSelector(airspaceInfos, setStyle, currentAirspaceIndex) {
-    return createSelector(
-        airspaceInfos,
-        () => setStyle,
-        currentAirspaceIndex,
-        getLinesPolygonsAndMarkers
-    );
-}
-
-function getPolygon(polygonProps, polygonAndMarkerStyle) {
-    let coordinates = new Array();
-    let markers = 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.points) {
-        pointsFix = 'points';
-        dataType = 2
-    } else {
-        pointsFix = 'polygonPoints';
-        dataType = 1 // 驼峰模式
-    }
-
-    if (Array.isArray(polygonProps[pointsFix])) {
-        for (let obj of polygonProps[pointsFix]) {
-            if (!obj) {
-                continue
-            }
-            coordinates.push(drawLineConfig(obj.lat, obj.lng, dataType));
-            markers.push(addOvalPointConfig(obj.lat, obj.lng, imageName, dataType));
-        }
-    }
-
-    let polygon = {lineWidth, strokeColor, fillColor, coordinates};
-
-    return { markers, polygon };
-
-}
-
-
-function getPolygonsAndMarkers(airspaceInfos, setStyle, currentAirspaceIndex) {
-    let markers = [];
-    let polygons = [];
-    if (!Array.isArray(airspaceInfos)) {
-        return { markers, polygons };
-    }
-    let polygonAndMarkerStyle = setStyle('polygon');
-    for (let i = 0; i < airspaceInfos.length; i++) {
-        let polygon = airspaceInfos[i]
-        let airspaceTypeFix;
-        if (polygon.airspaceType)
-            airspaceTypeFix = 'airspaceType';
-        else
-            airspaceTypeFix = 'airspace_type';
-
-        if (polygon[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, setStyle, currentAirspaceIndex) {
-    return createSelector(
-        airspaceInfos,
-        () => setStyle,
-        currentAirspaceIndex,
-        getPolygonsAndMarkers
-    );
-}
-
-function getMarkers(circlesAndMarkers, polygonAndMarkers, lineAndMarkers) {
-    let markers = [];
-    if (circlesAndMarkers) {
-        markers = [...circlesAndMarkers.markers]
-    }
-    if (polygonAndMarkers) {
-        markers = [...markers, ...polygonAndMarkers.markers]
-    }
-    if (lineAndMarkers) {
-        markers = [...markers, ...lineAndMarkers.markers]
-    }
-    return markers
-}
-
-function getMarkerSelector(circlesAndMarkers, polygonAndMarkers, lineAndMarkers) {
-    return createSelector(
-        circlesAndMarkers,
-        polygonAndMarkers,
-        lineAndMarkers,
-        getMarkers
-    )
-}
-
-function getRegionPoints(circles, lineAndMarkers, polygonAndMarkers) {
-    let regionPoints = 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, lineAndMarkers, polygonAndMarkers) {
-    return createSelector(
-        circles,
-        lineAndMarkers,
-        polygonAndMarkers,
-        getRegionPoints
-    );
-}
-
-function getCircles(circleAndMarker) {
-    return circleAndMarker.circles;
-}
-
-function getCircleSelector(circleAndMarker) {
-    return createSelector(
-        circleAndMarker,
-        getCircles
-    );
-}
-
-function getLines(lineAndMarker) {
-    return lineAndMarker.lines;
-}
-
-function getLineSelector(lineAndMarker) {
-    return createSelector(
-        lineAndMarker,
-        getLines
-    );
-}
-
-function getPolygons(polygonAndMarkers, linePolygonsAndMarkers) {
-    return [...polygonAndMarkers.polygons, ...linePolygonsAndMarkers.polygons];
-}
-
-function getPolygonSelector(polygonAndMarkers, linePolygonsAndMarkers) {
-    return createSelector(
-        polygonAndMarkers,
-        linePolygonsAndMarkers,
-        getPolygons
-    );
-}
-
-let setStyle = (style) => {
-    if (!style)
-        return () => { return null }
-    else
-        return (shapeName) => style[shapeName]
-}
-
-//获取selector
-export function getShapesSelector(airspaceInfos, style, currentAirspaceIndex) {
-    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, style, currentAirspaceIndex) {
-    let {markers, polygons, circles, lines, regionPoints} =
-        getShapesSelector((airspaceInfos)=>airspaceInfos, style, (currentAirspaceIndex)=>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(num, unit, type) {
-    let shortNum
-    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('*', num)
-    }
-}
-
-function getAirspaceName(airspaceInfo) {
-    if(airspaceInfo.airspace_name) {
-        return airspaceInfo.airspace_name
-    } else {
-        return airspaceInfo.name
-    }
-}
-
-export function circleContent(airspaceInfo, 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}`)
-
-        let result = content = content.join(',');
-        return result;
-    } 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}`)
-        content = content.join(',');
-        return content;
-    }
-}
-
-function flyingCenter(item = {}){
-    if(item == {}){
-        return ;
-    } else {
-        return (
-            "(E" + latLngDecimalToDegrees(item.lng) +
-            ', ' +
-            "N" + latLngDecimalToDegrees(item.lat) + ")"
-        );
-    }
-}
-
-export function lineContent(airspaceInfo, type=3) {
-    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];
-                const lat = latLngDecimalToDegrees(obj.lat)
-                const lng = latLngDecimalToDegrees(obj.lng)
-                if (obj.point_type == Global.pointTypes.point) {
-                    content.push(` - ${obj.point_name}`)
-                    if(type == 1 || type == 3)
-                        content.push(`(E${lng}, N${lat})`)
-                } else if (obj.point_type == Global.pointTypes.nav) {
-                    content.push(` - ${obj.point_code}`)
-                    if(type == 1 || type == 3)
-                        content.push(`(E${lng}, N${lat})`)
-                } else {
-                    content.push(` - ${obj.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) {
-                    content.push(` - ${obj.addr}`)
-                    if(type == 1 || type == 3)
-                        content.push(`(E${obj.lng}, N${obj.lat})`);
-                } else if (obj.pointType == Global.pointTypes.nav) {
-                    content.push(` - ${obj.pointCode}`)
-                    if(type == 1 || type == 3)
-                        content.push(`(E${obj.lng}, N${obj.lat})`);
-                } else {
-                    content.push(` - ${obj.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}`)
-        content = content.join('');
-        return content;
-    }
-}
-
-export function polygonContent(airspaceInfo, type=3) {
-    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)
-        }
-        content = content.join('、') + `${length}点连线范围内`
-        content += `,${getHeight(airspaceInfo.height, airspaceInfo.heightStandard, type)}`
-        if (airspaceInfo.note)
-            content = `${content},备注:${airspaceInfo.note}`
-        return content;
-    }
-}
-
-export {latLngDegreesToDecimal, latLngDecimalToDegrees}

+ 1349 - 0
index.ts

@@ -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}

+ 35 - 8
package.json

@@ -1,10 +1,10 @@
 {
   "name": "amap-shapes-generator",
-  "version": "0.0.1",
+  "version": "0.1.0",
   "private": true,
   "scripts": {
-    "build": "babel *.js --presets babel-preset-es2015 -d lib",
-    "postinstall": "[ -f ../../node_modules/.bin/babel ] && ../../node_modules/.bin/babel *.js --presets babel-preset-es2015 -d lib || echo 'no babel found, skip build'",
+    "build": "tsc",
+    "postinstall": "[ -f ../../node_modules/.bin/tsc ] && ../../node_modules/.bin/tsc || echo 'no typescript found, skip build'",
     "test": "jest"
   },
   "dependencies": {
@@ -12,11 +12,38 @@
     "reselect": "^3.0.1"
   },
   "devDependencies": {
+    "@types/jest": "^23.3.8",
+    "@types/node": "^10.12.0",
     "babel-cli": "6",
-    "babel-jest": "21.2.0",
-    "babel-preset-es2015": "6",
-    "jest": "21.2.1",
-    "react-test-renderer": "16.0.0-beta.5"
+    "babel-jest": "^23.6.0",
+    "babel-plugin-rewire": "^1.2.0",
+    "babel-preset-env": "^1.7.0",
+    "jest": "^23.6.0",
+    "react-test-renderer": "16.0.0-beta.5",
+    "ts-jest": "^23.10.4",
+    "typescript": "^3.1.4"
   },
-  "jest": {}
+  "jest": {
+    "preset": "ts-jest",
+    "testEnvironment": "node",
+    "testMatch": [ "**/__tests__/*-test.ts" ],
+    "globals": {
+      "ts-jest": {
+        "babelConfig": true,
+        "isolatedModules": true
+      }
+    }
+  },
+  "babel": {
+    "presets": [
+      "env"
+    ],
+    "env": {
+      "test": {
+        "plugins": [
+          "babel-plugin-rewire"
+        ]
+      }
+    }
+  }
 }

+ 36 - 0
tsconfig.json

@@ -0,0 +1,36 @@
+{
+  "compilerOptions": {
+    "target": "es2015",
+    "module": "es2015",
+    "lib": [
+      "es2015"
+    ],
+    "outDir": "./lib",
+    "declaration": true,
+
+    "noEmit": true,
+    "moduleResolution": "node",
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+
+    /* Strict Type-checking */
+    "strict": true,
+    "strictNullChecks": true,
+    "noImplicitAny": true,
+    "noImplicitThis": true,
+    "alwaysStrict": true,
+
+    /* Additional Checks */
+    "noUnusedLocals": true,                /* Report errors on unused locals. */
+    "noUnusedParameters": true,            /* Report errors on unused parameters. */
+    "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
+    "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
+  },
+  "include": [
+     "*.ts"
+  ],
+  "exclude": [
+    "node_modules",
+    "__tests__"
+  ]
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 353 - 191
yarn.lock


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно