Skip to content

Commit a119af6

Browse files
committed
feat: integrate geocoding functionality with backend support
- Added geocoding capabilities to the backend using OpenStreetMap and MNLR Address Server. - Implemented new API endpoints for geocoding and backend status. - Introduced a new frontend view for displaying backend information and geocoding documentation. - Updated router to include the new geocode backend view. - Enhanced the PMMapsViewer component to utilize the new geocoding API. - Added GeoResponse and BackendInfo types to manage geocoding responses. - Included the Maplibre Geocoder library for frontend geocoding functionality. - Updated environment variable handling for geocoding server configuration.
1 parent 0f81e29 commit a119af6

File tree

11 files changed

+521
-213
lines changed

11 files changed

+521
-213
lines changed

backend/go.mod

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
module mnlr.de/mnlrmap
22

3-
go 1.24
3+
go 1.24.1
4+
5+
toolchain go1.24.3
46

57
require (
8+
github.com/codingsince1985/geo-golang v1.8.5
9+
github.com/joho/godotenv v1.5.1
610
github.com/pocketbase/pocketbase v0.28.1
711
github.com/protomaps/go-pmtiles v1.28.0
812
)
@@ -15,34 +19,21 @@ require (
1519
cloud.google.com/go/iam v1.1.13 // indirect
1620
cloud.google.com/go/storage v1.43.0 // indirect
1721
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect
18-
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
1922
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
2023
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 // indirect
21-
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
22-
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
23-
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
2424
github.com/RoaringBitmap/roaring v1.5.0 // indirect
25-
github.com/alecthomas/kong v0.8.0 // indirect
2625
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
27-
github.com/aws/aws-sdk-go v1.55.5 // indirect
2826
github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect
2927
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.9 // indirect
30-
github.com/aws/aws-sdk-go-v2/config v1.29.6 // indirect
31-
github.com/aws/aws-sdk-go-v2/credentials v1.17.59 // indirect
32-
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
3328
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.62 // indirect
3429
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
3530
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
36-
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
3731
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32 // indirect
3832
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
3933
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.0 // indirect
4034
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
4135
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 // indirect
4236
github.com/aws/aws-sdk-go-v2/service/s3 v1.77.0 // indirect
43-
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
44-
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
45-
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
4637
github.com/aws/smithy-go v1.22.2 // indirect
4738
github.com/beorn7/perks v1.0.1 // indirect
4839
github.com/bits-and-blooms/bitset v1.2.0 // indirect
@@ -61,20 +52,16 @@ require (
6152
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
6253
github.com/google/s2a-go v0.1.9 // indirect
6354
github.com/google/uuid v1.6.0 // indirect
64-
github.com/google/wire v0.6.0 // indirect
6555
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
6656
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
6757
github.com/inconshreveable/mousetrap v1.1.0 // indirect
68-
github.com/jmespath/go-jmespath v0.4.0 // indirect
69-
github.com/kylelemons/godebug v1.1.0 // indirect
7058
github.com/mattn/go-colorable v0.1.14 // indirect
7159
github.com/mattn/go-isatty v0.0.20 // indirect
7260
github.com/mattn/go-runewidth v0.0.14 // indirect
7361
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
7462
github.com/mschoch/smat v0.2.0 // indirect
7563
github.com/ncruces/go-strftime v0.1.9 // indirect
7664
github.com/paulmach/orb v0.10.0 // indirect
77-
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
7865
github.com/pocketbase/dbx v1.11.0 // indirect
7966
github.com/prometheus/client_golang v1.19.1 // indirect
8067
github.com/prometheus/client_model v0.5.0 // indirect

backend/go.sum

Lines changed: 35 additions & 182 deletions
Large diffs are not rendered by default.

backend/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"log"
55
"os"
66

7+
"github.com/joho/godotenv"
78
"github.com/pocketbase/pocketbase"
89
)
910

@@ -31,10 +32,13 @@ func newApplication() *application {
3132
}
3233

3334
func main() {
35+
36+
godotenv.Load()
3437
app := newApplication()
3538
app.mountFs()
3639
app.useMigrations()
3740
app.usePMTiles()
41+
app.useGeocoder()
3842

3943
log.Fatal(app.pb.Start())
4044
}

backend/useGeocoder.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
8+
"github.com/codingsince1985/geo-golang/openstreetmap"
9+
"github.com/pocketbase/pocketbase/core"
10+
)
11+
12+
type Address struct {
13+
ID int64 `json:"id"`
14+
Street string `json:"street"`
15+
HouseNumber string `json:"house_number"`
16+
City string `json:"city"`
17+
Longitude float64 `json:"longitude"`
18+
Latitude float64 `json:"latitude"`
19+
}
20+
21+
type MNLRAddressServerResponse struct {
22+
Schema string `json:"$schema"`
23+
Addresses []Address `json:"addresses"`
24+
}
25+
26+
type BackendInfoResponse struct {
27+
Reachable bool `json:"reachable"`
28+
Name string `json:"name"`
29+
}
30+
31+
// fs to maps folder
32+
33+
// mountFs configures the embedded file system for the application's
34+
// front-end assets when building for production.
35+
func (app *application) useGeocoder() {
36+
37+
// register "GET /hello/{name}" route (allowed for everyone)
38+
app.pb.OnServe().BindFunc(func(se *core.ServeEvent) error {
39+
addresserver := getEnvOrDefault("MNLRADDRESSSERVER", "")
40+
// register "GET /hello/{name}" route (allowed for everyone)
41+
se.Router.GET("/geoapi/geocode", func(e *core.RequestEvent) error {
42+
43+
addresserver := getEnvOrDefault("MNLRADDRESSSERVER", "")
44+
45+
var resultAddress []Address
46+
search := e.Request.URL.Query().Get("q")
47+
48+
if addresserver == "" {
49+
resultAddress, _ = GetGeoFromOSM(search)
50+
}
51+
if addresserver != "" {
52+
resultAddress, _ = GetGeoFromMNLR(search, addresserver)
53+
}
54+
55+
return e.JSON(http.StatusOK, resultAddress)
56+
57+
})
58+
59+
// register "GET /hello/{name}" route (allowed for everyone)
60+
se.Router.GET("/geoapi/backend", func(e *core.RequestEvent) error {
61+
62+
var backendInfo BackendInfoResponse = BackendInfoResponse{
63+
Reachable: false,
64+
Name: "OSM",
65+
}
66+
67+
// Check if the address server is reachable by performing a demo request
68+
if addresserver != "" {
69+
_, err := GetGeoFromMNLR("test", addresserver)
70+
if err != nil {
71+
backendInfo.Reachable = false
72+
backendInfo.Name = addresserver + " (MNLRAddressServer)"
73+
} else {
74+
backendInfo.Reachable = true
75+
backendInfo.Name = addresserver + " (MNLRAddressServer)"
76+
}
77+
}
78+
79+
if addresserver == "" {
80+
_, err := GetGeoFromOSM("test")
81+
if err != nil {
82+
backendInfo.Reachable = false
83+
backendInfo.Name = "OSM"
84+
} else {
85+
backendInfo.Reachable = true
86+
backendInfo.Name = "OSM"
87+
}
88+
}
89+
90+
return e.JSON(http.StatusOK, backendInfo)
91+
92+
})
93+
94+
return se.Next()
95+
})
96+
97+
}
98+
99+
func GetGeoFromMNLR(address string, url string) ([]Address, error) {
100+
// Get Request to url/geocode?q=address
101+
102+
httpClient := &http.Client{}
103+
req, err := http.NewRequest("GET", url+"/api/search", nil)
104+
if err != nil {
105+
return nil, err
106+
}
107+
q := req.URL.Query()
108+
q.Add("q", address)
109+
req.URL.RawQuery = q.Encode()
110+
resp, err := httpClient.Do(req)
111+
if err != nil {
112+
return nil, err
113+
}
114+
defer resp.Body.Close()
115+
if resp.StatusCode != http.StatusOK {
116+
return nil, err
117+
}
118+
// Parse the response
119+
var mnlrrespnse MNLRAddressServerResponse
120+
121+
data, err := io.ReadAll(resp.Body)
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
err = json.Unmarshal(data, &mnlrrespnse)
127+
if err != nil {
128+
return nil, err
129+
}
130+
131+
return mnlrrespnse.Addresses, nil
132+
}
133+
134+
func GetGeoFromOSM(address string) ([]Address, error) {
135+
// Use OSM as default
136+
geocoder := openstreetmap.Geocoder()
137+
138+
// Geocode the address
139+
resultLatLong, err := geocoder.Geocode(address)
140+
if err != nil {
141+
return nil, err
142+
}
143+
144+
resulltReverse, err := geocoder.ReverseGeocode(resultLatLong.Lat, resultLatLong.Lng)
145+
if err != nil {
146+
return nil, err
147+
}
148+
149+
// Return results
150+
addresses := make([]Address, 1)
151+
addresses[0].ID = 1
152+
addresses[0].Street = resulltReverse.Street
153+
addresses[0].HouseNumber = resulltReverse.HouseNumber
154+
addresses[0].City = resulltReverse.City
155+
addresses[0].Longitude = resultLatLong.Lng
156+
addresses[0].Latitude = resultLatLong.Lat
157+
158+
return addresses, nil
159+
}

frontend/src/router/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useAuth } from "@/composeables/useAuth";
21
import { createRouter, createWebHistory } from "vue-router";
32
import HomeView from "../views/HomeView.vue";
43

@@ -28,6 +27,15 @@ const router = createRouter({
2827
title: "Map Extract",
2928
},
3029
},
30+
{
31+
path: "/geocodebackend",
32+
name: "geocodebackend",
33+
component: () => import("@/views/GecodeBackendView.vue"),
34+
meta: {
35+
icon: "search",
36+
title: "GeoCode Backend",
37+
},
38+
},
3139
{
3240
path: "/localmapslist",
3341
name: "localmapslist",

frontend/src/types/custom-types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,17 @@ export interface MapSize {
2020
name: string;
2121
sizeBytes: number;
2222
}
23+
24+
export interface GeoResponse {
25+
id: number;
26+
street: string;
27+
house_number: string;
28+
city: string;
29+
longitude: number;
30+
latitude: number;
31+
}
32+
33+
export interface BackendInfo {
34+
reachable: boolean;
35+
name: string;
36+
}

0 commit comments

Comments
 (0)