Compare commits

...

3 commits

Author SHA1 Message Date
93b69c4cbb
Remove dead query.go at project root
The QueryWriter interface and queryToString function had zero callers.
The commented-out insertQueryToString was a Bob remnant. The io import
was only used in this file.
2026-05-09 01:05:29 +00:00
7ffa2e891b
Remove dead esbuild build.js and flake.nix dependency
build.js was an esbuild-based build script from the pre-Vite era (March 2026).
It is not referenced by package.json, CI, or any config. Vite is used for
both sync and rmo builds. Also dropped pkgs.esbuild from flake.nix devShell.
2026-05-09 01:03:51 +00:00
be99baf64c
Remove unused tomtom/ integration
TomTom was added Feb 2026 for routing but was never imported outside
its own directory. Stadia Maps is now the geocoding and tile provider.
No references in go.mod, go.sum, or any Go file.
2026-05-09 01:01:49 +00:00
13 changed files with 8 additions and 814 deletions

View file

@ -115,15 +115,9 @@ Once all routes are ported or confirmed dead, remove the entire `html/template/`
---
## 3. esbuild (`build.js`) — Dead Build Tool
## 3. esbuild (`build.js`) — Removed ✅
**Status:** `build.js` is an esbuild-based build script that predates the Vite migration. It is not referenced by `package.json` scripts (those use `vite build`). It is also not referenced by any CI or Nix config.
### 3a. Remove esbuild-related files
- `build.js` — the esbuild build script
- Remove `pkgs.esbuild` from `flake.nix` devShell dependencies (if not used elsewhere)
- Remove esbuild-related dependencies from `package.json` if present (currently no esbuild deps are in `package.json` — they may have been already cleaned)
*(Completed 2026-05-09: `build.js` removed and `pkgs.esbuild` dropped from flake.nix devShell — Vite is the build tool)*
---
@ -169,14 +163,9 @@ The map-locator, address-suggestion, and photo-upload functionality has Vue equi
---
## 5. TomTom Integration — Unused
## 5. TomTom Integration — Removed ✅
**Status:** The `tomtom/` directory contains a TomTom routing/geocoding client. It is not imported by any file outside the `tomtom/` directory. Stadia Maps is now used for geocoding and tiles.
### 5a. Remove TomTom entirely
- `tomtom/` — entire directory
- `tomtom/example/` — example code
*(Completed 2026-05-09: `tomtom/` directory removed — zero imports outside itself, Stadia Maps is now the geocoding/tile provider)*
---
@ -247,9 +236,7 @@ Verify that all code references use the external package, not a local path.
## 10. Old Generated Files & Artifacts
### 10a. `query.go` at project root
Contains a commented-out Bob query interface and an unused `QueryWriter` interface. The `insertQueryToString` function is entirely commented out. Either repurpose or remove.
### 10a. `query.go` at project root — Removed ✅
### 10b. `db/sql/` directory
@ -295,10 +282,10 @@ Empty placeholder file. Remove.
## Priority Summary
1. **High impact, low effort:**
- Remove `tomtom/` (unused, no imports)
- Remove `build.js` (dead, replaced by Vite)
- ~~Remove `tomtom/` (unused, no imports)~~ ✅
- ~~Remove `build.js` (dead, replaced by Vite)~~ ✅
- Remove commented-out routes in `sync/routes.go` and `rmo/routes.go`
- Remove `query.go` commented-out code
- ~~Remove `query.go` commented-out code~~ ✅
- Remove `static/gen/main.js` stale artifact
- Remove `static/css/placeholder`

View file

@ -1,92 +0,0 @@
import esbuild from "esbuild";
import vue from "esbuild-plugin-vue3";
import { sassPlugin } from "esbuild-sass-plugin";
const args = process.argv.slice(2);
const watch = args.includes("--watch");
const minify = args.includes("--minify");
// Plugin to show build status
const buildStatusPlugin = {
name: "build-status",
setup(build) {
let buildStart;
build.onStart(() => {
buildStart = Date.now();
// Clear console and move cursor to top
console.clear();
console.log(
"\x1b[36m%s\x1b[0m",
`🔨 Building... [${new Date().toLocaleTimeString()}]`,
);
});
build.onEnd((result) => {
const buildTime = Date.now() - buildStart;
if (result.errors.length > 0) {
console.log(
"\x1b[31m%s\x1b[0m",
`❌ Build failed (${buildTime}ms) [${new Date().toLocaleTimeString()}]`,
);
} else {
console.log(
"\x1b[32m%s\x1b[0m",
`✅ Build complete (${buildTime}ms) [${new Date().toLocaleTimeString()}]`,
);
}
console.log("\x1b[33m%s\x1b[0m", "\n👀 Watching for changes...");
});
},
};
const config = {
entryPoints: ["ts/main.ts"],
bundle: true,
format: "esm",
plugins: [
buildStatusPlugin, // Add this first
sassPlugin({
quietDeps: true,
precompile(source, pathname) {
// Only inject variables into Vue component styles
// (not the main scss files to avoid circular imports)
if (pathname.endsWith(".vue")) {
return `@import "./ts/style/variables.scss";\n${source}`;
}
return source;
},
silenceDeprecations: ["import"],
type: "css",
}),
vue({
sourceMap: true,
}),
],
sourcemap: true,
sourcesContent: true,
define: {
__VUE_OPTIONS_API__: "true",
__VUE_PROD_DEVTOOLS__: "false",
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false",
},
minify,
loader: {
".css": "css",
".woff": "file",
".woff2": "file",
".ttf": "file",
".eot": "file",
},
outdir: "static/gen",
outbase: "ts",
assetNames: "fonts/[name]",
};
if (watch) {
const ctx = await esbuild.context(config);
await ctx.watch();
console.log("\x1b[33m%s\x1b[0m", "👀 Watching for changes...\n");
} else {
await esbuild.build(config);
}

View file

@ -31,7 +31,6 @@
pkgs.air
pkgs.autoprefixer
pkgs.dart-sass
pkgs.esbuild
pkgs.go
pkgs.go-jet
pkgs.golangci-lint

View file

@ -1,34 +0,0 @@
package main
import (
"bytes"
"context"
"fmt"
"io"
//"github.com/Gleipnir-Technology/bob"
//"github.com/Gleipnir-Technology/bob/dialect/psql"
)
type QueryWriter interface {
WriteQuery(ctx context.Context, w io.Writer, start int) ([]any, error)
}
func queryToString(query QueryWriter) string {
buf := new(bytes.Buffer)
_, err := query.WriteQuery(context.TODO(), buf, 0)
if err != nil {
return fmt.Sprintf("Failed to write query to buffer: %v", err)
}
return buf.String()
}
/*
func insertQueryToString(query bob.BaseQuery[*dialect.InsertQuery]) string {
buf := new(bytes.Buffer)
_, err := query.WriteQuery(context.TODO(), buf, 0)
if err != nil {
return fmt.Sprintf("Failed to write query: %v", err)
}
return buf.String()
}
*/

View file

@ -1,52 +0,0 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ./example/geocode-and-route"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
silent = false
time = false
[misc]
clean_on_exit = false
[proxy]
app_port = 0
enabled = false
proxy_port = 0
[screen]
clear_on_rebuild = false
keep_scroll = true

View file

@ -1,61 +0,0 @@
package main
import (
"log"
"github.com/Gleipnir-Technology/nidus-sync/tomtom"
)
func main() {
client := tomtom.NewClient()
// Example 1: Geocode a series of addresses
waypoints := []string{
"1737 W Houston Ave, Visalia, CA 93291",
"1138 W Prescott Ave, Visalia, CA 93291",
"3228 W Clinton Ct, Visalia, CA 93291",
"3800 N Mendonca St, Visalia, CA 93291",
}
coords := make([]tomtom.Point, 0)
for _, wp := range waypoints {
geocode, err := client.Geocode(wp)
if err != nil {
log.Fatal("Failed to geocode '%s': %w", wp, err)
}
if len(geocode.Results) == 0 {
log.Fatal("Failed to get any results for '%s'", wp)
}
result := geocode.Results[0]
coords = append(coords, result.Position.AsPoint())
log.Printf("Geocoded %s to %f, %f", wp, result.Position.Longitude, result.Position.Latitude)
}
// Example 2: Calculate a simple route through them
traffic := false
routeRequest := &tomtom.CalculateRouteRequest{
Locations: coords,
Traffic: &traffic,
TravelMode: tomtom.TravelModeCar,
RouteType: tomtom.RouteTypeFastest,
}
//client.SetDebug(true)
routeResp, err := client.CalculateRoute(routeRequest)
if err != nil {
log.Fatal(err)
}
all_points := make([]tomtom.Point, 0)
all_stops := make([]tomtom.Point, 0)
for i, route := range routeResp.Routes {
s := route.Summary
log.Printf("%d: %d meters, %d seconds, %s traffic delay", i, s.LengthInMeters, s.TravelTimeInSeconds, s.TrafficDelayInSeconds)
for _, leg := range route.Legs {
all_stops = append(all_stops, leg.Points[0])
all_points = append(all_points, leg.Points...)
}
}
lines := tomtom.PolylineToGeoJSON(all_points)
log.Printf("%s", lines)
stops := tomtom.PolylineToGeoJSON(all_stops)
log.Printf("%s", stops)
}

View file

@ -1,31 +0,0 @@
package main
import (
"fmt"
"log"
"github.com/Gleipnir-Technology/nidus-sync/tomtom"
)
func main() {
client := tomtom.NewClient()
// Example 1: Calculate a simple route
traffic := false
routeRequest := &tomtom.CalculateRouteRequest{
Locations: []tomtom.Point{
tomtom.P(52.50931, 13.42936),
tomtom.P(52.50274, 13.43872),
},
Traffic: &traffic,
TravelMode: tomtom.TravelModeCar,
RouteType: tomtom.RouteTypeFastest,
}
routeResp, err := client.CalculateRoute(routeRequest)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Route distance: %d meters\n", routeResp.Routes[0].Summary.LengthInMeters)
}

View file

@ -1,96 +0,0 @@
package tomtom
import (
"fmt"
)
type PointShort struct {
Latitude float64 `json:"lat"`
Longitude float64 `json:"lon"`
}
func (ps PointShort) AsPoint() Point {
return Point(ps)
}
type GeocodeResult struct {
Type string `json:"type"`
ID string `json:"id"`
Score float64 `json:"score"`
Dist float64 `json:"dist"`
MatchConfidence MatchConfidence `json:"matchConfidence"`
Address Address `json:"address"`
Position PointShort `json:"position"`
Viewport Viewport `json:"viewport"`
EntryPoints []EntryPoint `json:"entryPoints"`
}
// MatchConfidence represents the confidence score for a match
type MatchConfidence struct {
Score float64 `json:"score"`
}
// Address contains detailed address information
type Address struct {
StreetNumber string `json:"streetNumber"`
StreetName string `json:"streetName"`
Municipality string `json:"municipality"`
CountrySecondarySubdivision string `json:"countrySecondarySubdivision"`
CountrySubdivision string `json:"countrySubdivision"`
CountrySubdivisionName string `json:"countrySubdivisionName"`
CountrySubdivisionCode string `json:"countrySubdivisionCode"`
PostalCode string `json:"postalCode"`
ExtendedPostalCode string `json:"extendedPostalCode"`
CountryCode string `json:"countryCode"`
Country string `json:"country"`
CountryCodeISO3 string `json:"countryCodeISO3"`
FreeformAddress string `json:"freeformAddress"`
LocalName string `json:"localName"`
}
// Viewport defines a geographic bounding box
type Viewport struct {
TopLeftPoint PointShort `json:"topLeftPoint"`
BtmRightPoint PointShort `json:"btmRightPoint"`
}
// EntryPoint contains information about a point of entry to a location
type EntryPoint struct {
Type string `json:"type"`
Position PointShort `json:"position"`
}
type GeocodeSummary struct {
Query string `json:"query"`
QueryType string `json:"queryType"`
QueryTime uint `json:"queryTime"`
NumResults uint `json:"numResults"`
Offset uint `json:"offset"`
TotalResults uint `json:"totalResults"`
FuzzyLevel uint `json:"fuzzyLevel"`
GeoBias PointShort `json:"geoBias"`
}
type GeocodeResponse struct {
Summary GeocodeSummary `json:"summary"`
Results []GeocodeResult `json:"results"`
}
// CalculateRoute sends a route calculation request to TomTom API
func (c *TomTom) Geocode(address string) (*GeocodeResponse, error) {
var result GeocodeResponse
resp, err := c.client.R().
SetResult(&result).
SetPathParam("address", address).
SetPathParam("urlBase", c.urlBase).
SetQueryParam("key", c.APIKey).
SetQueryParam("storeResult", "false").
Get("https://{urlBase}/search/2/geocode/{address}.json")
if err != nil {
return nil, fmt.Errorf("calculate route get: %w", err)
}
if !resp.IsSuccess() {
return nil, fmt.Errorf("calculate route status: %d", resp.Status)
}
return &result, nil
}

View file

@ -1,27 +0,0 @@
package tomtom
import (
"fmt"
"strings"
)
// Convert a slice of points to GeoJSON
func PolylineToGeoJSON(polyline []Point) string {
var sb strings.Builder
sb.WriteString(`{"type":"LineString","coordinates":[`)
for i, point := range polyline {
// IMPORTANT: GeoJSON uses [longitude, latitude] order!
sb.WriteString(fmt.Sprintf("[%g,%g]", point.Longitude, point.Latitude))
// Add comma if not the last point
if i < len(polyline)-1 {
sb.WriteString(",")
}
}
sb.WriteString("]}")
return sb.String()
}

View file

@ -1,126 +0,0 @@
package tomtom
import (
"fmt"
"net/url"
"strings"
"github.com/google/go-querystring/query"
)
// CalculateRouteRequest combines both path parameters and POST body
type CalculateRouteRequest struct {
// Path parameters
Locations Locations
// Query parameters
MaxAlternatives *int
AlternativeType string
MinDeviationDistance *int
MinDeviationTime *int
InstructionsType string
Language string
ComputeBestOrder *bool
RouteRepresentation string
ComputeTravelTimeFor string
VehicleHeading *int
SectionType []string
IncludeTollPaymentTypes string
Callback string
Report string
DepartAt string
ArriveAt string
RouteType string
Traffic *bool
Avoid []string
TravelMode string
Hilliness string
Windingness string
VehicleMaxSpeed *int
VehicleWeight *int
VehicleAxleWeight *int
VehicleNumberOfAxles *int
VehicleLength *float64
VehicleWidth *float64
VehicleHeight *float64
VehicleCommercial *bool
VehicleLoadType []string
VehicleAdrTunnelRestrictionCode string
VehicleHasElectricTollCollectionTransponder string
VehicleEngineType string
ConstantSpeedConsumptionInLitersPerHundredkm string
CurrentFuelInLiters *float64
AuxiliaryPowerInLitersPerHour *float64
FuelEnergyDensityInMJoulesPerLiter *float64
AccelerationEfficiency *float64
DecelerationEfficiency *float64
UphillEfficiency *float64
DownhillEfficiency *float64
ConsumptionInkWhPerkmAltitudeGain *float64
RecuperationInkWhPerkmAltitudeLoss *float64
ConstantSpeedConsumptionInkWhPerHundredkm string
CurrentChargeInkWh *float64
MaxChargeInkWh *float64
AuxiliaryPowerInkW *float64
}
func (sgr CalculateRouteRequest) toQueryParams() (url.Values, error) {
return query.Values(sgr)
}
type CalculateRouteResponse struct {
Routes []Route `json:"routes"`
}
// CalculateRoute sends a route calculation request to TomTom API
func (c *TomTom) CalculateRoute(req *CalculateRouteRequest) (*CalculateRouteResponse, error) {
/*url, err := req.BuildURL(c.APIKey)
if err != nil {
return nil, err
}*/
var result CalculateRouteResponse
/*
query, err := req.toQueryParams()
if err != nil {
return nil, fmt.Errorf("structured geocode query: %w", err)
}
*/
resp, err := c.client.R().
//SetQueryParamsFromValues(query).
SetResult(&result).
SetPathParam("locations", locationString(req.Locations)).
SetPathParam("urlBase", c.urlBase).
SetQueryParam("key", c.APIKey).
Get("https://{urlBase}/routing/1/calculateRoute/{locations}/json")
if err != nil {
return nil, fmt.Errorf("calculate route get: %w", err)
}
if !resp.IsSuccess() {
return nil, fmt.Errorf("calculate route status: %d", resp.Status)
}
return &result, nil
}
func P(lat, lng float64) Point {
return Point{
Latitude: lat,
Longitude: lng,
}
}
type Locations = []Point
func locationString(locations Locations) string {
var sb strings.Builder
for i, p := range locations {
if i == 0 {
sb.WriteString(fmt.Sprintf("%f,%f", p.Latitude, p.Longitude))
} else {
sb.WriteString(fmt.Sprintf(":%f,%f", p.Latitude, p.Longitude))
}
}
return sb.String()
}

View file

@ -1 +0,0 @@
package tomtom

View file

@ -1,37 +0,0 @@
package tomtom
import (
"os"
"resty.dev/v3"
)
type TomTom struct {
APIKey string
client *resty.Client
urlBase string
}
func NewClient() *TomTom {
api_key := os.Getenv("TOMTOM_API_KEY")
r := resty.New()
return &TomTom{
APIKey: api_key,
client: r,
urlBase: "api.tomtom.com",
}
}
func (s *TomTom) Close() {
s.client.Close()
}
func (s *TomTom) SetDebug(enabled bool) {
s.client.Close()
if enabled {
s.client = resty.New().SetDebug(true)
} else {
s.client = resty.New().SetDebug(false)
}
}

View file

@ -1,235 +0,0 @@
package tomtom
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
// Base URLs and API constants
const (
BaseURL = "https://api.tomtom.com"
RouteTypeFastest = "fastest"
TravelModeCar = "car"
)
// Coordinates represents latitude and longitude values
type Coordinates struct {
Latitude string `json:"latitude" xml:"latitude,attr"`
Longitude string `json:"longitude" xml:"longitude,attr"`
}
// Rectangle represents a geographic rectangle
type Rectangle struct {
SouthWestCorner Coordinates `json:"southWestCorner" xml:"southWestCorner"`
NorthEastCorner Coordinates `json:"northEastCorner" xml:"northEastCorner"`
}
// AvoidAreas represents areas to avoid in routing
type AvoidAreas struct {
Rectangles []Rectangle `json:"rectangles" xml:"rectangles>rectangle"`
}
// SupportingPoint represents a supporting point in the route calculation
type SupportingPoint struct {
Latitude string `json:"latitude" xml:"latitude,attr"`
Longitude string `json:"longitude" xml:"longitude,attr"`
}
// Client represents a TomTom API client
type Client struct {
APIKey string
HTTPClient *http.Client
}
// CalculateRoutePostData represents the POST body for Calculate Route API
type CalculateRoutePostData struct {
SupportingPoints []SupportingPoint `json:"supportingPoints,omitempty" xml:"supportingPoints>supportingPoint,omitempty"`
AvoidVignette []string `json:"avoidVignette,omitempty" xml:"avoidVignette,omitempty"`
AllowVignette []string `json:"allowVignette,omitempty" xml:"allowVignette,omitempty"`
AvoidAreas *AvoidAreas `json:"avoidAreas,omitempty" xml:"avoidAreas,omitempty"`
}
// Route response structures - These would need to be completed based on actual API response
type Summary struct {
LengthInMeters int `json:"lengthInMeters"`
TravelTimeInSeconds int `json:"travelTimeInSeconds"`
TrafficDelayInSeconds int `json:"trafficDelayInSeconds"`
DepartureTime string `json:"departureTime"`
ArrivalTime string `json:"arrivalTime"`
FuelConsumptionInLiters float64 `json:"fuelConsumptionInLiters,omitempty"`
ElectricEnergyConsumptionInkWh float64 `json:"electricEnergyConsumptionInkWh,omitempty"`
}
type Point struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
type Leg struct {
Summary Summary `json:"summary"`
Points []Point `json:"points,omitempty"`
}
type Route struct {
Summary Summary `json:"summary"`
Legs []Leg `json:"legs,omitempty"`
}
// CalculateReachableRange API structures
// CalculateReachableRangeParams holds the parameters for the Calculate Reachable Range API
type CalculateReachableRangeParams struct {
// Path parameters
Origin string
ContentType string // "json" or "jsonp"
// Query parameters
FuelBudgetInLiters *float64
EnergyBudgetInkWh *float64
TimeBudgetInSec *float64
Callback string
Report string
DepartAt string
ArriveAt string
RouteType string
Traffic *bool
Avoid []string
TravelMode string
Hilliness string
Windingness string
VehicleMaxSpeed *int
VehicleWeight *int
VehicleAxleWeight *int
VehicleNumberOfAxles *int
VehicleLength *float64
VehicleWidth *float64
VehicleHeight *float64
VehicleCommercial *bool
VehicleLoadType []string
VehicleAdrTunnelRestrictionCode string
VehicleHasElectricTollCollectionTransponder string
VehicleEngineType string
ConstantSpeedConsumptionInLitersPerHundredkm string
CurrentFuelInLiters *float64
AuxiliaryPowerInLitersPerHour *float64
FuelEnergyDensityInMJoulesPerLiter *float64
AccelerationEfficiency *float64
DecelerationEfficiency *float64
UphillEfficiency *float64
DownhillEfficiency *float64
ConsumptionInkWhPerkmAltitudeGain *float64
RecuperationInkWhPerkmAltitudeLoss *float64
ConstantSpeedConsumptionInkWhPerHundredkm string
CurrentChargeInkWh *float64
MaxChargeInkWh *float64
AuxiliaryPowerInkW *float64
}
// CalculateReachableRangePostData represents the POST body for Calculate Reachable Range API
type CalculateReachableRangePostData struct {
AvoidVignette []string `json:"avoidVignette,omitempty" xml:"avoidVignette,omitempty"`
AllowVignette []string `json:"allowVignette,omitempty" xml:"allowVignette,omitempty"`
AvoidAreas *AvoidAreas `json:"avoidAreas,omitempty" xml:"avoidAreas,omitempty"`
}
// CalculateReachableRangeRequest combines both path parameters and POST body
type CalculateReachableRangeRequest struct {
Params CalculateReachableRangeParams
PostData *CalculateReachableRangePostData
}
// Reachable Range response structures
type Polygon struct {
Exterior []Point `json:"exterior"`
Interior [][]Point `json:"interior,omitempty"`
}
type CalculateReachableRangeResponse struct {
Polygon Polygon `json:"polygon"`
Summary struct {
DistanceLimit float64 `json:"distanceLimit,omitempty"`
TimeLimit int `json:"timeLimit,omitempty"`
FuelConsumptionLimit float64 `json:"fuelConsumptionLimit,omitempty"`
EnergyConsumptionLimit float64 `json:"energyConsumptionLimit,omitempty"`
} `json:"summary"`
}
// BuildURL builds the URL for the Calculate Reachable Range request
func (req *CalculateReachableRangeRequest) BuildURL(apiKey string) (string, error) {
baseURL := fmt.Sprintf("%s/routing/%d/calculateReachableRange/%s/%s",
BaseURL,
req.Params.Origin,
req.Params.ContentType)
// Add query parameters
query := url.Values{}
query.Add("key", apiKey)
if req.Params.FuelBudgetInLiters != nil {
query.Add("fuelBudgetInLiters", fmt.Sprintf("%f", *req.Params.FuelBudgetInLiters))
}
if req.Params.EnergyBudgetInkWh != nil {
query.Add("energyBudgetInkWh", fmt.Sprintf("%f", *req.Params.EnergyBudgetInkWh))
}
if req.Params.TimeBudgetInSec != nil {
query.Add("timeBudgetInSec", fmt.Sprintf("%f", *req.Params.TimeBudgetInSec))
}
// Add other parameters similarly...
return baseURL + "?" + query.Encode(), nil
}
// Client methods for executing requests
// CalculateReachableRange sends a reachable range calculation request to TomTom API
func (c *Client) CalculateReachableRange(req *CalculateReachableRangeRequest) (*CalculateReachableRangeResponse, error) {
url, err := req.BuildURL(c.APIKey)
if err != nil {
return nil, err
}
var response CalculateReachableRangeResponse
var httpReq *http.Request
if req.PostData != nil {
// POST request
jsonData, err := json.Marshal(req.PostData)
if err != nil {
return nil, err
}
httpReq, err = http.NewRequest("POST", url, strings.NewReader(string(jsonData)))
if err != nil {
return nil, err
}
httpReq.Header.Set("Content-Type", "application/json")
} else {
// GET request
httpReq, err = http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
}
resp, err := c.HTTPClient.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error: %s", resp.Status)
}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return nil, err
}
return &response, nil
}