From 78b6025ef1c5c8e1b281f1c741340fe14f3b8962 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 26 Oct 2023 14:26:11 +0200 Subject: [PATCH] Add minimal CSV export cf https://forum.openstreetmap.fr/t/export-csv-depuis-une-umap/18290 cf #123 fix #608 --- umap/static/umap/js/umap.controls.js | 16 ++++ umap/static/umap/js/umap.js | 6 ++ umap/tests/integration/test_export_map.py | 102 ++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 umap/tests/integration/test_export_map.py diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index d1ab87db..c88de1b0 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -900,6 +900,22 @@ L.U.Map.include({ ext: '.kml', filetype: 'application/vnd.google-earth.kml+xml', }, + csv: { + formatter: function (map) { + const table = [] + map.eachFeature((feature) => { + const row = feature.toGeoJSON()['properties'], + center = feature.getCenter() + delete row['_umap_options'] + row['Latitude'] = center.lat + row['Longitude'] = center.lng + table.push(row) + }) + return csv2geojson.dsv.csvFormat(table) + }, + ext: '.csv', + filetype: 'text/csv', + }, umap: { name: L._('Full map data'), formatter: function (map) { diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index b923e2b3..11235789 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -807,6 +807,12 @@ L.U.Map.include({ return geojson }, + eachFeature: function (callback, context) { + this.eachDataLayer((datalayer) => { + if (datalayer.isVisible()) datalayer.eachFeature(callback, context) + }) + }, + fullDownload: function () { // Make sure all data is loaded before downloading this.once('dataloaded', () => this.download()) diff --git a/umap/tests/integration/test_export_map.py b/umap/tests/integration/test_export_map.py new file mode 100644 index 00000000..f4d68ee0 --- /dev/null +++ b/umap/tests/integration/test_export_map.py @@ -0,0 +1,102 @@ +import json +from pathlib import Path + +import pytest +from playwright.sync_api import expect + +pytestmark = pytest.mark.django_db + + +def test_umap_export(map, live_server, datalayer, page): + page.goto(f"{live_server.url}{map.get_absolute_url()}?share") + button = page.locator("a").filter(has_text="Download data") + expect(button).to_be_visible() + with page.expect_download() as download_info: + button.click() + download = download_info.value + assert download.suggested_filename == "test_map.umap" + path = Path("/tmp/") / download.suggested_filename + download.save_as(path) + downloaded = json.loads(path.read_text()) + del downloaded["uri"] # Port changes at each run + assert downloaded == { + "geometry": { + "coordinates": [13.447265624999998, 48.94415123418794], + "type": "Point", + }, + "layers": [ + { + "_umap_options": { + "browsable": True, + "displayOnLoad": True, + "editMode": "disabled", + "inCaption": True, + "name": "test datalayer", + }, + "features": [ + { + "geometry": { + "coordinates": [13.688965, 48.552978], + "type": "Point", + }, + "properties": { + "_umap_options": {"color": "DarkCyan", "iconClass": "Ball"}, + "description": "Da place anonymous " "again 755", + "name": "Here", + }, + "type": "Feature", + } + ], + "type": "FeatureCollection", + } + ], + "properties": { + "captionBar": False, + "captionMenus": True, + "datalayersControl": True, + "description": "Which is just the Danube, at the end", + "displayPopupFooter": False, + "easing": False, + "embedControl": True, + "fullscreenControl": True, + "licence": "", + "limitBounds": {}, + "miniMap": False, + "moreControl": True, + "name": "test map", + "overlay": None, + "permanentCreditBackground": True, + "scaleControl": True, + "scrollWheelZoom": True, + "searchControl": True, + "slideshow": {}, + "tilelayer": { + "attribution": "© OSM Contributors", + "maxZoom": 18, + "minZoom": 0, + "url_template": "http://{s}.osm.fr/{z}/{x}/{y}.png", + }, + "tilelayersControl": True, + "zoom": 7, + "zoomControl": True, + }, + "type": "umap", + } + + +def test_csv_export(map, live_server, datalayer, page): + page.goto(f"{live_server.url}{map.get_absolute_url()}?share") + button = page.locator("a").filter(has_text="Download data") + expect(button).to_be_visible() + page.locator('select[name="format"]').select_option("csv") + with page.expect_download() as download_info: + button.click() + download = download_info.value + assert download.suggested_filename == "test_map.csv" + path = Path("/tmp/") / download.suggested_filename + download.save_as(path) + assert ( + path.read_text() + == """name,description,Latitude,Longitude +Here,Da place anonymous again 755,48.55297816440071,13.68896484375""" + )