-
Notifications
You must be signed in to change notification settings - Fork 10
Examples and Tutorials
Matt Akbarian edited this page Oct 6, 2025
·
3 revisions
This comprehensive guide provides runnable code examples for Java Leaflet (JLeaflet), covering both JavaFX and Vaadin implementations.
import io.github.makbn.jlmap.fx.JLMapView;
import io.github.makbn.jlmap.map.JLMapProvider;
import io.github.makbn.jlmap.model.JLLatLng;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class BasicMapJavaFX extends Application {
@Override
public void start(Stage stage) {
// Create a simple map centered on Paris
JLMapView map = JLMapView.builder()
.jlMapProvider(JLMapProvider.OSM_MAPNIK.build())
.startCoordinate(new JLLatLng(48.864716, 2.349014))
.showZoomController(true)
.build();
AnchorPane root = new AnchorPane(map);
Scene scene = new Scene(root, 800, 600);
stage.setTitle("Basic Map - JavaFX");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import io.github.makbn.jlmap.vaadin.JLMapView;
import io.github.makbn.jlmap.map.JLMapProvider;
import io.github.makbn.jlmap.model.JLLatLng;
@Route("basic-map")
public class BasicMapVaadin extends VerticalLayout {
public BasicMapVaadin() {
setSizeFull();
// Create a simple map centered on Paris
JLMapView map = JLMapView.builder()
.jlMapProvider(JLMapProvider.OSM_MAPNIK.build())
.startCoordinate(new JLLatLng(48.864716, 2.349014))
.showZoomController(true)
.build();
add(map);
expand(map);
}
}
In some cases adding map directly to HtmlContainer
or com.vaadin.flow.component.html.Main
deos not work as expected.
In such cases, you can wrap the map in a com.vaadin.flow.component.orderedlayout.*
or as an example VerticalLayout
!
import io.github.makbn.jlmap.model.JLLatLng;
import io.github.makbn.jlmap.model.JLMarker;
// Add a simple marker
JLMarker marker = map.getUiLayer()
.addMarker(new JLLatLng(48.864716, 2.349014), "Eiffel Tower", true);
// Add a marker without a popup
JLMarker simpleMarker = map.getUiLayer()
.addMarker(new JLLatLng(48.858844, 2.294351), null, false);
import io.github.makbn.jlmap.model.JLLatLng;
import io.github.makbn.jlmap.model.JLCircle;
import io.github.makbn.jlmap.model.JLOptions;
import io.github.makbn.jlmap.model.JLColor;
// Add a circle with custom styling
JLCircle circle = map.getVectorLayer()
.addCircle(
new JLLatLng(48.864716, 2.349014),
5000, // radius in meters
JLOptions.builder()
.color(JLColor.BLUE)
.fillColor(JLColor.PURPLE)
.fillOpacity(0.3)
.weight(2)
.build()
);
import io.github.makbn.jlmap.model.JLPolyline;
// Create a route between cities
JLLatLng[] route = {
new JLLatLng(48.864716, 2.349014), // Paris
new JLLatLng(52.520008, 13.404954), // Berlin
new JLLatLng(41.902783, 12.496366) // Rome
};
JLPolyline polyline = map.getVectorLayer()
.addPolyline(route, JLOptions.builder()
.color(JLColor.RED)
.weight(4)
.build());
import io.github.makbn.jlmap.model.JLPolygon;
// Create a triangle polygon
JLLatLng[][][] triangleVertices = {{
{
new JLLatLng(48.864716, 2.349014),
new JLLatLng(48.874716, 2.339014),
new JLLatLng(48.854716, 2.339014),
new JLLatLng(48.864716, 2.349014) // Close the polygon
}
}};
JLPolygon polygon = map.getVectorLayer()
.addPolygon(triangleVertices, JLOptions.builder()
.color(JLColor.ORANGE)
.fillColor(JLColor.YELLOW)
.fillOpacity(0.5)
.build());
import io.github.makbn.jlmap.model.JLPopup;
// Add a standalone popup
JLPopup popup = map.getUiLayer()
.addPopup(
new JLLatLng(48.864716, 2.349014),
"<b>Welcome!</b><br>This is the Eiffel Tower"
);
// Add a popup to an existing marker
marker.getPopup().setContent("<b>Updated Content</b>");
import io.github.makbn.jlmap.listener.event.ClickEvent;
import io.github.makbn.jlmap.listener.event.MoveEvent;
// Handle marker click events
marker.setOnActionListener((source, event) -> {
if (event instanceof ClickEvent) {
System.out.println("Marker clicked at: " + source.getLatLng());
}
});
// Handle marker drag events
JLMarker draggableMarker = map.getUiLayer()
.addMarker(new JLLatLng(48.864716, 2.349014), "Drag me", true);
draggableMarker.setOnActionListener((source, event) -> {
if (event instanceof MoveEvent) {
System.out.println("Marker moved to: " + source.getLatLng());
}
});
import io.github.makbn.jlmap.listener.OnJLActionListener;
import io.github.makbn.jlmap.JLMap;
// Listen to map events
map.setOnActionListener((JLMap source, event) -> {
System.out.println("Map event: " + event.action());
});
marker.setOnActionListener((source, event) -> {
switch (event.action()) {
case CLICK -> handleClick(source);
case DOUBLE_CLICK -> handleDoubleClick(source);
case MOUSE_OVER -> handleMouseOver(source);
case MOUSE_OUT -> handleMouseOut(source);
case DRAG_START -> handleDragStart(source);
case DRAG -> handleDrag(source);
case DRAG_END -> handleDragEnd(source);
}
});
import io.github.makbn.jlmap.model.JLIcon;
import io.github.makbn.jlmap.model.JLPoint;
// Create a custom icon
JLIcon customIcon = JLIcon.builder()
.iconUrl("https://cdn-icons-png.flaticon.com/512/3097/3097220.png")
.iconSize(new JLPoint(64, 64))
.iconAnchor(new JLPoint(32, 64))
.popupAnchor(new JLPoint(0, -64))
.build();
// Apply icon to marker
JLMarker markerWithIcon = map.getUiLayer()
.addMarker(new JLLatLng(48.864716, 2.349014), "Custom Icon", true);
markerWithIcon.setIcon(customIcon);
// Car icon
JLIcon carIcon = JLIcon.builder()
.iconUrl("https://cdn-icons-png.flaticon.com/512/3097/3097220.png")
.iconSize(new JLPoint(64, 64))
.iconAnchor(new JLPoint(32, 32))
.build();
// Airplane icon
JLIcon airplaneIcon = JLIcon.builder()
.iconUrl("https://cdn-icons-png.flaticon.com/512/3182/3182857.png")
.iconSize(new JLPoint(64, 64))
.iconAnchor(new JLPoint(32, 32))
.build();
// House icon
JLIcon houseIcon = JLIcon.builder()
.iconUrl("https://cdn-icons-png.flaticon.com/512/3750/3750400.png")
.iconSize(new JLPoint(64, 64))
.iconAnchor(new JLPoint(32, 64))
.build();
import io.github.makbn.jlmap.model.JLGeoJson;
// Load GeoJSON from URL
JLGeoJson geoJson = map.getGeoJsonLayer()
.addFromUrl("https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_outline_5m.json");
// Handle GeoJSON events
geoJson.setOnActionListener((source, event) -> {
System.out.println("GeoJSON clicked: " + event);
});
import java.io.File;
// Load GeoJSON from local file
File geoJsonFile = new File("path/to/data.geojson");
JLGeoJson geoJson = map.getGeoJsonLayer()
.addFromFile(geoJsonFile);
import io.github.makbn.jlmap.model.JLGeoJsonOptions;
// Load GeoJSON with custom styling
JLGeoJsonOptions options = JLGeoJsonOptions.builder()
.styleFunction(features -> JLOptions.builder()
.fill(true)
.fillColor(JLColor.fromHex((String) features.get(0).get("fill")))
.fillOpacity((Double) features.get(0).get("fill-opacity"))
.stroke(true)
.color(JLColor.fromHex((String) features.get(0).get("stroke")))
.build())
.build();
JLGeoJson styledGeoJson = map.getGeoJsonLayer()
.addFromUrl("https://example.com/data.geojson", options);
// Load GeoJSON with filtering
JLGeoJsonOptions filterOptions = JLGeoJsonOptions.builder()
.filter(features -> {
Map<String, Object> properties = features.get(0);
// Show only features with even IDs
return ((Integer) properties.get("id")) % 2 == 0;
})
.build();
JLGeoJson filteredGeoJson = map.getGeoJsonLayer()
.addFromFile(geoJsonFile, filterOptions);
import io.github.makbn.jlmap.element.menu.JLContextMenu;
import io.github.makbn.jlmap.element.menu.JLMenuItem;
// Add context menu to marker
JLContextMenu<JLMarker> contextMenu = marker.getContextMenu();
contextMenu
.addItem("edit", "Edit Marker", "edit-icon.png")
.addItem("info", "Show Info", "info-icon.png")
.addItem("delete", "Delete Marker", "delete-icon.png");
// Handle menu item selections
contextMenu.setOnMenuItemListener(selectedItem -> {
switch (selectedItem.getId()) {
case "edit" -> openEditDialog(marker);
case "info" -> showMarkerInfo(marker);
case "delete" -> marker.remove();
}
});
// Create a disabled menu item
JLMenuItem disabledItem = JLMenuItem.builder()
.id("disabled-action")
.text("Disabled Action")
.enabled(false)
.build();
contextMenu.addItem(disabledItem);
// Create a conditional menu item
JLMenuItem conditionalItem = JLMenuItem.builder()
.id("admin-action")
.text("Admin Action")
.enabled(userIsAdmin)
.visible(userIsAdmin)
.build();
contextMenu.addItem(conditionalItem);
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class AnimatedJourney {
private JLMapView map;
private JLMarker animatedMarker;
public void animateJourney() {
JLLatLng start = new JLLatLng(48.864716, 2.349014); // Paris
JLLatLng end = new JLLatLng(52.520008, 13.404954); // Berlin
// Create path points for smooth animation
JLLatLng[] path = createPath(start, end, 100);
// Create animated marker
animatedMarker = map.getUiLayer().addMarker(start, null, false);
// Animate marker along path
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
AtomicInteger currentStep = new AtomicInteger(0);
executor.scheduleAtFixedRate(() -> {
int step = currentStep.getAndIncrement();
if (step < path.length) {
animatedMarker.setLatLng(path[step]);
} else {
executor.shutdown();
}
}, 0, 50, TimeUnit.MILLISECONDS);
}
private JLLatLng[] createPath(JLLatLng start, JLLatLng end, int points) {
JLLatLng[] path = new JLLatLng[points];
for (int i = 0; i < points; i++) {
double ratio = (double) i / (points - 1);
double lat = start.getLat() + (end.getLat() - start.getLat()) * ratio;
double lng = start.getLng() + (end.getLng() - start.getLng()) * ratio;
path[i] = new JLLatLng(lat, lng);
}
return path;
}
}
// Create a curved path using Bezier curves
private JLLatLng[] createCurvedPath(JLLatLng start, JLLatLng end, int points) {
JLLatLng[] path = new JLLatLng[points];
// Calculate control point for curve
double midLat = (start.getLat() + end.getLat()) / 2;
double midLng = (start.getLng() + end.getLng()) / 2;
double offsetLat = (end.getLng() - start.getLng()) * 0.2;
double offsetLng = -(end.getLat() - start.getLat()) * 0.2;
JLLatLng control = new JLLatLng(midLat + offsetLat, midLng + offsetLng);
// Generate points along quadratic Bezier curve
for (int i = 0; i < points; i++) {
double t = (double) i / (points - 1);
double lat = Math.pow(1 - t, 2) * start.getLat() +
2 * (1 - t) * t * control.getLat() +
Math.pow(t, 2) * end.getLat();
double lng = Math.pow(1 - t, 2) * start.getLng() +
2 * (1 - t) * t * control.getLng() +
Math.pow(t, 2) * end.getLng();
path[i] = new JLLatLng(lat, lng);
}
return path;
}
// Style GeoJSON features based on properties
JLGeoJsonOptions dynamicStyle = JLGeoJsonOptions.builder()
.styleFunction(features -> {
Map<String, Object> properties = features.get(0);
// Get color from feature properties
String color = (String) properties.getOrDefault("color", "#3388ff");
Double opacity = (Double) properties.getOrDefault("opacity", 0.5);
Integer weight = (Integer) properties.getOrDefault("weight", 2);
return JLOptions.builder()
.color(JLColor.fromHex(color))
.fillOpacity(opacity)
.weight(weight)
.build();
})
.build();
// Style features based on conditions
JLGeoJsonOptions conditionalStyle = JLGeoJsonOptions.builder()
.styleFunction(features -> {
Map<String, Object> props = features.get(0);
Integer population = (Integer) props.get("population");
// Different colors based on population
JLColor color;
if (population > 1000000) {
color = JLColor.RED;
} else if (population > 500000) {
color = JLColor.ORANGE;
} else {
color = JLColor.GREEN;
}
return JLOptions.builder()
.fillColor(color)
.fillOpacity(0.6)
.color(JLColor.BLACK)
.weight(2)
.build();
})
.build();
// Manage multiple overlays
public class LayerManager {
private Map<String, JLGeoJson> layers = new HashMap<>();
private JLMapView map;
public void addLayer(String name, String url) {
JLGeoJson layer = map.getGeoJsonLayer().addFromUrl(url);
layers.put(name, layer);
}
public void toggleLayer(String name) {
JLGeoJson layer = layers.get(name);
if (layer != null) {
if (layer.isVisible()) {
layer.remove();
} else {
// Re-add layer logic
}
}
}
public void removeLayer(String name) {
JLGeoJson layer = layers.remove(name);
if (layer != null) {
layer.remove();
}
}
}
import io.github.makbn.jlmap.map.JLMapProvider;
import io.github.makbn.jlmap.model.JLMapOption;
// OpenStreetMap variants
JLMapProvider osmMapnik = JLMapProvider.OSM_MAPNIK.build();
JLMapProvider osmGerman = JLMapProvider.OSM_GERMAN.build();
JLMapProvider osmFrench = JLMapProvider.OSM_FRENCH.build();
JLMapProvider osmHot = JLMapProvider.OSM_HOT.build();
JLMapProvider osmCycle = JLMapProvider.OSM_CYCLE.build();
// Specialized providers
JLMapProvider openTopo = JLMapProvider.OPEN_TOPO.build();
JLMapProvider waterColor = JLMapProvider.WATER_COLOR.build();
// Map providers with API keys
JLMapProvider mapTiler = JLMapProvider.MAP_TILER
.parameter(new JLMapOption.Parameter("key", "YOUR_API_KEY"))
.build();
// Apply to map
JLMapView map = JLMapView.builder()
.jlMapProvider(mapTiler)
.startCoordinate(new JLLatLng(48.864716, 2.349014))
.build();
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SafeMapOperations {
private static final Logger log = LoggerFactory.getLogger(SafeMapOperations.class);
public void safelyAddGeoJson(JLMapView map, String url) {
try {
JLGeoJson geoJson = map.getGeoJsonLayer().addFromUrl(url);
geoJson.setOnActionListener((source, event) -> {
log.info("GeoJSON event: {}", event);
});
} catch (Exception e) {
log.error("Failed to load GeoJSON from URL: {}", url, e);
showErrorNotification("Failed to load map data");
}
}
public void safelyAddMarker(JLMapView map, double lat, double lng) {
try {
JLMarker marker = map.getUiLayer()
.addMarker(new JLLatLng(lat, lng), "Location", true);
marker.setOnActionListener((source, event) -> {
try {
handleMarkerEvent(source, event);
} catch (Exception e) {
log.error("Error handling marker event", e);
}
});
} catch (IllegalArgumentException e) {
log.error("Invalid coordinates: lat={}, lng={}", lat, lng, e);
showErrorNotification("Invalid location coordinates");
}
}
private void handleMarkerEvent(JLMarker marker, Event event) {
// Event handling logic
}
private void showErrorNotification(String message) {
// Platform-specific notification
}
}
import javafx.application.Platform;
import javafx.concurrent.Task;
public void loadGeoJsonAsync(JLMapView map, String url) {
Task<JLGeoJson> loadTask = new Task<>() {
@Override
protected JLGeoJson call() throws Exception {
return map.getGeoJsonLayer().addFromUrl(url);
}
};
loadTask.setOnSucceeded(event -> {
JLGeoJson geoJson = loadTask.getValue();
Platform.runLater(() -> {
// Update UI
System.out.println("GeoJSON loaded successfully");
});
});
loadTask.setOnFailed(event -> {
Throwable error = loadTask.getException();
Platform.runLater(() -> {
System.err.println("Failed to load GeoJSON: " + error.getMessage());
});
});
new Thread(loadTask).start();
}
import com.vaadin.flow.component.UI;
public void loadGeoJsonAsync(JLMapView map, String url) {
UI ui = UI.getCurrent();
CompletableFuture.supplyAsync(() -> {
return map.getGeoJsonLayer().addFromUrl(url);
}).thenAccept(geoJson -> {
ui.access(() -> {
// Update UI
Notification.show("GeoJSON loaded successfully");
});
}).exceptionally(error -> {
ui.access(() -> {
Notification.show("Failed to load GeoJSON: " + error.getMessage());
});
return null;
});
}
public class MarkerClusterer {
private Map<String, List<JLMarker>> clusters = new HashMap<>();
public void addMarkers(JLMapView map, List<LocationData> locations) {
// Only show markers in current viewport
map.getControlLayer().getBounds().whenComplete((bounds, error) -> {
if (bounds != null) {
locations.stream()
.filter(loc -> isInBounds(loc, bounds))
.forEach(loc -> addMarker(map, loc));
}
});
}
private boolean isInBounds(LocationData location, JLBounds bounds) {
double lat = location.getLatitude();
double lng = location.getLongitude();
return lat >= bounds.getSouthWest().getLat() &&
lat <= bounds.getNorthEast().getLat() &&
lng >= bounds.getSouthWest().getLng() &&
lng <= bounds.getNorthEast().getLng();
}
private void addMarker(JLMapView map, LocationData location) {
map.getUiLayer().addMarker(
new JLLatLng(location.getLatitude(), location.getLongitude()),
location.getName(),
true
);
}
}
public class LazyMapLoader {
private boolean dataLoaded = false;
public void setupLazyLoading(JLMapView map) {
map.setOnActionListener((source, event) -> {
if (event.action() == JLAction.MOVE_END && !dataLoaded) {
loadDataForCurrentView(map);
dataLoaded = true;
}
});
}
private void loadDataForCurrentView(JLMapView map) {
map.getControlLayer().getBounds().whenComplete((bounds, error) -> {
if (bounds != null) {
// Load only data within bounds
loadGeoJsonForBounds(map, bounds);
}
});
}
private void loadGeoJsonForBounds(JLMapView map, JLBounds bounds) {
String url = buildUrlWithBounds(bounds);
map.getGeoJsonLayer().addFromUrl(url);
}
private String buildUrlWithBounds(JLBounds bounds) {
return String.format("https://api.example.com/data?sw=%f,%f&ne=%f,%f",
bounds.getSouthWest().getLat(), bounds.getSouthWest().getLng(),
bounds.getNorthEast().getLat(), bounds.getNorthEast().getLng());
}
}
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.*;
public class MapServiceTest {
private MapService mapService;
@BeforeEach
public void setUp() {
mapService = new MapService();
}
@Test
public void testCoordinateValidation() {
assertTrue(mapService.isValidCoordinate(48.864716, 2.349014));
assertFalse(mapService.isValidCoordinate(91.0, 0.0)); // Invalid latitude
assertFalse(mapService.isValidCoordinate(0.0, 181.0)); // Invalid longitude
}
@Test
public void testDistanceCalculation() {
JLLatLng paris = new JLLatLng(48.864716, 2.349014);
JLLatLng berlin = new JLLatLng(52.520008, 13.404954);
double distance = paris.distanceTo(berlin);
assertTrue(distance > 877000 && distance < 878000); // ~877 km
}
@Test
public void testPathGeneration() {
JLLatLng start = new JLLatLng(48.864716, 2.349014);
JLLatLng end = new JLLatLng(52.520008, 13.404954);
JLLatLng[] path = mapService.createPath(start, end, 10);
assertEquals(10, path.length);
assertEquals(start, path[0]);
assertEquals(end, path[9]);
}
}
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
public class MapIntegrationTest {
@Test
public void testGeoJsonLoading() {
JLMapView map = mock(JLMapView.class);
JLGeoJsonLayer geoJsonLayer = mock(JLGeoJsonLayer.class);
when(map.getGeoJsonLayer()).thenReturn(geoJsonLayer);
String testUrl = "https://example.com/test.geojson";
map.getGeoJsonLayer().addFromUrl(testUrl);
verify(geoJsonLayer).addFromUrl(testUrl);
}
}
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import io.github.makbn.jlmap.fx.JLMapView;
import io.github.makbn.jlmap.map.JLMapProvider;
import io.github.makbn.jlmap.model.*;
public class MapExplorerApp extends Application {
private JLMapView mapView;
private ListView<String> markerList;
private List<JLMarker> markers = new ArrayList<>();
@Override
public void start(Stage primaryStage) {
// Create main layout
BorderPane root = new BorderPane();
// Create map
mapView = JLMapView.builder()
.jlMapProvider(JLMapProvider.OSM_MAPNIK.build())
.startCoordinate(new JLLatLng(48.864716, 2.349014))
.showZoomController(true)
.build();
// Create control panel
VBox controlPanel = createControlPanel();
controlPanel.setPadding(new Insets(10));
controlPanel.setSpacing(10);
controlPanel.setMinWidth(250);
// Layout
root.setCenter(mapView);
root.setRight(controlPanel);
// Create scene
Scene scene = new Scene(root, 1200, 800);
primaryStage.setTitle("Map Explorer - JavaFX");
primaryStage.setScene(scene);
primaryStage.show();
}
private VBox createControlPanel() {
VBox panel = new VBox(10);
// Add marker button
Button addMarkerBtn = new Button("Add Marker");
addMarkerBtn.setMaxWidth(Double.MAX_VALUE);
addMarkerBtn.setOnAction(e -> addMarkerDialog());
// Load GeoJSON button
Button loadGeoJsonBtn = new Button("Load GeoJSON");
loadGeoJsonBtn.setMaxWidth(Double.MAX_VALUE);
loadGeoJsonBtn.setOnAction(e -> loadGeoJsonDialog());
// Marker list
Label markerLabel = new Label("Markers:");
markerList = new ListView<>();
markerList.setPrefHeight(300);
markerList.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
int index = markerList.getSelectionModel().getSelectedIndex();
if (index >= 0 && index < markers.size()) {
JLMarker marker = markers.get(index);
mapView.getControlLayer().flyTo(marker.getLatLng(), 12);
}
}
});
// Clear all button
Button clearBtn = new Button("Clear All");
clearBtn.setMaxWidth(Double.MAX_VALUE);
clearBtn.setOnAction(e -> clearAll());
panel.getChildren().addAll(
addMarkerBtn,
loadGeoJsonBtn,
new Separator(),
markerLabel,
markerList,
clearBtn
);
return panel;
}
private void addMarkerDialog() {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Add Marker");
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
TextField latField = new TextField("48.864716");
TextField lngField = new TextField("2.349014");
TextField nameField = new TextField("New Marker");
grid.add(new Label("Latitude:"), 0, 0);
grid.add(latField, 1, 0);
grid.add(new Label("Longitude:"), 0, 1);
grid.add(lngField, 1, 1);
grid.add(new Label("Name:"), 0, 2);
grid.add(nameField, 1, 2);
dialog.getDialogPane().setContent(grid);
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
dialog.showAndWait().ifPresent(response -> {
if (response == ButtonType.OK) {
try {
double lat = Double.parseDouble(latField.getText());
double lng = Double.parseDouble(lngField.getText());
String name = nameField.getText();
JLMarker marker = mapView.getUiLayer()
.addMarker(new JLLatLng(lat, lng), name, true);
markers.add(marker);
markerList.getItems().add(name + " (" + lat + ", " + lng + ")");
// Add context menu
addContextMenu(marker, name);
} catch (NumberFormatException e) {
showError("Invalid coordinates");
}
}
});
}
private void loadGeoJsonDialog() {
TextInputDialog dialog = new TextInputDialog(
"https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_outline_5m.json"
);
dialog.setTitle("Load GeoJSON");
dialog.setHeaderText("Enter GeoJSON URL");
dialog.showAndWait().ifPresent(url -> {
try {
JLGeoJson geoJson = mapView.getGeoJsonLayer().addFromUrl(url);
showInfo("GeoJSON loaded successfully");
} catch (Exception e) {
showError("Failed to load GeoJSON: " + e.getMessage());
}
});
}
private void addContextMenu(JLMarker marker, String name) {
JLContextMenu<JLMarker> contextMenu = marker.getContextMenu();
contextMenu
.addItem("center", "Center on Map")
.addItem("delete", "Delete Marker");
contextMenu.setOnMenuItemListener(item -> {
switch (item.getId()) {
case "center" -> mapView.getControlLayer().flyTo(marker.getLatLng(), 12);
case "delete" -> {
marker.remove();
markers.remove(marker);
markerList.getItems().remove(name);
}
}
});
}
private void clearAll() {
markers.forEach(JLMarker::remove);
markers.clear();
markerList.getItems().clear();
}
private void showError(String message) {
Alert alert = new Alert(Alert.AlertType.ERROR, message);
alert.showAndWait();
}
private void showInfo(String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION, message);
alert.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.router.Route;
import io.github.makbn.jlmap.vaadin.JLMapView;
import io.github.makbn.jlmap.map.JLMapProvider;
import io.github.makbn.jlmap.model.*;
import java.util.ArrayList;
import java.util.List;
@Route("map-explorer")
public class MapExplorerVaadin extends FlexLayout {
private JLMapView mapView;
private VerticalLayout markerList;
private List<JLMarker> markers = new ArrayList<>();
public MapExplorerVaadin() {
setSizeFull();
setFlexDirection(FlexDirection.ROW);
// Create map
mapView = JLMapView.builder()
.jlMapProvider(JLMapProvider.OSM_MAPNIK.build())
.startCoordinate(new JLLatLng(48.864716, 2.349014))
.showZoomController(true)
.build();
mapView.setSizeFull();
// Create control panel
VerticalLayout controlPanel = createControlPanel();
controlPanel.setWidth("300px");
controlPanel.getStyle()
.set("border-left", "1px solid var(--lumo-contrast-10pct)")
.set("padding", "var(--lumo-space-m)");
add(mapView, controlPanel);
expand(mapView);
}
private VerticalLayout createControlPanel() {
VerticalLayout panel = new VerticalLayout();
panel.setSpacing(true);
panel.setPadding(false);
H2 title = new H2("Map Controls");
title.getStyle().set("margin-top", "0");
// Add marker button
Button addMarkerBtn = new Button("Add Marker");
addMarkerBtn.setWidthFull();
addMarkerBtn.addClickListener(e -> openAddMarkerDialog());
// Load GeoJSON button
Button loadGeoJsonBtn = new Button("Load GeoJSON from URL");
loadGeoJsonBtn.setWidthFull();
loadGeoJsonBtn.addClickListener(e -> openLoadGeoJsonDialog());
// Upload GeoJSON
MemoryBuffer buffer = new MemoryBuffer();
Upload uploadGeoJson = new Upload(buffer);
uploadGeoJson.setAcceptedFileTypes(".geojson", ".json");
uploadGeoJson.setMaxFiles(1);
uploadGeoJson.addSucceededListener(event -> {
try {
mapView.getGeoJsonLayer().addFromFile(
buffer.getInputStream(),
event.getFileName()
);
Notification.show("GeoJSON uploaded successfully");
} catch (Exception ex) {
Notification.show("Error uploading GeoJSON: " + ex.getMessage());
}
});
// Marker list
H2 markerListTitle = new H2("Markers");
markerListTitle.getStyle().set("margin-top", "var(--lumo-space-l)");
markerList = new VerticalLayout();
markerList.setPadding(false);
markerList.setSpacing(false);
// Clear button
Button clearBtn = new Button("Clear All");
clearBtn.setWidthFull();
clearBtn.addClickListener(e -> clearAll());
panel.add(
title,
addMarkerBtn,
loadGeoJsonBtn,
uploadGeoJson,
markerListTitle,
markerList,
clearBtn
);
return panel;
}
private void openAddMarkerDialog() {
Dialog dialog = new Dialog();
dialog.setHeaderTitle("Add Marker");
VerticalLayout content = new VerticalLayout();
TextField latField = new TextField("Latitude");
latField.setValue("48.864716");
latField.setWidthFull();
TextField lngField = new TextField("Longitude");
lngField.setValue("2.349014");
lngField.setWidthFull();
TextField nameField = new TextField("Name");
nameField.setValue("New Marker");
nameField.setWidthFull();
Button addBtn = new Button("Add", e -> {
try {
double lat = Double.parseDouble(latField.getValue());
double lng = Double.parseDouble(lngField.getValue());
String name = nameField.getValue();
addMarker(lat, lng, name);
dialog.close();
} catch (NumberFormatException ex) {
Notification.show("Invalid coordinates");
}
});
addBtn.getStyle().set("margin-top", "var(--lumo-space-m)");
content.add(latField, lngField, nameField, addBtn);
dialog.add(content);
dialog.open();
}
private void openLoadGeoJsonDialog() {
Dialog dialog = new Dialog();
dialog.setHeaderTitle("Load GeoJSON");
VerticalLayout content = new VerticalLayout();
TextField urlField = new TextField("GeoJSON URL");
urlField.setValue("https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_outline_5m.json");
urlField.setWidthFull();
Button loadBtn = new Button("Load", e -> {
try {
String url = urlField.getValue();
JLGeoJson geoJson = mapView.getGeoJsonLayer().addFromUrl(url);
geoJson.addContextMenu()
.addItem("remove", "Remove Layer")
.setOnMenuItemListener(item -> {
geoJson.remove();
Notification.show("GeoJSON layer removed");
});
Notification.show("GeoJSON loaded successfully");
dialog.close();
} catch (Exception ex) {
Notification.show("Error: " + ex.getMessage());
}
});
content.add(urlField, loadBtn);
dialog.add(content);
dialog.open();
}
private void addMarker(double lat, double lng, String name) {
JLMarker marker = mapView.getUiLayer()
.addMarker(new JLLatLng(lat, lng), name, true);
markers.add(marker);
// Add to list with click handler
Button markerBtn = new Button(name + " (" + lat + ", " + lng + ")");
markerBtn.setWidthFull();
markerBtn.addClickListener(e ->
mapView.getControlLayer().flyTo(marker.getLatLng(), 12)
);
markerList.add(markerBtn);
// Add context menu
JLContextMenu<JLMarker> contextMenu = marker.getContextMenu();
contextMenu
.addItem("center", "Center on Map")
.addItem("delete", "Delete Marker");
contextMenu.setOnMenuItemListener(item -> {
switch (item.getId()) {
case "center" -> mapView.getControlLayer().flyTo(marker.getLatLng(), 12);
case "delete" -> {
marker.remove();
markers.remove(marker);
markerList.remove(markerBtn);
Notification.show("Marker deleted");
}
}
});
Notification.show("Marker added: " + name);
}
private void clearAll() {
markers.forEach(JLMarker::remove);
markers.clear();
markerList.removeAll();
Notification.show("All markers cleared");
}
}
- API Documentation: Refer to JavaDoc for detailed API information
- GitHub Repository: https://github.com/makbn/java_leaflet
- Leaflet Documentation: https://leafletjs.com/
- GeoJSON Specification: https://geojson.org/
Here are some useful GeoJSON URLs for testing:
-
Simple Example:
https://pkgstore.datahub.io/examples/geojson-tutorial/example/data/db696b3bf628d9a273ca9907adcea5c9/example.geojson
-
US Outline:
https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_outline_5m.json
- Explore the Architecture Guide for deep-dive into the framework
- Check the Getting Started guide for setup instructions
- Review the Context Menu Guide for advanced menu features
- See Migration Guide if upgrading from v1.x