Compare commits

...

17 commits

Author SHA1 Message Date
4d9c42e932 Merge pull request 'Postgres version' (#1) from postgres-version into main
Reviewed-on: #1
2025-11-19 17:38:09 +00:00
6ea9f2fff5 fixed distance calculation 2023-01-04 21:29:11 -05:00
3e8b0bcf56 fixed distance calculation 2023-01-04 18:30:44 -05:00
f5a97fa9c6 added dependency for camel log 2023-01-04 18:15:48 -05:00
e01e9a9484 added new edge case and updated error handling polices in camel route 2023-01-04 18:02:21 -05:00
8cfbf2756b updated decoder to use regex, added dead letter queue, and added more decoder testing 2023-01-03 21:47:17 -05:00
f108cecd86 fixed decoder again 2023-01-03 00:40:19 -05:00
6527e6ea81 more reflection stuff 2023-01-03 00:26:27 -05:00
4077f70e11 more reflection stuff 2023-01-03 00:19:04 -05:00
2b398563dc more reflection stuff 2023-01-03 00:12:42 -05:00
5af8062ed2 added com.google.maps.model.GeocodingResult to register for reflection 2023-01-02 23:59:03 -05:00
5c3080ec22 improved decoder 2023-01-02 22:55:02 -05:00
a4222522c0 added annotation to register geocoding api response for reflection 2023-01-02 22:04:18 -05:00
eaeff564e0 added more configurations and fixed direct component scope 2023-01-02 21:46:04 -05:00
d4583748ca added more configurations 2023-01-02 21:32:32 -05:00
9df4f9edf4 fixed bug and added more configurations 2023-01-02 21:30:46 -05:00
05f457be84 modified to use postgres 2023-01-02 18:49:29 -05:00
10 changed files with 202 additions and 48 deletions

31
pom.xml
View file

@ -12,7 +12,7 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.13.1.Final</quarkus.platform.version> <quarkus.platform.version>2.15.1.Final</quarkus.platform.version>
<skipITs>true</skipITs> <skipITs>true</skipITs>
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version> <surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
</properties> </properties>
@ -35,10 +35,10 @@
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<dependencies> <dependencies>
<!-- <dependency>--> <dependency>
<!-- <groupId>org.apache.camel.quarkus</groupId>--> <groupId>org.apache.camel.quarkus</groupId>
<!-- <artifactId>camel-quarkus-telegram</artifactId>--> <artifactId>camel-quarkus-telegram</artifactId>
<!-- </dependency>--> </dependency>
<dependency> <dependency>
<groupId>org.apache.camel.quarkus</groupId> <groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core</artifactId> <artifactId>camel-quarkus-core</artifactId>
@ -49,20 +49,20 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.camel.quarkus</groupId> <groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-couchdb</artifactId> <artifactId>camel-quarkus-sql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.camel.quarkus</groupId> <groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-jackson</artifactId> <artifactId>camel-quarkus-direct</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>org.apache.camel.quarkus</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>camel-quarkus-log</artifactId>
</dependency> </dependency>
<!-- <dependency>-->
<!-- <groupId>io.quarkus</groupId>-->
<!-- <artifactId>quarkus-smallrye-reactive-messaging-rabbitmq</artifactId>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId> <artifactId>quarkus-arc</artifactId>
@ -91,11 +91,6 @@
<artifactId>camel-quarkus-mock</artifactId> <artifactId>camel-quarkus-mock</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-direct</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

View file

@ -2,12 +2,15 @@ package com.c4181.beans;
import com.c4181.properties.AppProperties; import com.c4181.properties.AppProperties;
import com.google.maps.GeoApiContext; import com.google.maps.GeoApiContext;
import io.quarkus.runtime.annotations.RegisterForReflection;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces; import javax.enterprise.inject.Produces;
import javax.inject.Inject; import javax.inject.Inject;
@ApplicationScoped @ApplicationScoped
@RegisterForReflection(targets = {com.google.maps.GeocodingApi.Response.class,
com.google.maps.model.GeocodingResult.class}, registerFullHierarchy = true)
public class GoogleApiBeans { public class GoogleApiBeans {
@Inject @Inject

View file

@ -3,15 +3,17 @@ package com.c4181.camel;
import com.c4181.model.JsoCall; import com.c4181.model.JsoCall;
import com.c4181.model.JsoCallDecoder; import com.c4181.model.JsoCallDecoder;
import com.c4181.properties.AppProperties; import com.c4181.properties.AppProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.logging.Log;
import com.fasterxml.jackson.databind.SerializationFeature; import org.apache.camel.Exchange;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.camel.builder.RouteBuilder; import org.apache.camel.builder.RouteBuilder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
@ApplicationScoped @ApplicationScoped
public class CamelConfiguration extends RouteBuilder { public class CamelConfiguration extends RouteBuilder {
@ -19,26 +21,58 @@ public class CamelConfiguration extends RouteBuilder {
@Inject @Inject
AppProperties appProperties; AppProperties appProperties;
@Inject
JsoCallDecoder jsoCallDecoder;
@Override @Override
public void configure() { public void configure() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule()); errorHandler(deadLetterChannel(appProperties.deadLetterRoute())
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); .onExceptionOccurred(exchange ->
Log.warnf("Failed to parse message in route %s. Sending to Dead Letter queue.", exchange.getProperty(Exchange.TO_ENDPOINT, String.class)))
.useOriginalMessage());
from(appProperties.jsoCadUpdateRouteIn()) from(appProperties.jsoCadUpdateRouteIn())
.filter(exchange -> StringUtils.isNotBlank(exchange.getIn().getBody(String.class))) .filter(exchange -> StringUtils.isNotBlank(exchange.getIn().getBody(String.class)))
.process((exchange -> { .process((exchange -> {
String updates = exchange.getIn().getBody(String.class); String updates = exchange.getIn().getBody(String.class);
List<JsoCall> jsoCalls = JsoCallDecoder.decodeJsoCallUpdates(updates); List<JsoCall> jsoCalls = jsoCallDecoder.decodeJsoCallUpdates(updates);
exchange.getIn().setBody(jsoCalls); exchange.getIn().setBody(jsoCalls);
})) }))
.removeHeader("*") .removeHeader("*")
.split(body()) .split(body())
.wireTap("direct:processedCalls")
.process(exchange -> { .process(exchange -> {
String jsonString = mapper.writeValueAsString(exchange.getIn().getBody(JsoCall.class)); JsoCall jsoCall = exchange.getIn().getBody(JsoCall.class);
exchange.getIn().setBody(jsonString); Map<String, Object> sqlCall = new HashMap<>();
sqlCall.put("incident_number", jsoCall.getIncidentNumber());
sqlCall.put("dispatched_time", jsoCall.getDispatchedTime());
sqlCall.put("address", jsoCall.getAddress());
sqlCall.put("signal", jsoCall.getSignal());
sqlCall.put("call_description", jsoCall.getCallDescription());
if (jsoCall.getPoint() != null) {
sqlCall.put("x", jsoCall.getPoint().getLat());
sqlCall.put("y", jsoCall.getPoint().getLng());
} else {
sqlCall.put("x", null);
sqlCall.put("y", null);
}
exchange.getIn().setBody(sqlCall);
}) })
.to(appProperties.jsoCadUpdateRouteOut()); .to(appProperties.jsoCadUpdateRouteOut());
from("direct:processedCalls")
.errorHandler(deadLetterChannel("log:dead?level=ERROR"))
.filter(exchange -> exchange.getIn().getBody(JsoCall.class).getPoint() != null)
.process(exchange -> {
JsoCall jsoCall = exchange.getIn().getBody(JsoCall.class);
if (Point2D.distance(jsoCall.getPoint().getLat(), jsoCall.getPoint().getLng(), appProperties.myLat(), appProperties.myLong())
<= appProperties.telegramNotificationThreshold()) {
exchange.setRouteStop(true);
}
})
.to(appProperties.telegramRoute());
} }
} }

View file

@ -10,6 +10,7 @@ public class JsoCall {
String address; String address;
String signal; String signal;
String callDescription; String callDescription;
Point point;
public String getIncidentNumber() { public String getIncidentNumber() {
return incidentNumber; return incidentNumber;
@ -51,17 +52,25 @@ public class JsoCall {
this.callDescription = callDescription; this.callDescription = callDescription;
} }
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
JsoCall jsoCall = (JsoCall) o; JsoCall jsoCall = (JsoCall) o;
return Objects.equals(incidentNumber, jsoCall.incidentNumber) && Objects.equals(dispatchedTime, jsoCall.dispatchedTime) && Objects.equals(address, jsoCall.address) && Objects.equals(signal, jsoCall.signal) && Objects.equals(callDescription, jsoCall.callDescription); return Objects.equals(incidentNumber, jsoCall.incidentNumber) && Objects.equals(dispatchedTime, jsoCall.dispatchedTime) && Objects.equals(address, jsoCall.address) && Objects.equals(signal, jsoCall.signal) && Objects.equals(callDescription, jsoCall.callDescription) && Objects.equals(point, jsoCall.point);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(incidentNumber, dispatchedTime, address, signal, callDescription); return Objects.hash(incidentNumber, dispatchedTime, address, signal, callDescription, point);
} }
@Override @Override
@ -72,6 +81,7 @@ public class JsoCall {
", address='" + address + '\'' + ", address='" + address + '\'' +
", signal='" + signal + '\'' + ", signal='" + signal + '\'' +
", callDescription='" + callDescription + '\'' + ", callDescription='" + callDescription + '\'' +
", point=" + point +
'}'; '}';
} }
} }

View file

@ -6,6 +6,7 @@ import com.google.maps.GeocodingApiRequest;
import com.google.maps.model.GeocodingResult; import com.google.maps.model.GeocodingResult;
import io.quarkus.logging.Log; import io.quarkus.logging.Log;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
@ -16,28 +17,44 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ApplicationScoped
public class JsoCallDecoder { public class JsoCallDecoder {
@Inject @Inject
GeoApiContext geoApiContext; GeoApiContext geoApiContext;
public static List<JsoCall> decodeJsoCallUpdates(String updates) { public List<JsoCall> decodeJsoCallUpdates(String updates) {
List<String> newCalls = Arrays.stream(updates.split("\n")) List<String> newCalls = Arrays.stream(updates.split("\n"))
.filter(line -> line.contains("added")) .filter(line -> line.contains("added"))
.filter(line -> !line.contains("Last refreshed")) .filter(line -> !line.contains("Last refreshed"))
.map(line -> line.replace("(added ) ", "")) .map(line -> line.replace("(added )", ""))
.map(String::trim)
.toList(); .toList();
String pattern = "(\\d+)\\s+(\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2})\\s+(.+)\\s+( \\d+\\s?[\\w\\d]*)\\s+(.+)";
Pattern p = Pattern.compile(pattern);
List<JsoCall> jsoCalls = new ArrayList<>(); List<JsoCall> jsoCalls = new ArrayList<>();
for (String call : newCalls) { for (String call : newCalls) {
String trimmedCall = call.trim(); Matcher m = p.matcher(call);
if (!m.matches() || m.groupCount() != 5) {
Log.warnf("Failed to parse call\n%s", call);
continue;
}
JsoCall jsoCall = new JsoCall(); JsoCall jsoCall = new JsoCall();
jsoCall.setIncidentNumber(trimmedCall.substring(0, 12)); jsoCall.setIncidentNumber(m.group(1).trim());
jsoCall.setDispatchedTime(parseTimeWithoutYear(trimmedCall.substring(14, 25))); jsoCall.setDispatchedTime(parseTimeWithoutYear(m.group(2).trim()));
jsoCall.setAddress(trimmedCall.substring(27, 69).trim()); jsoCall.setAddress(m.group(3).trim());
jsoCall.setSignal(trimmedCall.substring(69, 77).trim()); jsoCall.setSignal(m.group(4).trim());
jsoCall.setCallDescription(trimmedCall.substring(77).trim()); jsoCall.setCallDescription(m.group(5).trim());
if (!jsoCall.getAddress().contains("I95")
&& !jsoCall.getAddress().contains("I295") && !jsoCall.getAddress().contains("I10")) {
jsoCall.setPoint(geoCodeAddress(jsoCall.getAddress()));
}
jsoCalls.add(jsoCall); jsoCalls.add(jsoCall);
} }
@ -62,7 +79,7 @@ public class JsoCallDecoder {
private static LocalDateTime parseWithDefaultYear(String stringWithoutYear, int defaultYear) { private static LocalDateTime parseWithDefaultYear(String stringWithoutYear, int defaultYear) {
DateTimeFormatter parseFormatter = new DateTimeFormatterBuilder() DateTimeFormatter parseFormatter = new DateTimeFormatterBuilder()
.appendPattern("MM-dd HH:mm") .appendPattern("M-d HH:mm")
.parseDefaulting(ChronoField.YEAR, defaultYear) .parseDefaulting(ChronoField.YEAR, defaultYear)
.toFormatter(Locale.ENGLISH); .toFormatter(Locale.ENGLISH);
@ -71,7 +88,7 @@ public class JsoCallDecoder {
private Point geoCodeAddress(String address) { private Point geoCodeAddress(String address) {
GeocodingApiRequest request = GeocodingApi.newRequest(geoApiContext).address(address); GeocodingApiRequest request = GeocodingApi.newRequest(geoApiContext).address(address + "Jacksonville, FL");
GeocodingResult[] results; GeocodingResult[] results;
try { try {
results = request.await(); results = request.await();
@ -80,7 +97,7 @@ public class JsoCallDecoder {
return null; return null;
} }
if (results == null || results[0] == null) { if (results == null || results[0] == null || results[0].geometry.location == null) {
return null; return null;
} }

View file

@ -7,5 +7,10 @@ public interface AppProperties {
String jsoCadUpdateRouteIn(); String jsoCadUpdateRouteIn();
String jsoCadUpdateRouteOut(); String jsoCadUpdateRouteOut();
String deadLetterRoute();
String telegramRoute();
String googleApiKey(); String googleApiKey();
double myLat();
double myLong();
double telegramNotificationThreshold();
} }

View file

@ -1,3 +1,17 @@
app.jso-cad-update-route-in=rabbitmq:192.168.1.117/jso.cad.updates.to.couchdb?queue=jso.cad.update.received&declare=false&vhost=jso&username=${RABBITMQ_USER}&password=${RABBITMQ_PASSWORD}&autoDelete=false app.jso-cad-update-route-in=rabbitmq:${RABBITMQ_IP}/jso.cad.updates.to.postgres?queue=jso.cad.update.received&declare=false&vhost=jso&username=${RABBITMQ_USER}&password=${RABBITMQ_PASSWORD}&autoDelete=false
app.jso-cad-update-route-out=couchdb:http://192.168.1.220:5984/jso-calls?username=${COUCHDB_USER}&password=${COUCHDB_PASSWORD} app.jso-cad-update-route-out=sql:INSERT INTO calls(incident_number, dispatched_time, address, signal, call_description, point) VALUES (:#incident_number, :#dispatched_time, :#address, :#signal, :#call_description, point(:#x, :#y))
app.dead-letter-route=rabbitmq:${RABBITMQ_IP}/failed.updates?declare=false&vhost=jso&username=${RABBITMQ_USER}&password=${RABBITMQ_PASSWORD}&autoDelete=false
app.telegram-route=telegram:bots?authorizationToken=${TELEGRAM_BOT_ID}&chatId=${CHAT_ID}
app.google-api-key=${GOOGLE_API_KEY} app.google-api-key=${GOOGLE_API_KEY}
app.my-lat=30.3025061
app.my-long=-81.6436614
app.telegram-notification-threshold=2.0
RABBITMQ_IP=192.168.1.117
POSTGRES_IP=192.168.1.17
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://${POSTGRES_IP}:5432/jsoCad

View file

@ -28,14 +28,14 @@ class CamelConfigTest {
@Test @Test
void happyPathTest() throws IOException { void happyPathTest() throws IOException {
// MockEndpoint endpoint = (MockEndpoint) camelContext.getEndpoint(appProperties.jsoCadUpdateRouteOut()); MockEndpoint endpoint = (MockEndpoint) camelContext.getEndpoint(appProperties.jsoCadUpdateRouteOut());
String data = Files.readString(Paths.get("src/test/resources/test-payload.txt")); String data = Files.readString(Paths.get("src/test/resources/test-payload.txt"));
producerTemplate.sendBody(appProperties.jsoCadUpdateRouteIn(), data); producerTemplate.sendBody(appProperties.jsoCadUpdateRouteIn(), data);
// assertEquals(52, endpoint.getExchanges().size()); assertEquals(52, endpoint.getExchanges().size());
// JsoCall call = endpoint.getExchanges().get(0).getIn().getBody(JsoCall.class); JsoCall call = endpoint.getExchanges().get(0).getIn().getBody(JsoCall.class);
// assertEquals("202200769492", call.getIncidentNumber()); assertEquals("202200769492", call.getIncidentNumber());
} }
} }

View file

@ -8,6 +8,7 @@ import java.nio.file.Paths;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class JsoCallDecoderTest { class JsoCallDecoderTest {
@ -15,7 +16,59 @@ class JsoCallDecoderTest {
void testDecode() throws IOException { void testDecode() throws IOException {
String data = Files.readString(Paths.get("src/test/resources/test-payload.txt")); String data = Files.readString(Paths.get("src/test/resources/test-payload.txt"));
List<JsoCall> calls = JsoCallDecoder.decodeJsoCallUpdates(data); JsoCallDecoder decoder = new JsoCallDecoder();
List<JsoCall> calls = decoder.decodeJsoCallUpdates(data);
assertEquals(52, calls.size()); assertEquals(52, calls.size());
} }
@Test
void testEdgeCaseSignals() throws IOException {
String data = Files.readString(Paths.get("src/test/resources/edge-cases.txt"));
JsoCallDecoder decoder = new JsoCallDecoder();
List<JsoCall> calls = decoder.decodeJsoCallUpdates(data);
assertEquals(6, calls.size());
JsoCall firstCall = calls.get(0);
assertEquals("202200769492", firstCall.getIncidentNumber());
assertNotNull(firstCall.getDispatchedTime());
assertEquals("5200 RAMONA BLVD", firstCall.getAddress());
assertEquals("13", firstCall.getSignal());
assertEquals("SUSPICIOUS PERSON", firstCall.getCallDescription());
JsoCall secondCall = calls.get(1);
assertEquals("202200769474", secondCall.getIncidentNumber());
assertNotNull(secondCall.getDispatchedTime());
assertEquals("9100 MERRILL RD", secondCall.getAddress());
assertEquals("4", secondCall.getSignal());
assertEquals("AUTO CRASH", secondCall.getCallDescription());
JsoCall thirdCall = calls.get(2);
assertEquals("202200769409", thirdCall.getIncidentNumber());
assertNotNull(thirdCall.getDispatchedTime());
assertEquals("1000 ST CLAIR ST", thirdCall.getAddress());
assertEquals("21CT", thirdCall.getSignal());
assertEquals("BURGLARY CONVEYANCE TELESERVE", thirdCall.getCallDescription());
JsoCall fourthCall = calls.get(3);
assertEquals("202300004101", fourthCall.getIncidentNumber());
assertNotNull(fourthCall.getDispatchedTime());
assertEquals("12000 ATLANTIC BLVD", fourthCall.getAddress());
assertEquals("1050", fourthCall.getSignal());
assertEquals("TRAFFIC STOP", fourthCall.getCallDescription());
JsoCall fifthCall = calls.get(4);
assertEquals("202300004011", fifthCall.getIncidentNumber());
assertNotNull(fifthCall.getDispatchedTime());
assertEquals("BULLS BAY HWY / BEAVER ST W", fifthCall.getAddress());
assertEquals("0 13", fifthCall.getSignal());
assertEquals("ARMED SUSPICIOUS PERSON", fifthCall.getCallDescription());
JsoCall sixthCall = calls.get(5);
assertEquals("202300004560", sixthCall.getIncidentNumber());
assertNotNull(sixthCall.getDispatchedTime());
assertEquals("3700 TOLEDO RD", sixthCall.getAddress());
assertEquals("37", sixthCall.getSignal());
assertEquals("UNVERIFIED 911 CALL", sixthCall.getCallDescription());
}
} }

View file

@ -0,0 +1,23 @@
JSO CAD
JACKSONVILLE SHERIFF'S OFFICE
JSO Calls for Service
COMPLETED DISPATCHED CALLS FOR SERVICE
Welcome to the Jacksonville Sheriffs Office Completed Dispatched Calls for Service webpage. This page displays calls for service made to the Jacksonville Sheriff's Office that have recently been completed. The data on this page is refreshed automatically.
This information is not intended to be used as official crime data. This program does not provide information about all crimes, and excludes specific incidents such as sexual assaults and child abuse.
Disclaimer: The Jacksonville Sheriff's Office makes every effort to produce and publish current and accurate information. No warranties, expressed or implied, are provided for the data herein, its use, or its interpretation. The services provided are for informational purposes only and should not be relied on for any type of legal action.
(changed) Last refreshed 12/31 12:49:22
(into ) Last refreshed 12/31 13:45:54
Incident # Dispatched Block Address Signal Call Description
(added ) 202200769492 12-31 12:26 5200 RAMONA BLVD 13 SUSPICIOUS PERSON
(added ) 202200769474 12-31 12:19 9100 MERRILL RD 4 AUTO CRASH
(added ) 202200769409 12-31 11:18 1000 ST CLAIR ST 21CT BURGLARY CONVEYANCE TELESERVE
(added ) 202300004101 1-3 08:29 12000 ATLANTIC BLVD 1050 TRAFFIC STOP
(added ) 202300004011 1-3 07:37 BULLS BAY HWY / BEAVER ST W 0 13 ARMED SUSPICIOUS PERSON
(added ) 202300004560 1-3 11:44 3700 TOLEDO RD 37 UNVERIFIED 911 CALL