From b3691fb2918f66c17af10b74d513b52b24c9c3bf Mon Sep 17 00:00:00 2001 From: phillimorland <5871230+phillimorland@users.noreply.github.com> Date: Thu, 2 Dec 2021 17:09:54 +0100 Subject: [PATCH 1/2] apps/maps: rm map files to a4 and update imports and deps --- .../apps/maps/assets/i18n-leaflet-draw.js | 106 ------- meinberlin/apps/maps/assets/map-address.js | 155 --------- .../assets/map_choose_polygon_with_preset.js | 295 ------------------ meinberlin/assets/js/app.js | 4 +- package.json | 2 - webpack.common.js | 2 +- 6 files changed, 4 insertions(+), 560 deletions(-) delete mode 100644 meinberlin/apps/maps/assets/i18n-leaflet-draw.js delete mode 100644 meinberlin/apps/maps/assets/map-address.js delete mode 100644 meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js diff --git a/meinberlin/apps/maps/assets/i18n-leaflet-draw.js b/meinberlin/apps/maps/assets/i18n-leaflet-draw.js deleted file mode 100644 index d93ea705b1..0000000000 --- a/meinberlin/apps/maps/assets/i18n-leaflet-draw.js +++ /dev/null @@ -1,106 +0,0 @@ -window.L.drawLocal = { - draw: { - toolbar: { - actions: { - title: 'Bereich entfernen', - text: 'Abbrechen' - }, - finish: { - title: 'Bereich übernehmen', - text: 'Übernehmen' - }, - undo: { - title: 'Letzten Punkt entfernen', - text: 'Letzten Punkt entfernen' - }, - buttons: { - polyline: 'Draw a polyline', - polygon: 'Einen Bereich einzeichnen', - rectangle: 'Ein Rechteck einzeichnen', - circle: 'Draw a circle', - marker: 'Draw a marker', - circlemarker: 'Draw a circlemarker' - } - }, - handlers: { - circle: { - tooltip: { - start: 'Click and drag to draw circle.' - }, - radius: 'Radius' - }, - circlemarker: { - tooltip: { - start: 'Click map to place circle marker.' - } - }, - marker: { - tooltip: { - start: 'Click map to place marker.' - } - }, - polygon: { - tooltip: { - start: 'Klicken, um den Bereich anzufangen.', - cont: 'Klicken, um hier einen Punkt zu setzen.', - end: 'Den ersten Punkt anklicken, um den Bereich abzuschließen.' - } - }, - polyline: { - error: 'Error: shape edges cannot cross!', - tooltip: { - start: 'Click to start drawing line.', - cont: 'Click to continue drawing line.', - end: 'Click last point to finish line.' - } - }, - rectangle: { - tooltip: { - start: 'Klicken und ziehen um ein Rechteck zu zeichnen.' - } - }, - simpleshape: { - tooltip: { - end: 'Maustaste loslassen um den Bereich abzuschließen.' - } - } - } - }, - edit: { - toolbar: { - actions: { - save: { - title: 'Änderungen speichern', - text: 'Speichern' - }, - cancel: { - title: 'Bearbeitung abbrechen, Änderungen verwerfen', - text: 'Abbrechen' - }, - clearAll: { - title: 'Alle Ebenen entfernen', - text: 'Alles entfernen' - } - }, - buttons: { - edit: 'Ebenen bearbeiten', - editDisabled: 'Keine Ebenen zum Bearbeiten', - remove: 'Ebenen löschen', - removeDisabled: 'Keine Ebenen zum Löschen' - } - }, - handlers: { - edit: { - tooltip: { - text: 'Die Eckpunkte ziehen um den Bereich zu ändern.', - subtext: 'Abbrechen klicken um die Änderungen zu verwerfen..' - } - }, - remove: { - tooltip: { - text: 'Einen Bereich anklicken um ihn zu entfernen.' - } - } - } - } -} diff --git a/meinberlin/apps/maps/assets/map-address.js b/meinberlin/apps/maps/assets/map-address.js deleted file mode 100644 index 91f2329dff..0000000000 --- a/meinberlin/apps/maps/assets/map-address.js +++ /dev/null @@ -1,155 +0,0 @@ -/* global django */ - -const apiUrl = 'https://bplan-prod.liqd.net/api/addresses/' - -function pointInPolygon (point, polygon) { - const x = point[0] - const y = point[1] - - // Algorithm comes from: - // https://github.com/substack/point-in-polygon/blob/master/index.js - let inside = false - - for (let p = 0; p < polygon.length; p++) { - const ring = polygon[p] - - for (let i = 0; i < ring.length - 1; i++) { - const xi = ring[i][0] - const yi = ring[i][1] - const xj = ring[i + 1][0] - const yj = ring[i + 1][1] - - // * - // / - // *--/----------->> - // * - // Check that - // - // 1. yi and yj are on opposite sites of a ray to the right - // 2. the intersection of the ray and the segment is right of x - const intersect = ((yi > y) !== (yj > y)) && - (x < (xj - xi) * (y - yi) / (yj - yi) + xi) - if (intersect) inside = !inside - } - } - return inside -} - -const pointInObject = function (point, geojson, failByDefault) { - if (geojson.type === 'MultiPolygon') { - return geojson.coordinates.some(function (polygon) { - return pointInPolygon(point, polygon) - }) - } else if (geojson.type === 'Polygon') { - return pointInPolygon(point, geojson.coordinates) - } else if (geojson.type === 'Feature') { - return pointInObject(point, geojson.geometry, failByDefault) - } else if (geojson.type === 'FeatureCollection') { - return geojson.features.some(function (feature) { - return pointInObject(point, feature, true) - }) - } else { - return !failByDefault - } -} - -const setBusy = function ($group, busy) { - $group.attr('aria-busy', busy) - $group.find('input').attr('disabled', busy) - $group.find('button').attr('disabled', busy) - if (busy) { - $group.find('.fa, .fas, .far') - .addClass('fa-spinner fa-pulse') - .removeClass('fa-search') - } else { - $group.find('.fa, .fas, .far') - .addClass('fa-search') - .removeClass('fa-spinner fa-pulse') - } -} - -const getPoints = function (address, cb) { - $.ajax(apiUrl, { - data: { address: address }, - success: function (geojson) { - cb(geojson.features) - }, - error: function () { - const points = [] - cb(points) - } - }) -} - -const renderPoints = function (points) { - if (points.length === 0) { - return $('') - .text(django.gettext('No matches found within the project area')) - } else { - const $list = $('') - .text(django.gettext('Did you mean:')) - points.forEach(function (point) { - const text = point.properties.strname + ' ' + - point.properties.hsnr + ', ' + - point.properties.plz + ' ' + - point.properties.bezirk_name - $list.append($('') - .append($('') - .text(text) - .attr('data-map-point', JSON.stringify(point)) - ) - ) - }) - return $list - } -} - -function init () { - $('[data-map="address"]').each(function (i, e) { - const $group = $(e) - const name = $group.data('name') - const $input = $('#id_' + name) - const $map = $('[data-map="choose_point"][data-name="' + name + '"]') - const polygon = $map.data('polygon') - - const onSubmit = function (event) { - event.preventDefault() - setBusy($group, true) - const address = $group.find('input').val() - getPoints(address, function (points) { - setBusy($group, false) - $group.find('.complete') - .empty() - .append(renderPoints(points.filter(function (point) { - return pointInObject(point.geometry.coordinates, polygon) - }))) - }) - } - - // simulate a nested form - $group.find('button').click(onSubmit) - $group.find('input').on('change', onSubmit) - $group.find('input').on('keydown', function (event) { - if (event.keyCode === 13) { - onSubmit(event) - } - }) - - $group.on('click', '[data-map-point]', function (event) { - const data = $(event.target).attr('data-map-point') - $group.find('.complete').empty() - // NOTE that the text may not be a valid search query - $group.find('input').val($(event.target).text()) - $input.val(data).change() - }) - - $(document).on('focusout', function (event) { - if (!$.contains($group.get(0), event.relatedTarget)) { - $group.find('.complete').empty() - } - }) - }) -} - -document.addEventListener('DOMContentLoaded', init, false) -document.addEventListener('a4.embed.ready', init, false) diff --git a/meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js b/meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js deleted file mode 100644 index 17c5ee358f..0000000000 --- a/meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js +++ /dev/null @@ -1,295 +0,0 @@ -/* Adds extra select dropdown to choose predefined polygon instead of drawing one */ - -/* global django */ -import { maps } from 'adhocracy4' -import 'leaflet-draw' -import './i18n-leaflet-draw' -import FileSaver from 'file-saver' -import { shp } from 'shpjs' - -function getBaseBounds (L, polygon, bbox) { - if (polygon) { - if (polygon.type === 'FeatureCollection' && polygon.features.length === 0) { - return bbox - } - return L.geoJson(polygon).getBounds() - } else { - return bbox - } -} - -function init () { - // select2 needs stateful jQuery - const $ = window.jQuery - const L = window.L - - const ImportControl = L.Control.extend({ - // Options - options: { - position: 'topright', - polygonStyle: {} - }, - - initialize: function (layer, options) { - L.Util.setOptions(this, options) - this._layer = layer - }, - - onAdd: function (map) { - const container = this._createControls() - document.body.appendChild(this._createModal()) - - $(document).on('click', '#map-export-link', (e) => { - e.preventDefault() - this._export(map) - }) - - $(document).on('submit', '#map-import-form', (e) => { - e.preventDefault() - - const fileInput = $('#map-import-file-input')[0] - if (fileInput.files.length < 1) { - return - } - const file = fileInput.files[0] - - this._removeUploadError() - fileInput.value = '' - - this._import(map, file) - }) - - $('#map-import-modal').on('hidden.bs.modal', () => this._removeUploadError()) - return container - }, - - _createControls: function () { - const exportLabel = django.gettext('Export polygon as GeoJSON') - const importLabel = django.gettext('Import polygon via file upload') - return $.parseHTML( - '' + - '' + - '' + - '' - )[0] - }, - - _createModal: function () { - const modalTitle = django.gettext('Import polygon via file upload') - return $.parseHTML( - '' + - '' + - '' + - '' + - '' + modalTitle + '' + - '' + - '' + - '' + - '' + - '' + - '' + django.gettext('Import polygon via file upload') + '' + - '' + - django.gettext('Upload a polygon from a GeoJSON (.geojson) or a zipped Shapefile (.zip).') + '' + - django.gettext('Note that uploading Shapefiles is not supported with Internet Explorer 10') + '' + - '' + django.gettext('Attention importing a file will delete the existing polygons.') + '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' - )[0] - }, - - _removeUploadError: function () { - $('#map-import-form .errorlist').remove() - }, - - _showUploadError: function (msg) { - $('#map-import-form .form-group').append('' + msg + '') - }, - - _export: function (map) { - const geoJson = this._layer.toGeoJSON() - const blob = new window.Blob([JSON.stringify(geoJson)], { type: 'application/json' }) - FileSaver.saveAs(blob, 'export.geojson') - }, - - _import: function (map, file) { - if (file.name.slice(-3) === 'zip') { - const reader = new window.FileReader() - reader.onload = (e) => { - const buffer = e.target.result - shp(buffer).then((geoJson) => { - try { - const shape = L.geoJson(geoJson, { - style: this.options.polygonStyle - }) - this._addToMap(map, shape) - } catch (e) { - this._showUploadError(django.gettext('The uploaded file is not a valid shapefile.')) - } - }, (e) => this._showUploadError(django.gettext('The uploaded file is not a valid shapefile.')) - ).catch((e) => { - this._showUploadError(django.gettext('The uploaded file could not be imported.')) - }) - } - reader.readAsArrayBuffer(file) - } else if (file.name.slice(-4) === 'json') { - const reader = new window.FileReader() - reader.onload = (e) => { - try { - const geoJson = JSON.parse(e.target.result) - const shape = L.geoJson(geoJson, { - style: this.options.polygonStyle - }) - this._addToMap(map, shape) - } catch (e) { - this._showUploadError(django.gettext('The uploaded file is not a valid geojson file.')) - } - } - reader.readAsText(file, 'utf-8') - } else { - this._showUploadError(django.gettext('Invalid file format.')) - } - }, - - _addToMap: function (map, shape) { - $('#map-import-modal').modal('hide') - - this._layer.clearLayers() - shape.eachLayer((layer) => { - this._layer.addLayer(layer) - }) - map.fitBounds(this._layer.getBounds()) - map.fire(L.Draw.Event.EDITED) - } - }) - - $('[data-map="choose_polygon"]').each(function (i, e) { - const name = e.getAttribute('data-name') - const polygon = JSON.parse(e.getAttribute('data-polygon')) - const bbox = JSON.parse(e.getAttribute('data-bbox')) - - const map = maps.createMap(L, e, { - baseUrl: e.getAttribute('data-baseurl'), - useVectorMap: e.getAttribute('data-usevectormap'), - attribution: e.getAttribute('data-attribution'), - mapboxToken: e.getAttribute('data-mapbox-token'), - omtToken: e.getAttribute('data-omt-token'), - dragging: true, - scrollWheelZoom: false, - zoomControl: true, - minZoom: 2 - }) - - const polygonStyle = { - color: '#0076ae', - weight: 2, - opacity: 1, - fillOpacity: 0.2 - } - - let drawnItems - if (polygon) { - drawnItems = L.geoJson(polygon, { - style: polygonStyle - }) - if (drawnItems.getLayers().length > 0) { - map.fitBounds(drawnItems.getBounds()) - } else { - map.fitBounds(getBaseBounds(L, polygon, bbox)) - } - } else { - drawnItems = L.featureGroup() - map.fitBounds(getBaseBounds(L, polygon, bbox)) - } - drawnItems.addTo(map) - - map.addControl(new L.Control.Draw({ - edit: { - featureGroup: drawnItems, - edit: { - selectedPathOptions: { - maintainColor: true - } - } - }, - draw: { - polygon: { - shapeOptions: polygonStyle - }, - rectangle: { - shapeOptions: polygonStyle - }, - marker: false, - circlemarker: false, - polyline: false, - circle: false - } - })) - - map.addControl(new ImportControl(drawnItems, - { - polygonStyle: polygonStyle - } - )) - - map.on(L.Draw.Event.CREATED, function (event) { - const layer = event.layer - drawnItems.addLayer(layer) - const geoJson = drawnItems.toGeoJSON() - $('#id_' + name).val(JSON.stringify(geoJson)) - $('#id_' + name).trigger('change') - }) - - map.on(L.Draw.Event.EDITED, function (event) { - const geoJson = drawnItems.toGeoJSON() - $('#id_' + name).val(JSON.stringify(geoJson)) - $('#id_' + name).trigger('change') - }) - - map.on(L.Draw.Event.DELETED, function (event) { - const geoJson = drawnItems.toGeoJSON() - $('#id_' + name).val(JSON.stringify(geoJson)) - $('#id_' + name).trigger('change') - }) - - $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - map.invalidateSize().fitBounds(getBaseBounds(L, polygon, bbox)) - }) - - $('#select_' + name).on('change', function (event) { - const geoJson = event.target.value - if (geoJson) { - const shape = L.geoJson(JSON.parse(geoJson), { - style: polygonStyle - }) - - const isEmpty = drawnItems.getLayers().length === 0 - const msg = django.gettext('Do you want to load this preset and delete all the existing polygons?') - if (isEmpty || window.confirm(msg)) { - drawnItems.clearLayers() - shape.eachLayer(function (layer) { - drawnItems.addLayer(layer) - }) - map.fitBounds(drawnItems.getBounds()) - map.fire(L.Draw.Event.EDITED) - } - } - }) - }) -} - -document.addEventListener('DOMContentLoaded', init, false) -document.addEventListener('a4.embed.ready', init, false) diff --git a/meinberlin/assets/js/app.js b/meinberlin/assets/js/app.js index 55b5926d39..58733c346a 100644 --- a/meinberlin/assets/js/app.js +++ b/meinberlin/assets/js/app.js @@ -5,11 +5,13 @@ import 'shariff' import 'slick-carousel' import '../../apps/actions/assets/timestamps.js' -import '../../apps/maps/assets/map-address.js' import '../../apps/moderatorremark/assets/idea_remarks.js' import '../../apps/newsletters/assets/dynamic_fields.js' import '../../apps/dashboard/assets/init_accordeons_cookie.js' +// map search function +import 'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_address.js' + // expose react components import { comments as ReactComments, diff --git a/package.json b/package.json index 82a846c128..0c22b1ab7c 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "file-saver": "2.0.5", "immutability-helper": "3.1.1", "jest": "^27.3.1", - "jquery": "3.6.0", "leaflet": "1.7.1", "leaflet-draw": "1.0.4", "leaflet.markercluster": "github:liqd/Leaflet.markercluster#5ed89b26922c51083fc9632a2c01425b9261a0f5", @@ -51,7 +50,6 @@ "sass-planifolia": "0.6.0", "select2": "4.0.13", "shariff": "3.2.1", - "shpjs": "4.0.2", "slick-carousel": "github:liqd/slick#pm-2019-07-overwrites", "style-loader": "3.3.1", "tether": "2.0.0", diff --git a/webpack.common.js b/webpack.common.js index 826998392b..03f5f89817 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -99,7 +99,7 @@ module.exports = { 'leaflet/dist/leaflet.css', 'mapbox-gl/dist/mapbox-gl.css', 'leaflet-draw/dist/leaflet.draw.css', - './meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js' + 'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_choose_polygon_with_preset.js' ], dependOn: 'adhocracy4' }, From e9c30fc6af5292e8e296046b4fae0a1d263c5c60 Mon Sep 17 00:00:00 2001 From: phillimorland <5871230+phillimorland@users.noreply.github.com> Date: Thu, 9 Dec 2021 11:47:49 +0100 Subject: [PATCH 2/2] deps: update a4 hash --- package.json | 2 +- requirements/base.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0c22b1ab7c..eda15be4c6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@fortawesome/fontawesome-free": "5.15.4", "@testing-library/react-native": "^8.0.0", "acorn": "8.6.0", - "adhocracy4": "liqd/adhocracy4#83a7756ffbec42b5afac595073816aeaf0d930bf", + "adhocracy4": "liqd/adhocracy4#e03fe5a065c08d29b075f004b58f713e61557169", "autoprefixer": "10.4.0", "axios": "0.24.0", "babel-loader": "8.2.3", diff --git a/requirements/base.txt b/requirements/base.txt index 7b4e15ea60..3b692c85f1 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,5 @@ # A4 -git+git://github.com/liqd/adhocracy4.git@83a7756ffbec42b5afac595073816aeaf0d930bf#egg=adhocracy4 +git+git://github.com/liqd/adhocracy4.git@e03fe5a065c08d29b075f004b58f713e61557169#egg=adhocracy4 # Additional requirements bcrypt==3.2.0