diff --git a/src/main/java/org/vaadin/addons/maplibre/DrawControl.java b/src/main/java/org/vaadin/addons/maplibre/DrawControl.java index 59c59d3..cfe9817 100644 --- a/src/main/java/org/vaadin/addons/maplibre/DrawControl.java +++ b/src/main/java/org/vaadin/addons/maplibre/DrawControl.java @@ -156,6 +156,10 @@ public CompletableFuture getAll() { return v; } + public void clear() { + js("draw.deleteAll()", Collections.emptyMap()); + } + public void setGeometry(Geometry geometry) { String geojsonstr = new GeoJsonWriter().write(geometry); js(""" diff --git a/src/main/java/org/vaadin/addons/maplibre/MapLibre.java b/src/main/java/org/vaadin/addons/maplibre/MapLibre.java index ae7bd81..6684b7b 100644 --- a/src/main/java/org/vaadin/addons/maplibre/MapLibre.java +++ b/src/main/java/org/vaadin/addons/maplibre/MapLibre.java @@ -15,8 +15,10 @@ import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.io.geojson.GeoJsonWriter; import org.parttio.vaadinjsloader.JSLoader; import org.vaadin.addons.velocitycomponent.AbstractVelocityJsComponent; @@ -39,6 +41,8 @@ @Tag("div") public class MapLibre extends AbstractVelocityJsComponent implements HasSize, HasStyle { + static GeometryFactory gf = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4326); + private final HashMap idToLayer = new HashMap<>(); private ArrayList moveEndListeners; private Coordinate center = new Coordinate(0, 0); @@ -215,26 +219,44 @@ protected VelocityContext getVelocityContext() { } public void setCenter(double x, double y) { - this.center = new Coordinate(x, y); + setCenter(new Coordinate(x, y)); + } + + public void setCenter(Coordinate coordinate) { + this.center = coordinate; js("map.setCenter($GeoJsonHelper.toJs($this.center));"); } + public void setCenter(Geometry geom) { + setCenter(geom.getCentroid().getCoordinate()); + } + public void fitTo(Geometry geom, double padding) { Envelope envelope = geom.getEnvelopeInternal(); - envelope.expandBy(padding); - js(""" + fitTo(envelope, padding); + } + protected void fitTo(Envelope envelope, double padding) { + fitTo(""" const bounds = new maplibregl.LngLatBounds( [%s, %s], [%s, %s]);; - map.fitBounds(bounds); - """.formatted(envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY())); + map.fitBounds(bounds, {padding: %s}); + """.formatted(envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY(), padding)); } - public void flyTo(double x, double y, double zoom) { + private void fitTo(String envelope) { + js(envelope); + } + + public void flyTo(double x, double y, Double zoom) { js(""" - map.flyTo({ - center: [%s, %s], - zoom: %s - }); + const opts = { + center: [%s, %s] + } + const z = %s; + if(z != null) { + opts.zoom = z; + } + map.flyTo(opts); """.formatted(x, y, zoom)); } @@ -264,9 +286,14 @@ protected void js(String js) { js(js, Collections.emptyMap()); } - public void flyTo(Geometry geometry, int i) { + public void flyTo(Geometry geometry, double zoomLevel) { + Point centroid = geometry.getCentroid(); + flyTo(centroid.getX(), centroid.getY(), zoomLevel); + } + + public void flyTo(Geometry geometry) { Point centroid = geometry.getCentroid(); - flyTo(centroid.getX(), centroid.getY(), i); + flyTo(centroid.getX(), centroid.getY(), null); } String registerJsCallback(Runnable r) { @@ -367,6 +394,19 @@ public CompletableFuture getViewPort() { return res; } + public void fitToContent() { + List geometries = new ArrayList<>(); + idToLayer.values().forEach(layer -> + geometries.add(layer.getGeometry())); + if(geometries.size() > 0) { + Envelope env = geometries.get(0).getEnvelopeInternal(); + for(Geometry g : geometries) { + env.expandToInclude(g.getEnvelopeInternal()); + } + fitTo(env, 20); + } + } + public interface MoveEndListener { void onMove(MoveEndEvent event); } @@ -379,8 +419,22 @@ public interface MapClickListener { public record ViewPort(Point southWest, Point northEast, Point center, double bearing, double pitch) { + + public Polygon getBounds() { + // 4326 + // TODO this will most likely now not be perfect if bearing/pitch + // is used, would need some math... + Coordinate[] shell = new Coordinate[5]; + shell[0] = southWest.getCoordinate(); + shell[4] = southWest.getCoordinate(); + shell[2] = northEast.getCoordinate(); + shell[1] = new Coordinate(southWest.getX(), northEast.getY()); + shell[3] = new Coordinate(northEast.getX(), southWest.getY()); + return gf.createPolygon(shell); + + } + public static ViewPort of(JsonObject o) { - GeometryFactory gf = new GeometryFactory(); var southWest = gf.createPoint(new Coordinate( o.getObject("sw").getNumber("lng"), o.getObject("sw").getNumber("lat"))); @@ -391,6 +445,7 @@ public static ViewPort of(JsonObject o) { double clng = o.getObject("c").getNumber("lng"); return new ViewPort(southWest, northEast, gf.createPoint(new Coordinate(clng, clat)), o.getNumber("bearing"), o.getNumber("pitch")); } + } public class MoveEndEvent { @@ -448,4 +503,9 @@ record PointRecord(double x, double y) { } } + public void removeAll() { + ArrayList layers = new ArrayList<>(this.idToLayer.values()); + layers.forEach(l -> removeLayer(l)); + } + } diff --git a/src/main/java/org/vaadin/addons/maplibre/Marker.java b/src/main/java/org/vaadin/addons/maplibre/Marker.java index e588d69..9e42f59 100644 --- a/src/main/java/org/vaadin/addons/maplibre/Marker.java +++ b/src/main/java/org/vaadin/addons/maplibre/Marker.java @@ -15,7 +15,7 @@ public class Marker extends Layer { super(map, id,new GeometryFactory().createPoint(coordinate)); } - public void withPopup(String html) { + public Marker withPopup(String html) { map.js(""" const marker = component.markers['$id']; const popup = new maplibregl.Popup({closeButton: true, closeOnClick: true}) @@ -23,6 +23,7 @@ public void withPopup(String html) { .setHTML('$html'); marker.setPopup(popup); """, Map.of("id", id, "html", html)); + return this; } public void setPoint(Point point) { @@ -32,6 +33,12 @@ public void setPoint(Point point) { """, Map.of("id", id, "x", point.getX(), "y", point.getY())); } + public void openPopup() { + map.js(""" + const marker = component.markers['$id']; + marker.getPopup().addTo(map); + """, Map.of("id", id)); + } public interface DragEndListener { void dragEnd(Coordinate coordinate); diff --git a/src/main/java/org/vaadin/addons/maplibre/PointField.java b/src/main/java/org/vaadin/addons/maplibre/PointField.java index a9d3534..0113647 100644 --- a/src/main/java/org/vaadin/addons/maplibre/PointField.java +++ b/src/main/java/org/vaadin/addons/maplibre/PointField.java @@ -82,7 +82,7 @@ public PointField withStyleUrl(String styleUrl) { } private void assingPointFromCoordinate(Coordinate coordinate) { - point = new GeometryFactory().createPoint(coordinate); + point = MapLibre.gf.createPoint(coordinate); } @Override diff --git a/src/test/java/org/vaadin/addons/maplibre/OsmViaMapTiler.java b/src/test/java/org/vaadin/addons/maplibre/OsmViaMapTiler.java index 043a964..f01bde4 100644 --- a/src/test/java/org/vaadin/addons/maplibre/OsmViaMapTiler.java +++ b/src/test/java/org/vaadin/addons/maplibre/OsmViaMapTiler.java @@ -30,14 +30,16 @@ public OsmViaMapTiler() { MapLibre map = new MapLibre(new URI("https://api.maptiler.com/maps/streets/style.json?key=G5n7stvZjomhyaVYP0qU")); map.setHeight("400px"); map.setWidth("100%"); - map.addMarker(22.300, 60.452).withPopup("Hello from Vaadin!"); + map.addMarker(22.300, 60.452) + .withPopup("Hello from Vaadin!") + .openPopup(); + Polygon polygon = (Polygon) new WKTReader().read("POLYGON((22.290 60.428, 22.310 60.429, 22.31 60.47, 22.28 60.47, 22.290 60.428))"); map.addFillLayer(polygon, new FillPaint("red", 0.2)); - map.setCenter(22.300, 60.452); - map.setZoomLevel(13); + map.fitToContent(); map.addMapClickListener(e -> { String str = "Clicked on Map: " + e.getPoint().toString(); @@ -48,7 +50,8 @@ public OsmViaMapTiler() { }); map.addMoveEndListener(event -> { - Notification.show(event.toString()).setPosition(Notification.Position.TOP_END); + Notification notification = Notification.show(event.toString()); + notification.setPosition(Notification.Position.TOP_END); }); addAndExpand(map); @@ -64,7 +67,7 @@ public OsmViaMapTiler() { Button seeWorld = new Button("See the world (flyTo(0,0,0)"); seeWorld.addClickListener(e -> { - map.flyTo(0, 0, 0); + map.flyTo(0, 0, 0.0); }); Button plotYourself = new Button("Plot yourself"); plotYourself.addClickListener(e -> { @@ -85,7 +88,7 @@ public OsmViaMapTiler() { Button showExtent = new VButton("Detect viewport", e-> { map.getViewPort().thenAccept(vp -> { - Notification.show(vp.toString(), 3000, Notification.Position.TOP_END); + Notification.show(vp.toString() + vp.getBounds(), 3000, Notification.Position.TOP_END); }); }); diff --git a/src/test/java/org/vaadin/addons/maplibre/it/MopoSmokeIT.java b/src/test/java/org/vaadin/addons/maplibre/it/MopoSmokeIT.java index 239af69..28cedb8 100644 --- a/src/test/java/org/vaadin/addons/maplibre/it/MopoSmokeIT.java +++ b/src/test/java/org/vaadin/addons/maplibre/it/MopoSmokeIT.java @@ -87,6 +87,15 @@ public void osmViaMapTiler() { mopo.click(page.getByText("Plot yourself")); + // Wait for notifications to close + // TODO Vaadin should have API for it + // TODO Mopo should have API to wait for it + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + page.getByLabel("Map marker").last().click(); assertThat(page.locator("vaadin-notification-card").first())