From dd019b59aec67346e179869ce1dcae309f270d8d Mon Sep 17 00:00:00 2001 From: Cyrus Hiatt Date: Thu, 21 Nov 2024 17:08:05 -0800 Subject: [PATCH 1/5] Add maplibre dependency --- package-lock.json | 239 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 240 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ce10c5..c5727f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,8 @@ "name": "afrc", "license": "AGPL-3.0-only", "dependencies": { - "arches": "archesproject/arches#stable/7.6.2" + "arches": "archesproject/arches#stable/7.6.2", + "maplibre-gl": "^4.6.0" }, "devDependencies": { "arches-dev-dependencies": "archesproject/arches-dev-dependencies#stable/7.6.2" @@ -2610,6 +2611,44 @@ "node": ">=6.0.0" } }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz", + "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -4938,6 +4977,15 @@ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -5017,6 +5065,23 @@ "@types/node": "*" } }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", + "license": "MIT" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -5060,6 +5125,12 @@ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.16", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", @@ -5125,6 +5196,15 @@ "@types/node": "*" } }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", @@ -11470,6 +11550,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -11859,6 +11945,157 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, + "node_modules/maplibre-gl": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz", + "integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^20.3.1", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "global-prefix": "^4.0.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.3.0", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/maplibre-gl/node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==", + "license": "BSD-2-Clause" + }, + "node_modules/maplibre-gl/node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/maplibre-gl/node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "license": "MIT" + }, + "node_modules/maplibre-gl/node_modules/earcut": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.0.tgz", + "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/global-prefix": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "license": "MIT", + "dependencies": { + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/maplibre-gl/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/maplibre-gl/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/maplibre-gl/node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/maplibre-gl/node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", diff --git a/package.json b/package.json index fb8baa0..82144e5 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "vitest": "vitest --run --coverage" }, "dependencies": { - "arches": "archesproject/arches#stable/7.6.2" + "arches": "archesproject/arches#stable/7.6.2", + "maplibre-gl": "^4.6.0" }, "devDependencies": { "arches-dev-dependencies": "archesproject/arches-dev-dependencies#stable/7.6.2" From efe92e21169a418a9b39fc0fd14d507ffebf0961 Mon Sep 17 00:00:00 2001 From: Cyrus Hiatt Date: Thu, 21 Nov 2024 17:36:37 -0800 Subject: [PATCH 2/5] formatting --- afrc/celery.py | 6 +++--- afrc/urls.py | 2 +- afrc/wsgi.py | 9 ++++++--- manage.py | 4 ++-- tests/search_indexes/sample_index_tests.py | 21 +++++++++++++++++---- tests/test_settings.py | 9 ++------- 6 files changed, 31 insertions(+), 20 deletions(-) diff --git a/afrc/celery.py b/afrc/celery.py index 03435be..b9b1bd2 100644 --- a/afrc/celery.py +++ b/afrc/celery.py @@ -7,7 +7,7 @@ if platform.system().lower() == "windows": os.environ.setdefault("FORKED_BY_MULTIPROCESSING", "1") -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'afrc.settings') -app = Celery('afrc') -app.config_from_object('django.conf:settings', namespace='CELERY') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "afrc.settings") +app = Celery("afrc") +app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() diff --git a/afrc/urls.py b/afrc/urls.py index b541b3f..4f9c69b 100644 --- a/afrc/urls.py +++ b/afrc/urls.py @@ -8,7 +8,7 @@ ] # Ensure Arches core urls are superseded by project-level urls -urlpatterns.append(path('', include('arches.urls'))) +urlpatterns.append(path("", include("arches.urls"))) # Adds URL pattern to serve media files during development urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/afrc/wsgi.py b/afrc/wsgi.py index ccdeff4..31a99f7 100644 --- a/afrc/wsgi.py +++ b/afrc/wsgi.py @@ -1,4 +1,4 @@ -''' +""" ARCHES - a program developed to inventory and manage immovable cultural heritage. Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund @@ -14,11 +14,12 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -''' +""" import os import sys import inspect + path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) if path not in sys.path: @@ -27,10 +28,12 @@ # reverting back to the old style of setting the DJANGO_SETTINGS_MODULE env variable # refer to the following blog post under the heading "Leaking of process environment variables." # http://blog.dscpl.com.au/2012/10/requests-running-in-wrong-django.html -os.environ['DJANGO_SETTINGS_MODULE'] = "afrc.settings" +os.environ["DJANGO_SETTINGS_MODULE"] = "afrc.settings" from django.core.wsgi import get_wsgi_application + application = get_wsgi_application() from arches.app.models.system_settings import settings + settings.update_from_db() diff --git a/manage.py b/manage.py index ecdc91e..44a8cd2 100644 --- a/manage.py +++ b/manage.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -''' +""" ARCHES - a program developed to inventory and manage immovable cultural heritage. Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund @@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -''' +""" import os diff --git a/tests/search_indexes/sample_index_tests.py b/tests/search_indexes/sample_index_tests.py index 059e876..c44a1b7 100644 --- a/tests/search_indexes/sample_index_tests.py +++ b/tests/search_indexes/sample_index_tests.py @@ -4,21 +4,34 @@ from afrc.search_indexes.sample_index import SampleIndex from django.test import TestCase + class TestSampleIndex(TestCase): def test_prepare_index(self): sample_index = SampleIndex(index_name="Sample Index") sample_index.prepare_index() - expected_index_metadata = {"mappings": {"properties": {"tile_count": {"type": "keyword"}, "graph_id": {"type": "keyword"}}}} + expected_index_metadata = { + "mappings": { + "properties": { + "tile_count": {"type": "keyword"}, + "graph_id": {"type": "keyword"}, + } + } + } self.assertEqual(sample_index.index_metadata, expected_index_metadata) def test_get_documents_to_index(self): sample_index = SampleIndex(index_name="Sample Index") - + mock_resourceinstance = Mock(graph_id="test_graph_id") mock_tiles = [Mock(), Mock(), Mock()] # Mock tiles list - documents, doc_id = sample_index.get_documents_to_index(mock_resourceinstance, mock_tiles) + documents, doc_id = sample_index.get_documents_to_index( + mock_resourceinstance, mock_tiles + ) - self.assertEqual(documents, {"tile_count": len(mock_tiles), "graph_id": mock_resourceinstance.graph_id}) + self.assertEqual( + documents, + {"tile_count": len(mock_tiles), "graph_id": mock_resourceinstance.graph_id}, + ) self.assertEqual(doc_id, str(mock_resourceinstance.resourceinstanceid)) diff --git a/tests/test_settings.py b/tests/test_settings.py index c80a2ab..239906b 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -43,14 +43,9 @@ "PASSWORD": "postgis", "PORT": "5432", "POSTGIS_TEMPLATE": "template_postgis", - "TEST": { - "CHARSET": None, - "COLLATION": None, - "MIRROR": None, - "NAME": None - }, + "TEST": {"CHARSET": None, "COLLATION": None, "MIRROR": None, "NAME": None}, "TIME_ZONE": None, - "USER": "postgres" + "USER": "postgres", } } From 0e6deefccbac7fd7807609fd65b73f09e2af798e Mon Sep 17 00:00:00 2001 From: Cyrus Hiatt Date: Thu, 21 Nov 2024 17:40:15 -0800 Subject: [PATCH 3/5] Add necessary endpoints --- afrc/src/afrc/Search/api.ts | 66 +++++++++++++++++ afrc/templates/arches_urls.htm | 15 ++++ afrc/urls.py | 6 ++ afrc/views/map_api.py | 132 +++++++++++++++++++++++++++++++++ afrc/views/settings_api.py | 15 ++++ 5 files changed, 234 insertions(+) create mode 100644 afrc/src/afrc/Search/api.ts create mode 100644 afrc/templates/arches_urls.htm create mode 100644 afrc/views/map_api.py create mode 100644 afrc/views/settings_api.py diff --git a/afrc/src/afrc/Search/api.ts b/afrc/src/afrc/Search/api.ts new file mode 100644 index 0000000..ec01685 --- /dev/null +++ b/afrc/src/afrc/Search/api.ts @@ -0,0 +1,66 @@ +import arches from "arches"; +import Cookies from "js-cookie"; + +import type { FeatureCollection } from "geojson"; + +function getToken() { + const token = Cookies.get("csrftoken"); + if (!token) { + throw new Error("Missing csrftoken"); + } + return token; +} + +export const fetchGeoJSONBounds = async (features: FeatureCollection) => { + const response = await fetch(arches.urls["api-geojson-bounds"], { + method: "POST", + headers: { "X-CSRFTOKEN": getToken() }, + body: JSON.stringify(features), + }); + try { + const responseJson = await response.json(); + if (response.ok) { + return responseJson; + } + throw new Error(responseJson.message); + } catch (error) { + throw new Error((error as Error).message || response.statusText); + } +}; + +export const fetchDrawnFeaturesBuffer = async (features: FeatureCollection) => { + const response = await fetch(arches.urls["api-feature-buffer"], { + method: "POST", + headers: { "X-CSRFTOKEN": getToken() }, + body: JSON.stringify({ features }), + }); + try { + const responseJson = await response.json(); + if (response.ok) { + return responseJson; + } + throw new Error(responseJson.message); + } catch (error) { + throw new Error((error as Error).message || response.statusText); + } +}; + +export const createRequest = (url: string) => { + return async () => { + const response = await fetch(url); + try { + const responseJson = await response.json(); + if (response.ok) { + return responseJson; + } + throw new Error(responseJson.message); + } catch (error) { + throw new Error((error as Error).message || response.statusText); + } + }; +}; + +export const fetchSettings = createRequest(arches.urls["api-settings"]); +export const fetchMapData = createRequest(arches.urls["api-map-data"]); + + diff --git a/afrc/templates/arches_urls.htm b/afrc/templates/arches_urls.htm new file mode 100644 index 0000000..5eef4d9 --- /dev/null +++ b/afrc/templates/arches_urls.htm @@ -0,0 +1,15 @@ +{% extends "arches_urls.htm" %} + +{% load static %} +{% load l10n %} +{% load i18n %} + +{% block arches_urls %} +{{ block.super }} +
+{% endblock arches_urls %} diff --git a/afrc/urls.py b/afrc/urls.py index 4f9c69b..211ce08 100644 --- a/afrc/urls.py +++ b/afrc/urls.py @@ -2,9 +2,15 @@ from django.conf.urls.static import static from django.conf.urls.i18n import i18n_patterns from django.urls import include, path +from afrc.views.settings_api import SettingsAPI +from afrc.views.map_api import MapDataAPI, FeatureBufferAPI, GeoJSONBoundsAPI urlpatterns = [ # project-level urls + path("api-settings", SettingsAPI.as_view(), name="api-settings"), + path("api-map-data", MapDataAPI.as_view(), name="api-map-data"), + path("api-feature-buffer", FeatureBufferAPI.as_view(), name="api-feature-buffer"), + path("api-geojson-bounds", GeoJSONBoundsAPI.as_view(), name="api-geojson-bounds"), ] # Ensure Arches core urls are superseded by project-level urls diff --git a/afrc/views/map_api.py b/afrc/views/map_api.py new file mode 100644 index 0000000..24452b9 --- /dev/null +++ b/afrc/views/map_api.py @@ -0,0 +1,132 @@ +import json +from http import HTTPStatus + +from django.utils.translation import gettext as _ +from django.views.generic import View + +from arches.app.datatypes.datatypes import DataTypeFactory +from arches.app.models import models +from arches.app.models.concept import Concept +from arches.app.models.system_settings import settings +from arches.app.search.search_engine_factory import SearchEngineFactory +from arches.app.search.mappings import RESOURCES_INDEX +from arches.app.utils.file_validator import FileValidator +from arches.app.utils.response import JSONResponse, JSONErrorResponse +from arches.app.utils.permission_backend import user_can_read_map_layers +from arches.app.utils.geo_utils import GeoUtils + + +class MapDataAPI(View): + def get(self, request): + map_layers = user_can_read_map_layers(request.user) + map_sources = list(models.MapSource.objects.all()) + for map_source in map_sources: + if "tiles" in map_source.source: + if not map_source.source["tiles"][0].startswith("http"): + source = "{}{}".format( + settings.PUBLIC_SERVER_ADDRESS, map_source.source["tiles"][0] + ) + map_source.source["tiles"][0] = source + + return JSONResponse( + { + "map_layers": map_layers, + "map_sources": map_sources, + # "resource_map_layers": resource_map_layers, + # "resource_map_sources": resource_map_sources, + } + ) + + +class FeatureGeometriesAPI(View): + def get(self, request): + resource_instance_id = request.GET.get("resource_instance_id") + + se = SearchEngineFactory().create() + document = se.search(index=RESOURCES_INDEX, id=resource_instance_id) + + geo_utils = GeoUtils() + + for geometry in document["_source"]["geometries"]: + geometry["centroid"] = geo_utils.get_centroid(geometry["geom"]) + + return JSONResponse(document["_source"]["geometries"]) + + +class ResourceInstancesWithinGeometryAndBufferAPI(View): + def post(self, request): + data = json.loads(request.body) + + geo_utils = GeoUtils() + + intersection_and_difference_of_feature_collections = ( + geo_utils.get_intersection_and_difference_of_feature_collections( + data["drawnFeatures"], data["bufferedFeatures"] + ) + ) + + return JSONResponse( + { + "resources_intersecting_drawn_features": geo_utils.get_resource_instances_within_feature_collection( + data["drawnFeatures"] + ), + "resources_intersecting_buffered_features": geo_utils.get_resource_instances_within_feature_collection( + data["bufferedFeatures"] + ), + "resources_intersecting_buffers": geo_utils.get_resource_instances_within_feature_collection( + { + "type": "FeatureCollection", + "features": [ + feature + for feature in intersection_and_difference_of_feature_collections[ + "features" + ] + if feature["properties"].get("feature_type") + == "difference_from_second" + ], + } + ), + } + ) + + +class FeatureBufferAPI(View): + def post(self, request): + data = json.loads(request.body) + features = data["features"] + geo_utils = GeoUtils() + return JSONResponse(features) + + +class GeoJSONBoundsAPI(View): + def post(self, request): + geo_utils = GeoUtils() + + return JSONResponse(geo_utils.get_bounds_from_geojson(json.loads(request.body))) + + +class GeoJSONParseIntoCollectionAPI(View): + """Reshape arbitrary GeoJSON into a feature collection. The reshaping could + be done without a trip to the server, but doing so allows us to enforce + consistent (project-wide) file validation rules. + """ + + def post(self, request): + feature_file = request.FILES.get("geojson") + if not feature_file: + return JSONErrorResponse( + message=_("A geojson file is required."), + status=HTTPStatus.BAD_REQUEST, + ) + + validator = FileValidator() + if file_type_errors := validator.validate_file_type(feature_file): + return JSONErrorResponse( + message="\n".join(file_type_errors), + status=HTTPStatus.BAD_REQUEST, + ) + + feature_file.seek(0) + geojson_obj = json.load(feature_file) + + return JSONResponse(GeoUtils.shape_geojson_as_feature_collection(geojson_obj)) diff --git a/afrc/views/settings_api.py b/afrc/views/settings_api.py new file mode 100644 index 0000000..a3fe2ec --- /dev/null +++ b/afrc/views/settings_api.py @@ -0,0 +1,15 @@ +from django.views.generic import View +from django.utils.translation import get_language, get_language_bidi + +from arches.app.utils.response import JSONResponse +from arches.app.models.system_settings import settings + + +class SettingsAPI(View): + def get(self, request): + return JSONResponse( + { + "ACTIVE_LANGUAGE": get_language(), + "ACTIVE_LANGUAGE_DIRECTION": "rtl" if get_language_bidi() else "ltr", + } + ) From 175c54d03f6c8122c7e855fa51b024d29a2be4e2 Mon Sep 17 00:00:00 2001 From: Cyrus Hiatt Date: Thu, 21 Nov 2024 17:42:02 -0800 Subject: [PATCH 4/5] Add map component and child components --- .../InteractiveMap/InteractiveMap.vue | 141 ++++++ .../components/BasemapControls.vue | 51 ++ .../components/InteractionsDrawer.vue | 159 +++++++ .../components/MapComponent.vue | 447 ++++++++++++++++++ .../components/MapFilter/MapFilter.vue | 55 +++ .../MapFilter/components/BufferControls.vue | 105 ++++ .../MapFilter/components/DrawControls.vue | 121 +++++ .../MapFilter/components/FeatureUploader.vue | 81 ++++ .../components/OverlayControls.vue | 35 ++ .../PopupContainer/PopupContainer.vue | 69 +++ .../components/PopupContent.vue | 20 + afrc/src/afrc/Search/constants.ts | 34 ++ afrc/src/afrc/Search/types.ts | 99 ++++ afrc/src/afrc/declarations.d.ts | 1 + 14 files changed, 1418 insertions(+) create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/InteractiveMap.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/BasemapControls.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/InteractionsDrawer.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/MapComponent.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/MapFilter.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/BufferControls.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/DrawControls.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/FeatureUploader.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/OverlayControls.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/PopupContainer.vue create mode 100644 afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/components/PopupContent.vue create mode 100644 afrc/src/afrc/Search/constants.ts diff --git a/afrc/src/afrc/Search/components/InteractiveMap/InteractiveMap.vue b/afrc/src/afrc/Search/components/InteractiveMap/InteractiveMap.vue new file mode 100644 index 0000000..14f4cd5 --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/InteractiveMap.vue @@ -0,0 +1,141 @@ + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/BasemapControls.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/BasemapControls.vue new file mode 100644 index 0000000..af9bd2b --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/BasemapControls.vue @@ -0,0 +1,51 @@ + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/InteractionsDrawer.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/InteractionsDrawer.vue new file mode 100644 index 0000000..4350d5a --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/InteractionsDrawer.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/MapComponent.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/MapComponent.vue new file mode 100644 index 0000000..b3ba9cc --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/MapComponent.vue @@ -0,0 +1,447 @@ + + + + + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/MapFilter.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/MapFilter.vue new file mode 100644 index 0000000..7ad5041 --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/MapFilter.vue @@ -0,0 +1,55 @@ + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/BufferControls.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/BufferControls.vue new file mode 100644 index 0000000..13c8547 --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/BufferControls.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/DrawControls.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/DrawControls.vue new file mode 100644 index 0000000..3a7bf3d --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/DrawControls.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/FeatureUploader.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/FeatureUploader.vue new file mode 100644 index 0000000..cc26a1a --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/MapFilter/components/FeatureUploader.vue @@ -0,0 +1,81 @@ + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/OverlayControls.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/OverlayControls.vue new file mode 100644 index 0000000..2b057f7 --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/OverlayControls.vue @@ -0,0 +1,35 @@ + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/PopupContainer.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/PopupContainer.vue new file mode 100644 index 0000000..9605d0d --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/PopupContainer.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/components/PopupContent.vue b/afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/components/PopupContent.vue new file mode 100644 index 0000000..614cfc9 --- /dev/null +++ b/afrc/src/afrc/Search/components/InteractiveMap/components/PopupContainer/components/PopupContent.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/afrc/src/afrc/Search/constants.ts b/afrc/src/afrc/Search/constants.ts new file mode 100644 index 0000000..3bad3a0 --- /dev/null +++ b/afrc/src/afrc/Search/constants.ts @@ -0,0 +1,34 @@ +import type { InjectionKey } from "vue"; + +import type { UserRefAndSetter } from "@/afrc/Search/types.ts"; + +export const ACTIVE_LANGUAGE_DIRECTION = "ACTIVE_LANGUAGE_DIRECTION"; +export const ANONYMOUS = "anonymous"; +export const BUFFER_LAYER_ID = "buffer-layer"; +export const CLICK_EVENT = "click"; +export const DEFAULT_ERROR_TOAST_LIFE = 8000; +export const DIRECT_SELECT = "direct_select"; +export const DRAW_CREATE_EVENT = "draw.create"; +export const DRAW_DELETE_EVENT = "draw.delete"; +export const DRAW_UPDATE_EVENT = "draw.update"; +export const DRAW_SELECTION_CHANGE_EVENT = "draw.selectionchange"; +export const DRAW_LINE_STRING = "draw_line_string"; +export const DRAW_POINT = "draw_point"; +export const DRAW_POLYGON = "draw_polygon"; +export const ERROR = "error"; +export const GEOMETRY_TYPE_LINESTRING = "LineString"; +export const GEOMETRY_TYPE_POINT = "Point"; +export const GEOMETRY_TYPE_POLYGON = "Polygon"; +export const IDLE = "idle"; +export const LEFT = "left"; +export const LINE = "line"; +export const LTR = "ltr"; +export const METERS = "meters"; +export const POINT = "point"; +export const POLYGON = "polygon"; +export const RIGHT = "right"; +export const SIMPLE_SELECT = "simple_select"; +export const STYLE_LOAD_EVENT = "style.load"; +export const TOP_LEFT = "top-left"; +export const TOP_RIGHT = "top-right"; +export const USER_KEY = Symbol() as InjectionKey; diff --git a/afrc/src/afrc/Search/types.ts b/afrc/src/afrc/Search/types.ts index c1d3a03..fddc722 100644 --- a/afrc/src/afrc/Search/types.ts +++ b/afrc/src/afrc/Search/types.ts @@ -1,2 +1,101 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any export type GenericObject = { [key: string]: any }; +import type { Component, Ref } from "vue"; +import type { Feature, FeatureCollection, Geometry, GeoJSON } from "geojson"; +import type { FilterSpecification } from "maplibre-gl"; + +export interface MapInteractionItem { + name: string; + header: string; + component: Component; + icon: string; +} + +export interface DrawEvent { + features: Feature[]; +} + +export interface Basemap { + id: string; + name: string; + value: string; + active: boolean; +} +export interface MapLayer { + activated?: boolean; + addtomap: boolean; + centerx?: number | null; + centery?: number | null; + icon: string; + id: number; + isoverlay: boolean; + ispublic?: boolean; + layer_json?: string; + layerdefinitions: LayerDefinition[]; + legend?: string | null; + maplayerid?: string; + name: string; + searchonly?: boolean; + sortorder?: number; + visible: boolean; + zoom?: number | null; + nodeid?: string; +} + +export interface LayerDefinition { + id: string; + type: string; + source?: string; + "source-layer"?: string; + layout?: Record; + paint?: Record; + filter?: FilterSpecification; + minzoom?: number; + maxzoom?: number; +} + +export interface MapSource { + id: number; + name: string; + source: { + type: string; + url: string; + data?: GeoJSON; + tileSize?: number; + coordinates?: [number, number]; + }; + source_json: string; +} + +export interface Settings { + ACTIVE_LANGUAGE: string; + ACTIVE_LANGUAGE_DIRECTION: string; + ARCGIS_TOKEN: string; +} + +export interface WithinGeometryAndBufferRequestData { + drawnFeatures: FeatureCollection; + bufferedFeatures: FeatureCollection; +} + +export interface BufferRequestData { + features: FeatureCollection; + distance: number; + units: string; +} + +export interface Buffer { + distance: number; + units: string; +} + +export interface User { + first_name: string; + last_name: string; + username: string; +} + +export interface UserRefAndSetter { + user: Ref; + setUser: (userToSet: User | null) => void; +} diff --git a/afrc/src/afrc/declarations.d.ts b/afrc/src/afrc/declarations.d.ts index 6e9a067..952a3ab 100644 --- a/afrc/src/afrc/declarations.d.ts +++ b/afrc/src/afrc/declarations.d.ts @@ -2,3 +2,4 @@ // Module homepage on npmjs.com uses logos "TS" or "DT" to indicate if typed import("@/arches/declarations.d.ts"); +declare module "@mapbox/mapbox-gl-draw"; From 817273fcb4ebb19b9e0950a1c4bb8914a8b9ecd6 Mon Sep 17 00:00:00 2001 From: Cyrus Hiatt Date: Thu, 21 Nov 2024 17:42:33 -0800 Subject: [PATCH 5/5] Add map into SearchPage component --- afrc/src/afrc/Search/SearchPage.vue | 64 +++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/afrc/src/afrc/Search/SearchPage.vue b/afrc/src/afrc/Search/SearchPage.vue index 1da5c30..136efeb 100644 --- a/afrc/src/afrc/Search/SearchPage.vue +++ b/afrc/src/afrc/Search/SearchPage.vue @@ -1,19 +1,38 @@ @@ -163,7 +212,16 @@ onMounted(() => { - +
+ +