The Swedish Police has an Open API where the latest public police notifications can be fetched. I have made a self-hosted system that regularly collects that data and stores it in a local database. A self-hosted REST API makes the harvested data searchable and I have an Angular application that displays the events on a map based on area and location words in the summary text. As an additional exercise, I have also deployed a version of this system to Azure.
The code base can be downloaded from GitHub: https://github.com/LarsBergqvist/police-events
This blog post describes the design and implementation of this system.
I wanted to create an application where I can aggregate, search for and view public police events in Sweden. The Swedish Police has an Open Api where you can fetch the last 500 published events. The Api policy states that you are not allowed to make too frequent calls to the Api so I have made a service that makes a fetch every 10 minutes and stores the data locally. A benefit with this approach is that it will, over time, build up a history of all published data, and not be limited to the last 500 events.
Around 100 events are published daily. An event contains date/time, the name of the county or municipality where the event occurred, a short text summary, a type (e.g. accident, burglary) and a link to the event on the official web site (polisen.se) where updates and more details of the event can be viewed.
The event also contains a longitude/latitude position, but this is only the midpoint of the county or municipality, so it is not really useful. I want to decorate my map with more information so I make a look-up from public.opendatasoft.com/api for getting GeoJson boundary for the county or municipality and I show this as an overlay polygon on the map.
When the event is from a municipality, the summary text often contains some more useful location information in plain text (like an area name or a street name). I pick out the relevant words and use them for a query to nominatim.openstreetmap.org to search for the location. If a location match is found, this data is displayed as a bounding box for the likely area (this approach is not perfect but it works pretty well for most cases).
The system consists of these parts:
- A .NET Core/C# background service, CollectorService, that fetches the latest events, re-formats them and inserts/updates them into a MongoDB database
- A .NET Core/C# Web API service that provides look-up of the data via REST requests
- An Nginx web server that serves an Angular application to browsers
- An Angular application that runs in the browser. It lets you search and display events and show the information on a map with additional geo-information
I have packed the two services and the web server as Docker images that are instantiated as containers on a Linux machine at home.
The system uses these Open Data Api:s to gather information:
- polisen.se/api/events – Provides the event data
- tile.openstreetmap.org – Provides the tiles for the OpenStreetMap data (through OpenLayers in Angular)
- public.opendatasoft.com/api – Provides GeoJson polygons for boundaries of Swedish counties and municipalities
- nominatim.openstreetmap.org – Provides free text search for locations in OpenStreetMap
The Angular application
The PoliceEvent-application is developed with TypeScript using the Angular framework. PrimeNG is used a UI-library and OpenLayers is used for displaying OpenStreetMap maps with polygon overlays for the county/municipality and detailed area (when available). The application is very similar to the one described in my previous post, Traffic information with OpenLayers and Angular, but instead of using markers for displaying a set of positions I use polygons for displaying counties, municipalities and areas.
As the audience for this kind of data is Swedish, the UI i Swedish only. The main screen provides searching for events within different distances from the your position (your position is fetched from the browser). This distance calculation is based on the midpoint for the county/municipality that is provided with the events, so it is not perfect. You can also fetch events for the whole country and use a free-text filter for limiting the result list.
Each event has a map-button that opens a map that shows the county or municipality where the event occurred. If a more detailed area could be deduced from the summary text (with my super-simple algorithm described below), this area is displayed as an additional overlay.
As described earlier, I use nominatim.openstreetmap.org to search for likely areas based on information found in the summary text of the event. Preferably, I would like to have a lexical analysis of the text to pick out the perfect location words, but I tested a super-simple approach that actually worked quite well: Pick out all words that have a leading Uppercase letter and discard any words that are starting words for sentences. One-letter words and special police word combinations are also discarded. Combine the selected words into a free-text search for nominatim and for most cases you get a decent match. As streets and areas in openstreetmap can be divided into several parts with the same name (for example, a street can be divided into sections) you might get a a result with a bounding box for a section of a road. To overcome this I expand the returned bounding box area if the area is very small.
The backend implementation is based on a variant of the clean architecture pattern and CQRS. The Core project contains models, business logic and interfaces. The API and the CollectorService has a direct dependency on Core and dispatch all work to abstractions in the Core project. I use MediatR for dispatching Queries and Commands from the controller in the API and the CollectorService worker to handlers in the Core project. All concrete implementations with external dependencies, e.g. dependencies on MongoDB or external APIs are placed in the Infrastructure project. The other projects do not reference these implementations directly, the required objects are provided by the .NET Core dependency injection mechanism by binding interfaces to implementations in the service configuration setup.
Deploying the system to Azure
The initial goal for this project was to have all services and data locally hosted, but as an additional exercise I have also deployed it to Azure.
The API and the Angular application are served from two different Azure App Services. The CollectorService has been replaced by an Azure function that is triggered by a timer event from Azure. I avoid the cost of an Azure database by using a free tier of MongoDB Atlas – the free 512MB storage is sufficient for the needs of this system.
I use Azure DevOps for automatically re-building the system when a change has been pushed to the GitHub repository. A release pipeline in Azure DevOps updates the Azure resources after every successful build.
You can get the code from GitHub and experiment and improve it as you like: https://github.com/LarsBergqvist/police-events
The README-file contains information on how to build and run the system locally. There are Docker files included in the repository so you can use them to run the system with Docker containers.