Added custom serializer/deserializer logic for perimeter definitions

This commit is contained in:
dshvaika 2025-08-18 16:06:05 +03:00
parent cc3ecfc027
commit 29934d08bd
14 changed files with 236 additions and 59 deletions

View File

@ -622,13 +622,9 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes
Device device = createDevice("GF Device", "sn-geo-1");
// Allowed zone polygon (square)
String allowedPolygon = """
{"type":"POLYGON","polygonsDefinition":"[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"}
""";
String allowedPolygon = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]";
// Restricted zone polygon (square)
String restrictedPolygon = """
{"type":"POLYGON","polygonsDefinition":"[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"}
""";
String restrictedPolygon = "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]";
Asset allowedZoneAsset = createAsset("Allowed Zone", null);
doPost("/api/plugins/telemetry/ASSET/" + allowedZoneAsset.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE,
@ -768,9 +764,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes
Device device = createDevice("GF Device dyn", "sn-geo-dyn-1");
// Allowed Zone A: covers initial point (ENTERED)
String allowedPolygonA = """
{"type":"POLYGON","polygonsDefinition":"[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"}
""";
String allowedPolygonA = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]";
Asset allowedZoneA = createAsset("Allowed Zone A", null);
doPost("/api/plugins/telemetry/ASSET/" + allowedZoneA.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE,
@ -863,9 +857,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes
});
// --- Create Allowed Zone B covering the CURRENT location ---
String allowedPolygonB = """
{"type":"POLYGON","polygonsDefinition":"[[50.475500, 30.510500], [50.475500, 30.511500], [50.476500, 30.511500], [50.476500, 30.510500]]"}
""";
String allowedPolygonB = "[[50.475500, 30.510500], [50.475500, 30.511500], [50.476500, 30.511500], [50.476500, 30.510500]]";
Asset allowedZoneB = createAsset("Allowed Zone B", null);
doPost("/api/plugins/telemetry/ASSET/" + allowedZoneB.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE,

View File

@ -73,13 +73,11 @@ public class GeofencingCalculatedFieldStateTest {
private final SingleValueArgumentEntry latitudeArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 10, new DoubleDataEntry("latitude", 50.4730), 145L);
private final SingleValueArgumentEntry longitudeArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 6, new DoubleDataEntry("longitude", 30.5050), 165L);
private final JsonDataEntry allowedZoneDataEntry = new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"}""");
private final JsonDataEntry allowedZoneDataEntry = new JsonDataEntry("zone", "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]");
private final BaseAttributeKvEntry allowedZoneAttributeKvEntry = new BaseAttributeKvEntry(allowedZoneDataEntry, System.currentTimeMillis(), 0L);
private final GeofencingArgumentEntry geofencingAllowedZoneArgEntry = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry));
private final JsonDataEntry restrictedZoneDataEntry = new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"}""");
private final JsonDataEntry restrictedZoneDataEntry = new JsonDataEntry("zone", "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]");
private final BaseAttributeKvEntry restrictedZoneAttributeKvEntry = new BaseAttributeKvEntry(restrictedZoneDataEntry, System.currentTimeMillis(), 0L);
private final GeofencingArgumentEntry geofencingRestrictedZoneArgEntry = new GeofencingArgumentEntry(Map.of(ZONE_2_ID, restrictedZoneAttributeKvEntry));
@ -219,6 +217,7 @@ public class GeofencingCalculatedFieldStateTest {
assertThat(state.isReady()).isFalse();
}
// TODO: test different reporting strategies
@Test
void testPerformCalculation() throws ExecutionException, InterruptedException {
state.arguments = new HashMap<>(Map.of(
@ -241,8 +240,9 @@ public class GeofencingCalculatedFieldStateTest {
assertThat(result.getScope()).isEqualTo(output.getScope());
assertThat(result.getResult()).isEqualTo(
JacksonUtil.newObjectNode()
.put("allowedZoneEvent", "ENTERED")
.put("restrictedZoneEvent", "OUTSIDE")
.put("allowedZonesEvent", "ENTERED")
.put("allowedZonesStatus", "INSIDE")
.put("restrictedZonesStatus", "OUTSIDE")
);
SingleValueArgumentEntry newLatitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 50.4760), 146L);
@ -258,8 +258,10 @@ public class GeofencingCalculatedFieldStateTest {
assertThat(result2.getScope()).isEqualTo(output.getScope());
assertThat(result2.getResult()).isEqualTo(
JacksonUtil.newObjectNode()
.put("allowedZoneEvent", "LEFT")
.put("restrictedZoneEvent", "ENTERED")
.put("allowedZonesEvent", "LEFT")
.put("allowedZonesStatus", "OUTSIDE")
.put("restrictedZonesEvent", "ENTERED")
.put("restrictedZonesStatus", "INSIDE")
);

View File

@ -36,12 +36,10 @@ public class GeofencingValueArgumentEntryTest {
private final AssetId ZONE_1_ID = new AssetId(UUID.fromString("c0e3031c-7df1-45e4-9590-cfd621a4d714"));
private final AssetId ZONE_2_ID = new AssetId(UUID.fromString("e7da6200-2096-4038-a343-ade9ea4fa3e4"));
private final JsonDataEntry allowedZoneDataEntry = new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"}""");
private final JsonDataEntry allowedZoneDataEntry = new JsonDataEntry("zone", "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]");
private final BaseAttributeKvEntry allowedZoneAttributeKvEntry = new BaseAttributeKvEntry(allowedZoneDataEntry, 363L, 155L);
private final JsonDataEntry restrictedZoneDataEntry = new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"}""");
private final JsonDataEntry restrictedZoneDataEntry = new JsonDataEntry("zone", "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]");
private final BaseAttributeKvEntry restrictedZoneAttributeKvEntry = new BaseAttributeKvEntry(restrictedZoneDataEntry, 363L, 155L);
private GeofencingArgumentEntry entry;
@ -72,8 +70,7 @@ public class GeofencingValueArgumentEntryTest {
@Test
void testUpdateEntryWithTheSameTs() {
BaseAttributeKvEntry differentValueSameTs = new BaseAttributeKvEntry(new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"}"""), 363L, 156L);
BaseAttributeKvEntry differentValueSameTs = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 363L, 156L);
var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueSameTs, ZONE_2_ID, restrictedZoneAttributeKvEntry));
assertThat(entry.updateEntry(updated)).isFalse();
}
@ -81,8 +78,7 @@ public class GeofencingValueArgumentEntryTest {
@Test
@SuppressWarnings("unchecked")
void testUpdateEntryWhenNewVersionIsNull() {
BaseAttributeKvEntry differentValueNewVersionIsNull = new BaseAttributeKvEntry(new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"}"""), 364L, null);
BaseAttributeKvEntry differentValueNewVersionIsNull = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, null);
var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsNull, ZONE_2_ID, restrictedZoneAttributeKvEntry));
assertThat(entry.updateEntry(updated)).isTrue();
@ -104,8 +100,7 @@ public class GeofencingValueArgumentEntryTest {
@Test
@SuppressWarnings("unchecked")
void testUpdateEntryWhenNewVersionIsGreaterThanCurrent() {
BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"}"""), 364L, 156L);
BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 156L);
var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsSet, ZONE_2_ID, restrictedZoneAttributeKvEntry));
assertThat(entry.updateEntry(updated)).isTrue();
@ -126,8 +121,7 @@ public class GeofencingValueArgumentEntryTest {
@Test
void testUpdateEntryWhenNewVersionIsLessThanCurrent() {
BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"}"""), 364L, 154L);
BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 154L);
var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsSet, ZONE_2_ID, restrictedZoneAttributeKvEntry));
assertThat(entry.updateEntry(updated)).isFalse();
@ -152,8 +146,7 @@ public class GeofencingValueArgumentEntryTest {
@Test
void testUpdateEntryWithNewZone() {
final AssetId NEW_ZONE_ID = new AssetId(UUID.fromString("a3eacf1a-6af3-4e9f-87c4-502bb25c7dc3"));
BaseAttributeKvEntry newZone = new BaseAttributeKvEntry(new JsonDataEntry("zone", """
{"type":"POLYGON","polygonsDefinition":"[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"}"""), 364L, 156L);
BaseAttributeKvEntry newZone = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 156L);
var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry, ZONE_2_ID, restrictedZoneAttributeKvEntry, NEW_ZONE_ID, newZone));
assertThat(entry.updateEntry(updated)).isTrue();
}

View File

@ -38,9 +38,7 @@ public class GeofencingZoneStateTest {
@BeforeEach
void setUp() {
String POLYGON = """
{"type":"POLYGON","polygonsDefinition":"[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"}
""";
String POLYGON = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]";
state = new GeofencingZoneState(ZONE_ID, new BaseAttributeKvEntry(new JsonDataEntry("zone", POLYGON), 100L, 1L));
}

View File

@ -70,8 +70,8 @@ class CalculatedFieldUtilsTest {
AssetId z1 = new AssetId(zoneId1);
AssetId z2 = new AssetId(zoneId2);
JsonDataEntry zone1 = new JsonDataEntry("zone", "{\"type\":\"POLYGON\",\"polygonsDefinition\":\"[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]\"}");
JsonDataEntry zone2 = new JsonDataEntry("zone", "{\"type\":\"POLYGON\",\"polygonsDefinition\":\"[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]\"}");
JsonDataEntry zone1 = new JsonDataEntry("zone", "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]");
JsonDataEntry zone2 = new JsonDataEntry("zone", "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]");
BaseAttributeKvEntry zone1PerimeterAttribute = new BaseAttributeKvEntry(zone1, System.currentTimeMillis(), 0L);
BaseAttributeKvEntry zone2PerimeterAttribute = new BaseAttributeKvEntry(zone2, System.currentTimeMillis(), 0L);

View File

@ -83,7 +83,7 @@ public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldC
private void validateZoneGroupConfigurations(Map<String, Argument> zoneGroupsArguments) {
if (zoneGroupReportStrategies == null || zoneGroupReportStrategies.isEmpty()) {
throw new IllegalArgumentException("Zone groups configuration should be specified!");
throw new IllegalArgumentException("Zone groups reporting strategies should be specified!");
}
zoneGroupsArguments.forEach((zoneGroupName, zoneGroupArgument) -> {
GeofencingReportStrategy geofencingReportStrategy = zoneGroupReportStrategies.get(zoneGroupName);

View File

@ -251,7 +251,7 @@ public class GeofencingCalculatedFieldConfigurationTest {
assertThatThrownBy(cfg::validate)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Zone groups configuration should be specified!");
.hasMessage("Zone groups reporting strategies should be specified!");
}
@Test

View File

@ -20,10 +20,9 @@ import lombok.Data;
@Data
public class CirclePerimeterDefinition implements PerimeterDefinition {
private Double centerLatitude;
private Double centerLongitude;
private Double range;
private RangeUnit rangeUnit;
private final Double latitude;
private final Double longitude;
private final Double radius;
@Override
public PerimeterType getType() {
@ -32,9 +31,8 @@ public class CirclePerimeterDefinition implements PerimeterDefinition {
@Override
public boolean checkMatches(Coordinates entityCoordinates) {
Coordinates perimeterCoordinates = new Coordinates(centerLatitude, centerLongitude);
return range > GeoUtil.distance(entityCoordinates, perimeterCoordinates, rangeUnit);
Coordinates perimeterCoordinates = new Coordinates(latitude, longitude);
return radius > GeoUtil.distance(entityCoordinates, perimeterCoordinates, RangeUnit.METER);
}
}

View File

@ -17,19 +17,14 @@ package org.thingsboard.common.util.geo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.Serializable;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = PolygonPerimeterDefinition.class, name = "POLYGON"),
@JsonSubTypes.Type(value = CirclePerimeterDefinition.class, name = "CIRCLE")})
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonDeserialize(using = PerimeterDefinitionDeserializer.class)
@JsonSerialize(using = PerimeterDefinitionSerializer.class)
public interface PerimeterDefinition extends Serializable {
@JsonIgnore

View File

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2025 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.common.util.geo;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class PerimeterDefinitionDeserializer extends JsonDeserializer<PerimeterDefinition> {
@Override
public PerimeterDefinition deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
if (node.isObject()) {
double latitude = node.get("latitude").asDouble();
double longitude = node.get("longitude").asDouble();
double radius = node.get("radius").asDouble();
return new CirclePerimeterDefinition(latitude, longitude, radius);
}
if (node.isArray()) {
ObjectMapper mapper = (ObjectMapper) p.getCodec();
String polygonStrDefinition = mapper.writeValueAsString(node);
return new PolygonPerimeterDefinition(polygonStrDefinition);
}
throw new IOException("Failed to deserialize PerimeterDefinition from node: " + node);
}
}

View File

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2025 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.common.util.geo;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.thingsboard.server.common.data.StringUtils;
import java.io.IOException;
public class PerimeterDefinitionSerializer extends JsonSerializer<PerimeterDefinition> {
@Override
public void serialize(PerimeterDefinition value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value instanceof CirclePerimeterDefinition c) {
gen.writeStartObject();
gen.writeNumberField("latitude", c.getLatitude());
gen.writeNumberField("longitude", c.getLongitude());
gen.writeNumberField("radius", c.getRadius());
gen.writeEndObject();
return;
}
if (value instanceof PolygonPerimeterDefinition p) {
String raw = p.getPolygonDefinition();
if (StringUtils.isBlank(raw)) {
throw new IOException("Failed to serialize PolygonPerimeterDefinition with blank: " + value);
}
ObjectMapper mapper = (ObjectMapper) gen.getCodec();
gen.writeTree(mapper.readTree(raw));
return;
}
throw new IOException("Failed to serialize PerimeterDefinition from value: " + value);
}
}

View File

@ -20,7 +20,7 @@ import lombok.Data;
@Data
public class PolygonPerimeterDefinition implements PerimeterDefinition {
private String polygonsDefinition;
private final String polygonDefinition;
@Override
public PerimeterType getType() {
@ -29,7 +29,7 @@ public class PolygonPerimeterDefinition implements PerimeterDefinition {
@Override
public boolean checkMatches(Coordinates entityCoordinates) {
return GeoUtil.contains(polygonsDefinition, entityCoordinates);
return GeoUtil.contains(polygonDefinition, entityCoordinates);
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright © 2016-2025 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.common.util.geo;
import org.junit.jupiter.api.Test;
import org.thingsboard.common.util.JacksonUtil;
import static org.assertj.core.api.Assertions.assertThat;
public class PerimeterDefinitionDeserializerTest {
@Test
void shouldDeserializeCircle() {
String json = """
{"latitude":50.45,"longitude":30.52,"radius":100.0}""";
PerimeterDefinition def = JacksonUtil.fromString(json, PerimeterDefinition.class);
assertThat(def).isNotNull().isInstanceOf(CirclePerimeterDefinition.class);
CirclePerimeterDefinition circle = (CirclePerimeterDefinition) def;
assertThat(circle.getLatitude()).isEqualTo(50.45);
assertThat(circle.getLongitude()).isEqualTo(30.52);
assertThat(circle.getRadius()).isEqualTo(100.0);
}
@Test
void shouldDeserializePolygon() {
String json = "[[50.45,30.52],[50.46,30.53],[50.44,30.54]]";
PerimeterDefinition def = JacksonUtil.fromString(json, PerimeterDefinition.class);
assertThat(def).isInstanceOf(PolygonPerimeterDefinition.class);
PolygonPerimeterDefinition poly = (PolygonPerimeterDefinition) def;
assertThat(poly.getPolygonDefinition()).isEqualTo(json);
}
}

View File

@ -0,0 +1,52 @@
/**
* Copyright © 2016-2025 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.common.util.geo;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Test;
import org.thingsboard.common.util.JacksonUtil;
import static org.assertj.core.api.Assertions.assertThat;
public class PerimeterDefinitionSerializerTest {
@Test
void shouldSerializeCircle() {
PerimeterDefinition circle = new CirclePerimeterDefinition(50.45, 30.52, 120.0);
String json = JacksonUtil.writeValueAsString(circle);
JsonNode actual = JacksonUtil.toJsonNode(json);
assertThat(actual.get("latitude").asDouble()).isEqualTo(50.45);
assertThat(actual.get("longitude").asDouble()).isEqualTo(30.52);
assertThat(actual.get("radius").asDouble()).isEqualTo(120.0);
}
@Test
void shouldSerializePolygon() throws Exception {
String rawArray = "[[50.45,30.52],[50.46,30.53],[50.44,30.54]]";
PerimeterDefinition polygon = new PolygonPerimeterDefinition(rawArray);
String json = JacksonUtil.writeValueAsString(polygon);
JsonNode actual = JacksonUtil.toJsonNode(json);
JsonNode expected = JacksonUtil.toJsonNode(rawArray);
assertThat(actual).isEqualTo(expected);
assertThat(actual.isArray()).isTrue();
assertThat(actual.size()).isEqualTo(3);
}
}