Skip to content

Commit

Permalink
Merge pull request #2 from archesproject/add_ref_coll_layer
Browse files Browse the repository at this point in the history
Add reference collection overlay
  • Loading branch information
apeters authored Nov 27, 2024
2 parents f5d0e99 + 1fd9f33 commit 355f45d
Show file tree
Hide file tree
Showing 9 changed files with 1,728 additions and 19 deletions.
1,608 changes: 1,608 additions & 0 deletions afrc/migrations/0001_initial.py

Large diffs are not rendered by default.

Empty file added afrc/migrations/__init__.py
Empty file.
15 changes: 13 additions & 2 deletions afrc/src/afrc/Search/SearchPage.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { onMounted, ref, watch, provide } from "vue";
import type { Ref } from "vue";
import { useGettext } from "vue3-gettext";
Expand Down Expand Up @@ -30,10 +30,13 @@ const showMap = ref(false);
const basemaps: Ref<Basemap[]> = ref([]);
const overlays: Ref<MapLayer[]> = ref([]);
const sources: Ref<MapSource[]> = ref([]);
const resultsSelected: Ref<string[]> = ref([]);
const dataLoaded = ref(false);
const toast = useToast();
const { $gettext } = useGettext();
provide("resultsSelected", resultsSelected);
watch(queryString, () => {
doQuery();
});
Expand Down Expand Up @@ -97,6 +100,7 @@ const doQuery = function () {
console.log(data);
searchResults.value = data.results.hits.hits;
resultsCount.value = data.total_results;
resultsSelected.value = [];
});
// self.updateRequest = $.ajax({
Expand Down Expand Up @@ -150,7 +154,13 @@ async function fetchSystemMapData() {
);
layers.filter((layer: MapLayer) => !layer.isoverlay).forEach((layer: MapLayer) => {
basemaps.value.push({name: layer.name, active: layer.addtomap, value: layer.name, id: layer.name});
basemaps.value.push({
name: layer.name,
active: layer.addtomap,
value: layer.name,
id: layer.name,
url: "https://tiles.openfreemap.org/styles/positron"
});
});
sources.value = mapData.map_sources;
Expand Down Expand Up @@ -220,6 +230,7 @@ onMounted(async () =>{
:basemaps="basemaps"
:overlays="overlays"
:sources="sources"
:include-drawer="false"
/>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const props = defineProps<{
overlays: MapLayer[];
basemaps: Basemap[];
sources: MapSource[];
includeDrawer: boolean;
}>();
const map: Ref<Map | null> = ref(null);
Expand Down Expand Up @@ -66,11 +67,12 @@ const basemap: Ref<Basemap | null> = ref(null);
const selectedDrawnFeature: Ref<Feature | null> = ref(null);
const emits = defineEmits(["drawnFeatureSelected", "drawnFeaturesUpdated"]);
console.log(props);
provide("overlays", props.overlays);
provide("basemaps", props.basemaps);
provide("selectedDrawnFeature", selectedDrawnFeature);
watch(
() => props.basemaps,
(updatedBasemaps) => {
Expand All @@ -90,7 +92,6 @@ onMounted(async () => {
async function fetchSystemSettings() {
try {
console.log("Fetching settings");
settings.value = await fetchSettings();
} catch (error) {
toast.add({
Expand Down Expand Up @@ -132,7 +133,7 @@ function updateSelectedDrawnFeature(feature: Feature) {
@drawn-feature-selected="updateSelectedDrawnFeature"
/>
<InteractionsDrawer
v-if="map"
v-if="map && includeDrawer"
:map="map"
:settings="settings"
:items="mapInteractionItems"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { onMounted, ref, useTemplateRef, watch } from "vue";
import { onMounted, ref, useTemplateRef, watch, inject } from "vue";
import MapboxDraw from "@mapbox/mapbox-gl-draw";
import maplibregl from "maplibre-gl";
Expand Down Expand Up @@ -88,6 +88,8 @@ const {
drawnFeaturesBuffer,
} = props;
let resultsSelected = inject("resultsSelected") as Ref<string[]>;
const emits = defineEmits([
"mapInitialized",
"drawnFeatureSelected",
Expand Down Expand Up @@ -124,6 +126,15 @@ watch(
{ deep: true },
);
watch(
() => resultsSelected,
(selected) => {
if (selected) {
updateFeatureSelection(selected as Ref<string[]>);
}
}, {deep: true}
);
watch(
() => drawnFeaturesBuffer,
() => {
Expand Down Expand Up @@ -185,6 +196,28 @@ async function fitBoundsOfFeatures(features: FeatureCollection) {
});
}
function updateFeatureSelection(selected: Ref<string[]>) {
const layers: Array<string> = [];
overlays.forEach((overlay) => {
layers.push(...overlay.layerdefinitions.map(
(layerDefinition) => layerDefinition.id,
));
});
const features = map.value!.queryRenderedFeatures({layers:layers});
features.forEach(feature => {
const featureSelected = selected.value.includes(feature.properties?.resourceinstanceid);
map.value!.setFeatureState(
{
source: "referencecollections",
sourceLayer: "referencecollections",
id: feature.id,
},
{ selected: featureSelected }
);
});
}
function addBufferLayer() {
map.value!.addSource(BUFFER_LAYER_ID, {
type: "geojson",
Expand Down Expand Up @@ -250,17 +283,15 @@ function createMap() {
map.value = new maplibregl.Map({
container: mapContainer.value!,
zoom: 10,
center: [-118.805, 34.027],
center: [-122.105, 37.027],
});
emits("mapInitialized", map.value);
}
function updateBasemap(basemap: Basemap) {
console.log(basemap);
console.log(overlays);
map.value!.setStyle(
'https://demotiles.maplibre.org/style.json',
basemap.url,
);
map.value!.once(IDLE, () => {
Expand Down Expand Up @@ -334,15 +365,12 @@ function addOverlayToMap(overlay: MapLayer) {
);
}
}
if (!map.value!.getLayer(layerDefinition.id)) {
map.value!.addLayer(layerDefinition as AddLayerObject);
}
});
if (
overlay.maplayerid === "8914d88e-ac00-4140-a7d8-81893099b0ad" // Cultural Resource - Primary
) {
if (overlay.maplayerid && resultsSelected) {
resourceOverlaysClickHandlers[overlay.maplayerid] = function (
e: MapMouseEvent,
) {
Expand All @@ -355,6 +383,11 @@ function addOverlayToMap(overlay: MapLayer) {
popupContainerRerenderKey.value += 1;
clickedCoordinates.value = [e.lngLat.lng, e.lngLat.lat];
clickedFeatures.value = features;
resultsSelected.value = [];
const uniqueResourceIds = new Set(features.map((feature) => feature.properties?.resourceinstanceid as string));
resultsSelected.value = Array.from(uniqueResourceIds);
} else {
resultsSelected.value = [];
}
};
Expand Down
26 changes: 25 additions & 1 deletion afrc/src/afrc/Search/components/SearchResultItem.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
<script setup lang="ts">
import Button from "primevue/button";
import { inject } from "vue";
import type { Ref } from "vue";
const resultsSelected = inject("resultsSelected") as Ref<string[]>;
const props = defineProps({
searchResult: {
type: Object,
required: true,
},
});
function selectResult(resourceid: string) {
resultsSelected.value = [resourceid];
}
function clearResult() {
resultsSelected.value = [];
}
</script>

<template>
<section class="result">
<section
class="result"
:class="{
hovered: resultsSelected.includes(searchResult._source.resourceinstanceid)
}"
@mouseenter="selectResult(searchResult._source.resourceinstanceid)"
@mouseleave="clearResult"
>
<div class="image-placeholder">
<img src="https://picsum.photos/160" />
</div>
Expand Down Expand Up @@ -44,6 +64,10 @@ const props = defineProps({
display: flex;
flex-direction: row;
}
.result.hovered {
background-color: rgb(239 245 252);
border: 1px solid rgb(139 145 252);
}
.result .result-content {
height: 10rem;
overflow: hidden;
Expand Down
1 change: 1 addition & 0 deletions afrc/src/afrc/Search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Basemap {
name: string;
value: string;
active: boolean;
url: string;
}
export interface MapLayer {
activated?: boolean;
Expand Down
7 changes: 5 additions & 2 deletions afrc/urls.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from django.conf import settings
from django.conf.urls.static import static
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.urls import include, path, re_path
from afrc.views.settings_api import SettingsAPI
from afrc.views.map_api import MapDataAPI, FeatureBufferAPI, GeoJSONBoundsAPI
from afrc.views.map_api import MapDataAPI, FeatureBufferAPI, GeoJSONBoundsAPI, ReferenceCollectionMVT

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"),
re_path(r"^api-reference-collection-mvt/(?P<zoom>[0-9]+|\{z\})/(?P<x>[0-9]+|\{x\})/(?P<y>[0-9]+|\{y\}).pbf$",
ReferenceCollectionMVT.as_view(),
name="api-reference-collection-mvt"),
]

# Ensure Arches core urls are superseded by project-level urls
Expand Down
32 changes: 30 additions & 2 deletions afrc/views/map_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

from django.utils.translation import gettext as _
from django.views.generic import View
from django.http import HttpResponse
from django.db import connection

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
Expand Down Expand Up @@ -130,3 +130,31 @@ def post(self, request):
geojson_obj = json.load(feature_file)

return JSONResponse(GeoUtils.shape_geojson_as_feature_collection(geojson_obj))

class ReferenceCollectionMVT(View):
def get(self, request, zoom, x, y):
system_settings_resourceid = settings.SYSTEM_SETTINGS_RESOURCE_ID
with connection.cursor() as cursor:
result = cursor.execute(
"""
SELECT ST_AsMVT(tile, 'referencecollections', 4096, 'geom', 'id')
FROM (
SELECT
id,
resourceinstanceid,
nodeid,
ST_AsMVTGeom(
geom,
TileBBox(%s, %s, %s, 3857),
4096,
256,
false
) geom
FROM geojson_geometries gg
WHERE resourceinstanceid != %s and (gg.geom && ST_TileEnvelope(%s, %s, %s, margin => (64.0 / 4096)))
) tile
""",
[zoom, x, y, system_settings_resourceid, zoom, x, y],
)
result = bytes(cursor.fetchone()[0]) if result is None else result
return HttpResponse(result, content_type="application/x-protobuf")

0 comments on commit 355f45d

Please sign in to comment.