/**
 * Copyright © 2016-2021 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.thingsboard.rule.engine.geo;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
import org.locationtech.spatial4j.distance.DistanceUtils;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.ShapeFactory;
import org.locationtech.spatial4j.shape.SpatialRelation;

public class GeoUtil {

    private static final SpatialContext distCtx = SpatialContext.GEO;
    private static final JtsSpatialContext jtsCtx;

    static {
        JtsSpatialContextFactory factory = new JtsSpatialContextFactory();
        factory.normWrapLongitude = true;
        jtsCtx = factory.newSpatialContext();
    }

    public static synchronized double distance(Coordinates x, Coordinates y, RangeUnit unit) {
        Point xLL = distCtx.getShapeFactory().pointXY(x.getLongitude(), x.getLatitude());
        Point yLL = distCtx.getShapeFactory().pointXY(y.getLongitude(), y.getLatitude());
        return unit.fromKm(distCtx.getDistCalc().distance(xLL, yLL) * DistanceUtils.DEG_TO_KM);
    }

    public static synchronized boolean contains(String polygon, Coordinates coordinates) {
        ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon();
        JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray();
        boolean first = true;
        double firstLat = 0.0;
        double firstLng = 0.0;
        for (JsonElement jsonElement : polygonArray) {
            double lat = jsonElement.getAsJsonArray().get(0).getAsDouble();
            double lng = jsonElement.getAsJsonArray().get(1).getAsDouble();
            if (first) {
                firstLat = lat;
                firstLng = lng;
                first = false;
            }
            polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(lng), jtsCtx.getShapeFactory().normY(lat));
        }
        polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(firstLng), jtsCtx.getShapeFactory().normY(firstLat));
        Shape shape = polygonBuilder.buildOrRect();
        Point point = jtsCtx.getShapeFactory().pointXY(coordinates.getLongitude(), coordinates.getLatitude());
        return shape.relate(point).equals(SpatialRelation.CONTAINS);
    }
}

@@ -2024,168 +2024,719 @@
     
-ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon();%0A        JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray
+JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray();%0A%0A        JsonArray arrayWithCoords = polygonArray;%0A        JsonArray innerArray = polygonArray.get(0).getAsJsonArray();%0A        if (!containsPrimitives(innerArray)) %7B%0A            arrayWithCoords = innerArray;%0A        %7D%0A%0A        Shape shape = buildPolygonFromJsonCoords(arrayWithCoords);%0A        Point point = jtsCtx.getShapeFactory().pointXY(coordinates.getLongitude(), coordinates.getLatitude());%0A        return shape.relate(point).equals(SpatialRelation.CONTAINS);%0A    %7D%0A%0A    private static Shape buildPolygonFromJsonCoords(JsonArray coordinates) %7B%0A        ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon
 ();%0A
@@ -2751,17 +2751,19 @@
 boolean 
-f
+isF
 irst = t
@@ -2854,21 +2854,17 @@
 Element 
-jsonE
+e
 lement :
@@ -2864,28 +2864,27 @@
 ement : 
-polygonArray
+coordinates
 ) %7B%0A    
@@ -2904,21 +2904,17 @@
 e lat = 
-jsonE
+e
 lement.g
@@ -2980,13 +2980,9 @@
 g = 
-jsonE
+e
 leme
@@ -3039,17 +3039,19 @@
     if (
-f
+isF
 irst) %7B%0A
@@ -3126,25 +3126,27 @@
             
-f
+isF
 irst = false
@@ -3262,17 +3262,17 @@
 y().norm
-Y
+X
 (lat));%0A
@@ -3383,17 +3383,17 @@
 y().norm
-Y
+X
 (firstLa
@@ -3405,29 +3405,22 @@
         
-Shape shape =
+return
  polygon
@@ -3450,114 +3450,119 @@
     
-    Point point = jtsCtx.getShapeFactory().pointXY(coordinates.getLongitude(), coordinates.getLatitude());
+%7D%0A%0A    private static boolean containsPrimitives(JsonArray array) %7B%0A        for (JsonElement element : array) %7B
 %0A   
@@ -3570,67 +3570,76 @@
     
+    
 return 
-shape.relate(point).equals(SpatialRelation.CONTAINS)
+element.isJsonPrimitive();%0A        %7D%0A        return false
 ;%0A  

Fix exception after changing polygon on widget