From b44d51d0bfb82741ad1f403bc79067a4d4da721a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20I=C3=9Fbr=C3=BCcker?= Date: Fri, 31 Oct 2025 10:24:58 +0100 Subject: [PATCH] feat: show lines on the map --- .../flow/component/map/LineFeaturePage.java | 55 +++++ .../flow/components/map/LineFeatureIT.java | 125 +++++++++++ .../map/configuration/Constants.java | 1 + .../configuration/feature/LineFeature.java | 160 ++++++++++++++ .../configuration/geometry/LineString.java | 145 +++++++++++++ .../vaadin-map/synchronization/index.js | 12 ++ .../feature/LineFeatureTest.java | 196 ++++++++++++++++++ .../geometry/LineStringTest.java | 193 +++++++++++++++++ .../component/map/testbench/MapElement.java | 33 +++ 9 files changed, 920 insertions(+) create mode 100644 vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/main/java/com/vaadin/flow/component/map/LineFeaturePage.java create mode 100644 vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/test/java/com/vaadin/flow/components/map/LineFeatureIT.java create mode 100644 vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/feature/LineFeature.java create mode 100644 vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/geometry/LineString.java create mode 100644 vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/feature/LineFeatureTest.java create mode 100644 vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/geometry/LineStringTest.java diff --git a/vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/main/java/com/vaadin/flow/component/map/LineFeaturePage.java b/vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/main/java/com/vaadin/flow/component/map/LineFeaturePage.java new file mode 100644 index 00000000000..31f3093abdf --- /dev/null +++ b/vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/main/java/com/vaadin/flow/component/map/LineFeaturePage.java @@ -0,0 +1,55 @@ +/** + * Copyright 2000-2025 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.map; + +import java.util.List; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; +import com.vaadin.flow.component.map.configuration.Coordinate; +import com.vaadin.flow.component.map.configuration.feature.LineFeature; +import com.vaadin.flow.component.map.configuration.style.Stroke; +import com.vaadin.flow.component.map.configuration.style.Style; +import com.vaadin.flow.router.Route; + +@Route("vaadin-map/line-feature") +public class LineFeaturePage extends Div { + public LineFeaturePage() { + Map map = new Map(); + + LineFeature lineFeature = new LineFeature(new Coordinate(-10, 10), + new Coordinate(10, 10), new Coordinate(-10, -10), + new Coordinate(10, -10)); + + map.getFeatureLayer().addFeature(lineFeature); + add(map); + + NativeButton updateCoordinates = new NativeButton("Update coordinates", + event -> lineFeature.setCoordinates(List.of( + new Coordinate(-10, 10), new Coordinate(10, 10), + new Coordinate(-10, -10), new Coordinate(10, -10), + new Coordinate(0, 0)))); + updateCoordinates.setId("update-coordinates"); + add(updateCoordinates); + + NativeButton updateStyle = new NativeButton("Update style", e -> { + Style style = new Style(); + style.setStroke(new Stroke("red", 3)); + lineFeature.setStyle(style); + }); + updateStyle.setId("update-style"); + add(updateStyle); + + // Just used for manual testing + NativeButton activateDragAndDrop = new NativeButton( + "Activate Drag & Drop", e -> map.getFeatureLayer().getFeatures() + .forEach(feature -> feature.setDraggable(true))); + add(activateDragAndDrop); + } +} diff --git a/vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/test/java/com/vaadin/flow/components/map/LineFeatureIT.java b/vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/test/java/com/vaadin/flow/components/map/LineFeatureIT.java new file mode 100644 index 00000000000..e8d3749745c --- /dev/null +++ b/vaadin-map-flow-parent/vaadin-map-flow-integration-tests/src/test/java/com/vaadin/flow/components/map/LineFeatureIT.java @@ -0,0 +1,125 @@ +/** + * Copyright 2000-2025 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.components.map; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.map.testbench.MapElement; +import com.vaadin.flow.testutil.TestPath; +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.tests.AbstractComponentIT; + +@TestPath("vaadin-map/line-feature") +public class LineFeatureIT extends AbstractComponentIT { + private MapElement map; + private TestBenchElement updateCoordinates; + private TestBenchElement updateStyle; + + @Before + public void init() { + open(); + map = $(MapElement.class).waitForFirst(); + updateCoordinates = $("button").id("update-coordinates"); + updateStyle = $("button").id("update-style"); + } + + @Test + public void initialCoordinates() { + MapElement.FeatureCollectionReference features = getDefaultFeatureLayerFeatures(); + Assert.assertEquals(1, features.getLength()); + + MapElement.FeatureReference feature = features.getFeature(0); + Assert.assertTrue(feature.exists()); + + MapElement.Coordinate[] coordinates = feature.getGeometry() + .getCoordinatesArray(); + Assert.assertEquals(4, coordinates.length); + + Assert.assertEquals(-10.0, coordinates[0].getX(), 0.0001); + Assert.assertEquals(10.0, coordinates[0].getY(), 0.0001); + Assert.assertEquals(10.0, coordinates[1].getX(), 0.0001); + Assert.assertEquals(10.0, coordinates[1].getY(), 0.0001); + Assert.assertEquals(-10.0, coordinates[2].getX(), 0.0001); + Assert.assertEquals(-10.0, coordinates[2].getY(), 0.0001); + Assert.assertEquals(10.0, coordinates[3].getX(), 0.0001); + Assert.assertEquals(-10.0, coordinates[3].getY(), 0.0001); + } + + @Test + public void updateCoordinates() { + updateCoordinates.click(); + + MapElement.FeatureCollectionReference features = getDefaultFeatureLayerFeatures(); + Assert.assertEquals(1, features.getLength()); + + MapElement.FeatureReference feature = features.getFeature(0); + Assert.assertTrue(feature.exists()); + + MapElement.Coordinate[] coordinates = feature.getGeometry() + .getCoordinatesArray(); + Assert.assertEquals(5, coordinates.length); + + Assert.assertEquals(-10.0, coordinates[0].getX(), 0.0001); + Assert.assertEquals(10.0, coordinates[0].getY(), 0.0001); + Assert.assertEquals(10.0, coordinates[1].getX(), 0.0001); + Assert.assertEquals(10.0, coordinates[1].getY(), 0.0001); + Assert.assertEquals(-10.0, coordinates[2].getX(), 0.0001); + Assert.assertEquals(-10.0, coordinates[2].getY(), 0.0001); + Assert.assertEquals(10.0, coordinates[3].getX(), 0.0001); + Assert.assertEquals(-10.0, coordinates[3].getY(), 0.0001); + Assert.assertEquals(0.0, coordinates[4].getX(), 0.0001); + Assert.assertEquals(0.0, coordinates[4].getY(), 0.0001); + } + + @Test + public void defaultStyle() { + MapElement.FeatureCollectionReference features = getDefaultFeatureLayerFeatures(); + Assert.assertEquals(1, features.getLength()); + + MapElement.FeatureReference feature = features.getFeature(0); + Assert.assertTrue(feature.exists()); + + MapElement.StyleReference style = feature.getStyle(); + Assert.assertEquals("hsl(214, 100%, 48%)", + style.getStroke().getColor()); + Assert.assertEquals(2, style.getStroke().getWidth()); + Assert.assertFalse(style.getFill().exists()); + Assert.assertFalse(style.getImage().exists()); + Assert.assertFalse(style.getText().exists()); + } + + @Test + public void customStyle() { + updateStyle.click(); + + MapElement.FeatureCollectionReference features = getDefaultFeatureLayerFeatures(); + Assert.assertEquals(1, features.getLength()); + + MapElement.FeatureReference feature = features.getFeature(0); + Assert.assertTrue(feature.exists()); + + MapElement.StyleReference style = feature.getStyle(); + Assert.assertEquals("red", style.getStroke().getColor()); + Assert.assertEquals(3, style.getStroke().getWidth()); + Assert.assertFalse(style.getFill().exists()); + Assert.assertFalse(style.getImage().exists()); + Assert.assertFalse(style.getText().exists()); + } + + private MapElement.FeatureCollectionReference getDefaultFeatureLayerFeatures() { + MapElement.MapReference mapReference = map.getMapReference(); + MapElement.LayerReference featureLayer = mapReference.getLayers() + .getLayer(1); + MapElement.VectorSourceReference source = featureLayer.getSource() + .asVectorSource(); + return source.getFeatures(); + } +} diff --git a/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/Constants.java b/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/Constants.java index 8c25c43c9bb..0bf4b01ea69 100644 --- a/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/Constants.java +++ b/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/Constants.java @@ -27,6 +27,7 @@ public class Constants { public static final String OL_SOURCE_TILE_WMS = "ol/source/TileWMS"; public static final String OL_SOURCE_IMAGE_WMS = "ol/source/ImageWMS"; // Geometry + public static final String OL_GEOMETRY_LINESTRING = "ol/geom/LineString"; public static final String OL_GEOMETRY_POINT = "ol/geom/Point"; public static final String OL_GEOMETRY_POLYGON = "ol/geom/Polygon"; // Style diff --git a/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/feature/LineFeature.java b/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/feature/LineFeature.java new file mode 100644 index 00000000000..99c9137e3ab --- /dev/null +++ b/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/feature/LineFeature.java @@ -0,0 +1,160 @@ +/** + * Copyright 2000-2025 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.map.configuration.feature; + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonIdentityReference; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.vaadin.flow.component.map.configuration.Coordinate; +import com.vaadin.flow.component.map.configuration.Feature; +import com.vaadin.flow.component.map.configuration.geometry.LineString; +import com.vaadin.flow.component.map.configuration.geometry.SimpleGeometry; +import com.vaadin.flow.component.map.configuration.style.Stroke; +import com.vaadin.flow.component.map.configuration.style.Style; + +/** + * A convenience class for displaying a line with one or more segments on the + * map. + *

+ * Technically this is a {@link Feature} that uses a {@link LineString} geometry + * for representation. + */ +public class LineFeature extends Feature { + /** + * Creates a new line feature with the default style using the provided + * coordinates. The provided coordinates define the vertices of the line. A + * line must have at least two coordinates. Using more than two coordinates + * creates a multi-segment line, for example to represent a path or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the list of coordinates that define the vertices of the line + * @throws IllegalArgumentException + * if the provided coordinate list is null or has fewer than 2 + * coordinates + */ + public LineFeature(List coordinates) { + setGeometry(new LineString(coordinates)); + setStyle(createDefaultStyle()); + } + + /** + * Creates a new line feature with the default style using the provided + * coordinates. The provided coordinates define the vertices of the line. A + * line must have at least two coordinates. Using more than two coordinates + * creates a multi-segment line, for example to represent a path or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the coordinates that define the vertices of the line + * @throws IllegalArgumentException + * if the provided coordinate array is null or has fewer than 2 + * coordinates + */ + public LineFeature(Coordinate... coordinates) { + setGeometry(new LineString(coordinates)); + setStyle(createDefaultStyle()); + } + + /** + * The coordinates that define the line, as an array. Each coordinate + * represents a vertex in the line. + * + * @return the current coordinates + */ + @JsonIgnore + public Coordinate[] getCoordinates() { + return getGeometry().getCoordinates(); + } + + /** + * Sets the coordinates that define the line. The provided coordinates + * define the vertices of the line. A line must have at least two + * coordinates. Using more than two coordinates creates a multi-segment + * line, for example to represent a path or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the new coordinates + * @throws IllegalArgumentException + * if the provided coordinate list is null or has fewer than 2 + * coordinates + */ + public void setCoordinates(List coordinates) { + Objects.requireNonNull(coordinates); + getGeometry().setCoordinates(coordinates); + } + + /** + * Sets the coordinates that define the line. The provided coordinates + * define the vertices of the line. A line must have at least two + * coordinates. Using more than two coordinates creates a multi-segment + * line, for example to represent a path or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the new coordinates + * @throws IllegalArgumentException + * if the provided coordinate array is null or has fewer than 2 + * coordinates + */ + public void setCoordinates(Coordinate... coordinates) { + Objects.requireNonNull(coordinates); + getGeometry().setCoordinates(coordinates); + } + + /** + * The {@link LineString} geometry representing this feature. + * + * @return the current line string geometry + */ + @Override + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") + @JsonIdentityReference(alwaysAsId = true) + public LineString getGeometry() { + return (LineString) super.getGeometry(); + } + + /** + * Sets the geometry representing this feature. This must be a + * {@link LineString} geometry. + * + * @param geometry + * the new geometry, not null + * @throws IllegalArgumentException + * if the geometry is not an instance of {@link LineString} + */ + @Override + public void setGeometry(SimpleGeometry geometry) { + Objects.requireNonNull(geometry); + if (!(geometry instanceof LineString)) { + throw new IllegalArgumentException( + "Geometry must be a line string"); + } + super.setGeometry(geometry); + } + + private static Style createDefaultStyle() { + Style style = new Style(); + style.setStroke(new Stroke("hsl(214, 100%, 48%)", 2)); + return style; + } +} diff --git a/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/geometry/LineString.java b/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/geometry/LineString.java new file mode 100644 index 00000000000..fd86ec3ed1f --- /dev/null +++ b/vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/geometry/LineString.java @@ -0,0 +1,145 @@ +/** + * Copyright 2000-2025 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.map.configuration.geometry; + +import java.util.Arrays; +import java.util.List; + +import com.vaadin.flow.component.map.configuration.Constants; +import com.vaadin.flow.component.map.configuration.Coordinate; + +/** + * Geometry that represents a line string, which is a series of connected line + * segments. + */ +public class LineString extends SimpleGeometry { + + private Coordinate[] coordinates; + + /** + * Constructs a new {@code LineString} geometry based on the provided + * coordinates. The provided coordinates define the vertices of the line. A + * line string must have at least two coordinates. Using more than two + * coordinates creates a multi-segment line, for example to represent a path + * or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the coordinates that define the line string + * @throws IllegalArgumentException + * if the provided coordinate list is null or has fewer than 2 + * coordinates + */ + public LineString(List coordinates) { + validateCoordinates(coordinates); + this.coordinates = coordinates.toArray(new Coordinate[0]); + } + + /** + * Constructs a new {@code LineString} geometry based on the provided + * coordinates. The provided coordinates define the vertices of the line. A + * line string must have at least two coordinates. Using more than two + * coordinates creates a multi-segment line, for example to represent a path + * or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the coordinates that define the line string + * @throws IllegalArgumentException + * if the provided coordinate array is null or has fewer than 2 + * coordinates + */ + public LineString(Coordinate... coordinates) { + validateCoordinates(coordinates); + this.coordinates = coordinates; + } + + @Override + public String getType() { + return Constants.OL_GEOMETRY_LINESTRING; + } + + /** + * Sets the coordinates that define the line string. The provided + * coordinates define the vertices of the line. A line string must have at + * least two coordinates. Using more than two coordinates creates a + * multi-segment line, for example to represent a path or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the new coordinates + * @throws IllegalArgumentException + * if the provided coordinate list is null or has fewer than 2 + * coordinates + */ + public void setCoordinates(List coordinates) { + validateCoordinates(coordinates); + this.coordinates = coordinates.toArray(new Coordinate[0]); + markAsDirty(); + } + + /** + * Sets the coordinates that define the line string. The provided + * coordinates define the vertices of the line. A line string must have at + * least two coordinates. Using more than two coordinates creates a + * multi-segment line, for example to represent a path or route. + *

+ * Coordinates must be specified in the map's user projection, which by + * default is {@code EPSG:4326}, also referred to as GPS coordinates. + * + * @param coordinates + * the new coordinates + * @throws IllegalArgumentException + * if the provided coordinate array is null or has fewer than 2 + * coordinates + */ + public void setCoordinates(Coordinate... coordinates) { + validateCoordinates(coordinates); + this.coordinates = coordinates; + markAsDirty(); + } + + /** + * The coordinates that define the line string, as an array. + * + * @return the current coordinates + */ + public Coordinate[] getCoordinates() { + return coordinates; + } + + @Override + public void translate(double deltaX, double deltaY) { + Coordinate[] nextCoordinates = Arrays.stream(coordinates) + .map(coordinate -> new Coordinate(coordinate.getX() + deltaX, + coordinate.getY() + deltaY)) + .toArray(Coordinate[]::new); + setCoordinates(nextCoordinates); + } + + private static void validateCoordinates(Coordinate[] coordinates) { + if (coordinates == null || coordinates.length < 2) { + throw new IllegalArgumentException( + "LineString must have at least 2 coordinates"); + } + } + + private static void validateCoordinates(List coordinates) { + if (coordinates == null || coordinates.size() < 2) { + throw new IllegalArgumentException( + "LineString must have at least 2 coordinates"); + } + } +} diff --git a/vaadin-map-flow-parent/vaadin-map-flow/src/main/resources/META-INF/resources/frontend/vaadin-map/synchronization/index.js b/vaadin-map-flow-parent/vaadin-map-flow/src/main/resources/META-INF/resources/frontend/vaadin-map/synchronization/index.js index b115c0f9d6a..8e4fc90be14 100644 --- a/vaadin-map-flow-parent/vaadin-map-flow/src/main/resources/META-INF/resources/frontend/vaadin-map/synchronization/index.js +++ b/vaadin-map-flow-parent/vaadin-map-flow/src/main/resources/META-INF/resources/frontend/vaadin-map/synchronization/index.js @@ -8,6 +8,7 @@ * license. */ import Feature from 'ol/Feature'; +import LineString from 'ol/geom/LineString'; import Point from 'ol/geom/Point'; import Polygon from 'ol/geom/Polygon'; import Fill from 'ol/style/Fill'; @@ -66,6 +67,16 @@ function synchronizeView(target, source, _context) { return target; } +function synchronizeLineString(target, source, _context) { + if (!target) { + target = new LineString(source.coordinates.map((coord) => convertToCoordinateArray(coord))); + } + + target.setCoordinates(source.coordinates.map((coord) => convertToCoordinateArray(coord))); + + return target; +} + function synchronizePoint(target, source, _context) { if (!target) { target = new Point(convertToCoordinateArray(source.coordinates)); @@ -139,6 +150,7 @@ const synchronizerLookup = { 'ol/source/Vector': synchronizeVectorSource, 'ol/source/XYZ': synchronizeXYZSource, // Geometry + 'ol/geom/LineString': synchronizeLineString, 'ol/geom/Point': synchronizePoint, 'ol/geom/Polygon': synchronizePolygon, // Styles diff --git a/vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/feature/LineFeatureTest.java b/vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/feature/LineFeatureTest.java new file mode 100644 index 00000000000..4ea39c52215 --- /dev/null +++ b/vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/feature/LineFeatureTest.java @@ -0,0 +1,196 @@ +/** + * Copyright 2000-2025 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.map.configuration.feature; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.flow.component.map.configuration.Coordinate; +import com.vaadin.flow.component.map.configuration.geometry.LineString; + +public class LineFeatureTest { + private static final Coordinate coordinate1 = new Coordinate(0, 0); + private static final Coordinate coordinate2 = new Coordinate(10, 10); + private static final Coordinate coordinate3 = new Coordinate(20, 5); + + private static final Coordinate[] testCoordinates = { coordinate1, + coordinate2, coordinate3 }; + + @Test + public void initializeWithList() { + LineFeature lineFeature = new LineFeature(List.of(testCoordinates)); + + assertCoordinates(testCoordinates, lineFeature); + } + + @Test + public void initializeWithArray() { + LineFeature lineFeature = new LineFeature(testCoordinates); + + assertCoordinates(testCoordinates, lineFeature); + } + + @Test + public void initializeWithVarargs() { + LineFeature lineFeature = new LineFeature(coordinate1, coordinate2, + coordinate3); + + assertCoordinates(testCoordinates, lineFeature); + } + + @Test + public void defaultStyle() { + List coordinates = List.of(testCoordinates); + LineFeature lineFeature = new LineFeature(coordinates); + + Assert.assertNotNull(lineFeature.getStyle()); + Assert.assertNotNull(lineFeature.getStyle().getStroke()); + Assert.assertEquals("hsl(214, 100%, 48%)", + lineFeature.getStyle().getStroke().getColor()); + Assert.assertEquals(2, lineFeature.getStyle().getStroke().getWidth(), + 0); + Assert.assertNull(lineFeature.getStyle().getFill()); + Assert.assertNull(lineFeature.getStyle().getTextStyle()); + } + + @Test + public void individualStyleInstances() { + LineFeature line1 = new LineFeature(testCoordinates); + LineFeature line2 = new LineFeature(testCoordinates); + + Assert.assertNotEquals(line1.getStyle(), line2.getStyle()); + } + + @Test + public void setCoordinatesWithList() { + LineFeature lineFeature = new LineFeature(new Coordinate(0, 0), + new Coordinate(1, 1)); + lineFeature.setCoordinates(List.of(testCoordinates)); + + assertCoordinates(testCoordinates, lineFeature); + } + + @Test + public void setCoordinatesWithArray() { + LineFeature lineFeature = new LineFeature(new Coordinate(0, 0), + new Coordinate(1, 1)); + lineFeature.setCoordinates(testCoordinates); + + assertCoordinates(testCoordinates, lineFeature); + } + + @Test + public void setCoordinatesWithVarargs() { + LineFeature lineFeature = new LineFeature(new Coordinate(0, 0), + new Coordinate(1, 1)); + lineFeature.setCoordinates(coordinate1, coordinate2, coordinate3); + + assertCoordinates(testCoordinates, lineFeature); + } + + @Test + public void setGeometry() { + LineFeature lineFeature = new LineFeature(new Coordinate(0, 0), + new Coordinate(1, 1)); + LineString lineString = new LineString(testCoordinates); + lineFeature.setGeometry(lineString); + + Assert.assertSame(lineString, lineFeature.getGeometry()); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithNullList_throwsException() { + new LineFeature((List) null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithNullArray_throwsException() { + new LineFeature((Coordinate[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithEmptyList_throwsException() { + new LineFeature(List.of()); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithEmptyArray_throwsException() { + new LineFeature(new Coordinate[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithSingleCoordinateList_throwsException() { + new LineFeature(List.of(new Coordinate(0, 0))); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithSingleCoordinateArray_throwsException() { + new LineFeature(new Coordinate[] { new Coordinate(0, 0) }); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithSingleArgument_throwsException() { + new LineFeature(new Coordinate(0, 0)); + } + + @Test(expected = NullPointerException.class) + public void setCoordinatesWithNullList_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates((List) null); + } + + @Test(expected = NullPointerException.class) + public void setCoordinatesWithNullArray_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates((Coordinate[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithEmptyList_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates(List.of()); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithEmptyArray_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates(new Coordinate[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithSingleCoordinateList_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates(List.of(new Coordinate(0, 0))); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithSingleCoordinateArray_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates(new Coordinate[] { new Coordinate(0, 0) }); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithSingleCoordinate_throwsException() { + LineFeature lineFeature = new LineFeature(testCoordinates); + lineFeature.setCoordinates(new Coordinate(0, 0)); + } + + private void assertCoordinates(Coordinate[] expected, LineFeature feature) { + Coordinate[] actual = feature.getGeometry().getCoordinates(); + + Assert.assertEquals(expected.length, actual.length); + + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(expected[i].getX(), actual[i].getX(), 0); + Assert.assertEquals(expected[i].getY(), actual[i].getY(), 0); + } + } +} diff --git a/vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/geometry/LineStringTest.java b/vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/geometry/LineStringTest.java new file mode 100644 index 00000000000..0e7b19b8196 --- /dev/null +++ b/vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/geometry/LineStringTest.java @@ -0,0 +1,193 @@ +/** + * Copyright 2000-2025 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.map.configuration.geometry; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.map.configuration.Constants; +import com.vaadin.flow.component.map.configuration.Coordinate; + +public class LineStringTest { + + private LineString lineString; + + @Before + public void setup() { + Coordinate coord1 = new Coordinate(0, 0); + Coordinate coord2 = new Coordinate(10, 10); + lineString = new LineString(new Coordinate[] { coord1, coord2 }); + } + + @Test + public void getType_returnsCorrectType() { + Assert.assertEquals(Constants.OL_GEOMETRY_LINESTRING, + lineString.getType()); + } + + @Test + public void initializeWithList() { + Coordinate coord1 = new Coordinate(1, 2); + Coordinate coord2 = new Coordinate(3, 4); + Coordinate coord3 = new Coordinate(5, 6); + LineString line = new LineString(List.of(coord1, coord2, coord3)); + + Coordinate[] coordinates = line.getCoordinates(); + Assert.assertEquals(3, coordinates.length); + Assert.assertEquals(coord1, coordinates[0]); + Assert.assertEquals(coord2, coordinates[1]); + Assert.assertEquals(coord3, coordinates[2]); + } + + @Test + public void initializeWithArray() { + Coordinate coord1 = new Coordinate(1, 2); + Coordinate coord2 = new Coordinate(3, 4); + LineString line = new LineString(new Coordinate[] { coord1, coord2 }); + + Coordinate[] coordinates = line.getCoordinates(); + Assert.assertEquals(2, coordinates.length); + Assert.assertEquals(coord1, coordinates[0]); + Assert.assertEquals(coord2, coordinates[1]); + } + + @Test + public void initializeWithVarargs() { + Coordinate coord1 = new Coordinate(1, 2); + Coordinate coord2 = new Coordinate(3, 4); + LineString line = new LineString(coord1, coord2); + + Coordinate[] coordinates = line.getCoordinates(); + Assert.assertEquals(2, coordinates.length); + Assert.assertEquals(coord1, coordinates[0]); + Assert.assertEquals(coord2, coordinates[1]); + } + + @Test + public void setCoordinatesWithList() { + Coordinate coord1 = new Coordinate(5, 5); + Coordinate coord2 = new Coordinate(15, 15); + Coordinate coord3 = new Coordinate(20, 20); + lineString.setCoordinates(List.of(coord1, coord2, coord3)); + + Coordinate[] coordinates = lineString.getCoordinates(); + Assert.assertEquals(3, coordinates.length); + Assert.assertEquals(coord1, coordinates[0]); + Assert.assertEquals(coord2, coordinates[1]); + Assert.assertEquals(coord3, coordinates[2]); + } + + @Test + public void setCoordinatesWithArray() { + Coordinate coord1 = new Coordinate(5, 5); + Coordinate coord2 = new Coordinate(15, 15); + lineString.setCoordinates(new Coordinate[] { coord1, coord2 }); + + Coordinate[] coordinates = lineString.getCoordinates(); + Assert.assertEquals(2, coordinates.length); + Assert.assertEquals(coord1, coordinates[0]); + Assert.assertEquals(coord2, coordinates[1]); + } + + @Test + public void setCoordinatesWithVarargs() { + Coordinate coord1 = new Coordinate(5, 5); + Coordinate coord2 = new Coordinate(15, 15); + lineString.setCoordinates(new Coordinate[] { coord1, coord2 }); + + Coordinate[] coordinates = lineString.getCoordinates(); + Assert.assertEquals(2, coordinates.length); + Assert.assertEquals(coord1, coordinates[0]); + Assert.assertEquals(coord2, coordinates[1]); + } + + @Test + public void translate() { + lineString.translate(5, 10); + + Coordinate[] coordinates = lineString.getCoordinates(); + Assert.assertEquals(5, coordinates[0].getX(), 0); + Assert.assertEquals(10, coordinates[0].getY(), 0); + Assert.assertEquals(15, coordinates[1].getX(), 0); + Assert.assertEquals(20, coordinates[1].getY(), 0); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithNullList_throwsException() { + new LineString((List) null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithNullArray_throwsException() { + new LineString((Coordinate[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithEmptyList_throwsException() { + new LineString(List.of()); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithEmptyArray_throwsException() { + new LineString(new Coordinate[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithSingleCoordinateList_throwsException() { + new LineString(List.of(new Coordinate(0, 0))); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithSingleCoordinateArray_throwsException() { + new LineString(new Coordinate[] { new Coordinate(0, 0) }); + } + + @Test(expected = IllegalArgumentException.class) + public void constructWithSingleCoordinateArgument_throwsException() { + new LineString(new Coordinate(0, 0)); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithNullList_throwsException() { + lineString.setCoordinates((List) null); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithNullArray_throwsException() { + lineString.setCoordinates((Coordinate[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithEmptyList_throwsException() { + lineString.setCoordinates(List.of()); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithEmptyArray_throwsException() { + lineString.setCoordinates(new Coordinate[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithSingleCoordinateList_throwsException() { + lineString.setCoordinates(List.of(new Coordinate(0, 0))); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithSingleCoordinateArray_throwsException() { + lineString.setCoordinates(new Coordinate[] { new Coordinate(0, 0) }); + } + + @Test(expected = IllegalArgumentException.class) + public void setCoordinatesWithSingleCoordinate_throwsException() { + lineString.setCoordinates(new Coordinate(0, 0)); + } +} diff --git a/vaadin-map-flow-parent/vaadin-map-testbench/src/main/java/com/vaadin/flow/component/map/testbench/MapElement.java b/vaadin-map-flow-parent/vaadin-map-testbench/src/main/java/com/vaadin/flow/component/map/testbench/MapElement.java index 76ac908b424..7ea13cf44d1 100644 --- a/vaadin-map-flow-parent/vaadin-map-testbench/src/main/java/com/vaadin/flow/component/map/testbench/MapElement.java +++ b/vaadin-map-flow-parent/vaadin-map-testbench/src/main/java/com/vaadin/flow/component/map/testbench/MapElement.java @@ -566,6 +566,14 @@ public IconReference getImage() { public TextReference getText() { return new TextReference(executor, path("getText()")); } + + public FillReference getFill() { + return new FillReference(executor, path("getFill()")); + } + + public StrokeReference getStroke() { + return new StrokeReference(executor, path("getStroke()")); + } } public static class GeometryReference extends ConfigurationObjectReference { @@ -579,6 +587,31 @@ public Coordinate getCoordinates() { getDouble("getCoordinates()[1]")); } + public Coordinate[] getCoordinatesArray() { + // Get the coordinates array from OpenLayers + Object coordinatesObj = get("getCoordinates()"); + if (coordinatesObj == null) { + return null; + } + + // Cast to lists (OpenLayers returns coordinates as array) + @SuppressWarnings("unchecked") + List> coords = (List>) coordinatesObj; + + // Create result array with the size matching the number of coords + Coordinate[] result = new Coordinate[coords.size()]; + + // Process each coordinate + for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) { + List coord = coords.get(coordIndex); + double x = coord.get(0).doubleValue(); + double y = coord.get(1).doubleValue(); + result[coordIndex] = new Coordinate(x, y); + } + + return result; + } + public Coordinate[][] getPolygonCoordinates() { // Get the coordinates array from OpenLayers Object coordinatesObj = get("getCoordinates()");