Skip to content

Commit

Permalink
fix: clear now removes all rendered layers
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesLMilner committed May 21, 2023
1 parent 839bb90 commit fd7a208
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 88 deletions.
154 changes: 81 additions & 73 deletions development/index.html
Original file line number Diff line number Diff line change
@@ -1,80 +1,88 @@
<html>
<head>
<style>
button {
padding: 20px;
background: #fdfdfd;
border: solid 1px #ebebeb;
cursor: pointer;
border-radius: 10px;
font-weight: bold;
font-size: 20px;
color: #565656;
margin: 5px;
border: solid 3px #565656;
}

<head>
<style>
button {
padding: 20px;
background: #fdfdfd;
border: solid 1px #ebebeb;
cursor: pointer;
border-radius: 10px;
font-weight: bold;
font-size: 20px;
color: #565656;
margin: 5px;
border: solid 3px #565656;
}
button:hover {
background: white;
border: solid 3px #222222;
}

button:hover {
background: white;
border: solid 3px #222222;
}
.example {
cursor: pointer;
font-family: sans-serif;
height: 100%;
width: 25%;
outline: solid 1px #d7d7d7;
}

.example {
cursor: pointer;
font-family: sans-serif;
height: 100%;
width: 25%;
outline: solid 1px #d7d7d7;
}
#keybind {
font-family: sans-serif;
background: white;
width: 100px;
align-items: center;
display: flex;
justify-content: center;
border: solid 1px black;
font-weight: bold;
margin-left: 10px;
}
</style>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css"
integrity="sha512-hoalWLoI8r4UszCkZ5kL8vayOGVae1oxXe/2A4AO6J9+580uKHDO3JdHb7NzwwzK5xr/Fs0W40kiNHxM9vyTtQ=="
crossorigin=""
/>
<link
href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css"
rel="stylesheet"
/>
</head>

#keybind {
font-family: sans-serif;
background: white;
width: 100px;
align-items: center;
display: flex;
justify-content: center;
border: solid 1px black;
font-weight: bold;
margin-left: 10px;
}
</style>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css"
integrity="sha512-hoalWLoI8r4UszCkZ5kL8vayOGVae1oxXe/2A4AO6J9+580uKHDO3JdHb7NzwwzK5xr/Fs0W40kiNHxM9vyTtQ=="
crossorigin="" />
<link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet" />
</head>
<body style="width: 100%; height: 100%; margin: 0">
<div style="display: flex; flex-direction: row; height: 100%; width: 100%">
<div class="example" id="leaflet-map"></div>
<div class="example" id="openlayers-map"></div>
<div class="example" id="mapbox-map"></div>
<div class="example" id="maplibre-map"></div>
<div class="example" id="google-map"></div>
</div>

<body style="width: 100%; height: 100%; margin: 0">
<div style="display: flex; flex-direction: row; height: 100%; width: 100%">
<div class="example" id="leaflet-map"></div>
<div class="example" id="openlayers-map"></div>
<div class="example" id="mapbox-map"></div>
<div class="example" id="maplibre-map"></div>
<div class="example" id="google-map"></div>
</div>
<div
style="
position: absolute;
top: 0;
z-index: 1000;
width: 100%;
margin: auto 0;
display: flex;
justify-content: center;
"
>
<button id="select">Select</button>
<button id="point">Point</button>
<button id="greatcircle">Great Circle</button>
<button id="linestring">LineString</button>
<button id="polygon">Polygon</button>
<button id="freehand">Freehand</button>
<button id="circle">Circle</button>
<button id="rectangle">Rectangle</button>

<div style="
position: absolute;
top: 0;
z-index: 1000;
width: 100%;
margin: auto 0;
display: flex;
justify-content: center;
">
<button id="select">Select</button>
<button id="point">Point</button>
<button id="greatcircle">Great Circle</button>
<button id="linestring">LineString</button>
<button id="polygon">Polygon</button>
<button id="freehand">Freehand</button>
<button id="circle">Circle</button>
<button id="rectangle">Rectangle</button>
<div id="keybind"></div>
</div>
<script src="./bundle.js"></script>
</body>

</html>
<button id="clear">Clear</button>
<div id="keybind"></div>
</div>
<script src="./bundle.js"></script>
</body>
</html>
7 changes: 7 additions & 0 deletions development/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ const addModeChangeHandler = (
}
);
});

(document.getElementById("clear") as HTMLButtonElement).addEventListener(
"click",
() => {
draw.clear();
}
);
};

const getModes = () => {
Expand Down
3 changes: 3 additions & 0 deletions src/adapters/common/base.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,11 @@ export abstract class TerraDrawBaseAdapter {
this._listeners.forEach((listener) => {
listener.unregister();
});
this.clear();
}

public abstract clear(): void;

public abstract project(...args: Parameters<Project>): ReturnType<Project>;

public abstract unproject(
Expand Down
40 changes: 39 additions & 1 deletion src/adapters/google-maps.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ export class TerraDrawGoogleMapsAdapter extends TerraDrawBaseAdapter {
this._map.setOptions({ draggable: enabled });
}

private renderedFeatures: Set<string> = new Set();

/**
* Renders GeoJSON features on the map using the provided styling configuration.
* @param changes An object containing arrays of created, updated, and unchanged features to render.
Expand All @@ -238,7 +240,10 @@ export class TerraDrawGoogleMapsAdapter extends TerraDrawBaseAdapter {
if (this._layers) {
changes.deletedIds.forEach((deletedId) => {
const featureToDelete = this._map.data.getFeatureById(deletedId);
featureToDelete && this._map.data.remove(featureToDelete);
if (featureToDelete) {
this._map.data.remove(featureToDelete);
this.renderedFeatures.delete(deletedId);
}
});

changes.updated.forEach((updatedFeature) => {
Expand Down Expand Up @@ -324,6 +329,7 @@ export class TerraDrawGoogleMapsAdapter extends TerraDrawBaseAdapter {

// Create new features
changes.created.forEach((createdFeature) => {
this.renderedFeatures.add(createdFeature.id as string);
this._map.data.addGeoJson(createdFeature);
});
} else {
Expand Down Expand Up @@ -363,6 +369,10 @@ export class TerraDrawGoogleMapsAdapter extends TerraDrawBaseAdapter {
);
}

changes.created.forEach((feature) => {
this.renderedFeatures.add(feature.id as string);
});

const featureCollection = {
type: "FeatureCollection",
features: [...changes.created],
Expand Down Expand Up @@ -428,4 +438,32 @@ export class TerraDrawGoogleMapsAdapter extends TerraDrawBaseAdapter {

this._layers = true;
}

private clearLayers() {
if (this._layers) {
this._map.data.forEach((feature) => {
const id = feature.getId() as string;
const hasFeature = this.renderedFeatures.has(id);
if (hasFeature) {
this._map.data.remove(feature);
}
});
this.renderedFeatures = new Set();
this._layers = false;
}
}

/**
* Clears the map and store of all rendered data layers
* @returns void
* */
public clear() {
if (this._currentModeCallbacks) {
// Clean up state first
this._currentModeCallbacks.onClear();

// Then clean up rendering
this.clearLayers();
}
}
}
62 changes: 62 additions & 0 deletions src/adapters/leaflet.adapter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import { TerraDrawLeafletAdapter } from "./leaflet.adapter";
import { getMockPointerEvent } from "../test/mock-pointer-event";
import { TerraDrawCallbacks } from "../common";

const callbacks = () =>
({
getState: jest.fn(() => "started"),
onKeyUp: jest.fn(),
onKeyDown: jest.fn(),
onClick: jest.fn(),
onMouseMove: jest.fn(),
onDragStart: jest.fn(),
onDrag: jest.fn(),
onDragEnd: jest.fn(),
onClear: jest.fn(),
} as TerraDrawCallbacks);

const createLeafletMap = () => {
return {
Expand All @@ -8,6 +22,8 @@ const createLeafletMap = () => {
({
getBoundingClientRect: jest.fn(() => ({ top: 0, left: 0 })),
style: { removeProperty: jest.fn(), cursor: "initial" },
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
} as any)
),
latLngToContainerPoint: jest.fn(() => ({ x: 0, y: 0 } as any)),
Expand Down Expand Up @@ -280,4 +296,50 @@ describe("TerraDrawLeafletAdapter", () => {
expect(map.removeLayer).toBeCalledTimes(2); // 1 for update 1 for delete
});
});

it("clear", () => {
const map = createLeafletMap() as L.Map;

// Create the adapter instance with the mocked map
const lib = {
geoJSON: jest.fn(),
} as any;

// Create the adapter instance with the mocked map
const adapter = new TerraDrawLeafletAdapter({
lib,
map,
coordinatePrecision: 9,
});

adapter.render(
{
unchanged: [],
created: [
{
id: "1",
type: "Feature",
geometry: {
type: "Point",
coordinates: [1, 1],
},
properties: {
mode: "test",
},
},
],
deletedIds: [],
updated: [],
},
{
test: () => ({} as any),
}
);

adapter.register(callbacks());

adapter.clear();

expect(map.removeLayer).toBeCalledTimes(1);
});
});
43 changes: 43 additions & 0 deletions src/adapters/leaflet.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,34 @@ export class TerraDrawLeafletAdapter extends TerraDrawBaseAdapter {
return style;
}

/**
* Clears the panes created by the adapter
* @returns void
* */
private clearPanes() {
Object.values(this._panes).forEach((pane) => {
if (pane) {
pane.remove();
}
});
this._panes = {};
}

/**
* Clears the leaflet layers created by the adapter
* @returns void
* */
private clearLayers() {
Object.values(this._layers).forEach((layer) => {
this._map.removeLayer(layer);
});
this._layers = {};
}

/**
* Styles a GeoJSON layer based on the styling function
* @param styling - The styling function
* */
private styleGeoJSONLayer(
styling: TerraDrawStylingFunction
): L.GeoJSONOptions {
Expand Down Expand Up @@ -251,4 +279,19 @@ export class TerraDrawLeafletAdapter extends TerraDrawBaseAdapter {
this._map.addLayer(this._layers[updated.id as string]);
});
}

/**
* Clears the map and store of all rendered data layers
* @returns void
* */
public clear() {
if (this._currentModeCallbacks) {
// Clear up state first
this._currentModeCallbacks.onClear();

// Then clean up rendering
this.clearLayers();
this.clearPanes();
}
}
}
Loading

0 comments on commit fd7a208

Please sign in to comment.