Added "view image" button, if a wikimedia commons tag is present on node. (#78)

* Added "view image" button, if a wikimedia commons tag is present.

* show thumbnail, prevent 'LPR LPR'

---------

Co-authored-by: Will Freeman <hohosanta@me.com>
This commit is contained in:
Themis Megas
2026-02-04 12:55:25 -06:00
committed by GitHub
parent 72b0305b9b
commit 4cf471535b
5 changed files with 42 additions and 7 deletions

View File

@@ -20,7 +20,7 @@ docker buildx build --platform linux/arm64 -t $ECR_REPO_URL:latest --load .
docker push $ECR_REPO_URL:latest docker push $ECR_REPO_URL:latest
# update lambda function # update lambda function
# export AWS_PAGER="" export AWS_PAGER=""
# aws lambda update-function-code --function-name alpr_cache --image-uri $ECR_REPO_URL:latest aws lambda update-function-code --function-name alpr_cache --image-uri $ECR_REPO_URL:latest
echo "Deployed!" echo "Deployed!"

View File

@@ -38,7 +38,8 @@ WHITELISTED_TAGS = [
"camera:direction", "camera:direction",
"surveillance:brand", "surveillance:brand",
"surveillance:operator", "surveillance:operator",
"surveillance:manufacturer" "surveillance:manufacturer",
"wikimedia_commons"
] ]
def get_all_nodes(): def get_all_nodes():

View File

@@ -12,6 +12,7 @@
"@unhead/vue": "^1.11.14", "@unhead/vue": "^1.11.14",
"axios": "^1.7.7", "axios": "^1.7.7",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"js-md5": "^0.8.3",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"pinia": "^2.3.0", "pinia": "^2.3.0",
"vue": "^3.4.29", "vue": "^3.4.29",
@@ -1594,6 +1595,12 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/js-md5": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": { "node_modules/json-parse-even-better-errors": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz",

View File

@@ -16,6 +16,7 @@
"@unhead/vue": "^1.11.14", "@unhead/vue": "^1.11.14",
"axios": "^1.7.7", "axios": "^1.7.7",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"js-md5": "^0.8.3",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"pinia": "^2.3.0", "pinia": "^2.3.0",
"vue": "^3.4.29", "vue": "^3.4.29",

View File

@@ -2,9 +2,9 @@
<v-sheet min-width="240"> <v-sheet min-width="240">
<!-- TODO: if a field is unknown, prompt user to edit it --> <!-- TODO: if a field is unknown, prompt user to edit it -->
<div class="position-relative"> <div class="position-relative">
<v-img v-if="imageUrl" cover width="100%" height="150px" :src="imageUrl" class="rounded mt-5" /> <v-img v-if="imageUrl" cover width="100%" height="150px" :src="imageUrl" class="rounded mt-5" position="top" />
<div v-if="imageUrl" class="position-absolute bottom-0 left-0 right-0 text-center text-white text-caption" style="background: rgba(0, 0, 0, 0.5);"> <div v-if="imageUrl" class="position-absolute bottom-0 left-0 right-0 text-center text-white text-caption" style="background: rgba(0, 0, 0, 0.5);">
{{ manufacturer }} LPR {{ manufacturer }} {{ manufacturer.endsWith(' LPR') ? '' : ' LPR' }}
</div> </div>
</div> </div>
<v-list density="compact" class="my-2"> <v-list density="compact" class="my-2">
@@ -45,6 +45,7 @@
<div class="text-center"> <div class="text-center">
<v-btn target="_blank" size="x-small" :href="osmNodeLink(props.alpr.id)" variant="text" color="grey"><v-icon start>mdi-open-in-new</v-icon>View on OSM</v-btn> <v-btn target="_blank" size="x-small" :href="osmNodeLink(props.alpr.id)" variant="text" color="grey"><v-icon start>mdi-open-in-new</v-icon>View on OSM</v-btn>
<v-btn v-if="wikimediaImages" target="_blank" size="x-small" :href="wikimediaImages.wiki" variant="text" color="grey"><v-icon start>mdi-image</v-icon>View image</v-btn>
</div> </div>
</v-sheet> </v-sheet>
</template> </template>
@@ -55,6 +56,7 @@ import type { PropType } from 'vue';
import type { ALPR } from '@/types'; import type { ALPR } from '@/types';
import { VIcon, VList, VSheet, VListItem, VBtn, VImg, VListItemSubtitle, VDivider } from 'vuetify/components'; import { VIcon, VList, VSheet, VListItem, VBtn, VImg, VListItemSubtitle, VDivider } from 'vuetify/components';
import { useVendorStore } from '@/stores/vendorStore'; import { useVendorStore } from '@/stores/vendorStore';
import { md5 } from 'js-md5';
const props = defineProps({ const props = defineProps({
alpr: { alpr: {
@@ -72,11 +74,15 @@ const manufacturer = computed(() => (
)); ));
const store = useVendorStore(); const store = useVendorStore();
const imageUrl = ref<string | undefined | null>(undefined); const vendorImageUrl = ref<string | undefined | null>(undefined);
onMounted(async () => { onMounted(async () => {
const url = await store.getFirstImageForManufacturer(manufacturer.value as string); const url = await store.getFirstImageForManufacturer(manufacturer.value as string);
if (url) imageUrl.value = url; if (url) vendorImageUrl.value = url;
});
const imageUrl = computed(() => {
return wikimediaImages.value?.thumbnail ?? vendorImageUrl.value;
}); });
const abbreviatedOperator = computed(() => { const abbreviatedOperator = computed(() => {
@@ -104,6 +110,26 @@ const abbreviatedOperator = computed(() => {
return operator; return operator;
}); });
const wikimediaImages = computed(() => {
if (!props.alpr.tags.hasOwnProperty("wikimedia_commons")) {
return;
}
const filename = props.alpr.tags["wikimedia_commons"];
const thumbnailWidth = 300;
const cleanFilename = filename.replace(/^File:/, '').replace(/ /g, '_');
const md5Hash = md5(cleanFilename);
const hashPath = `${md5Hash[0]}/${md5Hash.slice(0, 2)}`;
const encodedFilename = encodeURIComponent(cleanFilename);
const wiki = `https://commons.wikimedia.org/wiki/${encodeURIComponent(filename)}`;
const thumbnail = `https://upload.wikimedia.org/wikipedia/commons/thumb/${hashPath}/${encodedFilename}/${thumbnailWidth}px-${encodedFilename}`;
return { wiki, thumbnail };
});
function osmNodeLink(id: string): string { function osmNodeLink(id: string): string {
return `https://www.openstreetmap.org/node/${id}`; return `https://www.openstreetmap.org/node/${id}`;
} }