wip: refactor isolateShape

This commit is contained in:
Yohan Boniface 2024-07-26 18:25:43 +02:00
parent cd30f49773
commit 65c5cb5ab5
7 changed files with 187 additions and 20 deletions

View file

@ -729,20 +729,11 @@ class Path extends Feature {
if (this.isEmpty()) this.del() if (this.isEmpty()) this.del()
} }
isolateShape(at) { isolateShape(latlngs) {
if (!this.isMulti()) return
const shape = this.ui.enableEdit().deleteShapeAt(at)
this.ui.disableEdit()
if (!shape) return
const properties = this.cloneProperties() const properties = this.cloneProperties()
const other = new (this instanceof LineString ? LineString : Polygon)( const type = this instanceof LineString ? 'LineString' : 'Polygon'
this.datalayer, const geometry = this._toGeometry(latlngs)
{ const other = this.datalayer.makeFeature({ type, geometry, properties })
properties,
geometry: this._toGeometry(shape),
}
)
this.datalayer.addFeature(other)
other.edit() other.edit()
return other return other
} }
@ -912,10 +903,11 @@ export class Polygon extends Path {
_toGeometry(latlngs) { _toGeometry(latlngs) {
const holes = !LineUtil.isFlat(latlngs) const holes = !LineUtil.isFlat(latlngs)
const multi = holes && !LineUtil.isFlat(latlngs[0]) let multi = holes && !LineUtil.isFlat(latlngs[0])
let coordinates = GeoJSON.latLngsToCoords(latlngs, multi ? 2 : holes ? 1 : 0, true) let coordinates = GeoJSON.latLngsToCoords(latlngs, multi ? 2 : holes ? 1 : 0, true)
if (!holes) { if (Utils.polygonMustBeFlattened(coordinates)) {
coordinates = [coordinates] coordinates = coordinates[0]
multi = false
} }
const type = multi ? 'MultiPolygon' : 'Polygon' const type = multi ? 'MultiPolygon' : 'Polygon'
return { coordinates, type } return { coordinates, type }

View file

@ -435,12 +435,13 @@ export class DataLayer {
} }
makeFeature(geojson = {}, sync = true, id = null) { makeFeature(geojson = {}, sync = true, id = null) {
// Both Feature and Geometry are valid geojson objects.
const geometry = geojson.geometry || geojson const geometry = geojson.geometry || geojson
let feature let feature
switch (geometry.type) { switch (geometry.type) {
case 'Point': case 'Point':
// FIXME: deal with MutliPoint // FIXME: deal with MultiPoint
feature = new Point(this, geojson, id) feature = new Point(this, geojson, id)
break break
case 'MultiLineString': case 'MultiLineString':

View file

@ -378,12 +378,21 @@ const PathMixin = {
items.push({ items.push({
text: translate('Extract shape to separate feature'), text: translate('Extract shape to separate feature'),
callback: () => { callback: () => {
this.feature.isolateShape(event.latlng, this._map.editedFeature) this.isolateShape(event.latlng)
}, },
}) })
} }
return items return items
}, },
isolateShape: function(atLatLng) {
if (!this.feature.isMulti()) return
const shape = this.enableEdit().deleteShapeAt(atLatLng)
this.geometryChanged()
this.disableEdit()
if (!shape) return
return this.feature.isolateShape(shape)
}
} }
export const LeafletPolyline = Polyline.extend({ export const LeafletPolyline = Polyline.extend({

View file

@ -302,6 +302,10 @@ export function flattenCoordinates(coords) {
return coords return coords
} }
export function polygonMustBeFlattened(coords) {
return coords.length === 1 && typeof coords?.[0]?.[0]?.[0] !== 'number'
}
export function buildQueryString(params) { export function buildQueryString(params) {
const query_string = [] const query_string = []
for (const key in params) { for (const key in params) {

View file

@ -244,7 +244,7 @@ U.ExtractShapeFromMultiAction = U.BaseFeatureAction.extend({
}, },
onClick: function (e) { onClick: function (e) {
this.feature.isolateShape(e.latlng) this.feature.ui.isolateShape(e.latlng)
}, },
}) })

View file

@ -479,6 +479,113 @@ describe('Utils', () => {
}) })
}) })
describe('#polygonMustBeFlattened', () => {
it('should return false for simple polygon', () => {
const coords = [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
]
assert.notOk(Utils.polygonMustBeFlattened(coords))
})
it('should return false for simple polygon with hole', () => {
const coords = [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
[
[100.8, 0.8],
[100.8, 0.2],
[100.2, 0.2],
[100.2, 0.8],
[100.8, 0.8],
],
]
assert.notOk(Utils.polygonMustBeFlattened(coords))
})
it('should return false for multipolygon', () => {
const coords = [
[
[
[102.0, 2.0],
[103.0, 2.0],
[103.0, 3.0],
[102.0, 3.0],
[102.0, 2.0],
],
],
[
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
[
[100.2, 0.2],
[100.2, 0.8],
[100.8, 0.8],
[100.8, 0.2],
[100.2, 0.2],
],
],
]
assert.notOk(Utils.polygonMustBeFlattened(coords))
})
it('should return true for false multi polygon', () => {
const coords = [
[
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
],
]
assert.ok(Utils.polygonMustBeFlattened(coords))
})
it('should return true for false multi polygon with hole', () => {
const coords = [
[
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
[
[100.8, 0.8],
[100.8, 0.2],
[100.2, 0.2],
[100.2, 0.8],
[100.8, 0.8],
],
],
]
assert.ok(Utils.polygonMustBeFlattened(coords))
})
it('should return false for empty coords', () => {
assert.notOk(Utils.polygonMustBeFlattened([]))
})
})
describe('#usableOption()', () => { describe('#usableOption()', () => {
it('should consider false', () => { it('should consider false', () => {
assert.ok(Utils.usableOption({ key: false }, 'key')) assert.ok(Utils.usableOption({ key: false }, 'key'))

View file

@ -243,7 +243,8 @@ def test_can_transfer_shape_from_simple_polygon(live_server, page, tilelayer):
expect(polygons).to_have_count(1) expect(polygons).to_have_count(1)
def test_can_extract_shape(live_server, page, tilelayer): def test_can_extract_shape(live_server, page, tilelayer, settings):
settings.UMAP_ALLOW_ANONYMOUS = True
page.goto(f"{live_server.url}/en/map/new/") page.goto(f"{live_server.url}/en/map/new/")
polygons = page.locator(".leaflet-overlay-pane path") polygons = page.locator(".leaflet-overlay-pane path")
expect(polygons).to_have_count(0) expect(polygons).to_have_count(0)
@ -269,6 +270,59 @@ def test_can_extract_shape(live_server, page, tilelayer):
polygons.first.click(position={"x": 20, "y": 20}, button="right") polygons.first.click(position={"x": 20, "y": 20}, button="right")
extract_button.click() extract_button.click()
expect(polygons).to_have_count(2) expect(polygons).to_have_count(2)
data = save_and_get_json(page)
print(data)
assert len(data["features"]) == 2
assert data["features"][0]["geometry"]["type"] == "Polygon"
assert data["features"][1]["geometry"]["type"] == "Polygon"
assert data["features"][0]["geometry"]["coordinates"] == [
[
[
-6.569824,
53.159947,
],
[
-6.569824,
52.49616,
],
[
-7.668457,
52.49616,
],
[
-7.668457,
53.159947,
],
[
-6.569824,
53.159947,
],
],
]
assert data["features"][1]["geometry"]["coordinates"] == [
[
[
-8.76709,
54.457267,
],
[
-8.76709,
53.813626,
],
[
-9.865723,
53.813626,
],
[
-9.865723,
54.457267,
],
[
-8.76709,
54.457267,
],
],
]
def test_cannot_transfer_shape_to_line(live_server, page, tilelayer): def test_cannot_transfer_shape_to_line(live_server, page, tilelayer):