Skip to content

Bearologics/DMRmap

Repository files navigation

DMRmap

A web app that visualizes DMR repeaters on an interactive map. Filter by band and network, search by callsign or address, find repeaters along a driving route, and export CPS-ready channel configurations.

Live: https://dmrmap.kida.io

Features

  • Interactive OpenStreetMap with repeater markers (blue = VHF/2m, red = UHF/70cm)
  • Band filtering (2m / 70cm) and network filtering (BrandMeister, DMR+, TGIF, Other)
  • Toggle visibility of personal hotspots and inactive repeaters
  • Free-text search by callsign, city, or DMR ID
  • Address-to-address routing via OSRM with adjustable corridor width
  • Click-to-pin with adjustable radius search
  • Detailed repeater popups (frequencies, color code, timeslots, hardware, power, antenna height, BrandMeister status)
  • Real-time Last Heard heatmap via BrandMeister WebSocket
  • CPS Studio: manage talk groups, set timeslots, export Motorola CPS 2.0 compatible XML
  • Coordinate display with Maidenhead grid locator
  • Dark mode (follows system preference)
  • Internationalization (English, German, Spanish, French, Italian, Polish)
  • No external CDN dependencies — all assets served locally

Tech Stack

  • Backend: Go (stdlib net/http), PostgreSQL 17 via pgx/v5, migrations via goose/v3
  • Frontend: TypeScript (bundled with esbuild), Leaflet, i18next
  • Testing: Vitest (92 unit tests)
  • Infrastructure: Docker Compose, multi-stage Dockerfile

External Services

Service Usage Auth
RadioID Repeater database (seeded from rptrs.json dump) None
BrandMeister API Device status sync (/v2/device/{id}), talk group registry (/v2/talkgroup) None (public read)
BrandMeister WebSocket Real-time Last Heard feed for heatmap via socket.io None
Nominatim Address geocoding and autocomplete (client-side) None
OSRM Driving route calculation (client-side) None

The BrandMeister talk group registry is fetched once at Docker build time and bundled as frontend/static/talkgroups.json. It is used for talk group name resolution and autocomplete in CPS Studio.

Running with Docker

docker compose up --build

Open http://localhost:8080. PostgreSQL data is persisted in a named volume.

Development

Hot-reload with air (Go) and esbuild watch (TypeScript):

docker compose -f compose.dev.yml up --build

The dev container mounts the project directory, so changes to Go and frontend files are picked up automatically.

Running Tests

docker compose -f compose.test.yml run --rm test

Environment Variables

Variable Default Description
DATABASE_URL postgres://dmrmap:dmrmap@localhost:5432/dmrmap?sslmode=disable PostgreSQL connection string
JSON_PATH rptrs.json Path to RadioID repeater dump
BMRPTRS_PATH bmrptrs.json Path to BrandMeister repeater dump
LISTEN_ADDR :8080 HTTP listen address
STATIC_DIR static Path to static frontend assets
MIGRATIONS_DIR migrations Path to SQL migration files
BM_SYNC (disabled) Set to true to enable periodic BrandMeister device sync
ADMIN_TOKEN (disabled) Set to enable the admin interface at /admin/

API Endpoints

Method Path Description
GET /api/repeaters Repeaters within map bounding box (query: minLat, maxLat, minLng, maxLng, band, network, hotspots, inactive)
GET /api/repeaters/radius Repeaters within radius of a point (query: lat, lng, radius)
POST /api/repeaters/route Repeaters along a route corridor (body: GeoJSON coordinates + corridor width)
GET /api/repeater Single repeater by ID (query: id)
GET /api/repeaters/search Free-text search (query: q)

Project Structure

├── backend/
│   ├── main.go          # Route registration, env config
│   ├── db.go            # DB queries, Repeater struct, geo helpers
│   ├── handlers.go      # Public API handlers
│   ├── admin.go         # Admin auth middleware + admin API
│   ├── bmdevice.go      # BrandMeister device sync
│   ├── seed.go          # Database seeding from JSON dumps
│   ├── migrate.go       # Goose migration runner
│   ├── go.mod / go.sum
│   ├── migrations/      # SQL migrations (goose)
│   └── data/            # RadioID + BrandMeister JSON dumps
├── frontend/
│   ├── src/             # TypeScript source + tests
│   ├── static/
│   │   ├── index.html   # Single-page HTML shell
│   │   ├── admin.html   # Admin interface
│   │   ├── style.css    # Styles with dark mode support
│   │   ├── i18n.js      # i18next initialization
│   │   ├── locales/     # Translation files (en, de, es, fr, it, pl)
│   │   └── lib/         # Vendored JS libraries (Leaflet, i18next, socket.io)
│   ├── package.json
│   ├── tsconfig.json
│   └── vitest.config.ts
├── Dockerfile           # Production multi-stage build
├── Dockerfile.dev       # Development image with air + esbuild
├── compose.yml          # Production Docker Compose
├── compose.dev.yml      # Development Docker Compose
└── compose.test.yml     # Test runner Docker Compose

Data

Repeater data provided by RadioID. Availability and talk group data by BrandMeister. This project is not affiliated with or endorsed by RadioID or BrandMeister.

License

MIT

Releases

No releases published

Packages

 
 
 

Contributors