Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright 2000-2025 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* Copyright 2000-2025 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* Copyright 2000-2025 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} 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.
* <p>
* 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.
* <p>
* 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<Coordinate> 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.
* <p>
* 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.
* <p>
* 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<Coordinate> 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.
* <p>
* 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;
}
}
Loading