-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Drafting an actual library feature around mapbox-gl-draw
Closes #2
- Loading branch information
Showing
3 changed files
with
208 additions
and
9 deletions.
There are no files selected for viewing
150 changes: 150 additions & 0 deletions
150
src/main/java/org/vaadin/addons/maplibre/DrawControl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package org.vaadin.addons.maplibre; | ||
|
||
import com.vaadin.flow.component.page.PendingJavaScriptResult; | ||
import org.locationtech.jts.geom.GeometryCollection; | ||
import org.locationtech.jts.io.ParseException; | ||
import org.locationtech.jts.io.geojson.GeoJsonReader; | ||
import org.parttio.vaadinjsloader.JSLoader; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.EventObject; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
/** | ||
* A "raw" Java API for mapbox-gl-draw control, that is | ||
* compatible with maplibre. Actual fields can then utilize | ||
* this API. | ||
*/ | ||
public class DrawControl { | ||
|
||
public class ModeChangeEvent extends EventObject{ | ||
private final DrawMode drawMode; | ||
|
||
public ModeChangeEvent(DrawMode drawMode) { | ||
super(DrawControl.this); | ||
this.drawMode = drawMode; | ||
} | ||
|
||
public DrawMode getDrawMode() { | ||
return drawMode; | ||
} | ||
} | ||
|
||
@FunctionalInterface | ||
public interface DrawEventListener<T extends EventObject> { | ||
public void onEvent(T event); | ||
} | ||
|
||
private List<DrawEventListener<ModeChangeEvent>> modeChangeListeners; | ||
public void addModeChangeListener(DrawEventListener<ModeChangeEvent> listener) { | ||
if(modeChangeListeners == null) { | ||
modeChangeListeners = new ArrayList<>(); | ||
map.getElement().addEventListener("modechange", e-> { | ||
ModeChangeEvent event = new ModeChangeEvent(DrawMode.valueOf(e.getEventData().getString("event.mode").toUpperCase())); | ||
modeChangeListeners.forEach(l -> l.onEvent(event)); | ||
}).addEventData("event.mode"); | ||
} | ||
modeChangeListeners.add(listener); | ||
} | ||
|
||
public enum DrawMode { | ||
SIMPLE_SELECT, | ||
DIRECT_SELECT, | ||
DRAW_LINE_STRING, | ||
DRAW_POLYGON, | ||
DRAW_POINT | ||
} | ||
|
||
private final MapLibre map; | ||
private final String id = "draw" + UUID.randomUUID().toString().substring(0,4); | ||
|
||
private DrawMode mode = DrawMode.SIMPLE_SELECT; | ||
|
||
public DrawControl(MapLibre map) { | ||
this.map = map; | ||
injectScript(); | ||
map.addAttachListener(e -> { | ||
doInit(); | ||
}); | ||
if(map.isAttached()) { | ||
doInit(); | ||
} | ||
} | ||
|
||
private void doInit() { | ||
map.js(""" | ||
const drawOptions = { | ||
defaultMode : "%s", | ||
displayControlsDefault: false | ||
}; | ||
const id = "%s"; | ||
const draw = new MapboxDraw(drawOptions); | ||
map[id] = draw; | ||
map.addControl(draw); | ||
map.on("draw.modechange", e => { | ||
// TODO figure out a proper way for cursors | ||
if(e.mode == "direct_select") { | ||
map._canvasContainer.style.cursor = "default"; | ||
} else { | ||
map._canvasContainer.style.cursor = ""; | ||
} | ||
const evt = new Event("modechange"); | ||
evt.mode = e.mode; | ||
component.dispatchEvent(evt); | ||
}); | ||
""".formatted(mode.toString().toLowerCase(), id)); | ||
} | ||
|
||
public void setMode(DrawMode mode) { | ||
this.mode = mode; | ||
if(map.isAttached()) { | ||
js(""" | ||
const mode = "$mode"; | ||
draw.changeMode(mode); | ||
if(mode.indexOf("draw") != -1 ) { | ||
map._canvasContainer.style.cursor = "default"; | ||
} | ||
""", Map.of("mode", mode.toString().toLowerCase())); | ||
} else { | ||
js(""" | ||
if($mode.indexOf("draw") != -1 ) { | ||
map._canvasContainer.style.cursor = "default"; | ||
} | ||
""", Map.of("mode", mode.toString().toLowerCase())); | ||
|
||
} | ||
} | ||
|
||
protected void injectScript() { | ||
JSLoader.loadFiles(map, "https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js", "mapbox-gl-draw", "v1.4.3"); | ||
} | ||
|
||
private PendingJavaScriptResult js(String js, Map args) { | ||
js = "const draw = map['%s'];\n".formatted(id) + js; | ||
return map.js(js,args); | ||
} | ||
|
||
public CompletableFuture<GeometryCollection> getAll() { | ||
var v = new CompletableFuture<GeometryCollection>(); | ||
js(""" | ||
const all = draw.getAll(); | ||
return JSON.stringify(all); | ||
""", Collections.emptyMap()).then(String.class, str -> { | ||
try { | ||
GeometryCollection geom = (GeometryCollection) new GeoJsonReader().read(str); | ||
v.complete(geom); | ||
} catch (ParseException e) { | ||
v.completeExceptionally(e); | ||
} | ||
}); | ||
return v; | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
src/test/java/org/vaadin/addons/maplibre/DrawControlView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package org.vaadin.addons.maplibre; | ||
|
||
import com.vaadin.flow.component.notification.Notification; | ||
import com.vaadin.flow.component.radiobutton.RadioButtonGroup; | ||
import com.vaadin.flow.router.Route; | ||
import org.vaadin.firitin.components.button.VButton; | ||
import org.vaadin.firitin.components.orderedlayout.VHorizontalLayout; | ||
import org.vaadin.firitin.components.orderedlayout.VVerticalLayout; | ||
|
||
@Route | ||
public class DrawControlView extends VVerticalLayout { | ||
|
||
public DrawControlView() { | ||
MapLibre map = new MapLibre("https://api.maptiler.com/maps/streets/style.json?key=G5n7stvZjomhyaVYP0qU"); | ||
DrawControl drawControl = new DrawControl(map); | ||
addAndExpand(map); | ||
|
||
var toolbar = new VHorizontalLayout(); | ||
|
||
// TODO disabled items in Viritin | ||
// EnumSelect<DrawControl.DrawMode> modeSelect = new EnumSelect<>(DrawControl.DrawMode.class); | ||
RadioButtonGroup<DrawControl.DrawMode> modeSelect = new RadioButtonGroup<>(); | ||
modeSelect.setItems(DrawControl.DrawMode.values()); | ||
modeSelect.setItemEnabledProvider(item -> { | ||
if(item == DrawControl.DrawMode.DIRECT_SELECT) { | ||
return false; // needs id | ||
} | ||
return true; | ||
}); | ||
modeSelect.addValueChangeListener(e -> { | ||
if(e.isFromClient()) | ||
drawControl.setMode(e.getValue()); | ||
}); | ||
modeSelect.setValue(DrawControl.DrawMode.SIMPLE_SELECT); | ||
|
||
// TODO figure out what is wrong with selecting drawing | ||
// modeSelect.setValue(DrawControl.DrawMode.DRAW_POLYGON); | ||
// drawControl.setMode(DrawControl.DrawMode.DRAW_POLYGON); | ||
// TODO figure out by setting mode with keyboard shortcut fails | ||
|
||
toolbar.add(modeSelect); | ||
|
||
drawControl.addModeChangeListener(e -> { | ||
modeSelect.setValue(e.getDrawMode()); | ||
}); | ||
|
||
toolbar.add(new VButton("Show geometries", buttonClickEvent -> { | ||
drawControl.getAll().thenAccept(geometryCollection -> { | ||
Notification.show("Drawn geometries:" + geometryCollection.toText()); | ||
}); | ||
|
||
})); | ||
|
||
add(toolbar); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters