Browse Source

fix:gis 适配

zhangweixin 3 years ago
parent
commit
8deb013acd
32 changed files with 3889 additions and 175 deletions
  1. 3 1
      gradle.properties
  2. 124 0
      persagy-kotlin-gis/build.gradle
  3. 193 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/ParseException.java
  4. 476 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SGisWktParser.java
  5. 56 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SGisWktParserConstants.java
  6. 686 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SGisWktParserTokenManager.java
  7. 474 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SimpleCharStream.java
  8. 131 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/Token.java
  9. 146 0
      persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/TokenMgrError.java
  10. 338 0
      persagy-kotlin-gis/src/main/javacc/SGisWktParser.jj
  11. 41 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SAbstractGisGeometry.kt
  12. 35 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisGeometryCollection.kt
  13. 52 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisLineString.kt
  14. 52 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisMultiLineString.kt
  15. 52 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisMultiPoint.kt
  16. 52 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisMultiPolygon.kt
  17. 52 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisPoint.kt
  18. 52 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisPolygon.kt
  19. 41 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisPosition.kt
  20. 47 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/enums/SGisGeometryType.kt
  21. 65 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionArray2Serializer.kt
  22. 65 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionArray3Serializer.kt
  23. 65 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionArraySerializer.kt
  24. 62 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionSerializer.kt
  25. 120 0
      persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/utils/SGisPositionUtil.kt
  26. 1 0
      persagy-kotlin-mybatis/build.gradle
  27. 17 170
      persagy-kotlin-mybatis/src/main/kotlin/com/persagy/mybatis/SAbstractDriver.kt
  28. 7 0
      persagy-kotlin-mybatis/src/main/kotlin/com/persagy/mybatis/SRowParser.kt
  29. 182 0
      persagy-kotlin-mysql/src/main/kotlin/com/persagy/mysql/SMySqlDriver.kt
  30. 1 0
      persagy-kotlin-postgresql/build.gradle
  31. 199 4
      persagy-kotlin-postgresql/src/main/kotlin/com/persagy/postgresql/SPostgreSqlDriver.kt
  32. 2 0
      settings.gradle

+ 3 - 1
gradle.properties

@@ -25,7 +25,7 @@
 #
 
 PERSAGY_URL = http://www.persagy.com/
-PERSAGY_KOTLIN_VERSION = 1.4.128
+PERSAGY_KOTLIN_VERSION = 1.5.3
 # android
 # SYBOTAN_KOTLIN_VERSION = 1.4.63
 
@@ -71,3 +71,5 @@ POSTGRESQL_VERSION = 42.2.9
 SLF4J_VERSION = 1.7.29
 # https://mvnrepository.com/artifact/org.testng/testng
 TESTNG_VERSION = 7.0.0
+# https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
+JACKSON_VERSION = 2.12.2

+ 124 - 0
persagy-kotlin-gis/build.gradle

@@ -0,0 +1,124 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+group rootProject.group
+version rootProject.version
+
+apply plugin: "kotlin"
+apply plugin: "org.jetbrains.dokka"
+apply plugin: "maven"
+apply plugin: "ca.coglinc.javacc"
+
+sourceCompatibility = JVM_TARGET
+
+compileKotlin {
+    kotlinOptions.jvmTarget = JVM_TARGET
+}
+compileTestKotlin {
+    kotlinOptions.jvmTarget = JVM_TARGET
+}
+
+compileJavacc {
+    inputDirectory = file("src/main/javacc")
+    outputDirectory = file("src/main/java/com/persagy/gis/parser")
+}
+
+dependencies {
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // kotlin 依赖
+    compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: KOTLIN_VERSION
+    compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: KOTLIN_VERSION
+
+    // JSON解析
+    // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
+    implementation group: "com.fasterxml.jackson.core", name: "jackson-core", version: JACKSON_VERSION
+    implementation group: "com.fasterxml.jackson.core", name: "jackson-databind", version: JACKSON_VERSION
+
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 文档生成
+dokka {
+    jdkVersion = JDK_VERSION
+    outputFormat = "javadoc"
+    outputDirectory = "$buildDir/javadoc"
+    //sourceDirs = files("src/main/kotlin")
+}
+
+// 上成上传的文档jar包
+task javadocJar(type: Jar, dependsOn: dokka) {
+    classifier = "javadoc"
+    from dokka.outputDirectory
+}
+
+// 上成上传的文档jar包
+task sourcesJar(type: Jar) {
+    classifier = "sources"
+    from sourceSets.main.allSource
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// maven 仓库管理
+
+// 设置上传的包,默认上传aar与pom
+artifacts {
+    // archives javadocJar
+    // archives sourcesJar
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.groupId = project.group
+            pom.version = project.version
+            pom.artifactId = project.name
+            pom.project {
+                name project.name
+                packaging "jar"
+                url PERSAGY_URL
+                description "斯伯坦机器人 Kotlin 基础支持库。"
+                licenses {
+                    license {
+                        name LICENSE_NAME
+                        url LICENSE_URL
+                    }
+                }
+                developers {
+                    developer {
+                        id DEVELOPER_ID
+                        name DEVELOPER_NAME
+                    }
+                }
+            }
+            snapshotRepository(url: MAVEN_REPO_SNAPSHOT_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+            repository(url: MAVEN_REPO_RELEASE_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+        }
+    }
+}

+ 193 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/ParseException.java

@@ -0,0 +1,193 @@
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 6.1 */
+/* JavaCCOptions:KEEP_LINE_COLUMN=true */
+package com.persagy.gis.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+  /**
+   * The version identifier for this Serializable class.
+   * Increment only if the <i>serialized</i> form of the
+   * class changes.
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * The end of line string for this machine.
+   */
+  protected static String EOL = System.getProperty("line.separator", "\n");
+
+  /**
+   * This constructor is used by the method "generateParseException"
+   * in the generated parser.  Calling this constructor generates
+   * a new object of this type with the fields "currentToken",
+   * "expectedTokenSequences", and "tokenImage" set.
+   */
+  public ParseException(Token currentTokenVal,
+                        int[][] expectedTokenSequencesVal,
+                        String[] tokenImageVal
+                       )
+  {
+    super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+    currentToken = currentTokenVal;
+    expectedTokenSequences = expectedTokenSequencesVal;
+    tokenImage = tokenImageVal;
+  }
+
+  /**
+   * The following constructors are for use by you for whatever
+   * purpose you can think of.  Constructing the exception in this
+   * manner makes the exception behave in the normal way - i.e., as
+   * documented in the class "Throwable".  The fields "errorToken",
+   * "expectedTokenSequences", and "tokenImage" do not contain
+   * relevant information.  The JavaCC generated code does not use
+   * these constructors.
+   */
+
+  public ParseException() {
+    super();
+  }
+
+  /** Constructor with message. */
+  public ParseException(String message) {
+    super(message);
+  }
+
+
+  /**
+   * This is the last token that has been consumed successfully.  If
+   * this object has been created due to a parse error, the token
+   * followng this token will (therefore) be the first error token.
+   */
+  public Token currentToken;
+
+  /**
+   * Each entry in this array is an array of integers.  Each array
+   * of integers represents a sequence of tokens (by their ordinal
+   * values) that is expected at this point of the parse.
+   */
+  public int[][] expectedTokenSequences;
+
+  /**
+   * This is a reference to the "tokenImage" array of the generated
+   * parser within which the parse error occurred.  This array is
+   * defined in the generated ...Constants interface.
+   */
+  public String[] tokenImage;
+
+  /**
+   * It uses "currentToken" and "expectedTokenSequences" to generate a parse
+   * error message and returns it.  If this object has been created
+   * due to a parse error, and you do not catch it (it gets thrown
+   * from the parser) the correct error message
+   * gets displayed.
+   */
+  private static String initialise(Token currentToken,
+                           int[][] expectedTokenSequences,
+                           String[] tokenImage) {
+
+    StringBuffer expected = new StringBuffer();
+    int maxSize = 0;
+    for (int i = 0; i < expectedTokenSequences.length; i++) {
+      if (maxSize < expectedTokenSequences[i].length) {
+        maxSize = expectedTokenSequences[i].length;
+      }
+      for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+        expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+      }
+      if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+        expected.append("...");
+      }
+      expected.append(EOL).append("    ");
+    }
+    String retval = "Encountered \"";
+    Token tok = currentToken.next;
+    for (int i = 0; i < maxSize; i++) {
+      if (i != 0) retval += " ";
+      if (tok.kind == 0) {
+        retval += tokenImage[0];
+        break;
+      }
+      retval += " " + tokenImage[tok.kind];
+      retval += " \"";
+      retval += add_escapes(tok.image);
+      retval += " \"";
+      tok = tok.next;
+    }
+    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    retval += "." + EOL;
+    
+    
+    if (expectedTokenSequences.length == 0) {
+        // Nothing to add here
+    } else {
+	    if (expectedTokenSequences.length == 1) {
+	      retval += "Was expecting:" + EOL + "    ";
+	    } else {
+	      retval += "Was expecting one of:" + EOL + "    ";
+	    }
+	    retval += expected.toString();
+    }
+    
+    return retval;
+  }
+
+
+  /**
+   * Used to convert raw characters to their escaped version
+   * when these raw version cannot be used as part of an ASCII
+   * string literal.
+   */
+  static String add_escapes(String str) {
+      StringBuffer retval = new StringBuffer();
+      char ch;
+      for (int i = 0; i < str.length(); i++) {
+        switch (str.charAt(i))
+        {
+           case '\b':
+              retval.append("\\b");
+              continue;
+           case '\t':
+              retval.append("\\t");
+              continue;
+           case '\n':
+              retval.append("\\n");
+              continue;
+           case '\f':
+              retval.append("\\f");
+              continue;
+           case '\r':
+              retval.append("\\r");
+              continue;
+           case '\"':
+              retval.append("\\\"");
+              continue;
+           case '\'':
+              retval.append("\\\'");
+              continue;
+           case '\\':
+              retval.append("\\\\");
+              continue;
+           default:
+              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                 String s = "0000" + Integer.toString(ch, 16);
+                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+              } else {
+                 retval.append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.toString();
+   }
+
+}
+/* JavaCC - OriginalChecksum=d7967fa46c8d76b2c5f460a5c887b082 (do not edit this line) */

+ 476 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SGisWktParser.java

@@ -0,0 +1,476 @@
+/* SGisWktParser.java */
+/* Generated By:JavaCC: Do not edit this line. SGisWktParser.java */
+package com.persagy.gis.parser;
+
+import com.persagy.gis.entity.*;
+import com.persagy.gis.enums.SGisGeometryType;
+import java.util.*;
+
+/**
+ * wkt 格�语法解��
+ *
+ * @author  å¼ ç»´æ–? <zhangweixin@sagacloud.cn>
+ */
+public class SGisWktParser implements SGisWktParserConstants {
+    public SAbstractGisGeometry parser() throws ParseException {
+        return passerGeometry();
+        }
+
+/** 解�几何对象 */
+  final public SAbstractGisGeometry passerGeometry() throws ParseException {SAbstractGisGeometry geometry;
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case POINT:{
+      jj_consume_token(POINT);
+geometry = passerPointArgs();
+      break;
+      }
+    case MULTIPOINT:{
+      jj_consume_token(MULTIPOINT);
+geometry = passerMultiPointArgs();
+      break;
+      }
+    case LINESTRING:{
+      jj_consume_token(LINESTRING);
+geometry = passerLineStringArgs();
+      break;
+      }
+    case MULTILINESTRING:{
+      jj_consume_token(MULTILINESTRING);
+geometry = passerMultiLineStringArgs();
+      break;
+      }
+    case POLYGON:{
+      jj_consume_token(POLYGON);
+geometry = passerPolygonArgs();
+      break;
+      }
+    case MULTIPOLYGON:{
+      jj_consume_token(MULTIPOLYGON);
+geometry = passerMultiPolygonArgs();
+      break;
+      }
+    default:
+      jj_la1[0] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+{if ("" != null) return geometry;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� Point �数 */
+  final public SGisPoint passerPointArgs() throws ParseException {SGisPoint point;
+    SGisPosition pos;
+point = new SGisPoint();
+    jj_consume_token(LPAREN);
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case NUM:{
+      pos = passerPosition();
+point.setCoordinates(pos);
+      break;
+      }
+    default:
+      jj_la1[1] = jj_gen;
+      ;
+    }
+    jj_consume_token(RPAREN);
+{if ("" != null) return point;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� MultiPoint �数 */
+  final public SGisMultiPoint passerMultiPointArgs() throws ParseException {SGisMultiPoint  multiPoint;
+    ArrayList<SGisPosition> posArr;
+multiPoint = new SGisMultiPoint();
+    jj_consume_token(LPAREN);
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case NUM:{
+      posArr = passerPositionArray();
+multiPoint.setCoordinates(posArr);
+      break;
+      }
+    default:
+      jj_la1[2] = jj_gen;
+      ;
+    }
+    jj_consume_token(RPAREN);
+{if ("" != null) return multiPoint;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� LineString �数 */
+  final public SGisLineString passerLineStringArgs() throws ParseException {SGisLineString  line;
+    ArrayList<SGisPosition> posArr;
+line = new SGisLineString();
+    jj_consume_token(LPAREN);
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case NUM:{
+      posArr = passerPositionArray();
+line.setCoordinates(posArr);
+      break;
+      }
+    default:
+      jj_la1[3] = jj_gen;
+      ;
+    }
+    jj_consume_token(RPAREN);
+{if ("" != null) return line;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� MultiLineString �数 */
+  final public SGisMultiLineString passerMultiLineStringArgs() throws ParseException {SGisMultiLineString  multiLine;
+    ArrayList<ArrayList<SGisPosition>> posArr2;
+multiLine = new SGisMultiLineString();
+    jj_consume_token(LPAREN);
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case LPAREN:{
+      posArr2 = passerPositionArray2();
+multiLine.setCoordinates(posArr2);
+      break;
+      }
+    default:
+      jj_la1[4] = jj_gen;
+      ;
+    }
+    jj_consume_token(RPAREN);
+{if ("" != null) return multiLine;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� Polygon �数 */
+  final public SGisPolygon passerPolygonArgs() throws ParseException {SGisPolygon  polygon;
+    ArrayList<ArrayList<SGisPosition>> posArr2;
+polygon = new SGisPolygon();
+    jj_consume_token(LPAREN);
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case LPAREN:{
+      posArr2 = passerPositionArray2();
+polygon.setCoordinates(posArr2);
+      break;
+      }
+    default:
+      jj_la1[5] = jj_gen;
+      ;
+    }
+    jj_consume_token(RPAREN);
+{if ("" != null) return polygon;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� MultiPolygon �数 */
+  final public SGisMultiPolygon passerMultiPolygonArgs() throws ParseException {SGisMultiPolygon  multiPolygon;
+    ArrayList<ArrayList<ArrayList<SGisPosition>>> posArr3;
+multiPolygon = new SGisMultiPolygon();
+    jj_consume_token(LPAREN);
+    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case LPAREN:{
+      posArr3 = passerPositionArray3();
+multiPolygon.setCoordinates(posArr3);
+      break;
+      }
+    default:
+      jj_la1[6] = jj_gen;
+      ;
+    }
+    jj_consume_token(RPAREN);
+{if ("" != null) return multiPolygon;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解�二维 Position 对象数组 */
+  final public ArrayList<ArrayList<ArrayList<SGisPosition>>> passerPositionArray3() throws ParseException {ArrayList<ArrayList<SGisPosition>> posArray;
+    ArrayList<ArrayList<ArrayList<SGisPosition>>> positionList = new ArrayList<ArrayList<ArrayList<SGisPosition>>>();
+    jj_consume_token(LPAREN);
+    posArray = passerPositionArray2();
+    jj_consume_token(RPAREN);
+positionList.add(posArray);
+    label_1:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      case COMMA:{
+        ;
+        break;
+        }
+      default:
+        jj_la1[7] = jj_gen;
+        break label_1;
+      }
+      jj_consume_token(COMMA);
+      jj_consume_token(LPAREN);
+      posArray = passerPositionArray2();
+      jj_consume_token(RPAREN);
+positionList.add(posArray);
+    }
+{if ("" != null) return positionList;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解�二维 Position 对象数组 */
+  final public ArrayList<ArrayList<SGisPosition>> passerPositionArray2() throws ParseException {ArrayList<SGisPosition> posArray;
+    ArrayList<ArrayList<SGisPosition>> positionList = new ArrayList<ArrayList<SGisPosition>>();
+    jj_consume_token(LPAREN);
+    posArray = passerPositionArray();
+    jj_consume_token(RPAREN);
+positionList.add(posArray);
+    label_2:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      case COMMA:{
+        ;
+        break;
+        }
+      default:
+        jj_la1[8] = jj_gen;
+        break label_2;
+      }
+      jj_consume_token(COMMA);
+      jj_consume_token(LPAREN);
+      posArray = passerPositionArray();
+      jj_consume_token(RPAREN);
+positionList.add(posArray);
+    }
+{if ("" != null) return positionList;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� Position 对象数组 */
+  final public ArrayList<SGisPosition> passerPositionArray() throws ParseException {SGisPosition pos;
+    ArrayList<SGisPosition> positionList = new ArrayList<SGisPosition>();
+    pos = passerPosition();
+positionList.add(pos);
+    label_3:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      case COMMA:{
+        ;
+        break;
+        }
+      default:
+        jj_la1[9] = jj_gen;
+        break label_3;
+      }
+      jj_consume_token(COMMA);
+      pos = passerPosition();
+positionList.add(pos);
+    }
+{if ("" != null) return positionList;}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解� Position 对象 */
+  final public SGisPosition passerPosition() throws ParseException {Double pos;
+    ArrayList<Double> valList = new ArrayList<Double>();
+    pos = parserNum();
+valList.add(pos);
+    label_4:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      case NUM:{
+        ;
+        break;
+        }
+      default:
+        jj_la1[10] = jj_gen;
+        break label_4;
+      }
+      pos = parserNum();
+valList.add(pos);
+    }
+if (valList.size() >= 3) {
+            {if ("" != null) return new SGisPosition(valList.get(0), valList.get(1), valList.get(2));}
+        }
+        if (valList.size() >= 2) {
+            {if ("" != null) return new SGisPosition(valList.get(0), valList.get(1), null);}
+        }
+        if (valList.size() >= 1) {
+            {if ("" != null) return new SGisPosition(valList.get(0), 0.0, null);}
+        }
+        {if ("" != null) return new SGisPosition();}
+    throw new Error("Missing return statement in function");
+  }
+
+/** 解�� */
+  final public Double parserNum() throws ParseException {Token t;
+    t = jj_consume_token(NUM);
+{if ("" != null) return Double.parseDouble(t.image);}
+    throw new Error("Missing return statement in function");
+  }
+
+  /** Generated Token Manager. */
+  public SGisWktParserTokenManager token_source;
+  SimpleCharStream jj_input_stream;
+  /** Current token. */
+  public Token token;
+  /** Next token. */
+  public Token jj_nt;
+  private int jj_ntk;
+  private int jj_gen;
+  final private int[] jj_la1 = new int[11];
+  static private int[] jj_la1_0;
+  static {
+      jj_la1_init_0();
+   }
+   private static void jj_la1_init_0() {
+      jj_la1_0 = new int[] {0x7e0,0x800,0x800,0x800,0x4,0x4,0x4,0x10,0x10,0x10,0x800,};
+   }
+
+  /** Constructor with InputStream. */
+  public SGisWktParser(java.io.InputStream stream) {
+     this(stream, null);
+  }
+  /** Constructor with InputStream and supplied encoding */
+  public SGisWktParser(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source = new SGisWktParserTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 11; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream) {
+     ReInit(stream, null);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 11; i++) jj_la1[i] = -1;
+  }
+
+  /** Constructor. */
+  public SGisWktParser(java.io.Reader stream) {
+    jj_input_stream = new SimpleCharStream(stream, 1, 1);
+    token_source = new SGisWktParserTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 11; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader stream) {
+	if (jj_input_stream == null) {
+      jj_input_stream = new SimpleCharStream(stream, 1, 1);
+   } else {
+      jj_input_stream.ReInit(stream, 1, 1);
+   }
+   if (token_source == null) {
+      token_source = new SGisWktParserTokenManager(jj_input_stream);
+   }
+
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 11; i++) jj_la1[i] = -1;
+  }
+
+  /** Constructor with generated Token Manager. */
+  public SGisWktParser(SGisWktParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 11; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(SGisWktParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 11; i++) jj_la1[i] = -1;
+  }
+
+  private Token jj_consume_token(int kind) throws ParseException {
+    Token oldToken;
+    if ((oldToken = token).next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    if (token.kind == kind) {
+      jj_gen++;
+      return token;
+    }
+    token = oldToken;
+    jj_kind = kind;
+    throw generateParseException();
+  }
+
+
+/** Get the next Token. */
+  final public Token getNextToken() {
+    if (token.next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    jj_gen++;
+    return token;
+  }
+
+/** Get the specific Token. */
+  final public Token getToken(int index) {
+    Token t = token;
+    for (int i = 0; i < index; i++) {
+      if (t.next != null) t = t.next;
+      else t = t.next = token_source.getNextToken();
+    }
+    return t;
+  }
+
+  private int jj_ntk_f() {
+    if ((jj_nt=token.next) == null)
+      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+    else
+      return (jj_ntk = jj_nt.kind);
+  }
+
+  private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
+  private int[] jj_expentry;
+  private int jj_kind = -1;
+
+  /** Generate ParseException. */
+  public ParseException generateParseException() {
+    jj_expentries.clear();
+    boolean[] la1tokens = new boolean[13];
+    if (jj_kind >= 0) {
+      la1tokens[jj_kind] = true;
+      jj_kind = -1;
+    }
+    for (int i = 0; i < 11; i++) {
+      if (jj_la1[i] == jj_gen) {
+        for (int j = 0; j < 32; j++) {
+          if ((jj_la1_0[i] & (1<<j)) != 0) {
+            la1tokens[j] = true;
+          }
+        }
+      }
+    }
+    for (int i = 0; i < 13; i++) {
+      if (la1tokens[i]) {
+        jj_expentry = new int[1];
+        jj_expentry[0] = i;
+        jj_expentries.add(jj_expentry);
+      }
+    }
+    int[][] exptokseq = new int[jj_expentries.size()][];
+    for (int i = 0; i < jj_expentries.size(); i++) {
+      exptokseq[i] = jj_expentries.get(i);
+    }
+    return new ParseException(token, exptokseq, tokenImage);
+  }
+
+  /** Enable tracing. */
+  final public void enable_tracing() {
+  }
+
+  /** Disable tracing. */
+  final public void disable_tracing() {
+  }
+
+}

+ 56 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SGisWktParserConstants.java

@@ -0,0 +1,56 @@
+/* Generated By:JavaCC: Do not edit this line. SGisWktParserConstants.java */
+package com.persagy.gis.parser;
+
+
+/**
+ * Token literal values and constants.
+ * Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+public interface SGisWktParserConstants {
+
+  /** End of File. */
+  int EOF = 0;
+  /** RegularExpression Id. */
+  int LPAREN = 2;
+  /** RegularExpression Id. */
+  int RPAREN = 3;
+  /** RegularExpression Id. */
+  int COMMA = 4;
+  /** RegularExpression Id. */
+  int POINT = 5;
+  /** RegularExpression Id. */
+  int MULTIPOINT = 6;
+  /** RegularExpression Id. */
+  int LINESTRING = 7;
+  /** RegularExpression Id. */
+  int MULTILINESTRING = 8;
+  /** RegularExpression Id. */
+  int POLYGON = 9;
+  /** RegularExpression Id. */
+  int MULTIPOLYGON = 10;
+  /** RegularExpression Id. */
+  int NUM = 11;
+  /** RegularExpression Id. */
+  int DIGIT = 12;
+
+  /** Lexical state. */
+  int DEFAULT = 0;
+
+  /** Literal token values. */
+  String[] tokenImage = {
+    "<EOF>",
+    "<token of kind 1>",
+    "\"(\"",
+    "\")\"",
+    "\",\"",
+    "\"POINT\"",
+    "\"MULTIPOINT\"",
+    "\"LINESTRING\"",
+    "\"MULTILINESTRING\"",
+    "\"POLYGON\"",
+    "\"MULTIPOLYGON\"",
+    "<NUM>",
+    "<DIGIT>",
+  };
+
+}

+ 686 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SGisWktParserTokenManager.java

@@ -0,0 +1,686 @@
+/* SGisWktParserTokenManager.java */
+/* Generated By:JavaCC: Do not edit this line. SGisWktParserTokenManager.java */
+package com.persagy.gis.parser;
+import com.persagy.gis.entity.*;
+import com.persagy.gis.enums.SGisGeometryType;
+import java.util.*;
+
+/** Token Manager. */
+@SuppressWarnings("unused")public class SGisWktParserTokenManager implements SGisWktParserConstants {
+
+  /** Debug output. */
+  public  java.io.PrintStream debugStream = System.out;
+  /** Set debug output. */
+  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0){
+   switch (pos)
+   {
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_0(int pos, long active0){
+   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private int jjStopAtPos(int pos, int kind)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_0(){
+   switch(curChar)
+   {
+      case 40:
+         return jjStopAtPos(0, 2);
+      case 41:
+         return jjStopAtPos(0, 3);
+      case 44:
+         return jjStopAtPos(0, 4);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa1_0(0x80L);
+      case 77:
+      case 109:
+         return jjMoveStringLiteralDfa1_0(0x540L);
+      case 80:
+      case 112:
+         return jjMoveStringLiteralDfa1_0(0x220L);
+      default :
+         return jjMoveNfa_0(0, 0);
+   }
+}
+private int jjMoveStringLiteralDfa1_0(long active0){
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(0, active0);
+      return 1;
+   }
+   switch(curChar)
+   {
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa2_0(active0, 0x80L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa2_0(active0, 0x220L);
+      case 85:
+      case 117:
+         return jjMoveStringLiteralDfa2_0(active0, 0x540L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(0, active0);
+}
+private int jjMoveStringLiteralDfa2_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(0, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(1, active0);
+      return 2;
+   }
+   switch(curChar)
+   {
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa3_0(active0, 0x20L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa3_0(active0, 0x740L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa3_0(active0, 0x80L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(1, active0);
+}
+private int jjMoveStringLiteralDfa3_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(1, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(2, active0);
+      return 3;
+   }
+   switch(curChar)
+   {
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa4_0(active0, 0x80L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa4_0(active0, 0x20L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa4_0(active0, 0x540L);
+      case 89:
+      case 121:
+         return jjMoveStringLiteralDfa4_0(active0, 0x200L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(2, active0);
+}
+private int jjMoveStringLiteralDfa4_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(2, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(3, active0);
+      return 4;
+   }
+   switch(curChar)
+   {
+      case 71:
+      case 103:
+         return jjMoveStringLiteralDfa5_0(active0, 0x200L);
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa5_0(active0, 0x540L);
+      case 83:
+      case 115:
+         return jjMoveStringLiteralDfa5_0(active0, 0x80L);
+      case 84:
+      case 116:
+         if ((active0 & 0x20L) != 0L)
+            return jjStopAtPos(4, 5);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(3, active0);
+}
+private int jjMoveStringLiteralDfa5_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(3, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(4, active0);
+      return 5;
+   }
+   switch(curChar)
+   {
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa6_0(active0, 0x100L);
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa6_0(active0, 0x200L);
+      case 80:
+      case 112:
+         return jjMoveStringLiteralDfa6_0(active0, 0x440L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa6_0(active0, 0x80L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(4, active0);
+}
+private int jjMoveStringLiteralDfa6_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(4, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(5, active0);
+      return 6;
+   }
+   switch(curChar)
+   {
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa7_0(active0, 0x100L);
+      case 78:
+      case 110:
+         if ((active0 & 0x200L) != 0L)
+            return jjStopAtPos(6, 9);
+         break;
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa7_0(active0, 0x440L);
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa7_0(active0, 0x80L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(5, active0);
+}
+private int jjMoveStringLiteralDfa7_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(5, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(6, active0);
+      return 7;
+   }
+   switch(curChar)
+   {
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa8_0(active0, 0xc0L);
+      case 76:
+      case 108:
+         return jjMoveStringLiteralDfa8_0(active0, 0x400L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa8_0(active0, 0x100L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(6, active0);
+}
+private int jjMoveStringLiteralDfa8_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(6, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(7, active0);
+      return 8;
+   }
+   switch(curChar)
+   {
+      case 69:
+      case 101:
+         return jjMoveStringLiteralDfa9_0(active0, 0x100L);
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa9_0(active0, 0xc0L);
+      case 89:
+      case 121:
+         return jjMoveStringLiteralDfa9_0(active0, 0x400L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(7, active0);
+}
+private int jjMoveStringLiteralDfa9_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(7, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(8, active0);
+      return 9;
+   }
+   switch(curChar)
+   {
+      case 71:
+      case 103:
+         if ((active0 & 0x80L) != 0L)
+            return jjStopAtPos(9, 7);
+         return jjMoveStringLiteralDfa10_0(active0, 0x400L);
+      case 83:
+      case 115:
+         return jjMoveStringLiteralDfa10_0(active0, 0x100L);
+      case 84:
+      case 116:
+         if ((active0 & 0x40L) != 0L)
+            return jjStopAtPos(9, 6);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(8, active0);
+}
+private int jjMoveStringLiteralDfa10_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(8, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(9, active0);
+      return 10;
+   }
+   switch(curChar)
+   {
+      case 79:
+      case 111:
+         return jjMoveStringLiteralDfa11_0(active0, 0x400L);
+      case 84:
+      case 116:
+         return jjMoveStringLiteralDfa11_0(active0, 0x100L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(9, active0);
+}
+private int jjMoveStringLiteralDfa11_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(9, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(10, active0);
+      return 11;
+   }
+   switch(curChar)
+   {
+      case 78:
+      case 110:
+         if ((active0 & 0x400L) != 0L)
+            return jjStopAtPos(11, 10);
+         break;
+      case 82:
+      case 114:
+         return jjMoveStringLiteralDfa12_0(active0, 0x100L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(10, active0);
+}
+private int jjMoveStringLiteralDfa12_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(10, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(11, active0);
+      return 12;
+   }
+   switch(curChar)
+   {
+      case 73:
+      case 105:
+         return jjMoveStringLiteralDfa13_0(active0, 0x100L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(11, active0);
+}
+private int jjMoveStringLiteralDfa13_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(11, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(12, active0);
+      return 13;
+   }
+   switch(curChar)
+   {
+      case 78:
+      case 110:
+         return jjMoveStringLiteralDfa14_0(active0, 0x100L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(12, active0);
+}
+private int jjMoveStringLiteralDfa14_0(long old0, long active0){
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(12, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(13, active0);
+      return 14;
+   }
+   switch(curChar)
+   {
+      case 71:
+      case 103:
+         if ((active0 & 0x100L) != 0L)
+            return jjStopAtPos(14, 8);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(13, active0);
+}
+private int jjMoveNfa_0(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 7;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0x3fe000000000000L & l) != 0L)
+                  {
+                     if (kind > 11)
+                        kind = 11;
+                     { jjCheckNAddTwoStates(3, 4); }
+                  }
+                  else if ((0x100002600L & l) != 0L)
+                  {
+                     if (kind > 1)
+                        kind = 1;
+                  }
+                  else if ((0x280000000000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 2;
+                  else if (curChar == 48)
+                  {
+                     if (kind > 11)
+                        kind = 11;
+                  }
+                  break;
+               case 1:
+                  if ((0x280000000000L & l) != 0L)
+                     jjstateSet[jjnewStateCnt++] = 2;
+                  break;
+               case 2:
+                  if ((0x3fe000000000000L & l) == 0L)
+                     break;
+                  if (kind > 11)
+                     kind = 11;
+                  { jjCheckNAddTwoStates(3, 4); }
+                  break;
+               case 3:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 11)
+                     kind = 11;
+                  { jjCheckNAddTwoStates(3, 4); }
+                  break;
+               case 4:
+                  if (curChar == 46)
+                     { jjCheckNAdd(5); }
+                  break;
+               case 5:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 11)
+                     kind = 11;
+                  { jjCheckNAdd(5); }
+                  break;
+               case 6:
+                  if (curChar == 48 && kind > 11)
+                     kind = 11;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int hiByte = (curChar >> 8);
+         int i1 = hiByte >> 6;
+         long l1 = 1L << (hiByte & 077);
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : if (i1 == 0 || l1 == 0 || i2 == 0 ||  l2 == 0) break; else break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 7 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+static final int[] jjnextStates = {
+};
+
+/** Token literal values. */
+public static final String[] jjstrLiteralImages = {
+"", null, "\50", "\51", "\54", null, null, null, null, null, null, null, null, };
+protected Token jjFillToken()
+{
+   final Token t;
+   final String curTokenImage;
+   final int beginLine;
+   final int endLine;
+   final int beginColumn;
+   final int endColumn;
+   String im = jjstrLiteralImages[jjmatchedKind];
+   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+   beginLine = input_stream.getBeginLine();
+   beginColumn = input_stream.getBeginColumn();
+   endLine = input_stream.getEndLine();
+   endColumn = input_stream.getEndColumn();
+   t = Token.newToken(jjmatchedKind, curTokenImage);
+
+   t.beginLine = beginLine;
+   t.endLine = endLine;
+   t.beginColumn = beginColumn;
+   t.endColumn = endColumn;
+
+   return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+/** Get the next Token. */
+public Token getNextToken() 
+{
+  Token matchedToken;
+  int curPos = 0;
+
+  EOFLoop :
+  for (;;)
+  {
+   try
+   {
+      curChar = input_stream.BeginToken();
+   }
+   catch(Exception e)
+   {
+      jjmatchedKind = 0;
+      jjmatchedPos = -1;
+      matchedToken = jjFillToken();
+      return matchedToken;
+   }
+
+   jjmatchedKind = 0x7fffffff;
+   jjmatchedPos = 0;
+   curPos = jjMoveStringLiteralDfa0_0();
+   if (jjmatchedKind != 0x7fffffff)
+   {
+      if (jjmatchedPos + 1 < curPos)
+         input_stream.backup(curPos - jjmatchedPos - 1);
+      if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+      {
+         matchedToken = jjFillToken();
+         return matchedToken;
+      }
+      else
+      {
+         continue EOFLoop;
+      }
+   }
+   int error_line = input_stream.getEndLine();
+   int error_column = input_stream.getEndColumn();
+   String error_after = null;
+   boolean EOFSeen = false;
+   try { input_stream.readChar(); input_stream.backup(1); }
+   catch (java.io.IOException e1) {
+      EOFSeen = true;
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+      if (curChar == '\n' || curChar == '\r') {
+         error_line++;
+         error_column = 0;
+      }
+      else
+         error_column++;
+   }
+   if (!EOFSeen) {
+      input_stream.backup(1);
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+   }
+   throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+  }
+}
+
+private void jjCheckNAdd(int state)
+{
+   if (jjrounds[state] != jjround)
+   {
+      jjstateSet[jjnewStateCnt++] = state;
+      jjrounds[state] = jjround;
+   }
+}
+private void jjAddStates(int start, int end)
+{
+   do {
+      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+   } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+   jjCheckNAdd(state1);
+   jjCheckNAdd(state2);
+}
+
+    /** Constructor. */
+    public SGisWktParserTokenManager(SimpleCharStream stream){
+
+      if (SimpleCharStream.staticFlag)
+            throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+
+    input_stream = stream;
+  }
+
+  /** Constructor. */
+  public SGisWktParserTokenManager (SimpleCharStream stream, int lexState){
+    ReInit(stream);
+    SwitchTo(lexState);
+  }
+
+  /** Reinitialise parser. */
+  public void ReInit(SimpleCharStream stream)
+  {
+	
+    jjmatchedPos = jjnewStateCnt = 0;
+    curLexState = defaultLexState;
+    input_stream = stream;
+    ReInitRounds();
+  }
+
+  private void ReInitRounds()
+  {
+    int i;
+    jjround = 0x80000001;
+    for (i = 7; i-- > 0;)
+      jjrounds[i] = 0x80000000;
+  }
+
+  /** Reinitialise parser. */
+  public void ReInit( SimpleCharStream stream, int lexState)
+  {
+  
+    ReInit( stream);
+    SwitchTo(lexState);
+  }
+
+  /** Switch to specified lex state. */
+  public void SwitchTo(int lexState)
+  {
+    if (lexState >= 1 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+    else
+      curLexState = lexState;
+  }
+
+/** Lexer state names. */
+public static final String[] lexStateNames = {
+   "DEFAULT",
+};
+static final long[] jjtoToken = {
+   0xffdL, 
+};
+static final long[] jjtoSkip = {
+   0x2L, 
+};
+    protected SimpleCharStream  input_stream;
+
+    private final int[] jjrounds = new int[7];
+    private final int[] jjstateSet = new int[2 * 7];
+
+    
+    protected int curChar;
+}

+ 474 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/SimpleCharStream.java

@@ -0,0 +1,474 @@
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 6.1 */
+/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package com.persagy.gis.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+/** Whether parser is static. */
+  public static final boolean staticFlag = false;
+  int bufsize;
+  int available;
+  int tokenBegin;
+/** Position in buffer. */
+  public int bufpos = -1;
+  protected int bufline[];
+  protected int bufcolumn[];
+
+  protected int column = 0;
+  protected int line = 1;
+
+  protected boolean prevCharIsCR = false;
+  protected boolean prevCharIsLF = false;
+
+  protected java.io.Reader inputStream;
+
+  protected char[] buffer;
+  protected int maxNextCharInd = 0;
+  protected int inBuf = 0;
+  protected int tabSize = 1;
+  protected boolean trackLineColumn = true;
+
+  public void setTabSize(int i) { tabSize = i; }
+  public int getTabSize() { return tabSize; }
+
+
+
+  protected void ExpandBuff(boolean wrapAround)
+  {
+    char[] newbuffer = new char[bufsize + 2048];
+    int newbufline[] = new int[bufsize + 2048];
+    int newbufcolumn[] = new int[bufsize + 2048];
+
+    try
+    {
+      if (wrapAround)
+      {
+        System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+        System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+        buffer = newbuffer;
+
+        System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+        System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+        bufline = newbufline;
+
+        System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+        System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+        bufcolumn = newbufcolumn;
+
+        maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+      }
+      else
+      {
+        System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+        buffer = newbuffer;
+
+        System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+        bufline = newbufline;
+
+        System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+        bufcolumn = newbufcolumn;
+
+        maxNextCharInd = (bufpos -= tokenBegin);
+      }
+    }
+    catch (Throwable t)
+    {
+      throw new Error(t.getMessage());
+    }
+
+
+    bufsize += 2048;
+    available = bufsize;
+    tokenBegin = 0;
+  }
+
+  protected void FillBuff() throws java.io.IOException
+  {
+    if (maxNextCharInd == available)
+    {
+      if (available == bufsize)
+      {
+        if (tokenBegin > 2048)
+        {
+          bufpos = maxNextCharInd = 0;
+          available = tokenBegin;
+        }
+        else if (tokenBegin < 0)
+          bufpos = maxNextCharInd = 0;
+        else
+          ExpandBuff(false);
+      }
+      else if (available > tokenBegin)
+        available = bufsize;
+      else if ((tokenBegin - available) < 2048)
+        ExpandBuff(true);
+      else
+        available = tokenBegin;
+    }
+
+    int i;
+    try {
+      if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1)
+      {
+        inputStream.close();
+        throw new java.io.IOException();
+      }
+      else
+        maxNextCharInd += i;
+      return;
+    }
+    catch(java.io.IOException e) {
+      --bufpos;
+      backup(0);
+      if (tokenBegin == -1)
+        tokenBegin = bufpos;
+      throw e;
+    }
+  }
+
+/** Start. */
+  public char BeginToken() throws java.io.IOException
+  {
+    tokenBegin = -1;
+    char c = readChar();
+    tokenBegin = bufpos;
+
+    return c;
+  }
+
+  protected void UpdateLineColumn(char c)
+  {
+    column++;
+
+    if (prevCharIsLF)
+    {
+      prevCharIsLF = false;
+      line += (column = 1);
+    }
+    else if (prevCharIsCR)
+    {
+      prevCharIsCR = false;
+      if (c == '\n')
+      {
+        prevCharIsLF = true;
+      }
+      else
+        line += (column = 1);
+    }
+
+    switch (c)
+    {
+      case '\r' :
+        prevCharIsCR = true;
+        break;
+      case '\n' :
+        prevCharIsLF = true;
+        break;
+      case '\t' :
+        column--;
+        column += (tabSize - (column % tabSize));
+        break;
+      default :
+        break;
+    }
+
+    bufline[bufpos] = line;
+    bufcolumn[bufpos] = column;
+  }
+
+/** Read a character. */
+  public char readChar() throws java.io.IOException
+  {
+    if (inBuf > 0)
+    {
+      --inBuf;
+
+      if (++bufpos == bufsize)
+        bufpos = 0;
+
+      return buffer[bufpos];
+    }
+
+    if (++bufpos >= maxNextCharInd)
+      FillBuff();
+
+    char c = buffer[bufpos];
+
+    UpdateLineColumn(c);
+    return c;
+  }
+
+  @Deprecated
+  /**
+   * @deprecated
+   * @see #getEndColumn
+   */
+
+  public int getColumn() {
+    return bufcolumn[bufpos];
+  }
+
+  @Deprecated
+  /**
+   * @deprecated
+   * @see #getEndLine
+   */
+
+  public int getLine() {
+    return bufline[bufpos];
+  }
+
+  /** Get token end column number. */
+  public int getEndColumn() {
+    return bufcolumn[bufpos];
+  }
+
+  /** Get token end line number. */
+  public int getEndLine() {
+     return bufline[bufpos];
+  }
+
+  /** Get token beginning column number. */
+  public int getBeginColumn() {
+    return bufcolumn[tokenBegin];
+  }
+
+  /** Get token beginning line number. */
+  public int getBeginLine() {
+    return bufline[tokenBegin];
+  }
+
+/** Backup a number of characters. */
+  public void backup(int amount) {
+
+    inBuf += amount;
+    if ((bufpos -= amount) < 0)
+      bufpos += bufsize;
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    available = bufsize = buffersize;
+    buffer = new char[buffersize];
+    bufline = new int[buffersize];
+    bufcolumn = new int[buffersize];
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream, int startline,
+                          int startcolumn)
+  {
+    this(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream)
+  {
+    this(dstream, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    if (buffer == null || buffersize != buffer.length)
+    {
+      available = bufsize = buffersize;
+      buffer = new char[buffersize];
+      bufline = new int[buffersize];
+      bufcolumn = new int[buffersize];
+    }
+    prevCharIsLF = prevCharIsCR = false;
+    tokenBegin = inBuf = maxNextCharInd = 0;
+    bufpos = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream, int startline,
+                     int startcolumn)
+  {
+    ReInit(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream)
+  {
+    ReInit(dstream, 1, 1, 4096);
+  }
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+  int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+  {
+    this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+                          int startcolumn) throws java.io.UnsupportedEncodingException
+  {
+    this(dstream, encoding, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, int startline,
+                          int startcolumn)
+  {
+    this(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+  {
+    this(dstream, encoding, 1, 1, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream)
+  {
+    this(dstream, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+                          int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+  {
+    ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, int startline,
+                          int startcolumn, int buffersize)
+  {
+    ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+  {
+    ReInit(dstream, encoding, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream)
+  {
+    ReInit(dstream, 1, 1, 4096);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+                     int startcolumn) throws java.io.UnsupportedEncodingException
+  {
+    ReInit(dstream, encoding, startline, startcolumn, 4096);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, int startline,
+                     int startcolumn)
+  {
+    ReInit(dstream, startline, startcolumn, 4096);
+  }
+  /** Get token literal value. */
+  public String GetImage()
+  {
+    if (bufpos >= tokenBegin)
+      return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+    else
+      return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+                            new String(buffer, 0, bufpos + 1);
+  }
+
+  /** Get the suffix. */
+  public char[] GetSuffix(int len)
+  {
+    char[] ret = new char[len];
+
+    if ((bufpos + 1) >= len)
+      System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+    else
+    {
+      System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+                                                        len - bufpos - 1);
+      System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+    }
+
+    return ret;
+  }
+
+  /** Reset buffer when finished. */
+  public void Done()
+  {
+    buffer = null;
+    bufline = null;
+    bufcolumn = null;
+  }
+
+  /**
+   * Method to adjust line and column numbers for the start of a token.
+   */
+  public void adjustBeginLineColumn(int newLine, int newCol)
+  {
+    int start = tokenBegin;
+    int len;
+
+    if (bufpos >= tokenBegin)
+    {
+      len = bufpos - tokenBegin + inBuf + 1;
+    }
+    else
+    {
+      len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+    }
+
+    int i = 0, j = 0, k = 0;
+    int nextColDiff = 0, columnDiff = 0;
+
+    while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+    {
+      bufline[j] = newLine;
+      nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+      bufcolumn[j] = newCol + columnDiff;
+      columnDiff = nextColDiff;
+      i++;
+    }
+
+    if (i < len)
+    {
+      bufline[j] = newLine++;
+      bufcolumn[j] = newCol + columnDiff;
+
+      while (i++ < len)
+      {
+        if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+          bufline[j] = newLine++;
+        else
+          bufline[j] = newLine;
+      }
+    }
+
+    line = bufline[j];
+    column = bufcolumn[j];
+  }
+  boolean getTrackLineColumn() { return trackLineColumn; }
+  void setTrackLineColumn(boolean tlc) { trackLineColumn = tlc; }
+}
+/* JavaCC - OriginalChecksum=61f45734e01c0d132cbe91ba89af0ece (do not edit this line) */

+ 131 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/Token.java

@@ -0,0 +1,131 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 6.1 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COLUMN=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package com.persagy.gis.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token implements java.io.Serializable {
+
+  /**
+   * The version identifier for this Serializable class.
+   * Increment only if the <i>serialized</i> form of the
+   * class changes.
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * An integer that describes the kind of this token.  This numbering
+   * system is determined by JavaCCParser, and a table of these numbers is
+   * stored in the file ...Constants.java.
+   */
+  public int kind;
+
+  /** The line number of the first character of this Token. */
+  public int beginLine;
+  /** The column number of the first character of this Token. */
+  public int beginColumn;
+  /** The line number of the last character of this Token. */
+  public int endLine;
+  /** The column number of the last character of this Token. */
+  public int endColumn;
+
+  /**
+   * The string image of the token.
+   */
+  public String image;
+
+  /**
+   * A reference to the next regular (non-special) token from the input
+   * stream.  If this is the last token from the input stream, or if the
+   * token manager has not read tokens beyond this one, this field is
+   * set to null.  This is true only if this token is also a regular
+   * token.  Otherwise, see below for a description of the contents of
+   * this field.
+   */
+  public Token next;
+
+  /**
+   * This field is used to access special tokens that occur prior to this
+   * token, but after the immediately preceding regular (non-special) token.
+   * If there are no such special tokens, this field is set to null.
+   * When there are more than one such special token, this field refers
+   * to the last of these special tokens, which in turn refers to the next
+   * previous special token through its specialToken field, and so on
+   * until the first special token (whose specialToken field is null).
+   * The next fields of special tokens refer to other special tokens that
+   * immediately follow it (without an intervening regular token).  If there
+   * is no such token, this field is null.
+   */
+  public Token specialToken;
+
+  /**
+   * An optional attribute value of the Token.
+   * Tokens which are not used as syntactic sugar will often contain
+   * meaningful values that will be used later on by the compiler or
+   * interpreter. This attribute value is often different from the image.
+   * Any subclass of Token that actually wants to return a non-null value can
+   * override this method as appropriate.
+   */
+  public Object getValue() {
+    return null;
+  }
+
+  /**
+   * No-argument constructor
+   */
+  public Token() {}
+
+  /**
+   * Constructs a new token for the specified Image.
+   */
+  public Token(int kind)
+  {
+    this(kind, null);
+  }
+
+  /**
+   * Constructs a new token for the specified Image and Kind.
+   */
+  public Token(int kind, String image)
+  {
+    this.kind = kind;
+    this.image = image;
+  }
+
+  /**
+   * Returns the image.
+   */
+  public String toString()
+  {
+    return image;
+  }
+
+  /**
+   * Returns a new Token object, by default. However, if you want, you
+   * can create and return subclass objects based on the value of ofKind.
+   * Simply add the cases to the switch for all those special cases.
+   * For example, if you have a subclass of Token called IDToken that
+   * you want to create if ofKind is ID, simply add something like :
+   *
+   *    case MyParserConstants.ID : return new IDToken(ofKind, image);
+   *
+   * to the following switch statement. Then you can cast matchedToken
+   * variable to the appropriate type and use sit in your lexical actions.
+   */
+  public static Token newToken(int ofKind, String image)
+  {
+    switch(ofKind)
+    {
+      default : return new Token(ofKind, image);
+    }
+  }
+
+  public static Token newToken(int ofKind)
+  {
+    return newToken(ofKind, null);
+  }
+
+}
+/* JavaCC - OriginalChecksum=6cbbba68e60e1462aa7cf18972b4afb5 (do not edit this line) */

+ 146 - 0
persagy-kotlin-gis/src/main/java/com/persagy/gis/parser/TokenMgrError.java

@@ -0,0 +1,146 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 6.1 */
+/* JavaCCOptions: */
+package com.persagy.gis.parser;
+
+/** Token Manager Error. */
+public class TokenMgrError extends Error
+{
+
+  /**
+   * The version identifier for this Serializable class.
+   * Increment only if the <i>serialized</i> form of the
+   * class changes.
+   */
+  private static final long serialVersionUID = 1L;
+
+  /*
+   * Ordinals for various reasons why an Error of this type can be thrown.
+   */
+
+  /**
+   * Lexical error occurred.
+   */
+  public static final int LEXICAL_ERROR = 0;
+
+  /**
+   * An attempt was made to create a second instance of a static token manager.
+   */
+  public static final int STATIC_LEXER_ERROR = 1;
+
+  /**
+   * Tried to change to an invalid lexical state.
+   */
+  public static final int INVALID_LEXICAL_STATE = 2;
+
+  /**
+   * Detected (and bailed out of) an infinite loop in the token manager.
+   */
+  public static final int LOOP_DETECTED = 3;
+
+  /**
+   * Indicates the reason why the exception is thrown. It will have
+   * one of the above 4 values.
+   */
+  int errorCode;
+
+  /**
+   * Replaces unprintable characters by their escaped (or unicode escaped)
+   * equivalents in the given string
+   */
+  protected static final String addEscapes(String str) {
+    StringBuffer retval = new StringBuffer();
+    char ch;
+    for (int i = 0; i < str.length(); i++) {
+      switch (str.charAt(i))
+      {
+        case '\b':
+          retval.append("\\b");
+          continue;
+        case '\t':
+          retval.append("\\t");
+          continue;
+        case '\n':
+          retval.append("\\n");
+          continue;
+        case '\f':
+          retval.append("\\f");
+          continue;
+        case '\r':
+          retval.append("\\r");
+          continue;
+        case '\"':
+          retval.append("\\\"");
+          continue;
+        case '\'':
+          retval.append("\\\'");
+          continue;
+        case '\\':
+          retval.append("\\\\");
+          continue;
+        default:
+          if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+            String s = "0000" + Integer.toString(ch, 16);
+            retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+          } else {
+            retval.append(ch);
+          }
+          continue;
+      }
+    }
+    return retval.toString();
+  }
+
+  /**
+   * Returns a detailed message for the Error when it is thrown by the
+   * token manager to indicate a lexical error.
+   * Parameters :
+   *    EOFSeen     : indicates if EOF caused the lexical error
+   *    curLexState : lexical state in which this error occurred
+   *    errorLine   : line number when the error occurred
+   *    errorColumn : column number when the error occurred
+   *    errorAfter  : prefix that was seen before this error occurred
+   *    curchar     : the offending character
+   * Note: You can customize the lexical error message by modifying this method.
+   */
+  protected static String LexicalErr(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar) {
+    char curChar1 = (char)curChar;
+    return("Lexical error at line " +
+          errorLine + ", column " +
+          errorColumn + ".  Encountered: " +
+          (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar1)) + "\"") + " (" + (int)curChar + "), ") +
+          "after : \"" + addEscapes(errorAfter) + "\"");
+  }
+
+  /**
+   * You can also modify the body of this method to customize your error messages.
+   * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+   * of end-users concern, so you can return something like :
+   *
+   *     "Internal Error : Please file a bug report .... "
+   *
+   * from this method for such cases in the release version of your parser.
+   */
+  public String getMessage() {
+    return super.getMessage();
+  }
+
+  /*
+   * Constructors of various flavors follow.
+   */
+
+  /** No arg constructor. */
+  public TokenMgrError() {
+  }
+
+  /** Constructor with message and reason. */
+  public TokenMgrError(String message, int reason) {
+    super(message);
+    errorCode = reason;
+  }
+
+  /** Full Constructor. */
+  public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) {
+    this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+  }
+}
+/* JavaCC - OriginalChecksum=d8013da27786552460a6a45ded025522 (do not edit this line) */

+ 338 - 0
persagy-kotlin-gis/src/main/javacc/SGisWktParser.jj

@@ -0,0 +1,338 @@
+options {
+    STATIC = false;             // 成员与方法不生成为静态
+    UNICODE_INPUT = true;       // 支持中文解析
+    JDK_VERSION = "1.8";        // JDK版本为1.8
+    IGNORE_CASE = true;         // 不区分大小写
+}
+
+PARSER_BEGIN(SGisWktParser)
+package com.persagy.gis.parser;
+
+import com.persagy.gis.entity.*;
+import com.persagy.gis.enums.SGisGeometryType;
+import java.util.*;
+
+/**
+ * wkt 格式语法解析器
+ *
+ * @author  张维新 <zhangweixin@sagacloud.cn>
+ */
+public class SGisWktParser {
+    public SAbstractGisGeometry parser() throws ParseException {
+        return passerGeometry();
+        }
+}
+
+PARSER_END(SGisWktParser)
+
+SKIP: { <[" ", "\t", "\r", "\n"]> }
+
+TOKEN:  /* 符号 */
+{
+      < LPAREN      : "(" >
+    | < RPAREN      : ")" >
+    | < COMMA       : "," >
+}
+
+TOKEN:  /* 关键字 */
+{
+      < POINT           : "POINT" >
+    | < MULTIPOINT      : "MULTIPOINT" >
+    | < LINESTRING      : "LINESTRING" >
+    | < MULTILINESTRING : "MULTILINESTRING" >
+    | < POLYGON         : "POLYGON" >
+    | < MULTIPOLYGON    : "MULTIPOLYGON" >
+}
+
+TOKEN:  /* 标识符 */
+{
+      < NUM     : ("+"|"-")?(["1"-"9"](<DIGIT>)*("."(<DIGIT>)+)?)|"0" >
+    | < #DIGIT  : ["0"-"9"] >
+}
+
+/** 解析几何对象 */
+SAbstractGisGeometry passerGeometry():
+{
+    SAbstractGisGeometry geometry;
+}
+{
+    (
+          <POINT>
+        {
+            geometry = passerPointArgs();
+        }
+        | <MULTIPOINT>
+        {
+            geometry = passerMultiPointArgs();
+        }
+        | <LINESTRING>
+        {
+            geometry = passerLineStringArgs();
+        }
+        | <MULTILINESTRING>
+        {
+            geometry = passerMultiLineStringArgs();
+        }
+        | <POLYGON>
+        {
+            geometry = passerPolygonArgs();
+        }
+        | <MULTIPOLYGON>
+        {
+            geometry = passerMultiPolygonArgs();
+        }
+    )
+    {
+        return geometry;
+    }
+}
+
+/** 解析 Point 参数 */
+SGisPoint passerPointArgs():
+{
+    SGisPoint point;
+    SGisPosition pos;
+}
+{
+    {
+        point = new SGisPoint();
+    }
+    <LPAREN>
+    (
+        pos = passerPosition()
+        {
+            point.setCoordinates(pos);
+        }
+    )?
+    <RPAREN>
+    {
+        return point;
+    }
+}
+
+/** 解析 MultiPoint 参数 */
+SGisMultiPoint passerMultiPointArgs():
+{
+    SGisMultiPoint  multiPoint;
+    ArrayList<SGisPosition> posArr;
+}
+{
+    {
+        multiPoint = new SGisMultiPoint();
+    }
+    <LPAREN>
+    (
+        posArr = passerPositionArray()
+        {
+            multiPoint.setCoordinates(posArr);
+        }
+    )?
+    <RPAREN>
+    {
+        return multiPoint;
+    }
+}
+
+/** 解析 LineString 参数 */
+SGisLineString passerLineStringArgs():
+{
+    SGisLineString  line;
+    ArrayList<SGisPosition> posArr;
+}
+{
+    {
+        line = new SGisLineString();
+    }
+    <LPAREN>
+    (
+        posArr = passerPositionArray()
+        {
+            line.setCoordinates(posArr);
+        }
+    )?
+    <RPAREN>
+    {
+        return line;
+    }
+}
+
+/** 解析 MultiLineString 参数 */
+SGisMultiLineString passerMultiLineStringArgs():
+{
+    SGisMultiLineString  multiLine;
+    ArrayList<ArrayList<SGisPosition>> posArr2;
+}
+{
+    {
+        multiLine = new SGisMultiLineString();
+    }
+    <LPAREN>
+    (
+        posArr2 = passerPositionArray2()
+        {
+            multiLine.setCoordinates(posArr2);
+        }
+    )?
+    <RPAREN>
+    {
+        return multiLine;
+    }
+}
+
+/** 解析 Polygon 参数 */
+SGisPolygon passerPolygonArgs():
+{
+    SGisPolygon  polygon;
+    ArrayList<ArrayList<SGisPosition>> posArr2;
+}
+{
+    {
+        polygon = new SGisPolygon();
+    }
+    <LPAREN>
+    (
+        posArr2 = passerPositionArray2()
+        {
+            polygon.setCoordinates(posArr2);
+        }
+    )?
+    <RPAREN>
+    {
+        return polygon;
+    }
+}
+
+/** 解析 MultiPolygon 参数 */
+SGisMultiPolygon passerMultiPolygonArgs():
+{
+    SGisMultiPolygon  multiPolygon;
+    ArrayList<ArrayList<ArrayList<SGisPosition>>> posArr3;
+}
+{
+    {
+        multiPolygon = new SGisMultiPolygon();
+    }
+    <LPAREN>
+    (
+        posArr3 = passerPositionArray3()
+        {
+            multiPolygon.setCoordinates(posArr3);
+        }
+    )?
+    <RPAREN>
+    {
+        return multiPolygon;
+    }
+}
+
+/** 解析二维 Position 对象数组 */
+ArrayList<ArrayList<ArrayList<SGisPosition>>> passerPositionArray3():
+{
+    ArrayList<ArrayList<SGisPosition>> posArray;
+    ArrayList<ArrayList<ArrayList<SGisPosition>>> positionList = new ArrayList<ArrayList<ArrayList<SGisPosition>>>();
+}
+{
+    <LPAREN>posArray = passerPositionArray2()<RPAREN>
+    {
+        positionList.add(posArray);
+    }
+    (
+        <COMMA>
+        <LPAREN>posArray = passerPositionArray2()<RPAREN>
+        {
+            positionList.add(posArray);
+        }
+    )*
+    {
+        return positionList;
+    }
+}
+
+/** 解析二维 Position 对象数组 */
+ArrayList<ArrayList<SGisPosition>> passerPositionArray2():
+{
+    ArrayList<SGisPosition> posArray;
+    ArrayList<ArrayList<SGisPosition>> positionList = new ArrayList<ArrayList<SGisPosition>>();
+}
+{
+    <LPAREN>posArray = passerPositionArray()<RPAREN>
+    {
+        positionList.add(posArray);
+    }
+    (
+        <COMMA>
+        <LPAREN>posArray = passerPositionArray()<RPAREN>
+        {
+            positionList.add(posArray);
+        }
+    )*
+    {
+        return positionList;
+    }
+}
+
+/** 解析 Position 对象数组 */
+ArrayList<SGisPosition> passerPositionArray():
+{
+    SGisPosition pos;
+    ArrayList<SGisPosition> positionList = new ArrayList<SGisPosition>();
+}
+{
+    pos = passerPosition()
+    {
+        positionList.add(pos);
+    }
+    (
+        <COMMA>
+        pos = passerPosition()
+        {
+            positionList.add(pos);
+        }
+    )*
+    {
+        return positionList;
+    }
+}
+
+/** 解析 Position 对象 */
+SGisPosition passerPosition():
+{
+    Double pos;
+    ArrayList<Double> valList = new ArrayList<Double>();
+}
+{
+    pos = parserNum()
+    {
+        valList.add(pos);
+    }
+    (
+        pos = parserNum()
+        {
+            valList.add(pos);
+        }
+    )*
+    {
+        if (valList.size() >= 3) {
+            return new SGisPosition(valList.get(0), valList.get(1), valList.get(2));
+        }
+        if (valList.size() >= 2) {
+            return new SGisPosition(valList.get(0), valList.get(1), null);
+        }
+        if (valList.size() >= 1) {
+            return new SGisPosition(valList.get(0), 0.0, null);
+        }
+        return new SGisPosition();
+    }
+}
+
+/** 解析数 */
+Double parserNum():
+{
+    Token t;
+}
+{
+    t = <NUM>
+    {
+        return Double.parseDouble(t.image);
+    }
+}

+ 41 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SAbstractGisGeometry.kt

@@ -0,0 +1,41 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.persagy.gis.enums.SGisGeometryType
+
+/**
+ * 几何对象 - 抽像类
+ *
+ * @author  张 <sybotan@126.com>
+ */
+abstract class SAbstractGisGeometry(val type: SGisGeometryType) {
+    /**
+     * 转换成 wkt 格式数据
+     */
+    abstract fun toWkt(): String
+}

+ 35 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisGeometryCollection.kt

@@ -0,0 +1,35 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+/**
+ * 几何对象集合
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisGeometryCollection {
+}

+ 52 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisLineString.kt

@@ -0,0 +1,52 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.persagy.gis.enums.SGisGeometryType
+import com.persagy.gis.json.SGisPositionArraySerializer
+import com.persagy.gis.utils.SGisPositionUtil
+
+/**
+ * 几何对象 - 线段
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisLineString : SAbstractGisGeometry(SGisGeometryType.LineString) {
+    /** 线段坐标 */
+    @JsonSerialize(using = SGisPositionArraySerializer::class)
+    var coordinates = ArrayList<SGisPosition>()
+
+    /**
+     * 转换成 wkt 格式数据
+     *
+     * @return  wkt 格式的数据
+     */
+    override fun toWkt(): String {
+        return "LINESTRING (${SGisPositionUtil.arrayToWkt(coordinates)})"
+    }
+}

+ 52 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisMultiLineString.kt

@@ -0,0 +1,52 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.persagy.gis.enums.SGisGeometryType
+import com.persagy.gis.json.SGisPositionArray2Serializer
+import com.persagy.gis.utils.SGisPositionUtil
+
+/**
+ * 几何对象 - 多个线段
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisMultiLineString : SAbstractGisGeometry(SGisGeometryType.MultiLineString) {
+    /** 多个线段 */
+    @JsonSerialize(using = SGisPositionArray2Serializer::class)
+    var coordinates = ArrayList<ArrayList<SGisPosition>>()
+
+    /**
+     * 转换成 wkt 格式数据
+     *
+     * @return  wkt 格式的数据
+     */
+    override fun toWkt(): String {
+        return "MULTILINESTRING (${SGisPositionUtil.array2ToWkt(coordinates)})"
+    }
+}

+ 52 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisMultiPoint.kt

@@ -0,0 +1,52 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.persagy.gis.enums.SGisGeometryType
+import com.persagy.gis.json.SGisPositionArraySerializer
+import com.persagy.gis.utils.SGisPositionUtil
+
+/**
+ * 几何对象 - 多个点
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisMultiPoint : SAbstractGisGeometry(SGisGeometryType.MultiPoint) {
+    /** 多个点 */
+    @JsonSerialize(using = SGisPositionArraySerializer::class)
+    var coordinates = ArrayList<SGisPosition>()
+
+    /**
+     * 转换成 wkt 格式数据
+     *
+     * @return  wkt 格式的数据
+     */
+    override fun toWkt(): String {
+        return "MULTIPOINT (${SGisPositionUtil.arrayToWkt(coordinates)})"
+    }
+}

+ 52 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisMultiPolygon.kt

@@ -0,0 +1,52 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.persagy.gis.enums.SGisGeometryType
+import com.persagy.gis.json.SGisPositionArray3Serializer
+import com.persagy.gis.utils.SGisPositionUtil
+
+/**
+ * 几何对象 - 多个多边形
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisMultiPolygon : SAbstractGisGeometry(SGisGeometryType.MultiPolygon) {
+    /** 多边形坐标 */
+    @JsonSerialize(using = SGisPositionArray3Serializer::class)
+    var coordinates = ArrayList<ArrayList<ArrayList<SGisPosition>>>();
+
+    /**
+     * 转换成 wkt 格式数据
+     *
+     * @return  wkt 格式的数据
+     */
+    override fun toWkt(): String {
+        return "MULTIPOLYGON (${SGisPositionUtil.array3ToWkt(coordinates)})"
+    }
+}

+ 52 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisPoint.kt

@@ -0,0 +1,52 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.persagy.gis.enums.SGisGeometryType
+import com.persagy.gis.json.SGisPositionSerializer
+import com.persagy.gis.utils.SGisPositionUtil
+
+/**
+ * 几何对象 - 点
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPoint : SAbstractGisGeometry(SGisGeometryType.Point) {
+    /** 坐标 */
+    @JsonSerialize(using = SGisPositionSerializer::class)
+    var coordinates = SGisPosition()
+
+    /**
+     * 转换成 wkt 格式数据
+     *
+     * @return  wkt 格式的数据
+     */
+    override fun toWkt(): String {
+        return "POINT (${SGisPositionUtil.toWkt(coordinates)})"
+    }
+}

+ 52 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisPolygon.kt

@@ -0,0 +1,52 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.persagy.gis.enums.SGisGeometryType
+import com.persagy.gis.json.SGisPositionArray2Serializer
+import com.persagy.gis.utils.SGisPositionUtil
+
+/**
+ * 几何对象 - 多边形
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPolygon : SAbstractGisGeometry(SGisGeometryType.Polygon){
+    /** 多边形坐标 */
+    @JsonSerialize(using = SGisPositionArray2Serializer::class)
+    var coordinates = ArrayList<ArrayList<SGisPosition>>()
+
+    /**
+     * 转换成 wkt 格式数据
+     *
+     * @return  wkt 格式的数据
+     */
+    override fun toWkt(): String {
+        return "POLYGON (${SGisPositionUtil.array2ToWkt(coordinates)})"
+    }
+}

+ 41 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/entity/SGisPosition.kt

@@ -0,0 +1,41 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.entity
+
+/**
+ * 位置
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPosition(
+        /** x 坐标 */
+        var x: Double = 0.0,
+        /** y 坐标 */
+        var y: Double = 0.0,
+        /** z 坐标 */
+        var z: Double? = null
+)

+ 47 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/enums/SGisGeometryType.kt

@@ -0,0 +1,47 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.enums
+
+/**
+ * 几何类型
+ *
+ * @author  张维新 <zhangweixin@sagacloud.cn>
+ */
+enum class SGisGeometryType {
+    /** 点 */
+    Point,
+    /** 多点 */
+    MultiPoint,
+    /** 线段 */
+    LineString,
+    /** 多线段 */
+    MultiLineString,
+    /** 面 */
+    Polygon,
+    /** 多面 */
+    MultiPolygon
+}

+ 65 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionArray2Serializer.kt

@@ -0,0 +1,65 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.json
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.databind.JsonSerializer
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.persagy.gis.entity.SGisPosition
+
+/**
+ * 自定义 SGisPosition 数组对象序列化器
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPositionArray2Serializer : JsonSerializer<ArrayList<ArrayList<SGisPosition>>>() {
+    /** SGisPosition 对象序列化器 */
+    private val positionSerializer = SGisPositionArraySerializer()
+
+    /**
+     * Method that can be called to ask implementation to serialize
+     * values of type this serializer handles.
+     *
+     * @param   value           Value to serialize; can **not** be null.
+     * @param   gen             Generator used to output resulting Json content
+     * @param   serializers     Provider that can be used to get serializers for
+     * serializing Objects value contains, if any.
+     */
+    override fun serialize(value: ArrayList<ArrayList<SGisPosition>>?, gen: JsonGenerator, serializers: SerializerProvider) {
+        // 如果被序列化对象为空,则直接返回
+        value ?: return;
+
+        gen.writeStartArray()
+
+        // 依次序列化所有 SGisPosition 对象
+        for (item in value) {
+            positionSerializer.serialize(item, gen, serializers)
+        }
+
+        gen.writeEndArray()
+    }
+}

+ 65 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionArray3Serializer.kt

@@ -0,0 +1,65 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.json
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.databind.JsonSerializer
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.persagy.gis.entity.SGisPosition
+
+/**
+ * 自定义 SGisPosition 数组对象序列化器
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPositionArray3Serializer : JsonSerializer<ArrayList<ArrayList<ArrayList<SGisPosition>>>>() {
+    /** SGisPosition 对象序列化器 */
+    private val positionSerializer = SGisPositionArray2Serializer()
+
+    /**
+     * Method that can be called to ask implementation to serialize
+     * values of type this serializer handles.
+     *
+     * @param   value           Value to serialize; can **not** be null.
+     * @param   gen             Generator used to output resulting Json content
+     * @param   serializers     Provider that can be used to get serializers for
+     * serializing Objects value contains, if any.
+     */
+    override fun serialize(value: ArrayList<ArrayList<ArrayList<SGisPosition>>>?, gen: JsonGenerator, serializers: SerializerProvider) {
+        // 如果被序列化对象为空,则直接返回
+        value ?: return;
+
+        gen.writeStartArray()
+
+        // 依次序列化所有 SGisPosition 对象
+        for (item in value) {
+            positionSerializer.serialize(item, gen, serializers)
+        }
+
+        gen.writeEndArray()
+    }
+}

+ 65 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionArraySerializer.kt

@@ -0,0 +1,65 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.json
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.databind.JsonSerializer
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.persagy.gis.entity.SGisPosition
+
+/**
+ * 自定义 SGisPosition 数组对象序列化器
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPositionArraySerializer : JsonSerializer<ArrayList<SGisPosition>>() {
+    /** SGisPosition 对象序列化器 */
+    private val positionSerializer = SGisPositionSerializer()
+
+    /**
+     * Method that can be called to ask implementation to serialize
+     * values of type this serializer handles.
+     *
+     * @param   value           Value to serialize; can **not** be null.
+     * @param   gen             Generator used to output resulting Json content
+     * @param   serializers     Provider that can be used to get serializers for
+     * serializing Objects value contains, if any.
+     */
+    override fun serialize(value: ArrayList<SGisPosition>?, gen: JsonGenerator, serializers: SerializerProvider) {
+        // 如果被序列化对象为空,则直接返回
+        value ?: return;
+
+        gen.writeStartArray()
+
+        // 依次序列化所有 SGisPosition 对象
+        for (item in value) {
+            positionSerializer.serialize(item, gen, serializers)
+        }
+
+        gen.writeEndArray()
+    }
+}

+ 62 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/json/SGisPositionSerializer.kt

@@ -0,0 +1,62 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.json
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.databind.JsonSerializer
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.persagy.gis.entity.SGisPosition
+
+/**
+ * 自定义 SGisPosition 对象序列化器
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+class SGisPositionSerializer : JsonSerializer<SGisPosition>() {
+    /**
+     * Method that can be called to ask implementation to serialize
+     * values of type this serializer handles.
+     *
+     * @param   value           Value to serialize; can **not** be null.
+     * @param   gen             Generator used to output resulting Json content
+     * @param   serializers     Provider that can be used to get serializers for
+     * serializing Objects value contains, if any.
+     */
+    override fun serialize(value: SGisPosition?, gen: JsonGenerator, serializers: SerializerProvider) {
+        // 如果被序列化对象为空,则直接返回
+        value ?: return;
+
+        // 如果 position 有 z 坐标,则生成的数组中有 3 个元素,否则有 2 个元素
+        val list = if (value.z != null) {
+            doubleArrayOf(value.x, value.y, value.z!!)
+        } else {
+            doubleArrayOf(value.x, value.y)
+        }
+
+        gen.writeArray(list, 0, list.size)
+    }
+}

+ 120 - 0
persagy-kotlin-gis/src/main/kotlin/com/persagy/gis/utils/SGisPositionUtil.kt

@@ -0,0 +1,120 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+package com.persagy.gis.utils
+
+import com.persagy.gis.entity.SGisPosition
+
+/**
+ * SGisPosition 工具对象
+ *
+ * @author  庞利祥 <sybotan@126.com>
+ */
+object SGisPositionUtil {
+    /**
+     * position 对象转换成 wkt 格式数据
+     *
+     * @param   pos     要转换的 position 对象
+     * @return  wkt 格式数据
+     */
+    fun toWkt(pos: SGisPosition): String {
+        // 如果有第三个坐标(z 轴),则生成带 Z 轴坐标值
+        return if (pos.z != null) {
+            "${pos.x} ${pos.y} ${pos.z}"
+        } else { // 否则,生成只有 x, y 坐标值
+            "${pos.x} ${pos.y}"
+        }
+    }
+
+    /**
+     * position 数组对象转换成 wkt 格式数据
+     *
+     * @param   posArr      要转换的 position 数组对象
+     * @return  wkt 格式数据
+     */
+    fun arrayToWkt(posArr: ArrayList<SGisPosition>): String {
+        // 用于生成结果的字符串 builder
+        val builder = StringBuilder()
+
+        // 依次加入所有坐标点
+        for (pos in posArr) {
+            // 如果已经加入过一个坐标点,则需要加入“,”分隔符
+            if (builder.isNotEmpty()) {
+                builder.append(", ")
+            }
+
+            builder.append(toWkt(pos))
+        }
+
+        return builder.toString()
+    }
+
+    /**
+     * 二维 position 数组对象转换成 wkt 格式数据
+     *
+     * @param   posArr      要转换的 position 二维数组对象
+     * @return  wkt 格式数据
+     */
+    fun array2ToWkt(posArr: ArrayList<ArrayList<SGisPosition>>): String {
+        // 用于生成结果的字符串 builder
+        val builder = StringBuilder()
+
+        // 依次加入所有坐标点
+        for (subArr in posArr) {
+            // 如果已经加入过一个坐标点,则需要加入“,”分隔符
+            if (builder.isNotEmpty()) {
+                builder.append(", ")
+            }
+
+            builder.append("(${arrayToWkt(subArr)})")
+        }
+
+        return builder.toString()
+    }
+
+    /**
+     * 三维 position 数组对象转换成 wkt 格式数据
+     *
+     * @param   posArr      要转换的 position 三维数组对象
+     * @return  wkt 格式数据
+     */
+    fun array3ToWkt(posArr: ArrayList<ArrayList<ArrayList<SGisPosition>>>): String {
+        // 用于生成结果的字符串 builder
+        val builder = StringBuilder()
+
+        // 依次加入所有坐标点
+        for (subArr in posArr) {
+            // 如果已经加入过一个坐标点,则需要加入“,”分隔符
+            if (builder.isNotEmpty()) {
+                builder.append(", ")
+            }
+
+            builder.append("(${array2ToWkt(subArr)})")
+        }
+
+        return builder.toString()
+    }
+}

+ 1 - 0
persagy-kotlin-mybatis/build.gradle

@@ -67,6 +67,7 @@ dependencies {
     // Sybotan依赖
     implementation project(':persagy-kotlin-base')
     implementation project(':persagy-kotlin-database')
+    implementation project(':persagy-kotlin-gis')
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ 17 - 170
persagy-kotlin-mybatis/src/main/kotlin/com/persagy/mybatis/SAbstractDriver.kt

@@ -27,11 +27,13 @@
 package com.persagy.mybatis
 
 import com.persagy.base.extensions.getAllDeclaredFields
+import com.persagy.base.extensions.toJson
 import com.persagy.database.SAbstractDao
 import com.persagy.database.SAbstractFunction
 import com.persagy.database.SFilter
 import com.persagy.database.SQueryBuilder
 import com.persagy.database.enums.SOps
+import com.persagy.gis.entity.SAbstractGisGeometry
 import org.apache.ibatis.jdbc.SQL
 import org.slf4j.LoggerFactory
 import javax.persistence.Column
@@ -74,92 +76,20 @@ abstract class SAbstractDriver {
     abstract fun delete(argsMap: Map<String, Any?>): String
 
     /**
-     * 删除所有记录
+     * 根据条件更新
      *
      * @param argsMap         注入参数
-     * @return 删除 SQL 语句
+     * @return 更新 SQL 语句
      */
-    abstract fun deleteAll(argsMap: Map<String, Any?>): String
+    abstract fun update(argsMap: Map<String, Any?>): String
 
     /**
-     * 根据条件更新
+     * 删除所有记录
      *
      * @param argsMap         注入参数
-     * @return 更新 SQL 语句
+     * @return 删除 SQL 语句
      */
-    @Suppress("UNCHECKED_CAST")
-    open fun update(argsMap: Map<String, Any?>): String {
-        val entityClass = argsMap["entityClass"] as Class<*>
-        val entity = argsMap["entity"] as HashMap<String, Any?>
-        val filterArgs = argsMap["filterArgs"] as List<SFilter>
-        val whereArgs = argsMap["whereArgs"] as HashMap<String, Any>
-        val nullItemList = ArrayList<String>()
-
-        if (argsMap.containsKey("nullItems")) {
-            val fields = argsMap["nullItems"] as ArrayList<String>?
-            /** 转换属性名 */
-            fields?.forEach {
-                try {
-                    val colName = SAbstractDao.columnName(entityClass, it.trim())
-                    nullItemList.add(colName)
-                } catch (e: Exception) {
-                    e.printStackTrace()
-                }
-            }
-        }
-
-//        val updateNull = fieldList.size > 0
-        //
-        val clause = buildWhereArgList(entityClass, filterArgs, whereArgs)
-
-        val sql = object: SQL() {
-            init {
-                UPDATE(SSqlProvider.driver.tableName(entityClass))
-
-                // 遍历传入的对象的属性 键值
-                for ((key, value) in entity) {
-//                    if (nullItemList.size > 0 && !nullItemList.contains(key)) {
-//                        continue
-//                    }
-//                    if (value == null && updateNull) {
-//                        // PostgreSQL使用参数更新,为空更新不成功。必须使用sql直接设置
-//                        SET("${SSqlProvider.driver.escName(key)} = null")
-//                    }
-                    // 值不为空的更新
-                    if (value != null) {
-                        if (value is Map<*,*>) {
-                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.updateJson(key, value)}")
-                        } else {
-                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.entityValue(entity, key)}")
-                        }
-//                        if (value::class.java.isPrimitive) {
-//                            SET("${driver.keyEsc}$key${driver.keyEsc} = ${driver.entityValue(entity, key)}")
-//                        } else {
-//                            SET("${driver.keyEsc}$key${driver.keyEsc} = ${driver.updateJson(key, value)}")
-//                        }
-                    }
-                }
-                // 遍历置空属性列表
-                for (nullItem in nullItemList){
-                    // PostgreSQL使用参数更新,为空更新不成功。必须使用sql直接设置
-                    val pos = nullItem.indexOf(".")
-                    if (pos > 0){
-                        SET(SSqlProvider.driver.updateNullItem(nullItem))
-                    }else{
-                        SET("${SSqlProvider.driver.escName(nullItem)} = null")
-                    }
-                }
-                // 更新条件为空不拼接条件
-                if (clause.isNotEmpty()) {
-                    WHERE(clause)
-                }
-            }
-        }.toString()
-
-        logger.debug("SQL= $sql")
-
-        return sql
-    }
+    abstract fun deleteAll(argsMap: Map<String, Any?>): String
 
     /**
      * 执行查询操作
@@ -168,97 +98,8 @@ abstract class SAbstractDriver {
      * @return 查询 SQL 语句
      */
     @Suppress("UNCHECKED_CAST")
-    fun execQuery(argsMap: Map<String, Any?>): String {
-        val entityClass = argsMap["entityClass"] as Class<*>
-        val builder = argsMap["builder"] as SQueryBuilder<*>
-        val whereArgs = argsMap["whereArgs"] as HashMap<String, Any>
-
-        val clause = buildWhereArgList(entityClass, builder.filterList, whereArgs)
-
-        var sql = object: SQL() {
-            init {
-                /** 如果选择去重 */
-                if (builder.isDistinct) {
-                    SELECT_DISTINCT(buildColumns(entityClass, builder.fieldList))
-                } else {
-                    SELECT(buildColumns(entityClass, builder.fieldList))
-                }
+    abstract fun execQuery(argsMap: Map<String, Any?>): String
 
-                /** 如果查询构建器朱指定了表名 */
-                if (builder.tableName.isNullOrEmpty()) {
-                    FROM(SSqlProvider.driver.tableName(entityClass))
-                } else {
-                    FROM(builder.tableName!!)
-                }
-
-                /** 过滤条件 */
-                if (clause.isNotEmpty()) {
-                    WHERE(clause)
-                }
-
-                /** 排序条件 */
-                for (col in builder.orderArgs) {
-                    var path: String? = null
-                    var propertyName = col.name
-                    val pos = propertyName.indexOf(".")
-                    if (pos > 0) {
-                        path = propertyName.substring(pos + 1)
-                        propertyName = propertyName.substring(0, pos)
-                    }
-                    val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, propertyName))
-
-                    ORDER_BY("${SSqlProvider.driver.columnName(colName, path)} ${col.direction}")
-                }
-
-                /** 分组 */
-                if (!builder.group.nameList.isNullOrEmpty()) {
-                    for (name in builder.group.nameList) {
-                        val colName = SAbstractDao.columnName(entityClass, name)
-                        GROUP_BY(SSqlProvider.driver.escName(colName))
-                    }
-
-                    if (builder.group.count !=null && builder.group.count!!) {
-                        SELECT("count(1) as _statistics_count")
-                    }
-
-                    if (!builder.group.sumList.isNullOrEmpty()) {
-                        for (name in builder.group.sumList) {
-                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
-                            SELECT("sum(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_sum_$name")
-                        }
-                    }
-
-                    if (!builder.group.avgList.isNullOrEmpty()) {
-                        for (name in builder.group.sumList) {
-                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
-                            SELECT("avg(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_avg_$name")
-                        }
-                    }
-
-                    if (!builder.group.maxList.isNullOrEmpty()) {
-                        for (name in builder.group.sumList) {
-                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
-                            SELECT("max(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_max_$name")
-                        }
-                    }
-
-                    if (!builder.group.minList.isNullOrEmpty()) {
-                        for (name in builder.group.sumList) {
-                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
-                            SELECT("min(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_min_$name")
-                        }
-                    }
-                }
-            }
-        }.toString()
-
-        if (builder.count != null) {
-            sql += SSqlProvider.driver.limit(builder.offset, builder.count)
-        }
-
-        logger.debug("SQL= $sql")
-        return sql
-    }
 
     /**
      * 执行查询操作
@@ -580,9 +421,10 @@ abstract class SAbstractDriver {
         if (fieldList == null || fieldList.size <= 0) {
             return "*"
         }
-
+        logger.debug("222222222222222222222222222222222222")
         val builder = StringBuilder()
         for (field in clazz.getAllDeclaredFields()) {
+            logger.debug("field = ${field.toJson()}")
             if (!fieldList.contains(field.name)) {
                 continue
             }
@@ -591,8 +433,13 @@ abstract class SAbstractDriver {
             if (builder.isNotEmpty()) {
                 builder.append(", ")
             }
+            if (SAbstractGisGeometry::class.java.isAssignableFrom(field.type)) {
+                val col = SSqlProvider.driver.escName(anna.name)
+                builder.append("ST_AsText($col) AS $col")
+            }else {
+                builder.append(SSqlProvider.driver.escName(anna.name))
+            }
 
-            builder.append(SSqlProvider.driver.escName(anna.name))
         }
         return builder.toString()
     }

+ 7 - 0
persagy-kotlin-mybatis/src/main/kotlin/com/persagy/mybatis/SRowParser.kt

@@ -29,7 +29,10 @@ package com.persagy.mybatis
 import com.alibaba.fastjson.PropertyNamingStrategy
 import com.persagy.base.extensions.*
 import com.persagy.base.utils.SJsonUtil
+import com.persagy.gis.entity.SAbstractGisGeometry
+import com.persagy.gis.parser.SGisWktParser
 import org.slf4j.LoggerFactory
+import java.io.ByteArrayInputStream
 import javax.persistence.Column
 
 /**
@@ -82,6 +85,10 @@ open class SRowParser<ENTITY: Any>(entityClazz: Class<ENTITY>) {
                             // DO NOTHING
                         }
                     }
+                    SAbstractGisGeometry::class.java.isAssignableFrom(field.type) ->{
+                        val parser = SGisWktParser(ByteArrayInputStream((value.toString().toByteArray())))
+                        field.set(entity, parser.parser())
+                    }
                     else -> {
                         // field.set(entity, SJsonUtil.fromJson(value.toString(), field.type))
                         field.set(entity, SJsonUtil.fromJson(value.toString(), field.type))

+ 182 - 0
persagy-kotlin-mysql/src/main/kotlin/com/persagy/mysql/SMySqlDriver.kt

@@ -29,8 +29,10 @@ package com.persagy.mysql
 import com.persagy.base.extensions.isBaseType
 import com.persagy.base.extensions.toJson
 import com.persagy.base.extensions.toJsonAll
+import com.persagy.database.SAbstractDao
 import com.persagy.database.SAbstractFunction
 import com.persagy.database.SFilter
+import com.persagy.database.SQueryBuilder
 import com.persagy.database.enums.SDatabaseType
 import com.persagy.database.enums.SOps
 import com.persagy.mybatis.SAbstractDriver
@@ -100,6 +102,86 @@ class SMySqlDriver : SAbstractDriver() {
     }
 
     /**
+     * 根据条件更新
+     *
+     * @param argsMap         注入参数
+     * @return 更新 SQL 语句
+     */
+    override fun update(argsMap: Map<String, Any?>): String {
+        val entityClass = argsMap["entityClass"] as Class<*>
+        val entity = argsMap["entity"] as HashMap<String, Any?>
+        val filterArgs = argsMap["filterArgs"] as List<SFilter>
+        val whereArgs = argsMap["whereArgs"] as HashMap<String, Any>
+        val nullItemList = ArrayList<String>()
+
+        if (argsMap.containsKey("nullItems")) {
+            val fields = argsMap["nullItems"] as ArrayList<String>?
+            /** 转换属性名 */
+            fields?.forEach {
+                try {
+                    val colName = SAbstractDao.columnName(entityClass, it.trim())
+                    nullItemList.add(colName)
+                } catch (e: Exception) {
+                    e.printStackTrace()
+                }
+            }
+        }
+
+//        val updateNull = fieldList.size > 0
+        //
+        val clause = buildWhereArgList(entityClass, filterArgs, whereArgs)
+
+        val sql = object: SQL() {
+            init {
+                UPDATE(SSqlProvider.driver.tableName(entityClass))
+
+                // 遍历传入的对象的属性 键值
+                for ((key, value) in entity) {
+//                    if (nullItemList.size > 0 && !nullItemList.contains(key)) {
+//                        continue
+//                    }
+//                    if (value == null && updateNull) {
+//                        // PostgreSQL使用参数更新,为空更新不成功。必须使用sql直接设置
+//                        SET("${SSqlProvider.driver.escName(key)} = null")
+//                    }
+                    // 值不为空的更新
+                    if (value != null) {
+                        if (value is Map<*,*>) {
+                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.updateJson(key, value)}")
+                        } else {
+                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.entityValue(entity, key)}")
+                        }
+//                        if (value::class.java.isPrimitive) {
+//                            SET("${driver.keyEsc}$key${driver.keyEsc} = ${driver.entityValue(entity, key)}")
+//                        } else {
+//                            SET("${driver.keyEsc}$key${driver.keyEsc} = ${driver.updateJson(key, value)}")
+//                        }
+                    }
+                }
+                // 遍历置空属性列表
+                for (nullItem in nullItemList){
+                    // PostgreSQL使用参数更新,为空更新不成功。必须使用sql直接设置
+                    val pos = nullItem.indexOf(".")
+                    if (pos > 0){
+                        SET(SSqlProvider.driver.updateNullItem(nullItem))
+                    }else{
+                        SET("${SSqlProvider.driver.escName(nullItem)} = null")
+                    }
+                }
+                // 更新条件为空不拼接条件
+                if (clause.isNotEmpty()) {
+                    WHERE(clause)
+                }
+            }
+        }.toString()
+
+        logger.debug("SQL= $sql")
+
+        return sql
+    }
+
+
+    /**
      * 根据条件删除
      *
      * @param argsMap         注入参数
@@ -146,6 +228,106 @@ class SMySqlDriver : SAbstractDriver() {
         return sql
     }
 
+
+    /**
+     * 执行查询操作
+     *
+     * @param argsMap         注入参数
+     * @return 查询 SQL 语句
+     */
+    @Suppress("UNCHECKED_CAST")
+    override fun execQuery(argsMap: Map<String, Any?>): String {
+        val entityClass = argsMap["entityClass"] as Class<*>
+        val builder = argsMap["builder"] as SQueryBuilder<*>
+        val whereArgs = argsMap["whereArgs"] as HashMap<String, Any>
+
+        val clause = buildWhereArgList(entityClass, builder.filterList, whereArgs)
+
+        var sql = object: SQL() {
+            init {
+                /** 如果选择去重 */
+                if (builder.isDistinct) {
+                    SELECT_DISTINCT(buildColumns(entityClass, builder.fieldList))
+                } else {
+                    SELECT(buildColumns(entityClass, builder.fieldList))
+                }
+
+                /** 如果查询构建器朱指定了表名 */
+                if (builder.tableName.isNullOrEmpty()) {
+                    FROM(SSqlProvider.driver.tableName(entityClass))
+                } else {
+                    FROM(builder.tableName!!)
+                }
+
+                /** 过滤条件 */
+                if (clause.isNotEmpty()) {
+                    WHERE(clause)
+                }
+
+                /** 排序条件 */
+                for (col in builder.orderArgs) {
+                    var path: String? = null
+                    var propertyName = col.name
+                    val pos = propertyName.indexOf(".")
+                    if (pos > 0) {
+                        path = propertyName.substring(pos + 1)
+                        propertyName = propertyName.substring(0, pos)
+                    }
+                    val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, propertyName))
+
+                    ORDER_BY("${SSqlProvider.driver.columnName(colName, path)} ${col.direction}")
+                }
+
+                /** 分组 */
+                if (!builder.group.nameList.isNullOrEmpty()) {
+                    for (name in builder.group.nameList) {
+                        val colName = SAbstractDao.columnName(entityClass, name)
+                        GROUP_BY(SSqlProvider.driver.escName(colName))
+                    }
+
+                    if (builder.group.count !=null && builder.group.count!!) {
+                        SELECT("count(1) as _statistics_count")
+                    }
+
+                    if (!builder.group.sumList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("sum(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_sum_$name")
+                        }
+                    }
+
+                    if (!builder.group.avgList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("avg(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_avg_$name")
+                        }
+                    }
+
+                    if (!builder.group.maxList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("max(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_max_$name")
+                        }
+                    }
+
+                    if (!builder.group.minList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("min(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_min_$name")
+                        }
+                    }
+                }
+            }
+        }.toString()
+
+        if (builder.count != null) {
+            sql += SSqlProvider.driver.limit(builder.offset, builder.count)
+        }
+
+        logger.debug("SQL= $sql")
+        return sql
+    }
+
     /**
      * 获得表名,取实体类@Table注解的名称,如果没有注解,则取实体类类名。
      *

+ 1 - 0
persagy-kotlin-postgresql/build.gradle

@@ -70,6 +70,7 @@ dependencies {
     compile project(':persagy-kotlin-base')
     compile project(':persagy-kotlin-database')
     compile project(':persagy-kotlin-mybatis')
+    compile project(':persagy-kotlin-gis')
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ 199 - 4
persagy-kotlin-postgresql/src/main/kotlin/com/persagy/postgresql/SPostgreSqlDriver.kt

@@ -32,6 +32,7 @@ import com.persagy.base.extensions.toJsonAll
 import com.persagy.database.*
 import com.persagy.database.enums.SDatabaseType
 import com.persagy.database.enums.SOps
+import com.persagy.gis.entity.SAbstractGisGeometry
 import com.persagy.mybatis.SAbstractDriver
 import com.persagy.mybatis.SSqlProvider
 import org.apache.ibatis.jdbc.SQL
@@ -79,7 +80,9 @@ class SPostgreSqlDriver : SAbstractDriver() {
                             VALUES(SSqlProvider.driver.escName(key), "'${value.toJsonAll().replace("'", "''")}'")
                         } else  if (value::class.java.isPrimitive){
                             VALUES(SSqlProvider.driver.escName(key), "'${value.toJsonAll().replace("'", "''")}'")
-                        }else {
+                        } else if (value is SAbstractGisGeometry) {
+                            VALUES(SSqlProvider.driver.escName(key), "ST_GeomFromText('${value.toWkt()}')")
+                        } else {
                             VALUES(SSqlProvider.driver.escName(key), SSqlProvider.driver.entityValue(entity, key))
                         }
 //                        if (value::class.java.isPrimitive) {
@@ -124,6 +127,9 @@ class SPostgreSqlDriver : SAbstractDriver() {
                     upSqlBuilder.append("${escName(key)} = '${value.toJsonAll().replace("'", "''")}'")
                 } else if (value::class.java.isPrimitive){
                     upSqlBuilder.append("${escName(key)} = '${value.toJsonAll().replace("'", "''")}'")
+                } else if (value is SAbstractGisGeometry) {
+                    upSqlBuilder.append("${escName(key)} = ST_GeomFromText('${value.toWkt()}')")
+//                    VALUES(SSqlProvider.driver.escName(key), "ST_GeomFromText('${value.toWkt()}')")
                 } else {
                     upSqlBuilder.append("${escName(key)} = #{entity.$key}")
                 }
@@ -154,6 +160,95 @@ class SPostgreSqlDriver : SAbstractDriver() {
         return sql
     }
 
+
+//    @Suppress("UNCHECKED_CAST")
+//    open fun update(argsMap: Map<String, Any?>): String {
+//
+//    }
+    /**
+     * 根据条件更新
+     *
+     * @param argsMap         注入参数
+     * @return 更新 SQL 语句
+     */
+    override fun update(argsMap: Map<String, Any?>): String {
+        val entityClass = argsMap["entityClass"] as Class<*>
+        val entity = argsMap["entity"] as HashMap<String, Any?>
+        val filterArgs = argsMap["filterArgs"] as List<SFilter>
+        val whereArgs = argsMap["whereArgs"] as HashMap<String, Any>
+        val nullItemList = ArrayList<String>()
+
+        if (argsMap.containsKey("nullItems")) {
+            val fields = argsMap["nullItems"] as ArrayList<String>?
+            /** 转换属性名 */
+            fields?.forEach {
+                try {
+                    val colName = SAbstractDao.columnName(entityClass, it.trim())
+                    nullItemList.add(colName)
+                } catch (e: Exception) {
+                    e.printStackTrace()
+                }
+            }
+        }
+
+//        val updateNull = fieldList.size > 0
+        //
+        val clause = buildWhereArgList(entityClass, filterArgs, whereArgs)
+
+        val sql = object: SQL() {
+            init {
+                UPDATE(SSqlProvider.driver.tableName(entityClass))
+
+                // 遍历传入的对象的属性 键值
+                for ((key, value) in entity) {
+//                    if (nullItemList.size > 0 && !nullItemList.contains(key)) {
+//                        continue
+//                    }
+//                    if (value == null && updateNull) {
+//                        // PostgreSQL使用参数更新,为空更新不成功。必须使用sql直接设置
+//                        SET("${SSqlProvider.driver.escName(key)} = null")
+//                    }
+                    // 值不为空的更新
+                    if (value != null) {
+                        if (value is Map<*,*>) {
+                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.updateJson(key, value)}")
+                        }else if (value is List<*>) {
+                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.updateJson(key, value)}")
+                        } else if (value is SAbstractGisGeometry) {
+                            SET("${SSqlProvider.driver.escName(key)} = ST_GeomFromText('${value.toWkt()}')")
+                        } else {
+                            SET("${SSqlProvider.driver.escName(key)} = ${SSqlProvider.driver.entityValue(entity, key)}")
+                        }
+//                        if (value::class.java.isPrimitive) {
+//                            SET("${driver.keyEsc}$key${driver.keyEsc} = ${driver.entityValue(entity, key)}")
+//                        } else {
+//                            SET("${driver.keyEsc}$key${driver.keyEsc} = ${driver.updateJson(key, value)}")
+//                        }
+                    }
+                }
+                // 遍历置空属性列表
+                for (nullItem in nullItemList){
+                    // PostgreSQL使用参数更新,为空更新不成功。必须使用sql直接设置
+                    val pos = nullItem.indexOf(".")
+                    if (pos > 0){
+                        SET(SSqlProvider.driver.updateNullItem(nullItem))
+                    }else{
+                        SET("${SSqlProvider.driver.escName(nullItem)} = null")
+                    }
+                }
+                // 更新条件为空不拼接条件
+                if (clause.isNotEmpty()) {
+                    WHERE(clause)
+                }
+            }
+        }.toString()
+
+        logger.debug("SQL= $sql")
+
+        return sql
+    }
+
+
     /**
      * 根据条件删除
      *
@@ -213,6 +308,106 @@ class SPostgreSqlDriver : SAbstractDriver() {
     }
 
     /**
+     * 执行查询操作
+     *
+     * @param argsMap         注入参数
+     * @return 查询 SQL 语句
+     */
+    @Suppress("UNCHECKED_CAST")
+    override fun execQuery(argsMap: Map<String, Any?>): String {
+        val entityClass = argsMap["entityClass"] as Class<*>
+        val builder = argsMap["builder"] as SQueryBuilder<*>
+        val whereArgs = argsMap["whereArgs"] as HashMap<String, Any>
+
+        val clause = buildWhereArgList(entityClass, builder.filterList, whereArgs)
+
+        var sql = object: SQL() {
+            init {
+                logger.debug("333333333333333")
+                /** 如果选择去重 */
+                if (builder.isDistinct) {
+                    SELECT_DISTINCT(buildColumns(entityClass, builder.fieldList))
+                } else {
+                    SELECT(buildColumns(entityClass, builder.fieldList))
+                }
+
+                /** 如果查询构建器朱指定了表名 */
+                if (builder.tableName.isNullOrEmpty()) {
+                    FROM(SSqlProvider.driver.tableName(entityClass))
+                } else {
+                    FROM(builder.tableName!!)
+                }
+
+                /** 过滤条件 */
+                if (clause.isNotEmpty()) {
+                    WHERE(clause)
+                }
+
+                /** 排序条件 */
+                for (col in builder.orderArgs) {
+                    var path: String? = null
+                    var propertyName = col.name
+                    val pos = propertyName.indexOf(".")
+                    if (pos > 0) {
+                        path = propertyName.substring(pos + 1)
+                        propertyName = propertyName.substring(0, pos)
+                    }
+                    val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, propertyName))
+
+                    ORDER_BY("${SSqlProvider.driver.columnName(colName, path)} ${col.direction}")
+                }
+
+                /** 分组 */
+                if (!builder.group.nameList.isNullOrEmpty()) {
+                    for (name in builder.group.nameList) {
+                        val colName = SAbstractDao.columnName(entityClass, name)
+                        GROUP_BY(SSqlProvider.driver.escName(colName))
+                    }
+
+                    if (builder.group.count !=null && builder.group.count!!) {
+                        SELECT("count(1) as _statistics_count")
+                    }
+
+                    if (!builder.group.sumList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("sum(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_sum_$name")
+                        }
+                    }
+
+                    if (!builder.group.avgList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("avg(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_avg_$name")
+                        }
+                    }
+
+                    if (!builder.group.maxList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("max(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_max_$name")
+                        }
+                    }
+
+                    if (!builder.group.minList.isNullOrEmpty()) {
+                        for (name in builder.group.sumList) {
+                            val colName = SAbstractDao.columnName(entityClass, SAbstractDao.propertyName(entityClass, name))
+                            SELECT("min(${SSqlProvider.driver.columnName(colName, null)}) as _statistics_min_$name")
+                        }
+                    }
+                }
+            }
+        }.toString()
+
+        if (builder.count != null) {
+            sql += SSqlProvider.driver.limit(builder.offset, builder.count)
+        }
+
+        logger.debug("SQL= $sql")
+        return sql
+    }
+
+    /**
      * 获得表名,取实体类 @Table 注解的名称,如果没有注解,则取实体类类名。
      *
      * @param clz         mapper 类类型
@@ -321,11 +516,11 @@ class SPostgreSqlDriver : SAbstractDriver() {
                     /// 临时代码开始
                     logger.debug("argName=${argName}=11111111111111111111111111111111")
                     if (argName.indexOf("label") > 0){
-                    "$argName::TEXT LIKE ('%' || $argValue || '%')"
+                        "$argName::TEXT LIKE ('%' || $argValue || '%')"
                     }else {
                         logger.debug("11111111111111111111111111111111111")
-                    "$argName LIKE ('%' || $argValue || '%')"
-                }
+                        "$argName LIKE ('%' || $argValue || '%')"
+                    }
                     // 临时代码结束
                     //////////////////////
                     // 原始代码开始

+ 2 - 0
settings.gradle

@@ -31,3 +31,5 @@ include 'persagy-kotlin-database'
 include 'persagy-kotlin-mybatis'
 include 'persagy-kotlin-mysql'
 include 'persagy-kotlin-postgresql'
+include 'persagy-kotlin-gis'
+