diff --git a/README.md b/README.md
index 16962c6..dcd69b0 100644
--- a/README.md
+++ b/README.md
@@ -14,24 +14,24 @@ I created this project after noticing the mass deployment of ALPRs in cities, to
Uses OpenStreetMap data to populate a map with crowdsourced locations of ALPRs, along with their type and direction they face.
### Report ALPRs
-Provides OSM tags for easy reporting of ALPRs based on brand on OSM's editing site. Evemtually, this will be a native feature of the site.
+Provides OSM tags for easy reporting of ALPRs based on brand on OSM's editing site. Eventually, this will be a native feature of the site.
### Learn About ALPRs
See photos of common ALPRs and learn about their capabilities.
## Tech Stack
-_Likely to change soon._
-
### Backend
* Scala
* PekkoHTTP
* Nginx
### Cloud
-* AWS Lambda (for [clustering](serverless/alpr_clusters) and [counts](serverless/alpr_counts))
+* AWS Lambda (for [region segmenting](serverless/alpr_clusters) and [counts](serverless/alpr_counts))
* AWS S3
* AWS ECR
+* Cloudflare as DNS + Proxy
+* Directus CDN
### Frontend
* Vue3
@@ -39,9 +39,8 @@ _Likely to change soon._
* Vue Leaflet (mapping library)
### Services
-* OpenStreetMap - Overpass API
+* OpenStreetMap - Overpass API, Basic Map Tiles
* Nominatim - Geocoding
-* Stadia Maps - Map Tiles
## Usage
@@ -60,6 +59,10 @@ _Likely to change soon._
1. `cd shotgun`
2. `sbt run`
+### Building for Production
+
+See [Dockerfile](./Dockerfile).
+
## Contributing
We welcome contributions from anyone. Here's how you can help:
diff --git a/webapp/public/donate.webp b/webapp/public/donate.webp
new file mode 100644
index 0000000..7644f2b
Binary files /dev/null and b/webapp/public/donate.webp differ
diff --git a/webapp/public/torches.webp b/webapp/public/torches.webp
deleted file mode 100644
index 6bc78db..0000000
Binary files a/webapp/public/torches.webp and /dev/null differ
diff --git a/webapp/src/assets/main.css b/webapp/src/assets/main.css
index 7d65277..3491c0a 100644
--- a/webapp/src/assets/main.css
+++ b/webapp/src/assets/main.css
@@ -1,44 +1,169 @@
+/* Modern CSS Custom Properties */
+:root {
+ --df-background-color: white;
+ --df-blue: rgb(18, 151, 195);
+
+ /* Modern Typography Scale */
+ --font-size-base: 1rem;
+ --font-size-lg: 1.125rem;
+ --font-size-xl: 1.25rem;
+ --font-size-2xl: 1.5rem;
+
+ /* Responsive font sizes */
+ --font-size-body: clamp(1rem, 2.5vw, 1.125rem);
+ --line-height-base: 1.6;
+ --line-height-relaxed: 1.75;
+
+ /* Spacing scale */
+ --space-xs: 0.5rem;
+ --space-sm: 0.75rem;
+ --space-md: 1rem;
+ --space-lg: 1.5rem;
+ --space-xl: 2rem;
+ --space-2xl: 3rem;
+}
+
+/* Improved base typography */
+html {
+ font-size: 16px; /* Base font size for rem calculations */
+}
+
+body {
+ font-size: var(--font-size-body);
+ line-height: var(--line-height-relaxed);
+ font-feature-settings: "kern" 1, "liga" 1;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ margin: 0;
+ padding: 0;
+}
+
+/* Modern link styles */
a {
- font-weight: bold;
+ font-weight: 600;
color: var(--df-blue);
text-decoration: none;
+ transition: color 0.2s ease-in-out;
+}
+
+a:hover {
+ color: color-mix(in srgb, var(--df-blue) 80%, black);
}
/* underline only simple elements */
a:not([class]):hover {
text-decoration: underline;
-}
-
-:root {
- --df-background-color: white;
- --df-text-color: #ccc;
- --df-blue: rgb(18, 151, 195);
+ text-decoration-thickness: 2px;
+ text-underline-offset: 2px;
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip, .leaflet-bar a {
background: var(--df-background-color) !important;
}
-.leaflet-bar a {
- color: var(--df-text-color) !important;
-}
-
.marker-cluster span {
/* changing to dark mode should not affect the map yet */
color: #000000 !important;
}
-p {
- font-family: "PT Serif", serif;
- /* font-size: 1.1em; */
+/* Content elements use serif for better readability */
+p, li, blockquote, .content {
+ font-family: "PT Serif", Georgia, "Times New Roman", serif;
}
+/* Modern paragraph styling */
+p {
+ font-size: var(--font-size-body);
+ line-height: var(--line-height-relaxed);
+ margin: var(--space-md) 0;
+}
+
+/* Responsive headings */
+h1, h2, h3, h4, h5, h6 {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ font-weight: 700;
+ line-height: 1.2;
+ margin-top: var(--space-xl);
+ margin-bottom: var(--space-md);
+}
+
+/* Reduce heading top margins in Vuetify components */
+.v-card h1, .v-card h2, .v-card h3, .v-card h4, .v-card h5, .v-card h6, .v-card p,
+.v-dialog h1, .v-dialog h2, .v-dialog h3, .v-dialog h4, .v-dialog h5, .v-dialog h6, .v-dialog p,
+.v-sheet h1, .v-sheet h2, .v-sheet h3, .v-sheet h4, .v-sheet h5, .v-sheet h6, .v-sheet p,
+.v-toolbar h1, .v-toolbar h2, .v-toolbar h3, .v-toolbar h4, .v-toolbar h5, .v-toolbar h6, .v-toolbar p,
+.v-expansion-panel h1, .v-expansion-panel h2, .v-expansion-panel h3, .v-expansion-panel h4, .v-expansion-panel h5, .v-expansion-panel h6, .v-expansion-panel p {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+h1 {
+ font-size: clamp(1.75rem, 5vw, 3rem);
+}
+
+h2 {
+ font-size: clamp(1.5rem, 4vw, 2.25rem);
+}
+
+h3 {
+ font-size: clamp(1.25rem, 3vw, 1.875rem);
+}
+
+/* Utility classes */
.serif {
- font-family: "PT Serif", serif;
+ font-family: "PT Serif", Georgia, "Times New Roman", serif;
}
.sans-serif {
- font-family: unset !important;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
+}
+
+/* Improved list styling */
+ul, ol {
+ margin: var(--space-md) 0;
+ padding-left: var(--space-lg);
+ line-height: var(--line-height-relaxed);
+}
+
+ul li, ol li {
+ margin: var(--space-sm) 0;
+}
+
+/* Better text spacing for improved readability */
+blockquote {
+ margin: var(--space-xl) 0;
+ padding: var(--space-md) var(--space-lg);
+ border-left: 4px solid var(--df-blue);
+ font-style: italic;
+ background-color: #f8f9fa;
+}
+
+/* Code styling */
+code, pre {
+ font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace;
+ font-size: 0.875em;
+ background-color: rgb(33,33,33);
+ color: white;
+ padding: 0.125em 0.25em;
+ border-radius: 3px;
+}
+
+pre {
+ padding: var(--space-md);
+ overflow-x: auto;
+ border-radius: 6px;
+}
+
+pre code {
+ background: none;
+ padding: 0;
+}
+
+.narrow-text {
+ max-width: 85ch !important;
+ margin-left: auto;
+ margin-right: auto;
}
/* Prevent Scrolling Horizontally */
diff --git a/webapp/src/assets/typography.css b/webapp/src/assets/typography.css
deleted file mode 100644
index 146f5e3..0000000
--- a/webapp/src/assets/typography.css
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Typography rules */
-
-body {
- line-height: 1.6;
- margin: 0;
- padding: 0;
-}
-
-h1, h2, h3, h4, h5, h6 {
- margin-top: 1.5em;
- margin-bottom: 0.5em;
- font-weight: bold;
-}
-
-p {
- margin: 0.8em 0;
-}
-
-ul, ol {
- margin: 1em 0;
- padding-left: 1.5em;
-}
-
-ul li, ol li {
- margin: 0.5em 0;
-}
diff --git a/webapp/src/components/DiscordWarningDialog.vue b/webapp/src/components/DiscordWarningDialog.vue
index 4a3ad7c..2a834e3 100644
--- a/webapp/src/components/DiscordWarningDialog.vue
+++ b/webapp/src/components/DiscordWarningDialog.vue
@@ -29,8 +29,6 @@
diff --git a/webapp/src/router/index.ts b/webapp/src/router/index.ts
index d8b42c7..3d8506a 100644
--- a/webapp/src/router/index.ts
+++ b/webapp/src/router/index.ts
@@ -49,26 +49,19 @@ const router = createRouter({
},
{
path: '/report',
- name: 'reportChoose',
- component: () => import('../views/ReportBase.vue'),
- children: [
- {
- path: '',
- name: 'report',
- component: () => import('../views/ReportChoose.vue'),
- meta: {
- title: 'Submit Cameras | DeFlock'
- },
- },
- {
- path: '/report/id',
- name: 'reportID',
- component: () => import('../views/ReportID.vue'),
- meta: {
- title: 'Submit Cameras | DeFlock'
- }
- },
- ]
+ name: 'report',
+ component: () => import('../views/ReportChoose.vue'),
+ meta: {
+ title: 'Submit Cameras | DeFlock'
+ }
+ },
+ {
+ path: '/report/id',
+ name: 'reportID',
+ component: () => import('../views/ReportID.vue'),
+ meta: {
+ title: 'Submit Cameras | DeFlock'
+ }
},
{
path: '/council',
@@ -138,6 +131,9 @@ const router = createRouter({
path: '/foia',
name: 'foia',
component: () => import('../views/FOIA.vue'),
+ meta: {
+ title: 'How to Request Public Records | DeFlock'
+ }
},
{
path: '/identify',
diff --git a/webapp/src/views/404.vue b/webapp/src/views/404.vue
index 238adc2..4825d9a 100644
--- a/webapp/src/views/404.vue
+++ b/webapp/src/views/404.vue
@@ -1,21 +1,25 @@
- just like the license plate this guy is searching for ... just like the license plate this guy is searching for.
- Welcome to DeFlock, your go-to resource for understanding and addressing the growing presence of Automated License Plate Readers (ALPRs) in our communities.
-
- Our mission is simple: to shine a light on the widespread use of ALPR technology, raise awareness about the threats it poses to personal privacy and civil liberties, and empower the public to take action. ALPRs are increasingly being deployed by law enforcement and private companies without the public's full understanding or consent, and they collect vast amounts of data on our movements, often without any real oversight.
-
- Privacy is a fundamental right, and the growing use of ALPRs threatens to erode it. Every day, ALPRs capture and store details about the movements of millions of innocent people, creating a massive database that can be used for anything from routine surveillance to potentially more nefarious purposes. This constant monitoring can deter people from exercising their freedoms, like attending protests or engaging in other forms of free expression, which should concern all of us.
-
- We provide easy-to-understand information about what ALPRs are, how they work, and why you should care.
+ Welcome to DeFlock, your go-to resource for understanding and addressing the growing presence of Automated License Plate Readers (ALPRs) in our communities.
- Through our platform, you can report locations where you spot ALPRs, and view an interactive map showing where these surveillance devices are deployed.
+ Our mission is simple: to shine a light on the widespread use of ALPR technology, raise awareness about the threats it poses to personal privacy and civil liberties, and empower the public to take action. ALPRs are increasingly being deployed by law enforcement and private companies without the public's full understanding or consent, and they collect vast amounts of data on our movements, often without any real oversight.
- We encourage communities to stand up against the unchecked spread of these surveillance systems. Together, we can push for greater accountability, stronger privacy protections, and a rollback of invasive surveillance practices.
+ Privacy is a fundamental right, and the growing use of ALPRs threatens to erode it. Every day, ALPRs capture and store details about the movements of millions of innocent people, creating a massive database that can be used for anything from routine surveillance to potentially more nefarious purposes. This constant monitoring can deter people from exercising their freedoms, like attending protests or engaging in other forms of free expression, which should concern all of us.
- Want to help us in our mission? Here are a few ways you can get involved:
-
+ We provide easy-to-understand information about what ALPRs are, how they work, and why you should care.
+
+ Through our platform, you can report locations where you spot ALPRs, and view an interactive map showing where these surveillance devices are deployed.
+
+ We encourage communities to stand up against the unchecked spread of these surveillance systems. Together, we can push for greater accountability, stronger privacy protections, and a rollback of invasive surveillance practices.
+
- If you spot an ALPR in your community,
- Share our site with your friends, family, and social networks to help raise awareness about the dangers of ALPRs.
-
+ If you spot an ALPR in your community,
+ Share our site with your friends, family, and social networks to help raise awareness about the dangers of ALPRs.
+
- ALPRs are being installed across the country with little transparency. Public records requests allow us to track where these cameras are, ensuring that all installations are documented.
-
- Your help is crucial. Filing a public records request is easy, and your contribution will help us build a complete map of these surveillance networks. We'll even link you to several hundred examples to follow.[1]
-
+ ALPRs are being installed across the country with little transparency. Public records requests allow us to track where these cameras are, ensuring that all installations are documented.
+
+ Your help is crucial. Filing a public records request is easy, and your contribution will help us build a complete map of these surveillance networks. We'll even link you to several hundred examples to follow.[1]
+
- Browse Muckrock for examples of public records requests related to ALPRs. Use these as templates to draft your own request. Here are some common requests you can adapt:
-
+ Browse Muckrock for examples of public records requests related to ALPRs. Use these as templates to draft your own request. Here are some common requests you can adapt:
+ If you need help tailoring this to your situation, appealing rejections, etc., ChatGPT is a great resource. If you need help tailoring this to your situation, appealing rejections, etc., ChatGPT is a great resource.
- A public records request is a formal way to ask government agencies for public records. Laws vary by state, but most allow residents to request documents such as contracts, emails, and invoices.
-
+ A public records request is a formal way to ask government agencies for public records. Laws vary by state, but most allow residents to request documents such as contracts, emails, and invoices.
+
- Most states allow anyone to file a public records request, while some restrict it to state residents.
-
+ Most states allow anyone to file a public records request, while some restrict it to state residents.
+
- Invoices, contracts, emails, and other documents related to government spending and agreements with companies, including these surveillance companies.
-
+ Invoices, contracts, emails, and other documents related to government spending and agreements with companies, including these surveillance companies.
+
- Agencies may reject requests for various reasons, but you can often appeal. Let us know if you encounter issues, and we can help.
-
- Typically, yes. Many states allow you to submit public records requests anonymously or through an alias. If anonymity is important to you, consider using a disposable email address and avoiding personally identifying information in your request. If you're requesting records from a state that requires residency, you may need to provide an in-state address.
-
+ Agencies may reject requests for various reasons, but you can often appeal. Let us know if you encounter issues, and we can help.
+
+ Typically, yes. Many states allow you to submit public records requests anonymously or through an alias. If anonymity is important to you, consider using a disposable email address and avoiding personally identifying information in your request. If you're requesting records from a state that requires residency, you may need to provide an in-state address.
+ Effective Date: 1/4/2025Page Not Found
About Us
- Our Mission
- Why This Matters
- What We Do
- Educate
+ About Us
Report & Track
+
+ Our Mission
Advocate for Change
+
+ Why This Matters
Get Involved
- Report ALPRs
+
+ What We Do
+ Educate
+ Report & Track
+ Advocate for Change
+ Get Involved
Spread the Word
- Report ALPRs
+ Spread the Word
+ Contact Us
@@ -126,12 +127,11 @@
Why File a Public Records Request?
- Why File a Public Records Request?
+ How to File a Public Records Request
-
-
-
-
+ How to File a Public Records Request
+
+
+
+
- Public Records Basics
+ Public Records Basics
- What is a public records request?
- What is a public records request?
+ Who can file a public records request?
- Who can file a public records request?
+ What can be requested?
- What can be requested?
+ What if my request is rejected?
- Can I remain anonymous?
- What if my request is rejected?
+ Can I remain anonymous?
+ Privacy Policy
The OSM Web Editor provides a more advanced interface for detailed reporting in your web browser.
+The OSM Web Editor provides a more advanced interface for detailed reporting in your browser.
+
+
+ Add cameras as standalone points only! Do not connect them to roads, buildings, or other objects. Place the point exactly where the camera is physically located, but keep it as an independent point on the map.
+
To add the ALPR, click the Point button at the top center of the editor, then click on the location of the ALPR on the map. In the popup that appears, paste one of the following sets of tags based on the brand of the ALPR:
@@ -113,9 +127,11 @@