Minor Improvements (#24)

* remove dangers page, fix counter

* fix bad instrucitons

* cleanup

* allow nonstandard direction tag, link to OSM

* update privacy policy to account for intro message
This commit is contained in:
Will Freeman
2025-01-04 17:01:14 -07:00
committed by GitHub
parent e03cb9da7c
commit a937c43e76
44 changed files with 168 additions and 176 deletions
+28 -8
View File
@@ -12,6 +12,13 @@ import { useDisplay } from 'vuetify'
import { getALPRCounts } from '@/services/apiService';
import { CountUp } from 'countup.js';
const props = defineProps({
delayMs: {
type: Number,
default: 200,
}
});
const counterEl: Ref<HTMLElement|null> = ref(null);
const countupOptions = {
useEasing: true,
@@ -33,13 +40,17 @@ const counts: Ref<Counts> = ref({
const showFinalAnimation = ref(false);
const { xs: isMobile } = useDisplay();
let timeOfMount: number|undefined = undefined;
onMounted(() => {
getALPRCounts().then((response) => {
counts.value = response;
timeOfMount = new Date().getTime();
getALPRCounts().then((countResponse) => {
counts.value = countResponse;
countUp(countResponse);
});
});
watch(counts, (newCounts: Counts) => {
function countUp(newCounts: Counts) {
if (!newCounts.worldwide) return;
if (!counterEl.value) {
console.error('Counter element not found');
@@ -48,14 +59,23 @@ watch(counts, (newCounts: Counts) => {
if (!counter) {
counter = new CountUp(counterEl.value, newCounts.worldwide, countupOptions);
setTimeout(() => {
counter?.start();
}, 500);
if (timeOfMount) {
const timeSinceMount = new Date().getTime() - timeOfMount;
if (timeSinceMount < props.delayMs) {
setTimeout(() => {
counter?.start();
}, props.delayMs - timeSinceMount);
} else {
counter.start();
}
}
setTimeout(() => {
showFinalAnimation.value = true;
}, 3000);
}, 2500);
}
});
}
</script>
<style scoped>
+13 -12
View File
@@ -22,7 +22,7 @@
{{ alpr.tags.brand }}
</span>
<span v-else>
Unknown Manufacturer
Unspecified Manufacturer
</span>
</b>
</v-list-item>
@@ -32,19 +32,14 @@
{{ alpr.tags.operator }}
</span>
<span v-else>
Unknown Operator
Unspecified Operator
</span>
</b>
</v-list-item>
</v-list>
<div class="text-center text-grey-darken-1">
<v-tooltip open-delay="500" text="OSM Node ID" location="bottom">
<template #activator="{ props }">
<span style="font-size: 0.9em; cursor: default" v-bind="props">node/{{ alpr.id }}</span>
</template>
</v-tooltip>
<div class="text-center">
<v-btn target="_blank" size="x-small" :href="osmNodeLink(props.alpr.id)" variant="text" color="grey-darken-1"><v-icon start>mdi-open-in-new</v-icon>View on OSM</v-btn>
</div>
</v-sheet>
</template>
@@ -53,7 +48,7 @@
import { defineProps, computed } from 'vue';
import type { PropType } from 'vue';
import type { ALPR } from '@/types';
import { VIcon, VList, VSheet, VListItem, VTooltip } from 'vuetify/components';
import { VIcon, VList, VSheet, VListItem, VBtn } from 'vuetify/components';
const props = defineProps({
alpr: {
@@ -64,12 +59,18 @@ const props = defineProps({
const isFaceRecognition = computed(() => props.alpr.tags.brand === 'Avigilon');
const cardinalDirection = computed(() =>
props.alpr.tags.direction === undefined ? 'Unknown Direction' : degreesToCardinal(parseInt(props.alpr.tags.direction))
const cardinalDirection = computed(() => {
const direction = props.alpr.tags.direction || props.alpr.tags["camera:direction"];
return direction === undefined ? 'Unspecified Direction' : degreesToCardinal(parseInt(direction))
}
);
function degreesToCardinal(degrees: number): string {
const cardinals = ['North', 'Northeast', 'East', 'Southeast', 'South', 'Southwest', 'West', 'Northwest'];
return 'Faces ' + cardinals[Math.round(degrees / 45) % 8];
}
function osmNodeLink(id: string): string {
return `https://www.openstreetmap.org/node/${id}`;
}
</script>
+2 -2
View File
@@ -56,7 +56,7 @@ let currentLocationLayer: FeatureGroup;
// Marker Creation Utilities
function createSVGMarker(alpr: ALPR): string {
const orientationDegrees = alpr.tags.direction;
const orientationDegrees = alpr.tags.direction || alpr.tags['camera:direction'];
const fovPath = `
<path class="someSVGpath" d="M215.248,221.461L99.696,43.732C144.935,16.031 198.536,0 256,0C313.464,0 367.065,16.031 412.304,43.732L296.752,221.461C287.138,209.593 272.448,202.001 256,202.001C239.552,202.001 224.862,209.593 215.248,221.461Z" style="fill:rgb(87,87,87);fill-opacity:0.46;"/>
<path class="someSVGpath" d="M215.248,221.461L99.696,43.732C144.935,16.031 198.536,0 256,0C313.464,0 367.065,16.031 412.304,43.732L296.752,221.461C287.138,209.593 272.448,202.001 256,202.001C239.552,202.001 224.862,209.593 215.248,221.461ZM217.92,200.242C228.694,192.652 241.831,188.195 256,188.195C270.169,188.195 283.306,192.652 294.08,200.242C294.08,200.242 392.803,48.4 392.803,48.4C352.363,26.364 305.694,13.806 256,13.806C206.306,13.806 159.637,26.364 119.197,48.4L217.92,200.242Z" style="fill:rgb(137,135,135);"/>
@@ -73,7 +73,7 @@ function createSVGMarker(alpr: ALPR): string {
}
function createMarker(alpr: ALPR): Marker | CircleMarker {
if (hasPlottableOrientation(alpr.tags.direction)) {
if (hasPlottableOrientation(alpr.tags.direction || alpr.tags['camera:direction'])) {
const icon = L.divIcon({
className: 'leaflet-data-marker',
html: createSVGMarker(alpr),
+54
View File
@@ -0,0 +1,54 @@
<template>
<v-dialog v-model="show" max-width="900">
<v-card>
<v-card-title class="text-center py-4 font-weight-bold">
<h3 class="headline">Welcome to DeFlock</h3>
</v-card-title>
<p class="mx-8 text-center">
DeFlock is powered by <b>crowdsourced data</b> from the OpenStreetMap community.
</p>
<v-divider class="mx-4 mt-4" />
<v-list class="text-center">
<v-list-item class="my-4">
<v-icon size="x-large" color="primary" class="mb-2">mdi-progress-pencil</v-icon>
<v-list-item-title class="font-weight-bold">The map is incomplete!</v-list-item-title>
<v-list-item-subtitle>The ALPRs displayed here are a starting point, and new locations are constantly being added.</v-list-item-subtitle>
</v-list-item>
</v-list>
<div class="small text-grey-darken-2 text-center mx-4 mb-2">By using this site, you agree to our <router-link to="/terms">Terms of Service</router-link>.</div>
<v-card-actions>
<v-btn flat class="w-100" size="x-large" color="rgb(18, 151, 195)" variant="elevated" @click="acknowledge">Got it</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
const show = ref(false);
onMounted(() => {
if (!localStorage.getItem('acknowledged')) {
show.value = true;
}
});
function acknowledge() {
show.value = false;
localStorage.setItem('acknowledged', 'true');
}
</script>
<style scoped>
.no-small {
min-width: 160px;
}
.small {
font-size: 0.8rem;
}
</style>
+6 -5
View File
@@ -1,6 +1,6 @@
<template>
<v-row class="align-center justify-center my-4">
<v-col cols="12" sm="6">
<v-col cols="12" sm="6" class="text-center">
<v-select
color="rgb(18, 151, 195)"
prepend-inner-icon="mdi-factory"
@@ -22,6 +22,7 @@
max-width="100%"
class="my-4"
></v-img>
<v-btn to="/what-is-an-alpr#photos" color="grey-darken-2" variant="text" size="small"><v-icon start>mdi-image-multiple</v-icon> See All Photos</v-btn>
</v-col>
<v-col cols="12" sm="6">
@@ -56,25 +57,25 @@ const alprBrands: WikidataItem[] = [
name: 'Flock Safety',
nickname: 'Flock',
wikidata: 'Q108485435',
exampleImage: '/flock-1.jpg',
exampleImage: '/alprs/flock-1.jpg',
},
{
name: 'Motorola Solutions',
nickname: 'Motorola/Vigilant',
wikidata: 'Q634815',
exampleImage: '/vigilant-1.jpg',
exampleImage: '/alprs/motorola-4.jpg',
},
{
name: 'Leonardo',
nickname: 'Leonardo/ELSAG',
wikidata: 'Q910379',
exampleImage: '/elsag.webp',
exampleImage: '/alprs/elsag-1.jpg',
},
{
name: 'Neology, Inc.',
nickname: 'Neology',
wikidata: 'Q130958232',
exampleImage: '/neology-1.jpg',
exampleImage: '/alprs/neology-2.jpg',
},
{
name: '(brand goes here)',
+1 -1
View File
@@ -57,7 +57,7 @@
<div>
<p>&copy; {{ currentYear }} DeFlock. All Rights Reserved</p>
<p>Map data © <a href="https://www.openstreetmap.org/copyright" target="_blank" style="color: unset; font-weight: normal;">OpenStreetMap contributors</a></p>
<p class="mt-4">v1.0</p>
<p class="mt-4">v1.0.1</p>
</div>
</v-col>
</v-row>