Added custom serializer/deserializer logic for perimeter definitions
This commit is contained in:
parent
cc3ecfc027
commit
29934d08bd
@ -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,
|
||||
|
||||
@ -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")
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user