geospatial-routing-data

$npx mdskill add elizaOS/eliza/geospatial-routing-data

Use this skill before building a routing model or validating a routing report that contains coordinates, depots, station IDs, and route sequences.

SKILL.md

.github/skills/geospatial-routing-dataView on GitHub ↗
---
name: geospatial-routing-data
description: Geospatial routing data handling for depot and station coordinates, route node IDs, internal index mappings, great-circle distance matrices, and route-distance reconstruction. Use when optimization or reporting tasks involve latitude/longitude, station IDs, depots, distance metrics, vehicle routes, or validating travel distance from reported paths.
---

# Geospatial Routing Data

Use this skill before building a routing model or validating a routing report that contains coordinates, depots, station IDs, and route sequences.

The main risk is mixing user-facing IDs with internal array indices or using a different distance metric from the task.

## Parse Data Safely

Load structured data with a parser and build explicit mappings:

```python
import json
from pathlib import Path

data = json.loads(Path("/root/data.json").read_text())
stations_data = data["stations"]

station_ids = [int(s["id"]) for s in stations_data]
if len(station_ids) != len(set(station_ids)):
    raise ValueError("duplicate station ids")

id_to_idx = {sid: idx for idx, sid in enumerate(station_ids)}
idx_to_id = {idx: sid for sid, idx in id_to_idx.items()}
```

Use internal indices in optimization variables. Use original station IDs in final reports.

## Coordinate Validation

Check coordinates before building distances:

```python
def parse_location(record, label):
    lat = float(record["latitude"])
    lon = float(record["longitude"])
    if not (-90.0 <= lat <= 90.0):
        raise ValueError(f"{label} latitude out of range: {lat}")
    if not (-180.0 <= lon <= 180.0):
        raise ValueError(f"{label} longitude out of range: {lon}")
    return {"latitude": lat, "longitude": lon}

depot = parse_location(data["depot"], "depot")
station_locations = [parse_location(s, f"station {s['id']}") for s in stations_data]
```

Latitude and longitude are degrees. Convert to radians only inside the distance function.

## Great-Circle Distance

Match the task's declared distance metric. If the task specifies an Earth radius, use that exact value.

For great-circle miles with Earth radius `3960.0`, use:

```python
import math

def great_circle_miles(a, b, radius=3960.0):
    lat1 = float(a["latitude"])
    lon1 = float(a["longitude"])
    lat2 = float(b["latitude"])
    lon2 = float(b["longitude"])

    deg_to_rad = math.pi / 180.0
    phi1 = (90.0 - lat1) * deg_to_rad
    phi2 = (90.0 - lat2) * deg_to_rad
    theta1 = lon1 * deg_to_rad
    theta2 = lon2 * deg_to_rad

    cos_arc = (
        math.sin(phi1) * math.sin(phi2) * math.cos(theta1 - theta2)
        + math.cos(phi1) * math.cos(phi2)
    )
    cos_arc = max(-1.0, min(1.0, cos_arc))
    return math.acos(cos_arc) * radius
```

Clamp `cos_arc` into `[-1, 1]` to avoid floating-point domain errors.

Do not mix:

- Euclidean distance on degrees;
- haversine with a different Earth radius;
- miles and meters;
- rounded distances inside the optimization objective.

## Build Routing Nodes

Use separate depot labels when the route output must show a start and end depot:

```python
START = "depot_start"
END = "depot_end"

stations = range(len(stations_data))
from_nodes = [START, *stations]
to_nodes = [*stations, END]

def node_location(node):
    if node in (START, END):
        return depot
    return station_locations[int(node)]
```

Build distances over the same arc set used by the optimization model:

```python
distances = {}
for i in from_nodes:
    for j in to_nodes:
        if i == j:
            continue
        if i == START and j == END:
            continue  # omit if vehicles must visit at least one station
        distances[i, j] = great_circle_miles(node_location(i), node_location(j))
```

If direct depot-to-depot travel is allowed, keep the `(START, END)` arc.

## Convert Routes Between IDs and Indices

Optimization route using internal indices:

```python
route_nodes = [START, 3, 7, 2, END]
```

Report route using original station IDs:

```python
report_route = [
    node if isinstance(node, str) else idx_to_id[int(node)]
    for node in route_nodes
]
```

Parse a reported route back to internal indices:

```python
def parse_report_route(route):
    if route[0] != START or route[-1] != END:
        raise ValueError("route must start at depot_start and end at depot_end")

    parsed = [START]
    for raw in route[1:-1]:
        sid = int(raw)
        if sid not in id_to_idx:
            raise ValueError(f"unknown station id {sid}")
        parsed.append(id_to_idx[sid])
    parsed.append(END)
    return parsed
```

Never assume station IDs are `0..n-1`.

## Reconstruct Route Distance

Recompute reported travel distance from route sequences:

```python
def pairwise(items):
    return list(zip(items, items[1:]))

def route_distance_internal(route_nodes):
    total = 0.0
    for i, j in pairwise(route_nodes):
        total += distances[i, j]
    return total

def route_distance_reported_ids(route):
    internal = parse_report_route(route)
    return route_distance_internal(internal)
```

For multiple vehicles:

```python
travel_distance = sum(
    route_distance_reported_ids(vehicle["route"])
    for vehicle in report["vehicles"]
)
```

Compare with tolerance, not exact string equality:

```python
def assert_close(actual, expected, tol=1e-6):
    if abs(actual - expected) > max(tol, tol * max(1.0, abs(expected))):
        raise AssertionError(f"{actual} != {expected}")
```

## Route Data Checks

Before trusting a route:

- first node is the start depot label;
- last node is the end depot label;
- every non-depot node is a known station ID;
- route has at least one station if vehicles cannot stay at the depot;
- station sequence length equals the stop list length;
- no repeated station within a route when per-vehicle no-repeat is required;
- distance is recomputed from coordinates, not copied from model output.

More from elizaOS/eliza

SkillDescription
ac-branch-pi-modelAC branch pi-model power flow equations (P/Q and |S|) with transformer tap ratio and phase shift, matching `acopf-math-model.md` and MATPOWER branch fields. Use when computing branch flows in either direction, aggregating bus injections for nodal balance, checking MVA (rateA) limits, computing branch loading %, or debugging sign/units issues in AC power flow.
academic-pdf-redactionRedact text from PDF documents for blind review anonymization
ada-plan-view-accessibilityUse when checking simplified ADA-derived plan-view bathroom accessibility constraints such as turning space, door clear width, toilet centerline, grab bars, and lavatory knee/toe clearance.
analyze-ciAnalyze failed GitHub Action jobs for a pull request.
architectural-dxf-extractionUse when extracting plan-view architectural geometry from DXF files with semantic CAD layers, especially when outputs must normalize rooms, doors, fixtures, clearances, and grab bars into machine-checkable JSON.
attitude-controller-plannerUse this skill when implementing the inner control loop for a quadrotor — attitude (roll/pitch/yaw) PID control and attitude planning (converting desired acceleration to desired Euler angles). Covers gain layout, integral reset pattern, and the attitude planner inverse kinematics.
azure-bgpAnalyze and resolve BGP oscillation and BGP route leaks in Azure Virtual WAN–style hub-and-spoke topologies (and similar cloud-managed BGP environments). Detect preference cycles, identify valley-free violations, and propose allowed policy-level mitigations while rejecting prohibited fixes.
box-least-squaresBox Least Squares (BLS) periodogram for detecting transiting exoplanets and eclipsing binaries. Use when searching for periodic box-shaped dips in light curves. Alternative to Transit Least Squares, available in astropy.timeseries. Based on Kovács et al. (2002).
browser-testingVERIFY your changes work. Measure CLS, detect theme flicker, test visual stability, check performance. Use BEFORE and AFTER making changes to confirm fixes. Includes ready-to-run scripts: measure-cls.ts, detect-flicker.ts
cache-policy-comparisonCompare and implement eviction policies (LRU, LFU, FIFO, S3FIFO, ARC) for bounded-capacity caches. Use when choosing or implementing an eviction policy for a buffer pool, page cache, CDN edge, or LLM KV cache, or when writing a replay simulator that supports multiple policies. Clarifies recency vs frequency semantics, queue topology, saturating counters, ghost buffers, and the second-chance rule that distinguishes modern FIFO-family policies from classic LRU.