mirror of
https://github.com/FoggedLens/deflock.git
synced 2026-09-30 20:49:37 +02:00
a little hacky but it works
This commit is contained in:
1 parent
acfff6bb40
commit
6d8b3ba42f
7 files changed
+372
-11
No files matched your search
+5
-1
@@ -16,5 +16,9 @@ val PekkoHttpVersion = "1.0.1"
|
|||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"org.apache.pekko" %% "pekko-actor-typed" % PekkoVersion,
|
"org.apache.pekko" %% "pekko-actor-typed" % PekkoVersion,
|
||||||
"org.apache.pekko" %% "pekko-stream" % PekkoVersion,
|
"org.apache.pekko" %% "pekko-stream" % PekkoVersion,
|
||||||
"org.apache.pekko" %% "pekko-http" % PekkoHttpVersion
|
"org.apache.pekko" %% "pekko-http" % PekkoHttpVersion,
|
||||||
|
"io.circe" %% "circe-core" % "0.14.7",
|
||||||
|
"io.circe" %% "circe-generic" % "0.14.9",
|
||||||
|
"io.circe" %% "circe-parser" % "0.14.9",
|
||||||
|
"org.apache.pekko" %% "pekko-http-cors" % PekkoHttpVersion
|
||||||
)
|
)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.deflock.shotgun
|
package me.deflock.shotgun
|
||||||
|
|
||||||
import org.apache.pekko
|
import org.apache.pekko
|
||||||
|
import org.apache.pekko.http.scaladsl.model.headers.{HttpOrigin, HttpOriginRange, `Access-Control-Allow-Origin`}
|
||||||
import pekko.actor.typed.ActorSystem
|
import pekko.actor.typed.ActorSystem
|
||||||
import pekko.actor.typed.scaladsl.Behaviors
|
import pekko.actor.typed.scaladsl.Behaviors
|
||||||
import pekko.http.scaladsl.Http
|
import pekko.http.scaladsl.Http
|
||||||
@@ -17,16 +18,32 @@ object ShotgunServer {
|
|||||||
implicit val system: ActorSystem[Any] = ActorSystem(Behaviors.empty, "my-system")
|
implicit val system: ActorSystem[Any] = ActorSystem(Behaviors.empty, "my-system")
|
||||||
implicit val executionContext: ExecutionContextExecutor = system.executionContext
|
implicit val executionContext: ExecutionContextExecutor = system.executionContext
|
||||||
|
|
||||||
val route =
|
val routes = {
|
||||||
path("oauth2" / "callback") {
|
concat {
|
||||||
get {
|
path("oauth2" / "callback") {
|
||||||
parameters(Symbol("code").?) { (code) =>
|
get {
|
||||||
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to Pekko HTTP</h1><p><b>Code: " + code.getOrElse("None") + "</b></p>"))
|
parameters(Symbol("code").?) { (code) =>
|
||||||
|
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to Pekko HTTP</h1><p><b>Code: " + code.getOrElse("None") + "</b></p>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path("alpr") {
|
||||||
|
get {
|
||||||
|
parameters("minLat".as[Double], "minLng".as[Double], "maxLat".as[Double], "maxLng".as[Double]) { (minLat, minLng, maxLat, maxLng) =>
|
||||||
|
val client = new services.OverpassClient() // TODO: make this global
|
||||||
|
val bBox = services.BoundingBox(minLat, minLng, maxLat, maxLng)
|
||||||
|
onSuccess(client.getALPRs(bBox)) { json =>
|
||||||
|
respondWithHeader(`Access-Control-Allow-Origin`.*) {
|
||||||
|
complete(HttpEntity(ContentTypes.`application/json`, json.toString()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)
|
val bindingFuture = Http().newServerAt("localhost", 8080).bind(routes)
|
||||||
|
|
||||||
println(s"Server now online. Please navigate to http://localhost:8080\nPress RETURN to stop...")
|
println(s"Server now online. Please navigate to http://localhost:8080\nPress RETURN to stop...")
|
||||||
StdIn.readLine() // let it run until user presses return
|
StdIn.readLine() // let it run until user presses return
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import org.apache.pekko
|
||||||
|
import pekko.actor.typed.ActorSystem
|
||||||
|
import pekko.actor.typed.scaladsl.Behaviors
|
||||||
|
import pekko.http.scaladsl.Http
|
||||||
|
import pekko.http.scaladsl.model._
|
||||||
|
import pekko.http.scaladsl.unmarshalling.Unmarshal
|
||||||
|
import io.circe._
|
||||||
|
import io.circe.parser._
|
||||||
|
import org.apache.pekko.http.scaladsl.client.RequestBuilding._
|
||||||
|
|
||||||
|
import scala.concurrent.duration.DurationInt
|
||||||
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
case class BoundingBox(minLat: Double, minLng: Double, maxLat: Double, maxLng: Double)
|
||||||
|
|
||||||
|
class OverpassClient(implicit val system: ActorSystem[_], implicit val executionContext: ExecutionContextExecutor) {
|
||||||
|
val baseUrl = "https://overpass-api.de/api/interpreter"
|
||||||
|
|
||||||
|
def getALPRs(bBox: BoundingBox): Future[Json] = {
|
||||||
|
val query = s"""[out:json][bbox:${bBox.minLat},${bBox.minLng},${bBox.maxLat},${bBox.maxLng}];node["man_made"="surveillance"]["surveillance:type"="ALPR"];out body;>;out skel qt;"""
|
||||||
|
val formData = FormData("data" -> query).toEntity
|
||||||
|
val request = HttpRequest(
|
||||||
|
method = HttpMethods.POST,
|
||||||
|
uri = baseUrl,
|
||||||
|
entity = formData
|
||||||
|
)
|
||||||
|
|
||||||
|
Http().singleRequest(request).flatMap { response =>
|
||||||
|
response.entity.toStrict(5.seconds).flatMap { entity =>
|
||||||
|
Unmarshal(entity).to[String].map(parse(_).getOrElse(Json.Null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Generated
+152
@@ -8,6 +8,7 @@
|
|||||||
"name": "deflock",
|
"name": "deflock",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
"vue": "^3.4.29",
|
"vue": "^3.4.29",
|
||||||
"vue-router": "^4.3.3",
|
"vue-router": "^4.3.3",
|
||||||
"vuetify": "^3.7.2"
|
"vuetify": "^3.7.2"
|
||||||
@@ -882,6 +883,21 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||||
|
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -897,6 +913,17 @@
|
|||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/computeds": {
|
"node_modules/computeds": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz",
|
||||||
@@ -928,6 +955,14 @@
|
|||||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@@ -982,6 +1017,38 @@
|
|||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -1043,6 +1110,25 @@
|
|||||||
"node": ">= 0.10.0"
|
"node": ">= 0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
@@ -1174,6 +1260,11 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/read-package-json-fast": {
|
"node_modules/read-package-json-fast": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
|
||||||
@@ -1967,6 +2058,21 @@
|
|||||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "1.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||||
|
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -1982,6 +2088,14 @@
|
|||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"requires": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"computeds": {
|
"computeds": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz",
|
||||||
@@ -2010,6 +2124,11 @@
|
|||||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||||
|
},
|
||||||
"entities": {
|
"entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@@ -2051,6 +2170,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||||
},
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
|
||||||
|
},
|
||||||
|
"form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fsevents": {
|
"fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -2096,6 +2230,19 @@
|
|||||||
"integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
|
"integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
@@ -2170,6 +2317,11 @@
|
|||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"read-package-json-fast": {
|
"read-package-json-fast": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"type-check": "vue-tsc --build --force"
|
"type-check": "vue-tsc --build --force"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
"vue": "^3.4.29",
|
"vue": "^3.4.29",
|
||||||
"vue-router": "^4.3.3",
|
"vue-router": "^4.3.3",
|
||||||
"vuetify": "^3.7.2"
|
"vuetify": "^3.7.2"
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export interface BoundingBox {
|
||||||
|
minLat: number;
|
||||||
|
maxLat: number;
|
||||||
|
minLng: number;
|
||||||
|
maxLng: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiService = axios.create({
|
||||||
|
baseURL: "http://localhost:8080",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getALPRs = async (boundingBox: BoundingBox) => {
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
minLat: boundingBox.minLat.toString(),
|
||||||
|
maxLat: boundingBox.maxLat.toString(),
|
||||||
|
minLng: boundingBox.minLng.toString(),
|
||||||
|
maxLng: boundingBox.maxLng.toString(),
|
||||||
|
});
|
||||||
|
const response = await apiService.get(`/alpr?${queryParams.toString()}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
@@ -1,12 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="map-container">
|
<div class="map-container">
|
||||||
<!-- use-global-leaflet=false is a workaround for a bug in current version of vue-leaflet -->
|
<!-- use-global-leaflet=false is a workaround for a bug in current version of vue-leaflet -->
|
||||||
<l-map v-if="center" ref="map" v-model:zoom="zoom" :center="center" :use-global-leaflet="false">
|
<l-map
|
||||||
|
v-if="center"
|
||||||
|
ref="map"
|
||||||
|
v-model:zoom="zoom"
|
||||||
|
v-model:center="center"
|
||||||
|
:use-global-leaflet="false"
|
||||||
|
@update:bounds="updateBounds"
|
||||||
|
@ready="mapLoaded"
|
||||||
|
>
|
||||||
<l-tile-layer
|
<l-tile-layer
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
layer-type="base"
|
layer-type="base"
|
||||||
name="OpenStreetMap"
|
name="OpenStreetMap"
|
||||||
></l-tile-layer>
|
></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! Fuck it!</l-popup></l-marker>
|
||||||
</l-map>
|
</l-map>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
loading...
|
loading...
|
||||||
@@ -16,12 +30,58 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import { LMap, LTileLayer, LMarker } from '@vue-leaflet/vue-leaflet';
|
import { LMap, LTileLayer, LMarker, LPopup } from '@vue-leaflet/vue-leaflet';
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
|
import type { BoundingBox } from '@/services/apiService';
|
||||||
|
import { getALPRs } from '@/services/apiService';
|
||||||
|
|
||||||
const zoom: Ref<number> = ref(12);
|
const zoom: Ref<number> = ref(12);
|
||||||
const center: Ref<[number, number]|null> = ref(null);
|
const center: Ref<any|null> = ref(null);
|
||||||
|
const bounds: Ref<BoundingBox|null> = ref(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const alprsInView = 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
function getUserLocation(): Promise<[number, number]> {
|
function getUserLocation(): Promise<[number, number]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -44,10 +104,73 @@ function getUserLocation(): Promise<[number, number]> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function mapLoaded(map: any) {
|
||||||
|
updateBounds(map.getBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBounds(newBounds: any) {
|
||||||
|
bounds.value = {
|
||||||
|
minLat: newBounds.getSouth(),
|
||||||
|
maxLat: newBounds.getNorth(),
|
||||||
|
minLng: newBounds.getWest(),
|
||||||
|
maxLng: newBounds.getEast(),
|
||||||
|
};
|
||||||
|
|
||||||
|
updateMarkers();
|
||||||
|
|
||||||
|
if (center.value) {
|
||||||
|
updateURL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateURL() {
|
||||||
|
if (!center.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.replace({
|
||||||
|
hash: `#map=${zoom.value}/${center.value.lat.toFixed(6)}/${center.value.lng.toFixed(6)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMarkers() {
|
||||||
|
// Fetch ALPRs in the current view
|
||||||
|
if (!bounds.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoom.value < 12) {
|
||||||
|
console.log('zoomed out too far');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getALPRs(bounds.value)
|
||||||
|
// .then((alprs: any) => {
|
||||||
|
// alprsInView.value = alprs.elements;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
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);
|
||||||
|
center.value = {
|
||||||
|
lat: parseFloat(parts[1]),
|
||||||
|
lng: parseFloat(parts[2]),
|
||||||
|
};
|
||||||
|
console.log('center', center.value);
|
||||||
|
console.log('zoom', zoom.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getUserLocation()
|
getUserLocation()
|
||||||
.then(location => {
|
.then(location => {
|
||||||
center.value = location;
|
if (!hash)
|
||||||
|
center.value = { lat: location[0], lng: location[1] };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user