Ddongyu1009init
cdf6f0e0创建于 2024年9月6日历史提交
/**
  This file is created by Yu Dong (2023).
  License  : Apache 2.0
  FileName : Polyline.cj
  Author   : Yu Dong
  Version  : 0.0.1
  Date     : 20230424
  Note     : First Commit
 */

/**
 * @file
 * Polyline
 */
package gistools.geometry

import std.math.*
import std.convert.*
import std.collection.*
import encoding.json.*
import encoding.json.stream.*
import serialization.serialization.*

import gistools.*
import gistools.base.*

/**
 * Polyline 
 *  
 * @author Yu Dong
 * @since 0.38.2
 */
public class Polyline <: Geometry & Serializable<Polyline>{

    // the coordinates of the Polyline
    public var coordinates : ArrayList<Coordinate>


    /**
     * The Property is to get the x coordinates
     *
     * @since 0.38.2
     */
    public prop xs : ArrayList<Float64> {
        get() {
            ArrayList(coordinates.size, { i => 
                coordinates[i].x
            })
        }
    }

    /**
     * The Property is to get the y coordinates
     *
     * @since 0.38.2
     */
    public prop ys : ArrayList<Float64> {
        get() {
            ArrayList(coordinates.size, { i => 
                coordinates[i].y
            })
        }
    }

    /**
     * The Function is init constructor
     *
     * @since 0.38.2
     */
    public init() {
        this.coordinates = ArrayList<Coordinate>()
        this.spatialReference = SpatialReference.unknown()
    }


    /**
     * The Function is init constructor
     *
     * @param coordinates of ArrayList<Coordinate>
     *
     * @since 0.38.2
     */
    public init(coordinates : ArrayList<Coordinate>) {
        if (coordinates.size < 2) {
            throw Exception("the size of Coordinates is less than 2. ")
        }
        this.coordinates = coordinates
        this.spatialReference = SpatialReference.unknown()
    }


    /**
     * The Function is init constructor
     *
     * @param coordinates of ArrayList<Coordinate>
     * @param sr of SpatialReference
     *
     * @since 0.38.2
     */
    public init(coordinates : ArrayList<Coordinate>, sr : SpatialReference) {
        if (coordinates.size < 2) {
            throw Exception("the size of Coordinates is less than 2. ")
        }
        this.coordinates = coordinates
        this.spatialReference = sr
    }

    /**
     * The Function is to create polyline by array of coordinates
     *
     * @param arr of ArrayList<Array<Float64>>
     *
     * @return Type of ?Polyline
     * @since 0.38.2
     */
    public static func fromArray(arr : ArrayList<Array<Float64>>) : ?Polyline {
        try {
            let coors = ArrayList<Coordinate>(arr.size, { i =>
                Coordinate.fromArray(arr[i]).getOrThrow()
            })
            Polyline(coors)
        } catch (e : Exception){
            NullPolyline()
        }
    }


    /**
     * The Function is to create null polyline object
     *
     * @return Type of Polyline
     * @since 0.38.2
     */
    public static func NullPolyline() : Polyline {
        Polyline()
    }

    /**
     * The Function is to determine whether the polyline is Null
     *
     * @return Type of Bool
     * @since 0.38.2
     */
    public func isNull() : Bool {
        coordinates.size == 0
    }

    /**
     * The Function is to String
     *
     * @return Type of String
     * @since 0.38.2
     */
    public func toString() {

        if(coordinates.size == 0) {
            return "Polyline[ ] sr:${spatialReference}"
        } 
        var coorStr = ""
        for (coor in coordinates) {
            coorStr += "(${coor.x}, ${coor.y}) "
        }

        "Polyline[${coorStr}] sr:${spatialReference}"
    }
    

    /**
     * The Function is to JSON
     *
     * @return Type of JsonValue
     * @since 0.38.2
     */
    public func toJson(): JsonValue  {
        serialize().toJson()
    }

    /**
     * The Function is to transfrom from data model to json value
     * NaN (Not a number) is not acceptable
     *
     * @param jv of JsonValue
     *
     * @return Type of DataModel
     * @since 0.38.2
     */
    public static func fromJson(jv: JsonValue): DataModel {
        try {
            let geometry = jv.asObject().get("geometry").getOrThrow().asObject()
            let geometry_type = geometry.get("type").getOrThrow().asString().getValue()
            if (geometry_type != "Polyline") {
                return DataModelNull()
            }
            let wkid = match(geometry.get("spatialReference")) {
                case Some(v) => Int64.parse(v.toString())
                case None => 0
            }
            let coordinates = (geometry.get("coordinates").getOrThrow() as JsonArray).getOrThrow()
            let coordinates_dm_list = ArrayList<DataModel>(coordinates.size(), { i =>
                let coordinate = (coordinates[i] as JsonArray).getOrThrow()
                let x = Float64.parse(coordinate[0].toString()) 
                let y = Float64.parse(coordinate[1].toString()) 
                DataModelSeq(ArrayList<DataModel>([DataModelFloat(x), DataModelFloat(y)]))
            })
            let coordinates_dm = DataModelSeq(coordinates_dm_list)
            var geometry_dm = DataModelStruct()
                .add(field<String>("type", "Polyline"))
                .add(Field("coordinates", coordinates_dm))
            if (wkid != 0) {
                geometry_dm = geometry_dm.add(field<Int64>("spatialReference", wkid))
            }
            DataModelStruct().add(Field("geometry", geometry_dm))
        }catch (e : Exception) {
            DataModelNull()
        }
    }

    /**
     * The Function is to serialize the polyline object to data model
     *
     * @return Type of DataModel
     * @since 0.38.2
     */
    public func serialize(): DataModel {

        let coordinates_dm_list = ArrayList<DataModel>(coordinates.size, { i =>
            let x = coordinates[i].x
            let y = coordinates[i].y
            DataModelSeq(ArrayList<DataModel>([DataModelFloat(x), DataModelFloat(y)]))
        })

        let coordinates_dm = DataModelSeq(coordinates_dm_list)
        var geometry_dm = DataModelStruct()
            .add(field<String>("type", "Polyline"))
            .add(Field("coordinates", coordinates_dm))

        if (!spatialReference.isUnknown() && (spatialReference.wkid != 0)) {
            geometry_dm = geometry_dm.add(field<Int64>("spatialReference", spatialReference.wkid))
        }
        DataModelStruct().add(Field("geometry", geometry_dm))
    }

    /**
     * The Function is to deserialize data model to the polyline object
     *
     * @param dm of DataModel
     *
     * @return Type of DataModel
     * @since 0.38.2
     */
    public static func deserialize(dm: DataModel): Polyline {
        try {
            let geometry = ((dm as DataModelStruct).getOrThrow().get("geometry") as DataModelStruct).getOrThrow()
            let geometry_type = String.deserialize(geometry.get("type"))
            if (geometry_type != "Polyline") {
                throw Exception()
            }
            let coordinates = (geometry.get("coordinates") as DataModelSeq).getOrThrow().getItems()
            let coordinates_arr = ArrayList<Coordinate>(coordinates.size, { i =>
                let coordinate = (coordinates[i] as DataModelSeq).getOrThrow().getItems()
                let x = Float64.deserialize(coordinate[0])
                let y = Float64.deserialize(coordinate[1])
                Coordinate(x, y)
            })
            match(geometry.get("spatialReference") as DataModelInt) {
                case Some(v) => Polyline(coordinates_arr, SpatialReference.fromWKID(v.getValue()) ?? SpatialReference.unknown())
                case None => Polyline(coordinates_arr)
            }
        } catch (e : Exception) {
            Polyline.NullPolyline()
        }
    }

    /**
     * The Function is to compute the polyline length 
     *
     * @return Type of Float64
     * @since 0.38.2
     */
    public func length() : Float64 {
        CoordinateEngine.length(this.coordinates.toArray())
    }
}

/**
 * implemtentation of CoordinateTransformer<Polyline>
 *  
 * @author Yu Dong
 * @since 0.38.2
 */
extend Polyline <: CoordinateTransformer<Polyline> {


    /**
     * The Function is to transfrom polyline to GCJ-02
     *
     * @return Type of ?Polyline
     * @since 0.38.2
     */
    public func toGCJ02() : ?Polyline {
        if (spatialReference.isWGS1984()) {
            return Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.WGS84ToGCJ02(coordinates[i])
            }), SpatialReference.gcj02())
        }
        if (spatialReference.isBD09()) {
            return Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.BD09ToGCJ02(coordinates[i])
            }), SpatialReference.gcj02())
        }
        None
    }

    /**
     * The Function is to transfrom polyline to BD-09
     *
     * @return Type of ?Polyline
     * @since 0.38.2
     */
    public func toBD09() : ?Polyline {
        if (spatialReference.isWGS1984()) {
            return Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.WGS84ToBD09(coordinates[i])
            }), SpatialReference.bd09())
        }
        if (spatialReference.isGCJ02()) {
            return Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.GCJ02ToBD09(coordinates[i])
            }), SpatialReference.bd09())
        }
        None
    }

    /**
     * The Function is to transfrom polyline to WGS 1984
     *
     * @return Type of ?Polyline
     * @since 0.38.2
     */
    public func toWGS1984() : ?Polyline {
        if (spatialReference.isGCJ02()) {
            return Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.GCJ02ToWGS84(coordinates[i])
            }), SpatialReference.wgs84())
        }
        if (spatialReference.isBD09()) {
            return Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.BD09ToWGS84(coordinates[i])
            }), SpatialReference.wgs84())
        }
        None
    }


    /**
     * The Function is to project polyline to Mercator
     *
     * @return Type of ?Polyline
     * @since 0.38.2
     */
    public func projectToMercator() : ?Polyline { 
        if (this.spatialReference.isProjected()) {
            return None
        }
        Polyline(ArrayList<Coordinate>(coordinates.size, {i =>
                CoordinateEngine.GeographicToMercator(coordinates[i])
            }), ProjectedSpatialReference.WebMercator(
                (this.spatialReference as GeographicSpatialReference) ?? GeographicSpatialReference.Unknown
            )
        )
    }
}