mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
wip: refactor isolateShape
This commit is contained in:
parent
cd30f49773
commit
65c5cb5ab5
7 changed files with 187 additions and 20 deletions
|
@ -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 }
|
||||||
|
|
|
@ -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':
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue