diff --git a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMap.java b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMap.java
index 8f41cd9..0e8007c 100644
--- a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMap.java
+++ b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMap.java
@@ -51,8 +51,8 @@
@SuppressWarnings("serial")
@Tag("google-map")
@JsModule("@flowingcode/google-map/google-map.js")
-@NpmPackage(value = "@flowingcode/google-map", version = "3.9.0")
-@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.0.8")
+@NpmPackage(value = "@flowingcode/google-map", version = "3.10.1")
+@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.6.2")
@JsModule("./googlemaps/geolocation.js")
@ExtensionMethod(value = JsonMigration.class, suppressBaseMethods = true)
public class GoogleMap extends Component implements HasSize {
@@ -270,6 +270,38 @@ public void removeMarker(GoogleMapMarker marker) {
getElement().executeJs("this._updateMarkers()");
}
+ /**
+ * Adds an advanced marker to the map. Advanced markers require the map to have a Map ID (see
+ * {@link #setMapId(String)}), set before the map is shown.
+ *
+ * @param marker The marker to add.
+ * @throws IllegalStateException if the map is already shown and has no Map ID
+ * @since 2.7.0
+ */
+ public void addMarker(GoogleMapAdvancedMarker marker) {
+ if (isAttached()) {
+ // checked before adding the marker, so that it isn't left in the map on failure
+ GoogleMapAdvancedMarker.checkMapId(this);
+ }
+ getElement().appendChild(marker.getElement());
+ if (getElement().getParent() != null) {
+ getElement().executeJs("this._updateMarkers()");
+ }
+ }
+
+ /**
+ * Removes an advanced marker from the map.
+ *
+ * @param marker The marker to remove.
+ * @since 2.7.0
+ */
+ @SuppressWarnings("squid:S3242")
+ public void removeMarker(GoogleMapAdvancedMarker marker) {
+ getElement().removeChild(marker.getElement());
+ // markers need to be updated on removal
+ getElement().executeJs("this._updateMarkers()");
+ }
+
/**
* Sets the type of the base map.
*
diff --git a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapAdvancedMarker.java b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapAdvancedMarker.java
new file mode 100644
index 0000000..6d6d00c
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapAdvancedMarker.java
@@ -0,0 +1,493 @@
+/*-
+ * #%L
+ * Google Maps Addon
+ * %%
+ * Copyright (C) 2020 - 2026 Flowing Code
+ * %%
+ * 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.
+ * #L%
+ */
+
+package com.flowingcode.vaadin.addons.googlemaps;
+
+import com.flowingcode.vaadin.jsonmigration.JsonMigration;
+import com.vaadin.flow.component.AttachEvent;
+import com.vaadin.flow.component.ClickEvent;
+import com.vaadin.flow.component.Component;
+import com.vaadin.flow.component.ComponentEvent;
+import com.vaadin.flow.component.ComponentEventListener;
+import com.vaadin.flow.component.DomEvent;
+import com.vaadin.flow.component.EventData;
+import com.vaadin.flow.component.Synchronize;
+import com.vaadin.flow.component.Tag;
+import com.vaadin.flow.component.dependency.JsModule;
+import com.vaadin.flow.component.dependency.NpmPackage;
+import com.vaadin.flow.shared.Registration;
+import lombok.experimental.ExtensionMethod;
+
+/**
+ * A marker of the Google Map based on Google's Advanced Markers ({@code AdvancedMarkerElement}),
+ * the replacement for the deprecated markers used by {@link GoogleMapMarker}. Both kinds of
+ * markers can be used on the same map.
+ *
+ * Advanced Markers require the map to have a Map ID (see {@link GoogleMap#setMapId(String)}),
+ * set before the map is shown. Otherwise, an {@link IllegalStateException} is thrown when the
+ * marker is attached.
+ *
+ *
These {@link GoogleMapMarker} features are not available, as Advanced Markers have no
+ * equivalent: animation, optimization, icon sprites, a label together with an icon, and the font
+ * and CSS class of a label.
+ *
+ * @since 2.7.0
+ */
+@SuppressWarnings("serial")
+@Tag("google-map-advanced-marker")
+@JsModule("@flowingcode/google-map/google-map-advanced-marker.js")
+@NpmPackage(value = "@flowingcode/google-map", version = "3.10.1")
+@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.6.2")
+@ExtensionMethod(value = JsonMigration.class, suppressBaseMethods = true)
+public class GoogleMapAdvancedMarker extends Component {
+
+ private static long idCounter = 0;
+
+ private long id;
+
+ /** Instantiates a new GoogleMapAdvancedMarker. */
+ public GoogleMapAdvancedMarker() {
+ id = idCounter;
+ idCounter++;
+ this.getElement().setAttribute("slot", "markers");
+ }
+
+ /**
+ * Instantiates a new GoogleMapAdvancedMarker.
+ *
+ * @param caption The caption to use.
+ * @param position The position of the marker
+ * @param draggable Can marker be dragged?
+ */
+ public GoogleMapAdvancedMarker(String caption, LatLon position, boolean draggable) {
+ this();
+ this.setCaption(caption);
+ this.setPosition(position);
+ this.setDraggable(draggable);
+ }
+
+ /**
+ * Instantiates a new GoogleMapAdvancedMarker.
+ *
+ * @param caption The caption to use.
+ * @param position The position of the marker
+ * @param draggable Can marker be dragged?
+ * @param iconUrl the icon url as a string
+ */
+ public GoogleMapAdvancedMarker(String caption, LatLon position, boolean draggable,
+ String iconUrl) {
+ this(caption, position, draggable);
+ this.setIconUrl(iconUrl);
+ }
+
+ /**
+ * Instantiates a new GoogleMapAdvancedMarker.
+ *
+ * @param caption The caption to use.
+ * @param position The position of the marker
+ * @param draggable Can marker be dragged?
+ * @param icon the icon image for the marker
+ */
+ public GoogleMapAdvancedMarker(String caption, LatLon position, boolean draggable,
+ GoogleMapIcon icon) {
+ this(caption, position, draggable);
+ this.setIcon(icon);
+ }
+
+ /**
+ * Sets the content of the info window that is shown when the marker is clicked.
+ *
+ * @param htmlContent the HTML content of the info window
+ */
+ public void addInfoWindow(String htmlContent) {
+ this.getElement().setProperty("innerHTML", htmlContent);
+ }
+
+ @Synchronize(
+ value = {"google-map-marker-open", "google-map-marker-close"},
+ property = "open")
+ public boolean isInfoWindowVisible() {
+ return this.getElement().getProperty("open", false);
+ }
+
+ public void setInfoWindowVisible(boolean visible) {
+ this.getElement().setProperty("open", visible);
+ }
+
+ /**
+ * Returns the position of the marker.
+ *
+ * @return The position of the marker.
+ */
+ public LatLon getPosition() {
+ final LatLon latLon = new LatLon();
+ latLon.setLat(this.getElement().getProperty("latitude", 0d));
+ latLon.setLon(this.getElement().getProperty("longitude", 0d));
+ return latLon;
+ }
+
+ /**
+ * Sets the position of the marker.
+ *
+ * @param position The new position of the marker.
+ */
+ public void setPosition(LatLon position) {
+ this.getElement().setProperty("latitude", position.getLat());
+ this.getElement().setProperty("longitude", position.getLon());
+ }
+
+ /**
+ * Gets the caption of the marker.
+ *
+ * @return The caption of the marker.
+ */
+ public String getCaption() {
+ return this.getElement().getProperty("title");
+ }
+
+ /**
+ * Sets the caption of the marker.
+ *
+ * @param caption The new caption of the marker.
+ */
+ public void setCaption(String caption) {
+ this.getElement().setProperty("title", caption != null ? caption : "");
+ }
+
+ /**
+ * Checks if the marker is draggable.
+ *
+ * @return true, if it is draggable
+ */
+ public boolean isDraggable() {
+ return this.getElement().getProperty("draggable", false);
+ }
+
+ /**
+ * Enables/disables dragging of the marker.
+ *
+ * @param draggable Set to true to enable dragging.
+ */
+ public void setDraggable(boolean draggable) {
+ this.getElement().setProperty("draggable", draggable);
+ }
+
+ /**
+ * Returns the url of the icon of the marker.
+ *
+ * @return the url of the icon, default null.
+ */
+ public String getIconUrl() {
+ return this.getElement().getProperty("icon");
+ }
+
+ /**
+ * Sets the url of the icon of the marker, which is shown instead of the default pin.
+ *
+ * @param iconUrl The new url of the icon.
+ */
+ public void setIconUrl(String iconUrl) {
+ this.getElement().setProperty("icon", iconUrl);
+ }
+
+ /**
+ * Sets the icon image of the marker, which is shown instead of the default pin. A
+ * {@link MarkerIcon} is shown with its url, scaled size and anchor (sprites are not supported); a
+ * {@link Symbol} is shown with its SVG path.
+ *
+ * @param icon the icon image of the marker
+ */
+ public void setIcon(GoogleMapIcon icon) {
+ this.getElement().setPropertyJson("icon", icon.getJson());
+ }
+
+ /**
+ * Sets the label of the marker, which is shown in the default pin. Only the text and the color
+ * of the label are used, and the label is not shown when the marker has an icon.
+ *
+ * @param label the new marker's label
+ */
+ public void setLabel(MarkerLabel label) {
+ this.getElement().setPropertyJson("label", label.getJson());
+ }
+
+ /**
+ * Returns the background color of the default pin.
+ *
+ * @return the background color, or null if not set
+ */
+ public String getPinBackground() {
+ return this.getElement().getProperty("pinBackground");
+ }
+
+ /**
+ * Sets the background color of the default pin.
+ *
+ * @param color a CSS color
+ */
+ public void setPinBackground(String color) {
+ this.getElement().setProperty("pinBackground", color);
+ }
+
+ /**
+ * Returns the border color of the default pin.
+ *
+ * @return the border color, or null if not set
+ */
+ public String getPinBorderColor() {
+ return this.getElement().getProperty("pinBorderColor");
+ }
+
+ /**
+ * Sets the border color of the default pin.
+ *
+ * @param color a CSS color
+ */
+ public void setPinBorderColor(String color) {
+ this.getElement().setProperty("pinBorderColor", color);
+ }
+
+ /**
+ * Returns the color of the default pin's glyph.
+ *
+ * @return the glyph color, or null if not set
+ */
+ public String getPinGlyphColor() {
+ return this.getElement().getProperty("pinGlyphColor");
+ }
+
+ /**
+ * Sets the color of the default pin's glyph (the circle in its center).
+ *
+ * @param color a CSS color
+ */
+ public void setPinGlyphColor(String color) {
+ this.getElement().setProperty("pinGlyphColor", color);
+ }
+
+ /**
+ * Returns the scale of the default pin.
+ *
+ * @return the scale, 1 if not set
+ */
+ public double getPinScale() {
+ return this.getElement().getProperty("pinScale", 1d);
+ }
+
+ /**
+ * Sets the scale of the default pin.
+ *
+ * @param scale the scale, e.g. 1.5 for a pin 50% larger than the default
+ */
+ public void setPinScale(double scale) {
+ this.getElement().setProperty("pinScale", scale);
+ }
+
+ /**
+ * Checks that the map of this marker has a Map ID, which Advanced Markers require.
+ *
+ * @throws IllegalStateException if the map has no Map ID
+ */
+ @Override
+ protected void onAttach(AttachEvent attachEvent) {
+ super.onAttach(attachEvent);
+ getParent().filter(GoogleMap.class::isInstance).map(GoogleMap.class::cast)
+ .ifPresent(GoogleMapAdvancedMarker::checkMapId);
+ }
+
+ // Without a Map ID the marker is silently not shown, and the Map ID of a map can't be changed
+ // after the map is created, so this is reported as soon as possible.
+ static void checkMapId(GoogleMap map) {
+ if (map.getMapId() == null || map.getMapId().isEmpty()) {
+ throw new IllegalStateException("Advanced markers require a Map ID. "
+ + "Set it with GoogleMap.setMapId(...) before the map is shown.");
+ }
+ }
+
+ public long getIdentifier() {
+ return id;
+ }
+
+ public void setIdentifier(long id) {
+ this.id = id;
+ }
+
+ /** Event that is called on marker's drag end. */
+ @DomEvent("google-map-marker-dragend")
+ public static class DragEndEvent extends ComponentEvent {
+
+ private final double lat;
+ private final double lon;
+
+ /**
+ * Creates a new event with the drag end coordinates.
+ *
+ * @param source the marker that was dragged
+ * @param fromClient whether the event originated on the client side
+ * @param lat the latitude of the drag end
+ * @param lon the longitude of the drag end
+ */
+ public DragEndEvent(
+ GoogleMapAdvancedMarker source,
+ boolean fromClient,
+ @EventData("event.detail.latLng.lat()") double lat,
+ @EventData("event.detail.latLng.lng()") double lon) {
+ super(source, fromClient);
+ this.lat = lat;
+ this.lon = lon;
+ }
+
+ public double getLatitude() {
+ return this.lat;
+ }
+
+ public double getLongitude() {
+ return this.lon;
+ }
+ }
+
+ /**
+ * Adds a DragEndEvent listener. The listener is called when a marker's drag ends.
+ *
+ * @param listener a drag end event listener
+ * @return a handle that can be used for removing the listener
+ */
+ public Registration addDragEndEventListener(ComponentEventListener listener) {
+ this.getElement().setProperty("dragEvents", true);
+ return addListener(DragEndEvent.class, listener);
+ }
+
+ /** Event called on marker's click. */
+ @DomEvent("google-map-marker-click")
+ public static class GoogleMapAdvancedMarkerClickEvent
+ extends ClickEvent {
+
+ private final double lat;
+ private final double lon;
+
+ /**
+ * Creates a new event with the click coordinates.
+ *
+ * @param source the marker that was clicked
+ * @param fromClient whether the event originated on the client side
+ * @param lat the latitude of the marker
+ * @param lon the longitude of the marker
+ */
+ public GoogleMapAdvancedMarkerClickEvent(GoogleMapAdvancedMarker source, boolean fromClient,
+ @EventData("event.detail.latLng.lat()") double lat,
+ @EventData("event.detail.latLng.lng()") double lon) {
+ super(source);
+ this.lat = lat;
+ this.lon = lon;
+ }
+
+ public double getLatitude() {
+ return this.lat;
+ }
+
+ public double getLongitude() {
+ return this.lon;
+ }
+ }
+
+ /**
+ * Adds a click event listener to a marker.
+ *
+ * @param listener a click event listener
+ * @return a handle that can be used for removing the listener
+ */
+ public Registration addClickListener(
+ ComponentEventListener listener) {
+ this.getElement().setProperty("clickEvents", true);
+ return addListener(GoogleMapAdvancedMarkerClickEvent.class, listener);
+ }
+
+ /**
+ * Event called on marker's right click. On touch devices, a touch-and-hold on the marker fires
+ * it too.
+ */
+ @DomEvent("google-map-marker-rightclick")
+ public static class GoogleMapAdvancedMarkerRightClickEvent
+ extends ClickEvent {
+
+ private final double lat;
+ private final double lon;
+
+ /**
+ * Creates a new event with the right click data.
+ *
+ * @param source the marker that was right clicked
+ * @param fromClient whether the event originated on the client side
+ * @param screenX the screen X coordinate of the click
+ * @param screenY the screen Y coordinate of the click
+ * @param clientX the client X coordinate of the click
+ * @param clientY the client Y coordinate of the click
+ * @param clickCount the click count
+ * @param button the mouse button
+ * @param ctrlKey whether Ctrl key was pressed
+ * @param shiftKey whether Shift key was pressed
+ * @param altKey whether Alt key was pressed
+ * @param metaKey whether Meta key was pressed
+ * @param lat the latitude of the marker
+ * @param lon the longitude of the marker
+ */
+ public GoogleMapAdvancedMarkerRightClickEvent(GoogleMapAdvancedMarker source,
+ boolean fromClient,
+ @EventData("event.detail.domEvent.screenX") int screenX,
+ @EventData("event.detail.domEvent.screenY") int screenY,
+ @EventData("event.detail.domEvent.clientX") int clientX,
+ @EventData("event.detail.domEvent.clientY") int clientY,
+ @EventData("event.detail.domEvent.detail") int clickCount,
+ @EventData("event.detail.domEvent.button") int button,
+ @EventData("event.detail.domEvent.ctrlKey") boolean ctrlKey,
+ @EventData("event.detail.domEvent.shiftKey") boolean shiftKey,
+ @EventData("event.detail.domEvent.altKey") boolean altKey,
+ @EventData("event.detail.domEvent.metaKey") boolean metaKey,
+ @EventData("event.detail.latLng.lat()") double lat,
+ @EventData("event.detail.latLng.lng()") double lon) {
+ super(source, fromClient, screenX, screenY, clientX, clientY, clickCount, button, ctrlKey,
+ shiftKey, altKey, metaKey);
+ this.lat = lat;
+ this.lon = lon;
+ }
+
+ public double getLatitude() {
+ return this.lat;
+ }
+
+ public double getLongitude() {
+ return this.lon;
+ }
+ }
+
+ /**
+ * Adds a right click event listener to a marker. On touch devices, a touch-and-hold on the
+ * marker fires it too.
+ *
+ * @param listener a right click event listener
+ * @return a handle for the listener
+ */
+ public Registration addRightClickListener(
+ ComponentEventListener listener) {
+ this.getElement().setProperty("clickEvents", true);
+ return addListener(GoogleMapAdvancedMarkerRightClickEvent.class, listener);
+ }
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapMarker.java b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapMarker.java
index 6c06b48..0f57c6f 100644
--- a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapMarker.java
+++ b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapMarker.java
@@ -41,8 +41,8 @@
@SuppressWarnings("serial")
@Tag("google-map-marker")
@JsModule("@flowingcode/google-map/google-map-marker.js")
-@NpmPackage(value = "@flowingcode/google-map", version = "3.9.0")
-@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.0.8")
+@NpmPackage(value = "@flowingcode/google-map", version = "3.10.1")
+@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.6.2")
@ExtensionMethod(value = JsonMigration.class, suppressBaseMethods = true)
public class GoogleMapMarker extends Component {
diff --git a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoint.java b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoint.java
index f22cfdd..5f44e67 100644
--- a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoint.java
+++ b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoint.java
@@ -28,8 +28,8 @@
@SuppressWarnings("serial")
@Tag("google-map-point")
@JsModule("@flowingcode/google-map/google-map-point.js")
-@NpmPackage(value = "@flowingcode/google-map", version = "3.9.0")
-@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.0.8")
+@NpmPackage(value = "@flowingcode/google-map", version = "3.10.1")
+@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.6.2")
public class GoogleMapPoint extends Component {
public GoogleMapPoint(LatLon latlon) {
diff --git a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoly.java b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoly.java
index c8b1e1d..cabe420 100644
--- a/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoly.java
+++ b/src/main/java/com/flowingcode/vaadin/addons/googlemaps/GoogleMapPoly.java
@@ -41,8 +41,8 @@
@Tag("google-map-poly")
@JsModule("@flowingcode/google-map/google-map-poly.js")
@JsModule("@flowingcode/google-map/google-map-point.js")
-@NpmPackage(value = "@flowingcode/google-map", version = "3.9.0")
-@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.0.8")
+@NpmPackage(value = "@flowingcode/google-map", version = "3.10.1")
+@NpmPackage(value = "@googlemaps/markerclusterer", version = "2.6.2")
@ExtensionMethod(value = JsonMigration.class, suppressBaseMethods = true)
public abstract class GoogleMapPoly extends Component {
diff --git a/src/test/java/com/flowingcode/vaadin/addons/googlemaps/AdvancedMarkersClusteringDemo.java b/src/test/java/com/flowingcode/vaadin/addons/googlemaps/AdvancedMarkersClusteringDemo.java
new file mode 100644
index 0000000..efd742e
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/googlemaps/AdvancedMarkersClusteringDemo.java
@@ -0,0 +1,69 @@
+/*-
+ * #%L
+ * Google Maps Addon
+ * %%
+ * Copyright (C) 2020 - 2026 Flowing Code
+ * %%
+ * 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.
+ * #L%
+ */
+package com.flowingcode.vaadin.addons.googlemaps;
+
+import com.flowingcode.vaadin.addons.demo.DemoSource;
+import com.flowingcode.vaadin.addons.googlemaps.GoogleMap.MapType;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+
+@PageTitle("Advanced Markers Clustering Demo")
+@DemoSource
+@Route(value = "googlemaps/advancedmarkersclustering", layout = GooglemapsDemoView.class)
+@SuppressWarnings("serial")
+public class AdvancedMarkersClusteringDemo extends AbstractGoogleMapsDemo {
+
+ @Override
+ protected void createGoogleMapsDemo(String apiKey) {
+ GoogleMap gmaps = new GoogleMap(apiKey, null, null);
+ gmaps.setMapType(MapType.ROADMAP);
+ gmaps.setSizeFull();
+ gmaps.setZoom(5);
+ gmaps.setCenter(new LatLon(-31.636036, -60.7055271));
+ // Advanced markers require a Map ID. "DEMO_MAP_ID" can be used for testing; use your own Map ID
+ // in production.
+ gmaps.setMapId("DEMO_MAP_ID");
+
+ gmaps.addMarker(createMarker("Marker 1", new LatLon(-31.646036, -60.7055290), true, "#F06292"));
+ gmaps.addMarker(createMarker("Marker 2", new LatLon(-31.562346, -60.6176364), true, "#F06292"));
+ gmaps.addMarker(createMarker("Marker 3", new LatLon(-31.531917, -60.8456027), true, "#F06292"));
+ gmaps.addMarker(createMarker("Marker 4", new LatLon(-31.651667, -60.9555557), true, "#F06292"));
+
+ gmaps.addMarker(createMarker("Marker 5", new LatLon(-30.997815, -65.60542944), false, "#4285F4"));
+ gmaps.addMarker(createMarker("Marker 6", new LatLon(-31.298693, -66.50630836), false, "#4285F4"));
+ gmaps.addMarker(createMarker("Marker 7", new LatLon(-31.878914, -66.28658178), false, "#4285F4"));
+
+ gmaps.addMarker(createMarker("Marker 8", new LatLon(-33.138889, -65.95699194), false, "#34A853"));
+ gmaps.addMarker(createMarker("Marker 9", new LatLon(-33.322683, -65.12203100), false, "#34A853"));
+ gmaps.addMarker(createMarker("Marker 10", new LatLon(-33.652538, -65.737265381), false, "#34A853"));
+
+ gmaps.enableMarkersClustering();
+
+ add(gmaps);
+ }
+
+ private GoogleMapAdvancedMarker createMarker(String caption, LatLon position, boolean draggable,
+ String color) {
+ GoogleMapAdvancedMarker marker = new GoogleMapAdvancedMarker(caption, position, draggable);
+ marker.setPinBackground(color);
+ return marker;
+ }
+
+}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/googlemaps/AdvancedMarkersDemo.java b/src/test/java/com/flowingcode/vaadin/addons/googlemaps/AdvancedMarkersDemo.java
new file mode 100644
index 0000000..d4044ac
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/googlemaps/AdvancedMarkersDemo.java
@@ -0,0 +1,122 @@
+/*-
+ * #%L
+ * Google Maps Addon
+ * %%
+ * Copyright (C) 2020 - 2026 Flowing Code
+ * %%
+ * 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.
+ * #L%
+ */
+
+package com.flowingcode.vaadin.addons.googlemaps;
+
+import com.flowingcode.vaadin.addons.demo.DemoSource;
+import com.flowingcode.vaadin.addons.googlemaps.GoogleMap.MapType;
+import com.vaadin.flow.component.button.Button;
+import com.vaadin.flow.component.checkbox.Checkbox;
+import com.vaadin.flow.component.notification.Notification;
+import com.vaadin.flow.component.orderedlayout.FlexLayout;
+import com.vaadin.flow.component.orderedlayout.FlexLayout.FlexWrap;
+import com.vaadin.flow.component.textfield.TextField;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+
+@PageTitle("Advanced Markers Demo")
+@DemoSource
+@Route(value = "googlemaps/advancedmarkers", layout = GooglemapsDemoView.class)
+@SuppressWarnings("serial")
+public class AdvancedMarkersDemo extends AbstractGoogleMapsDemo {
+
+ @Override
+ protected void createGoogleMapsDemo(String apiKey) {
+ GoogleMap gmaps = new GoogleMap(apiKey, null, null);
+ gmaps.setMapType(MapType.ROADMAP);
+ gmaps.setSizeFull();
+ gmaps.setCenter(new LatLon(-31.636036, -60.7055271));
+ gmaps.setZoom(14);
+ // Advanced markers require a Map ID. "DEMO_MAP_ID" can be used for testing; use your own Map ID
+ // in production.
+ gmaps.setMapId("DEMO_MAP_ID");
+
+ // A marker with a customized pin and an info window
+ GoogleMapAdvancedMarker flowingCode =
+ new GoogleMapAdvancedMarker("Flowing Code", new LatLon(-31.636036, -60.7055271), false);
+ flowingCode.setPinBackground("#1a73e8");
+ flowingCode.setPinBorderColor("#0b4f9c");
+ flowingCode.setPinGlyphColor("white");
+ flowingCode.addInfoWindow("Flowing Code");
+ gmaps.addMarker(flowingCode);
+
+ // A draggable marker
+ GoogleMapAdvancedMarker draggable =
+ new GoogleMapAdvancedMarker("Drag me", new LatLon(-31.6300, -60.6980), true);
+ draggable.setPinScale(1.4);
+ draggable.addDragEndEventListener(e -> Notification.show(
+ "Dragged to -> Latitude: " + e.getLatitude() + " - Longitude: " + e.getLongitude()));
+ gmaps.addMarker(draggable);
+
+ // A marker with an image as icon
+ gmaps.addMarker(new GoogleMapAdvancedMarker("Icon", new LatLon(-31.6420, -60.7120), false,
+ Markers.GREEN));
+
+ // A marker using a MarkerIcon: url icon image + anchor point
+ MarkerIcon markerIcon = new MarkerIcon(Markers.PURPLE);
+ markerIcon.setAnchor(new GoogleMapPoint(15.0, 10.0));
+ gmaps.addMarker(new GoogleMapAdvancedMarker("MarkerIcon", new LatLon(-31.6335, -60.7110), false,
+ markerIcon));
+
+ // A marker using a Symbol: svg path for the icon's image + customization
+ Symbol symbol = new Symbol(
+ "M-1.547 12l6.563-6.609-1.406-1.406-5.156 5.203-2.063-2.109-1.406 1.406zM0 0q2.906 0 4.945 2.039t2.039 4.945q0 1.453-0.727 3.328t-1.758 3.516-2.039 3.070-1.711 2.273l-0.75 0.797q-0.281-0.328-0.75-0.867t-1.688-2.156-2.133-3.141-1.664-3.445-0.75-3.375q0-2.906 2.039-4.945t4.945-2.039z");
+ symbol.setFillColor("purple");
+ symbol.setFillOpacity(1.0);
+ symbol.setAnchor(new GoogleMapPoint(20.0, -0.5));
+ symbol.setRotation(180.0);
+ symbol.setScale(1.5);
+ gmaps.addMarker(new GoogleMapAdvancedMarker("Symbol", new LatLon(-31.6380, -60.7010), false,
+ symbol));
+
+ // A marker with a label, that handles clicks and right clicks (touch-and-hold on touch devices)
+ GoogleMapAdvancedMarker labeled =
+ new GoogleMapAdvancedMarker("Label", new LatLon(-31.6400, -60.6960), false);
+ MarkerLabel label = new MarkerLabel("1");
+ label.setColor("white");
+ labeled.setLabel(label);
+ labeled.addClickListener(e -> Notification.show("Clicked"));
+ labeled.addRightClickListener(e -> Notification.show("Right clicked"));
+ gmaps.addMarker(labeled);
+
+ // Legacy markers can be used on the same map
+ gmaps.addMarker(new GoogleMapMarker("Legacy marker", new LatLon(-31.6310, -60.7150), false));
+
+ TextField labelText = new TextField();
+ labelText.setPlaceholder("Label Text");
+ Checkbox isDraggable = new Checkbox("Draggable");
+ Button addMarker = new Button("Add Advanced Marker", ev -> {
+ GoogleMapAdvancedMarker marker =
+ new GoogleMapAdvancedMarker("New Marker", gmaps.getCenter(), isDraggable.getValue());
+ if (!labelText.getValue().isEmpty()) {
+ marker.setLabel(new MarkerLabel(labelText.getValue()));
+ }
+ gmaps.addMarker(marker);
+ });
+
+ FlexLayout layout = new FlexLayout();
+ layout.setFlexWrap(FlexWrap.WRAP); // hide-source
+ layout.getStyle().set("gap", "var(--lumo-space-s, var(--vaadin-gap-m))"); // hide-source
+ layout.add(labelText, isDraggable, addMarker);
+ layout.setAlignItems(Alignment.BASELINE); // hide-source
+ layout.getStyle().set("margin-top", "0"); // hide-source
+ add(gmaps, layout);
+ }
+}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/googlemaps/GooglemapsDemoView.java b/src/test/java/com/flowingcode/vaadin/addons/googlemaps/GooglemapsDemoView.java
index 84c3486..cc54241 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/googlemaps/GooglemapsDemoView.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/googlemaps/GooglemapsDemoView.java
@@ -49,6 +49,8 @@ public GooglemapsDemoView() {
addDemo(TrackLocationDemo.class);
addDemo(StyleFeaturesDemo.class);
addDemo(CustomControlsDemo.class);
+ addDemo(AdvancedMarkersDemo.class);
+ addDemo(AdvancedMarkersClusteringDemo.class);
setSizeFull();
}
}