dockerize

This commit is contained in:
Will Freeman
2024-10-02 18:27:02 -05:00
parent ff0aff59f4
commit fd94bd3cee
18 changed files with 359 additions and 133 deletions
+1 -7
View File
@@ -20,16 +20,10 @@ const drawer = ref(false)
<v-app-bar-nav-icon variant="text" @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<v-toolbar-title>
<v-img height="36" width="200" src="/deflock-logo.svg" />
<v-img height="36" width="130" src="/deflock-logo.svg" />
</v-toolbar-title>
<v-spacer></v-spacer>
<template v-if="$vuetify.display.mdAndUp">
<v-btn disabled icon="mdi-magnify" variant="text"></v-btn>
<v-btn disabled icon="mdi-filter" variant="text"></v-btn>
</template>
</v-app-bar>
<v-navigation-drawer
+9
View File
@@ -0,0 +1,9 @@
a {
font-weight: bold;
color: rgb(18, 151, 195);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
+39
View File
@@ -0,0 +1,39 @@
<template>
<div style="position: relative">
<v-btn @click="copyToClipboard" variant="plain" flat class="copy-button">
<v-icon>mdi-content-copy</v-icon>
</v-btn>
<code ref="codeContent">
<slot></slot>
</code>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const codeContent = ref<HTMLElement | null>(null);
function copyToClipboard() {
if (codeContent.value) {
navigator.clipboard.writeText(codeContent.value.innerText);
}
}
</script>
<style scoped>
code {
background-color: #f4f4f4;
padding: 0.5rem;
border-radius: 0.25rem;
display: block;
margin-top: 0.5rem;
}
.copy-button {
position: absolute;
right: 0;
top: 0;
z-index: 1000;
}
</style>
+32 -2
View File
@@ -1,14 +1,44 @@
import axios from "axios";
export interface BoundingBox {
export interface BoundingBoxLiteral {
minLat: number;
maxLat: number;
minLng: number;
maxLng: number;
}
export class BoundingBox implements BoundingBoxLiteral {
minLat: number;
maxLat: number;
minLng: number;
maxLng: number;
constructor({minLat, maxLat, minLng, maxLng}: BoundingBoxLiteral) {
this.minLat = minLat;
this.maxLat = maxLat;
this.minLng = minLng;
this.maxLng = maxLng;
}
updateFromOther(boundingBoxLiteral: BoundingBoxLiteral) {
this.minLat = boundingBoxLiteral.minLat;
this.maxLat = boundingBoxLiteral.maxLat;
this.minLng = boundingBoxLiteral.minLng;
this.maxLng = boundingBoxLiteral.maxLng;
}
isSubsetOf(other: BoundingBoxLiteral) {
return (
this.minLat >= other.minLat &&
this.maxLat <= other.maxLat &&
this.minLng >= other.minLng &&
this.maxLng <= other.maxLng
);
}
}
const apiService = axios.create({
baseURL: "http://localhost:8080",
baseURL: window.location.hostname === "localhost" ? "http://localhost:8080/api" : "/api",
headers: {
"Content-Type": "application/json",
},
+26 -54
View File
@@ -20,11 +20,14 @@
name="OpenStreetMap"
></l-tile-layer>
<l-marker
@click="console.log('marker clicked')"
v-for="alpr in alprsInView"
:key="alpr.id"
:lat-lng="[alpr.lat, alpr.lon]"
><l-popup>This is an ALPR! More data (such as direction) coming soon.</l-popup></l-marker>
><l-popup>
<h2>ALPR</h2>
<p v-if="alpr.tags.brand || alpr.tags.operator"><strong>Brand: </strong><a target="_blank" :href="`https://www.wikidata.org/wiki/${alpr.tags['brand:wikidata'] || alpr.tags['operator:wikidata']}`">{{ alpr.tags.brand || alpr.tags.operator || 'Unknown' }}</a></p>
<p v-if="alpr.tags.direction"><strong>Faces: {{ degreesToCardinal(alpr.tags.direction) }}</strong></p>
</l-popup></l-marker>
</l-map>
<div v-else>
loading...
@@ -38,7 +41,7 @@ import { LMap, LTileLayer, LMarker, LPopup } from '@vue-leaflet/vue-leaflet';
import { ref, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router'
import type { Ref } from 'vue';
import type { BoundingBox } from '@/services/apiService';
import { BoundingBox } from '@/services/apiService';
import { getALPRs } from '@/services/apiService';
const zoom: Ref<number> = ref(12);
@@ -48,46 +51,8 @@ const router = useRouter();
const canRefreshMarkers = computed(() => zoom.value >= 10);
const alprsInView: Ref<any[]> = ref([
// {
// "type": "node",
// "id": 12187369976,
// "lat": 34.6616103,
// "lon": -86.4870137,
// "tags": {
// "brand": "Flock Safety",
// "brand:wikidata": "Q108485435",
// "camera:mount": "pole",
// "camera:type": "fixed",
// "direction": "335",
// "man_made": "surveillance",
// "operator": "Flock Safety",
// "operator:wikidata": "Q108485435",
// "surveillance": "traffic",
// "surveillance:type": "ALPR",
// "surveillance:zone": "traffic"
// }
// },
// {
// "type": "node",
// "id": 12187369977,
// "lat": 34.6615727,
// "lon": -86.4881948,
// "tags": {
// "brand": "Flock Safety",
// "brand:wikidata": "Q108485435",
// "camera:mount": "pole",
// "camera:type": "fixed",
// "direction": "295",
// "man_made": "surveillance",
// "operator": "Flock Safety",
// "operator:wikidata": "Q108485435",
// "surveillance": "traffic",
// "surveillance:type": "ALPR",
// "surveillance:zone": "traffic"
// }
// }
]);
const alprsInView: Ref<any[]> = ref([]);
const bboxForLastRequest: Ref<BoundingBox|null> = ref(null);
function getUserLocation(): Promise<[number, number]> {
return new Promise((resolve, reject) => {
@@ -115,18 +80,22 @@ function mapLoaded(map: any) {
}
function updateBounds(newBounds: any) {
bounds.value = {
updateURL();
const newBoundingBox = new BoundingBox({
minLat: newBounds.getSouth(),
maxLat: newBounds.getNorth(),
minLng: newBounds.getWest(),
maxLng: newBounds.getEast(),
};
});
bounds.value = newBoundingBox;
if (bboxForLastRequest.value && newBoundingBox.isSubsetOf(bboxForLastRequest.value)) {
console.debug('new bounds are a subset of the last request, skipping');
return;
}
updateMarkers();
if (center.value) {
updateURL();
}
}
function updateURL() {
@@ -153,14 +122,19 @@ function updateMarkers() {
getALPRs(bounds.value)
.then((alprs: any) => {
alprsInView.value = alprs.elements;
bboxForLastRequest.value = bounds.value;
});
}
function degreesToCardinal(degrees: number): string {
const cardinals = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
return cardinals[Math.round(degrees / 45) % 8];
}
onMounted(() => {
const hash = router.currentRoute.value.hash;
if (hash) {
const parts = hash.split('/');
console.log('parts', parts);
if (parts.length === 3 && parts[0].startsWith('#map')) {
const zoomLevelString = parts[0].replace('#map=', '');
zoom.value = parseInt(zoomLevelString, 10);
@@ -168,8 +142,6 @@ onMounted(() => {
lat: parseFloat(parts[1]),
lng: parseFloat(parts[2]),
};
console.log('center', center.value);
console.log('zoom', zoom.value);
}
}
@@ -196,9 +168,9 @@ onMounted(() => {
left: 32px;
width: calc(100% - 64px);
z-index: 1000;
background-color: rgba(255, 255, 255, 0.8);
background-color: rgba(0, 0, 0, 0.8);
border-radius: 4px;
padding: 4px;
color: #333;
color: #eee;
}
</style>
+77 -30
View File
@@ -2,33 +2,85 @@
<v-container>
<h2>How to Report an ALPR</h2>
<p>
If you've spotted an ALPR in your area, you can help us track it by reporting it to our database. Here's how you can do it:
If you've spotted an ALPR in your area, you can help us track it by reporting it to OpenStreetMap, where we source our information. Here's how you can do it:
</p>
<h3>Coming Soon</h3>
<p>
We're working on a way for you to report ALPRs directly from this site. Check back soon for updates!
</p>
<p>
Until then, you can report them on OpenStreetMap. <a href="https://wiki.openstreetmap.org/wiki/Tag:man_made%3Dsurveillance" target="_blank">Learn how to add ALPRs to OpenStreetMap</a>.
</p>
<v-alert
variant="tonal"
type="warning"
class="my-6"
title="Are you sure it's an ALPR?"
>
<p>
Before you report an ALPR, please read our <router-link style="color: unset !important" to="/what-is-an-alpr">guide on what ALPRs look like</router-link> to make sure you're reporting the right thing.
</p>
</v-alert>
<!-- <p>
If you can do so safely, take a picture of the ALPR camera. Make sure the camera is visible in the image, and try to capture any identifying information, such as the brand name or any logos.
</p>
<div class="ml-4 mt-4">
<h3>1. Create an OpenStreetMap Account</h3>
<p>
<a href="https://www.openstreetmap.org/user/new" target="_blank">Sign up for an OpenStreetMap account</a> in order to submit changes.
</p>
<h3>Step 2: Note the Location</h3>
<p>
Record the location of the ALPR camera. You can use your phone's GPS to get the coordinates, or note the nearest address or intersection.
</p>
<h3>2. Find the ALPR's Location on OpenStreetMap</h3>
<p>
<a href="https://www.openstreetmap.org" target="_blank">Launch OpenStreetMap</a> and search for the location of the ALPR. You can use the search bar at the top of the page to find the location.
</p>
<h3>Step 3: Submit the Report</h3>
<p>
Click the "Report" button below to submit your report. You'll be asked to upload the picture you took and provide the location information. Your report will help us build a more comprehensive database of ALPRs in the United States.
</p> -->
<h3>3. Add the ALPR to OpenStreetMap</h3>
<p>
Once you've found the location of the ALPR, click the <strong>Edit</strong> button in the top left corner of the page. This will open the OpenStreetMap editor, where you can add the ALPR to the map.
</p>
<v-img src="/edit-map.png" />
<p>
To add the ALPR, click the <strong>Point</strong> button in the top left corner of the editor, then click on the location of the ALPR on the map. In the popup that appears, paste the following tags:
</p>
<DFCode>
man_made=surveillance<br>
surveillance:type=ALPR<br>
camera:mount=pole<br>
camera:type=fixed<br>
surveillance=traffic<br>
surveillance:zone=traffic<br>
</DFCode>
<v-img class="my-4" src="/paste-tags.png" />
<p>
If you've identified the brand of the ALPR as Flock Safety, then you can also add the following tags:
</p>
<DFCode>
operator=Flock Safety<br>
operator:wikidata=Q108485435<br>
brand=Flock Safety<br>
brand:wikidata=Q108485435<br>
</DFCode>
<h3>4. Adjust the Direction</h3>
<v-img src="/adjust-angle.png" />
<p>
If you know the direction that the ALPR is facing, you can use the up and down arrows to set the direction it faces.
</p>
<h3>5. Submit Your Changes</h3>
<p>
Once you've added the ALPR to the map, click the <strong>Save</strong> button in the top left corner of the editor. You'll be asked to provide a brief description of your changes. Once you've submitted your changes, the ALPR will be added to OpenStreetMap.
</p>
</div>
</v-container>
</template>
<script setup lang="ts">
import DFCode from '@/components/DFCode.vue';
</script>
<style scoped>
/* TODO: put this all in one place, also in what-is view */
h2 {
@@ -50,16 +102,11 @@ p {
margin-top: 0.5rem;
}
a {
color: var(--v-primary-base);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: var(--v-primary-base);
code {
background-color: #f4f4f4;
padding: 0.5rem;
border-radius: 0.25rem;
display: block;
margin-top: 0.5rem;
}
</style>