# Architecture Overview This document provides a comprehensive overview of the Java Leaflet architecture, explaining the multi-module design, layer system, and key architectural patterns. ## Table of Contents - [Multi-Module Architecture](#multi-module-architecture) - [Layer System](#layer-system) - [Transport Layer](#transport-layer) - [Event System Architecture](#event-system-architecture) - [Builder Pattern](#builder-pattern) - [Context Menu Architecture](#context-menu-architecture) - [Design Patterns](#design-patterns) - [Platform Abstraction](#platform-abstraction) --- ## Multi-Module Architecture Java Leaflet v2.0.0 introduces a clean multi-module Maven architecture: ``` java_leaflet/ ├── jlmap-parent/ # Parent POM ├── jlmap-api/ # Core API ├── jlmap-fx/ # JavaFX implementation ├── jlmap-vaadin/ # Vaadin implementation └── jlmap-vaadin-demo/ # Demo application ``` ### Module Dependencies ``` jlmap-fx ──────┐ ├──> jlmap-api jlmap-vaadin ──┘ jlmap-vaadin-demo ──> jlmap-vaadin ──> jlmap-api ``` ### jlmap-api (Core Module) **Purpose**: Provides platform-independent abstractions and models. **Exports**: - `io.github.makbn.jlmap` - Main package - `io.github.makbn.jlmap.layer` - Layer management - `io.github.makbn.jlmap.layer.leaflet` - Leaflet layer interfaces - `io.github.makbn.jlmap.listener` - Event listeners - `io.github.makbn.jlmap.model` - Data models - `io.github.makbn.jlmap.exception` - Custom exceptions - `io.github.makbn.jlmap.geojson` - GeoJSON support - `io.github.makbn.jlmap.engine` - Web engine abstractions - `io.github.makbn.jlmap.element.menu` - Context menu system - `io.github.makbn.jlmap.journey` - Journey system **Key Dependencies**: - Gson & Jackson for JSON processing - SLF4J for logging - JetBrains annotations - SnakeYAML for YAML support ### jlmap-fx (JavaFX Implementation) **Purpose**: JavaFX-specific implementation using WebView. **Key Classes**: - `io.github.makbn.jlmap.fx.JLMapView` - Main JavaFX component - `io.github.makbn.jlmap.fx.JLEngine` - JavaFX WebView wrapper - `io.github.makbn.jlmap.fx.element.menu.JavaFXContextMenuMediator` - Context menu support **Dependencies**: - jlmap-api - JavaFX (controls, web, graphics, base) - JDK jsobject module ### jlmap-vaadin (Vaadin Implementation) **Purpose**: Vaadin component for web applications. **Key Classes**: - `io.github.makbn.jlmap.vaadin.JLMapView` - Main Vaadin component - `io.github.makbn.jlmap.vaadin.JLEngine` - Vaadin web component wrapper - `io.github.makbn.jlmap.vaadin.element.menu.VaadinContextMenuMediator` - Context menu support - `io.github.makbn.jlmap.vaadin.journey.VaadinJourneyPlayer` - Journey player **Dependencies**: - jlmap-api - Vaadin Core (24.8.6+) - Vaadin Spring Boot Starter (optional) --- ## Layer System Java Leaflet organizes map functionality into four specialized layers, each with distinct responsibilities: ### 1. UI Layer (LeafletUILayerInt) **Responsibilities**: User interface elements that appear on top of the map. ```java public interface LeafletUILayerInt { // Markers JLMarker addMarker(JLLatLng latLng, String text, boolean draggable); void removeMarker(String id); // Popups JLPopup addPopup(JLLatLng latLng, String text); JLPopup addPopup(JLLatLng latLng, String text, JLOptions options); void removePopup(String id); // Image overlays JLImageOverlay addImage(JLBounds bounds, String url, JLOptions options); void removeImage(String id); } ``` **Use Cases**: - Adding markers at points of interest - Displaying informational popups - Overlaying images on specific geographic areas ### 2. Vector Layer (LeafletVectorLayerInt) **Responsibilities**: Geometric shapes and vector graphics. ```java public interface LeafletVectorLayerInt { // Polylines JLPolyline addPolyline(JLLatLng[] vertices, JLOptions options); JLMultiPolyline addMultiPolyline(JLLatLng[][] vertices, JLOptions options); // Polygons JLPolygon addPolygon(JLLatLng[][][] vertices, JLOptions options); // Circles JLCircle addCircle(JLLatLng center, int radius, JLOptions options); JLCircleMarker addCircleMarker(JLLatLng center, int radius, JLOptions options); // Removal void removePolyline(String id); void removePolygon(String id); void removeCircle(String id); } ``` **Key Differences**: - **JLCircle**: Radius in meters (geographic), scales with zoom - **JLCircleMarker**: Radius in pixels, maintains size regardless of zoom ### 3. Control Layer (LeafletControlLayerInt) **Responsibilities**: Programmatic control of map navigation and viewport. ```java public interface LeafletControlLayerInt { // Zoom operations void setZoom(int zoom); void zoomIn(int delta); void zoomOut(int delta); void setZoomAround(JLLatLng latLng, int zoom); // Pan operations void panTo(JLLatLng latLng); void flyTo(JLLatLng latLng, int zoom); // Bounds operations void fitBounds(JLBounds bounds); void flyToBounds(JLBounds bounds); void setMaxBounds(JLBounds bounds); // Constraints void setMinZoom(int zoom); void setMaxZoom(int zoom); } ``` **Use Cases**: - Smooth camera transitions - Constraining viewable area - Programmatic navigation ### 4. GeoJSON Layer (LeafletGeoJsonLayerInt) **Responsibilities**: Loading and styling GeoJSON geographic data. ```java public interface LeafletGeoJsonLayerInt { JLGeoJson addFromFile(File file, JLGeoJsonOptions options); JLGeoJson addFromUrl(String url, JLGeoJsonOptions options); JLGeoJson addFromContent(String content, JLGeoJsonOptions options); void removeGeoJson(String id); } ``` **Advanced Features**: - **Style Functions**: Dynamic styling based on feature properties - **Filter Functions**: Selective feature rendering - **Point-to-Layer**: Custom marker creation for point features --- ## Transport Layer The transport layer bridges Java code with JavaScript execution in the web engine. ### JLServerToClientTransporter **Architecture**: ``` ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ Java Code │───>│ Transport Layer │───>│ JavaScript Code │ │ (JLObject) │ │ (JLTransporter) │ │ (Leaflet) │ └─────────────────┘ └──────────────────┘ └─────────────────┘ │ │ Convert Result ▼ ┌──────────────────┐ │ Java Result │ │ (CompletableFuture)│ └──────────────────┘ ``` **Interface**: ```java public interface JLServerToClientTransporter { BiFunction serverToClientTransport(); M convertResult(T value, Class target); default M execute(JLTransportRequest request) { String jsCode = generateJavaScript(request); T rawResult = serverToClientTransport().apply(jsCode, request.returnable()); return convertResult(rawResult, request.resultClass()); } } ``` ### JLTransportRequest **Purpose**: Encapsulates method calls from Java to JavaScript. ```java public record JLTransportRequest( JLObject self, String function, Class clazz, Object... params ) { public static JLTransportRequest voidCall(JLObject self, String function, Object... params) { return new JLTransportRequest(self, function, Void.class, params); } public static JLTransportRequest returnableCall(JLObject self, String function, Class clazz, Object... params) { return new JLTransportRequest(self, function, clazz, params); } } ``` **Example Flow**: ```java // Java: Set circle radius circle.setRadius(1000); // Generates Transport Request: JLTransportRequest.voidCall(circle, "setRadius", 1000); // Generates JavaScript: "window.jlmap_objects['circle-uuid-123'].setRadius(1000);" // Executed in WebView transport.execute(request); ``` ### Async Operations Operations that return values use `CompletableFuture`: ```java // Get circle bounds asynchronously CompletableFuture boundsF future = circle.getBounds(); boundsFuture.thenAccept(bounds -> { System.out.println("Bounds: " + bounds); }); ``` --- ## Event System Architecture ### Event Flow ``` ┌─────────────────┐ │ User Action │ (click, drag, etc.) └────────┬────────┘ │ ▼ ┌─────────────────┐ │ JavaScript │ (Leaflet event) │ Event Handler │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Bridge to Java │ (JSObject callback) └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Event Parser │ (JLInteractionEventHandler) └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Typed Event │ (ClickEvent, DragEvent, etc.) └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Listener │ (OnJLActionListener) └─────────────────┘ ``` ### Event Types Hierarchy ```java interface Event { JLAction action(); } // Concrete event types record ClickEvent(JLAction action, JLLatLng center) implements Event record DragEvent(JLAction action, JLLatLng center, JLBounds bounds, int zoom) implements Event record MoveEvent(JLAction action, JLLatLng center, JLBounds bounds, int zoom) implements Event record ZoomEvent(JLAction action, int zoom) implements Event record ContextMenuEvent(JLAction action, double x, double y, boolean open, boolean close) implements Event ``` ### JLAction Enum Maps Java actions to Leaflet JavaScript events: ```java public enum JLAction { CLICK("click"), DOUBLE_CLICK("dblclick"), DRAG("drag"), DRAG_START("dragstart"), DRAG_END("dragend"), ZOOM("zoom"), MOVE("move"), CONTEXT_MENU("contextmenu"), // ... more events } ``` ### Listener Pattern ```java marker.setOnActionListener((source, event) -> { switch (event.action()) { case CLICK -> { ClickEvent clickEvent = (ClickEvent) event; System.out.println("Clicked at: " + clickEvent.center()); } case DRAG_END -> { DragEvent dragEvent = (DragEvent) event; System.out.println("Dragged to: " + dragEvent.center()); } } }); ``` --- ## Builder Pattern Java Leaflet uses a sophisticated builder pattern that serves dual purposes: building Java objects and generating JavaScript code. ### JLObjectBuilder **Type Parameters**: - `M`: Model type (e.g., JLMarker) - `T`: Builder type (for self-typing) **Dual Build Methods**: ```java public abstract class JLObjectBuilder, T extends JLObjectBuilder> { // Builds JavaScript code protected abstract String buildJsElement(); // Builds Java object protected abstract M buildJLObject(); // Public build method public M build() { String jsCode = buildJsElement(); M object = buildJLObject(); object.setJsCode(jsCode); return object; } } ``` **Example: JLMarkerBuilder**: ```java JLMarker marker = JLMarkerBuilder.create() .latLng(new JLLatLng(51.5, -0.09)) .withOptions(JLOptions.builder() .draggable(true) .build()) .build(); // Generates JavaScript: // "L.marker([51.5, -0.09], {draggable: true})" // And creates Java object: // JLMarker instance with same properties ``` ### Fluent API with Self-Typing ```java public class JLMarkerBuilder extends JLObjectBuilder { private JLLatLng latLng; public JLMarkerBuilder latLng(JLLatLng latLng) { this.latLng = latLng; return this; // Returns concrete type, not base } @Override protected JLMarkerBuilder self() { return this; } } ``` This allows method chaining without losing type information. --- ## Context Menu Architecture The context menu system uses the **Mediator Pattern** to abstract platform-specific implementations. ### Architecture Diagram ``` ┌──────────────────┐ │ JLHasContextMenu │ (interface) └────────┬─────────┘ │ ├──> JLMarker ├──> JLPolygon ├──> JLPolyline └──> JLMap │ ▼ ┌──────────────────┐ │ JLContextMenu │ (manages menu items) └────────┬─────────┘ │ ▼ ┌───────────────────────┐ │ JLContextMenuMediator │ (interface) └────────┬──────────────┘ │ ├──> JavaFXContextMenuMediator └──> VaadinContextMenuMediator ``` ### Service Locator Pattern Mediators are discovered at runtime using `ServiceLoader`: ``` META-INF/services/io.github.makbn.jlmap.element.menu.JLContextMenuMediator ├── io.github.makbn.jlmap.fx.element.menu.JavaFXContextMenuMediator └── io.github.makbn.jlmap.vaadin.element.menu.VaadinContextMenuMediator ``` **Discovery Algorithm**: 1. Load all available mediators via ServiceLoader 2. Filter by platform availability (`isAvailable()`) 3. Filter by object type support (`supportsObjectType()`) 4. Sort by priority (`getPriority()`) 5. Cache the selected mediator --- ## Design Patterns ### 1. Interface Segregation Separate interfaces for different responsibilities: - `LeafletUILayerInt` - UI elements - `LeafletVectorLayerInt` - Vector graphics - `LeafletControlLayerInt` - Map control - `LeafletGeoJsonLayerInt` - GeoJSON data ### 2. Strategy Pattern Pluggable transport implementations: - `JLServerToClientTransporter` for JavaFX - `JLServerToClientTransporter` for Vaadin ### 3. Observer Pattern Event listeners for user interactions: - `OnJLActionListener` for object events - `OnJLContextMenuItemListener` for menu selections ### 4. Builder Pattern Fluent object construction with self-typing: - `JLMarkerBuilder`, `JLPolygonBuilder`, `JLOptionsBuilder` ### 5. Mediator Pattern Platform abstraction for context menus: - `JLContextMenuMediator` interface - Platform-specific implementations ### 6. Service Locator Runtime service discovery: - `JLContextMenuMediatorLocator` - Java ServiceLoader integration ### 7. Async/Await Non-blocking operations: - `CompletableFuture` for returnable operations - `toGeoJSON()`, `getBounds()`, `getAttribution()` --- ## Platform Abstraction ### JavaFX Specifics **Web Engine**: Uses `javafx.scene.web.WebView` **JavaScript Execution**: ```java Object result = webEngine.executeScript("window.jlmap_objects['id'].method()"); ``` **Event Bridge**: ```java JSObject window = (JSObject) webEngine.executeScript("window"); window.setMember("javaEventHandler", javaEventHandler); ``` ### Vaadin Specifics **Web Component**: Uses Vaadin's `WebComponent` API **JavaScript Execution**: ```java JsonValue result = element.executeJs("return window.jlmap_objects[$0].method()", id); ``` **Event Bridge**: ```java element.addEventListener("custom-event", event -> { JsonValue detail = event.getEventData().get("detail"); // Handle event }); ``` --- ## Memory Management ### Object Lifecycle 1. **Creation**: Builder creates Java object + JS code 2. **Registration**: Object registered in `window.jlmap_objects` 3. **Usage**: Transport layer facilitates communication 4. **Disposal**: `remove()` method cleans up both sides ### Cleanup Pattern ```java public void remove() { // Remove from JavaScript transport.execute(JLTransportRequest.voidCall(this, "remove")); // Unregister from global map transport.execute(JLTransportRequest.voidCall(this, "delete", "window.jlmap_objects['" + jLId + "']")); // Clean up Java references this.popup = null; this.contextMenu = null; } ``` --- ## Performance Considerations ### 1. Lazy Initialization Context menus are created only when first accessed: ```java public JLContextMenu getContextMenu() { if (contextMenu == null) { synchronized (this) { if (contextMenu == null) { contextMenu = new JLContextMenu<>(self()); registerContextMenuWithMediator(); } } } return contextMenu; } ``` ### 2. Efficient Caching Mediators are discovered once and cached: ```java private final Map, JLContextMenuMediator> mediatorCache = new ConcurrentHashMap<>(); ``` ### 3. Minimal Object Creation Builders reuse instances where possible. ### 4. Async Operations Non-blocking operations return `CompletableFuture`. --- ## Thread Safety ### Concurrent Data Structures ```java private final Map menuItems = new ConcurrentHashMap<>(); ``` ### Double-Checked Locking Used for lazy initialization of expensive objects. ### Platform Threading - **JavaFX**: Operations must run on JavaFX Application Thread - **Vaadin**: Operations synchronized via Vaadin's session lock --- ## Extensibility Points ### 1. Custom Mediators Implement `JLContextMenuMediator` for new UI frameworks. ### 2. Custom Events Implement `Event` interface for new event types. ### 3. Custom Map Providers Create custom tile providers by extending `JLMapProvider`. ### 4. Custom Styling Use `JLGeoJsonOptions` with style and filter functions. --- **Previous**: [← Getting Started](Getting-Started) | **Next**: [API Reference →](API-Reference)