Spaces:
Sleeping
Sleeping
Upload 34 files
Browse files- .dockerignore +26 -0
- .gitattributes +37 -35
- Dockerfile +28 -0
- README.md +10 -10
- analytics.py +193 -0
- app.py +473 -0
- apt.txt +4 -0
- baseline.py +93 -0
- cache/090c41e37bdb93f0bcebd4b4bc1f40b7bab09935.json +1 -0
- cache/2df67666927fdbbb503bc38e6fa304e5f7232647.json +0 -0
- cache/462dafbdff2cf3940ddb594f9d84d228a81a8662.json +0 -0
- cache/681ccfe17697053de5ea8ada83e18772e2e1b958.json +0 -0
- cache/8c72638f670e14db619bc7fb41926d3eda0b3a8c.json +1 -0
- challenge_description.txt +58 -0
- cities_graph.graphml +0 -0
- cities_graph_dropoffs.png +3 -0
- coords.csv +427 -0
- coords_original.csv +1369 -0
- dirs.csv +1369 -0
- firestore_writer.py +313 -0
- geometry.py +54 -0
- graph_manager.py +251 -0
- interhack26.pdf +3 -0
- loader.py +136 -0
- optimize_box.py +572 -0
- products.json +123 -0
- render.yaml +17 -0
- requirements.txt +15 -0
- sample_request.json +169 -0
- solver.py +157 -0
- travel_time.npz +3 -0
- travel_time.py +77 -0
- travel_time_request.npz +3 -0
- vans.json +37 -0
.dockerignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
*.pyo
|
| 4 |
+
*.pyd
|
| 5 |
+
.pytest_cache/
|
| 6 |
+
.cache/
|
| 7 |
+
cache/
|
| 8 |
+
.venv/
|
| 9 |
+
venv/
|
| 10 |
+
|
| 11 |
+
# Plots and binders generated during local exploration
|
| 12 |
+
*.png
|
| 13 |
+
*.pdf
|
| 14 |
+
challenge_description.txt
|
| 15 |
+
obsolete/
|
| 16 |
+
|
| 17 |
+
# Local secrets — must never enter the image. Use env vars instead.
|
| 18 |
+
seed/service-account.json
|
| 19 |
+
.env
|
| 20 |
+
.env.*
|
| 21 |
+
|
| 22 |
+
# Editor / OS
|
| 23 |
+
.vscode/
|
| 24 |
+
.idea/
|
| 25 |
+
.DS_Store
|
| 26 |
+
Thumbs.db
|
.gitattributes
CHANGED
|
@@ -1,35 +1,37 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
cities_graph_dropoffs.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
interhack26.pdf filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 4 |
+
PYTHONUNBUFFERED=1 \
|
| 5 |
+
MPLBACKEND=Agg \
|
| 6 |
+
PORT=7860
|
| 7 |
+
|
| 8 |
+
# Native deps for shapely / pyproj / osmnx.
|
| 9 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 10 |
+
libgeos-dev \
|
| 11 |
+
libspatialindex-dev \
|
| 12 |
+
libproj-dev \
|
| 13 |
+
proj-bin \
|
| 14 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 15 |
+
|
| 16 |
+
WORKDIR /app
|
| 17 |
+
|
| 18 |
+
# Install Python deps first so layer caches across code changes.
|
| 19 |
+
COPY requirements.txt .
|
| 20 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 21 |
+
|
| 22 |
+
# Copy backend sources. .dockerignore filters out pycache, plots, secrets.
|
| 23 |
+
COPY . .
|
| 24 |
+
|
| 25 |
+
EXPOSE 7860
|
| 26 |
+
|
| 27 |
+
# Listen on $PORT (set by HF Spaces, Cloud Run, Fly, Render). Fallback to 7860.
|
| 28 |
+
CMD ["sh", "-c", "uvicorn app:app --host 0.0.0.0 --port ${PORT:-7860} --workers 1"]
|
README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Interhack26
|
| 3 |
-
emoji: 🚀
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: pink
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
---
|
| 9 |
-
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Interhack26
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: pink
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
analytics.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""KPIs, explanations, load profile, warehouse-staging recommendations.
|
| 2 |
+
|
| 3 |
+
Everything here turns a `FleetPlan` from `solver.py` into the human-readable
|
| 4 |
+
add-ons that make the demo pitch land:
|
| 5 |
+
- per-van capacity-over-time profile (returnables visualization)
|
| 6 |
+
- per-stop arrival reasons
|
| 7 |
+
- fleet-level KPIs vs the naive baseline (savings %, CO2, utilization)
|
| 8 |
+
- warehouse staging recommendations for warehouse prep
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
|
| 13 |
+
from dataclasses import dataclass
|
| 14 |
+
|
| 15 |
+
from loader import Depot, Driver, Fleet, Stop
|
| 16 |
+
from solver import FleetPlan, VanPlan
|
| 17 |
+
from travel_time import TravelMatrix
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
# UK DEFRA factor for diesel light commercial vehicle, kg CO2 per km.
|
| 21 |
+
CO2_KG_PER_KM = 0.20
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@dataclass
|
| 25 |
+
class LoadPoint:
|
| 26 |
+
after_stop: str
|
| 27 |
+
cells: int
|
| 28 |
+
kg: int
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@dataclass
|
| 32 |
+
class KPIs:
|
| 33 |
+
fleet_drive_min: float
|
| 34 |
+
baseline_drive_min: float
|
| 35 |
+
savings_pct: float
|
| 36 |
+
fleet_km: float
|
| 37 |
+
baseline_km: float
|
| 38 |
+
co2_kg_saved: float
|
| 39 |
+
driver_utilization_pct: float
|
| 40 |
+
capacity_utilization_pct: float
|
| 41 |
+
stops_per_van: list[int]
|
| 42 |
+
feasible_vans: int
|
| 43 |
+
total_vans: int
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _hm(s: int) -> str:
|
| 47 |
+
h, m = divmod(int(s) // 60, 60)
|
| 48 |
+
return f"{h:02d}:{m:02d}"
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def load_profile(van: VanPlan, stops_by_id: dict[str, Stop]) -> list[LoadPoint]:
|
| 52 |
+
"""Truck departs depot fully loaded with all deliveries; load decreases as
|
| 53 |
+
we drop off and increases as we collect empties."""
|
| 54 |
+
if not van.stops:
|
| 55 |
+
return []
|
| 56 |
+
cells = sum(stops_by_id[p.id].delivery_cells for p in van.stops)
|
| 57 |
+
kg = sum(stops_by_id[p.id].delivery_kg for p in van.stops)
|
| 58 |
+
profile = [LoadPoint("DEPOT", int(cells), int(round(kg)))]
|
| 59 |
+
for p in van.stops:
|
| 60 |
+
s = stops_by_id[p.id]
|
| 61 |
+
cells = cells - s.delivery_cells + s.pickup_cells
|
| 62 |
+
kg = kg - s.delivery_kg + s.pickup_kg
|
| 63 |
+
profile.append(LoadPoint(p.id, int(cells), int(round(kg))))
|
| 64 |
+
return profile
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def explain_van(van: VanPlan, stops_by_id: dict[str, Stop], driver: Driver) -> list[str]:
|
| 68 |
+
"""One line per visited stop, naming the driver-readable reason."""
|
| 69 |
+
out: list[str] = []
|
| 70 |
+
if not van.stops:
|
| 71 |
+
out.append(f"{driver.id} idle (no stops in this run).")
|
| 72 |
+
return out
|
| 73 |
+
|
| 74 |
+
first = van.stops[0]
|
| 75 |
+
s_first = stops_by_id[first.id]
|
| 76 |
+
idle_min = max(0, (first.arrival_s - driver.shift_start_s) // 60)
|
| 77 |
+
win_w_min = (s_first.t_close_s - s_first.t_open_s) // 60
|
| 78 |
+
if idle_min > 30:
|
| 79 |
+
out.append(
|
| 80 |
+
f"{driver.id} departs {_hm(driver.shift_start_s)}, parks at {first.id} "
|
| 81 |
+
f"by {_hm(first.arrival_s)} — its {win_w_min}min window opens at "
|
| 82 |
+
f"{_hm(s_first.t_open_s)}, so the truck waits then unloads at the open."
|
| 83 |
+
)
|
| 84 |
+
else:
|
| 85 |
+
out.append(
|
| 86 |
+
f"{driver.id} hits {first.id} at {_hm(first.arrival_s)} — "
|
| 87 |
+
f"first stop is the closest with an open window."
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
for prev_p, cur_p in zip(van.stops, van.stops[1:]):
|
| 91 |
+
s_cur = stops_by_id[cur_p.id]
|
| 92 |
+
slack_min = max(0, (s_cur.t_close_s - cur_p.arrival_s) // 60)
|
| 93 |
+
out.append(
|
| 94 |
+
f" → {cur_p.id} at {_hm(cur_p.arrival_s)}, "
|
| 95 |
+
f"{slack_min}min before window {_hm(s_cur.t_close_s)} closes."
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
out.append(
|
| 99 |
+
f" → returns to depot. Peak load {van.peak_cells} cells / "
|
| 100 |
+
f"{van.peak_kg} kg ({van.travel_s // 60} min driving)."
|
| 101 |
+
)
|
| 102 |
+
return out
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def _van_distance_km(van: VanPlan, matrix: TravelMatrix) -> float:
|
| 106 |
+
if not van.stops:
|
| 107 |
+
return 0.0
|
| 108 |
+
depot_i = matrix.index_of("DEPOT")
|
| 109 |
+
total_m = 0.0
|
| 110 |
+
prev = depot_i
|
| 111 |
+
for sp in van.stops:
|
| 112 |
+
i = matrix.index_of(sp.id)
|
| 113 |
+
total_m += float(matrix.dist_m[prev, i])
|
| 114 |
+
prev = i
|
| 115 |
+
total_m += float(matrix.dist_m[prev, depot_i])
|
| 116 |
+
return total_m / 1000.0
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def compute_kpis(
|
| 120 |
+
plan: FleetPlan,
|
| 121 |
+
baseline: FleetPlan,
|
| 122 |
+
fleet: Fleet,
|
| 123 |
+
drivers: list[Driver],
|
| 124 |
+
matrix: TravelMatrix,
|
| 125 |
+
) -> KPIs:
|
| 126 |
+
drive_min = plan.drive_s / 60
|
| 127 |
+
base_drive_min = baseline.drive_s / 60
|
| 128 |
+
savings = (
|
| 129 |
+
((base_drive_min - drive_min) / base_drive_min * 100)
|
| 130 |
+
if base_drive_min > 0 else 0.0
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
fleet_km = sum(_van_distance_km(v, matrix) for v in plan.vans)
|
| 134 |
+
base_km = sum(_van_distance_km(v, matrix) for v in baseline.vans)
|
| 135 |
+
co2_saved = max(0.0, (base_km - fleet_km) * CO2_KG_PER_KM)
|
| 136 |
+
|
| 137 |
+
busy = sum(v.total_s for v in plan.vans)
|
| 138 |
+
shift = sum(d.shift_end_s - d.shift_start_s for d in drivers)
|
| 139 |
+
driver_util = (busy / shift * 100) if shift > 0 else 0.0
|
| 140 |
+
|
| 141 |
+
used = [v for v in plan.vans if v.stops]
|
| 142 |
+
cap_util = (
|
| 143 |
+
sum(v.peak_cells / fleet.capacity_cells for v in used) / len(used) * 100
|
| 144 |
+
if used else 0.0
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
return KPIs(
|
| 148 |
+
fleet_drive_min=round(drive_min, 2),
|
| 149 |
+
baseline_drive_min=round(base_drive_min, 2),
|
| 150 |
+
savings_pct=round(savings, 1),
|
| 151 |
+
fleet_km=round(fleet_km, 2),
|
| 152 |
+
baseline_km=round(base_km, 2),
|
| 153 |
+
co2_kg_saved=round(co2_saved, 2),
|
| 154 |
+
driver_utilization_pct=round(driver_util, 1),
|
| 155 |
+
capacity_utilization_pct=round(cap_util, 1),
|
| 156 |
+
stops_per_van=[len(v.stops) for v in plan.vans],
|
| 157 |
+
feasible_vans=sum(1 for v in plan.vans if v.feasible),
|
| 158 |
+
total_vans=len(plan.vans),
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def warehouse_prep(
|
| 163 |
+
plan: FleetPlan,
|
| 164 |
+
request_stops: list[dict],
|
| 165 |
+
stops_by_id: dict[str, Stop],
|
| 166 |
+
) -> list[str]:
|
| 167 |
+
"""Per-van staging hints, derived from each van's actual delivery list."""
|
| 168 |
+
recs: list[str] = []
|
| 169 |
+
by_id = {s["id"]: s for s in request_stops}
|
| 170 |
+
for v in plan.vans:
|
| 171 |
+
if not v.stops:
|
| 172 |
+
continue
|
| 173 |
+
sku_qty: dict[str, int] = {}
|
| 174 |
+
total_returnable = 0
|
| 175 |
+
for sp in v.stops:
|
| 176 |
+
req = by_id[sp.id]
|
| 177 |
+
for line in req.get("deliveries", []):
|
| 178 |
+
sku_qty[line["product_id"]] = sku_qty.get(line["product_id"], 0) + line["qty"]
|
| 179 |
+
for line in req.get("pickups", []):
|
| 180 |
+
total_returnable += line["qty"]
|
| 181 |
+
top = sorted(sku_qty.items(), key=lambda x: -x[1])[:3]
|
| 182 |
+
recs.append(
|
| 183 |
+
f"{v.driver_id}: stage "
|
| 184 |
+
f"{', '.join(f'{q}× {p}' for p, q in top)} "
|
| 185 |
+
f"({sum(sku_qty.values())} units total)"
|
| 186 |
+
)
|
| 187 |
+
if total_returnable:
|
| 188 |
+
first = stops_by_id[v.stops[0].id]
|
| 189 |
+
recs.append(
|
| 190 |
+
f" reserve ~{first.pickup_cells} returnable cells near {v.driver_id}'s door "
|
| 191 |
+
f"(pickups start at {v.stops[0].id})"
|
| 192 |
+
)
|
| 193 |
+
return recs
|
app.py
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FastAPI server: OR-tools VRPSPD-TW + analytics layer.
|
| 2 |
+
|
| 3 |
+
GET /health liveness probe
|
| 4 |
+
GET /sample-request bundled example body
|
| 5 |
+
POST /optimize main endpoint, returns plan + polylines + KPIs + explanations
|
| 6 |
+
POST /whatif perturb the request (driver out, traffic, stop cancelled) and re-solve
|
| 7 |
+
POST /baseline naive comparator alone
|
| 8 |
+
GET /docs auto Swagger UI
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
|
| 13 |
+
import json
|
| 14 |
+
from contextlib import asynccontextmanager
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from typing import Literal
|
| 17 |
+
|
| 18 |
+
import os
|
| 19 |
+
import httpx
|
| 20 |
+
from anthropic import Anthropic
|
| 21 |
+
import numpy as np
|
| 22 |
+
from fastapi import FastAPI, HTTPException, Response
|
| 23 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 24 |
+
from pydantic import BaseModel
|
| 25 |
+
|
| 26 |
+
from analytics import (
|
| 27 |
+
CO2_KG_PER_KM, KPIs, compute_kpis, explain_van, load_profile, warehouse_prep,
|
| 28 |
+
)
|
| 29 |
+
from baseline import solve_baseline
|
| 30 |
+
from firestore_writer import build_route_doc, is_enabled as fs_enabled, write_routes
|
| 31 |
+
from geometry import route_polyline
|
| 32 |
+
from graph_manager import get_or_build_graph
|
| 33 |
+
from loader import build_travel_matrix, load
|
| 34 |
+
from solver import solve_vrp
|
| 35 |
+
|
| 36 |
+
SAMPLE = Path(__file__).with_name("sample_request.json")
|
| 37 |
+
PRODUCTS = Path(__file__).with_name("products.json")
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
# --- Request schema (mirrors sample_request.json) ----------------------
|
| 41 |
+
|
| 42 |
+
class Coords(BaseModel):
|
| 43 |
+
lat: float
|
| 44 |
+
lng: float
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class TimeWindow(BaseModel):
|
| 48 |
+
open: str
|
| 49 |
+
close: str
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class Line(BaseModel):
|
| 53 |
+
product_id: str
|
| 54 |
+
qty: int
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class StopReq(BaseModel):
|
| 58 |
+
id: str
|
| 59 |
+
coords: Coords
|
| 60 |
+
time_window: TimeWindow
|
| 61 |
+
deliveries: list[Line]
|
| 62 |
+
pickups: list[Line] = []
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class DepotReq(BaseModel):
|
| 66 |
+
id: str
|
| 67 |
+
coords: Coords
|
| 68 |
+
open: str
|
| 69 |
+
close: str
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class FleetReq(BaseModel):
|
| 73 |
+
num_vans: int
|
| 74 |
+
van_type: str
|
| 75 |
+
vans_ref: str | None = None
|
| 76 |
+
products_ref: str | None = None
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class DriverReq(BaseModel):
|
| 80 |
+
id: str
|
| 81 |
+
shift_start: str
|
| 82 |
+
shift_end: str
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class OptimizeRequest(BaseModel):
|
| 86 |
+
request_id: str | None = None
|
| 87 |
+
date: str | None = None
|
| 88 |
+
depot: DepotReq
|
| 89 |
+
fleet: FleetReq
|
| 90 |
+
drivers: list[DriverReq]
|
| 91 |
+
stops: list[StopReq]
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class Disruption(BaseModel):
|
| 95 |
+
type: Literal["driver_unavailable", "stop_cancelled", "traffic"]
|
| 96 |
+
driver_id: str | None = None
|
| 97 |
+
stop_id: str | None = None
|
| 98 |
+
multiplier: float | None = None # for "traffic"
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class WhatIfRequest(BaseModel):
|
| 102 |
+
request: OptimizeRequest
|
| 103 |
+
disruption: Disruption
|
| 104 |
+
|
| 105 |
+
class ChatRequest(BaseModel):
|
| 106 |
+
transcript: str
|
| 107 |
+
context: str
|
| 108 |
+
|
| 109 |
+
class TTSRequest(BaseModel):
|
| 110 |
+
text: str
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
# --- Response schema ---------------------------------------------------
|
| 114 |
+
|
| 115 |
+
class StopOut(BaseModel):
|
| 116 |
+
sequence: int
|
| 117 |
+
id: str
|
| 118 |
+
arrival_time: str
|
| 119 |
+
coords: Coords
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class LoadPointOut(BaseModel):
|
| 123 |
+
after_stop: str
|
| 124 |
+
cells: int
|
| 125 |
+
kg: int
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class VanOut(BaseModel):
|
| 129 |
+
van_idx: int
|
| 130 |
+
driver_id: str
|
| 131 |
+
feasible: bool
|
| 132 |
+
travel_time_min: float
|
| 133 |
+
total_time_h: float
|
| 134 |
+
peak_cells: int
|
| 135 |
+
peak_kg: int
|
| 136 |
+
stops: list[StopOut]
|
| 137 |
+
polyline: list[Coords]
|
| 138 |
+
load_profile: list[LoadPointOut]
|
| 139 |
+
explanations: list[str]
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class KPIsOut(BaseModel):
|
| 143 |
+
fleet_drive_min: float
|
| 144 |
+
baseline_drive_min: float
|
| 145 |
+
savings_pct: float
|
| 146 |
+
fleet_km: float
|
| 147 |
+
baseline_km: float
|
| 148 |
+
co2_kg_saved: float
|
| 149 |
+
driver_utilization_pct: float
|
| 150 |
+
capacity_utilization_pct: float
|
| 151 |
+
stops_per_van: list[int]
|
| 152 |
+
feasible_vans: int
|
| 153 |
+
total_vans: int
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class OptimizeResponse(BaseModel):
|
| 157 |
+
request_id: str | None
|
| 158 |
+
fleet_drive_min: float
|
| 159 |
+
fleet_total_h: float
|
| 160 |
+
all_feasible: bool
|
| 161 |
+
depot: DepotReq
|
| 162 |
+
vans: list[VanOut]
|
| 163 |
+
kpis: KPIsOut
|
| 164 |
+
warehouse_prep: list[str]
|
| 165 |
+
firestore_written: int = 0
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
# --- App ---------------------------------------------------------------
|
| 169 |
+
|
| 170 |
+
@asynccontextmanager
|
| 171 |
+
async def lifespan(_: FastAPI):
|
| 172 |
+
get_or_build_graph()
|
| 173 |
+
yield
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
app = FastAPI(title="Damm Smart Truck API", version="2.0.0", lifespan=lifespan)
|
| 177 |
+
app.add_middleware(
|
| 178 |
+
CORSMiddleware, allow_origins=["*"],
|
| 179 |
+
allow_methods=["GET", "POST"], allow_headers=["*"],
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def _hm(s: int) -> str:
|
| 184 |
+
h, m = divmod(int(s) // 60, 60)
|
| 185 |
+
return f"{h:02d}:{m:02d}"
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def _run_pipeline(
|
| 189 |
+
body: dict,
|
| 190 |
+
*,
|
| 191 |
+
write_firestore: bool,
|
| 192 |
+
traffic_multiplier: float = 1.0,
|
| 193 |
+
time_limit_s: int = 5,
|
| 194 |
+
) -> OptimizeResponse:
|
| 195 |
+
"""Shared core for /optimize and /whatif."""
|
| 196 |
+
try:
|
| 197 |
+
depot, fleet, drivers, stops = load(body)
|
| 198 |
+
except KeyError as e:
|
| 199 |
+
raise HTTPException(422, f"unknown reference: {e}")
|
| 200 |
+
if not drivers:
|
| 201 |
+
raise HTTPException(422, "no drivers in fleet")
|
| 202 |
+
if len(drivers) != fleet.num_vans:
|
| 203 |
+
raise HTTPException(
|
| 204 |
+
422, f"num_vans={fleet.num_vans} but {len(drivers)} drivers"
|
| 205 |
+
)
|
| 206 |
+
if not stops:
|
| 207 |
+
raise HTTPException(422, "no stops to deliver")
|
| 208 |
+
|
| 209 |
+
matrix = build_travel_matrix(depot, stops)
|
| 210 |
+
if traffic_multiplier != 1.0:
|
| 211 |
+
matrix.time_s = (matrix.time_s * float(traffic_multiplier)).astype(np.float32)
|
| 212 |
+
|
| 213 |
+
plan = solve_vrp(depot, fleet, drivers, stops, matrix, time_limit_s=time_limit_s)
|
| 214 |
+
if not plan.vans:
|
| 215 |
+
# The disruption made the day infeasible — return a structured
|
| 216 |
+
# "no plan" response instead of 500 so the UI can show it.
|
| 217 |
+
return OptimizeResponse(
|
| 218 |
+
request_id=body.get("request_id"),
|
| 219 |
+
fleet_drive_min=0.0,
|
| 220 |
+
fleet_total_h=0.0,
|
| 221 |
+
all_feasible=False,
|
| 222 |
+
depot=DepotReq(**body["depot"]),
|
| 223 |
+
vans=[],
|
| 224 |
+
kpis=KPIsOut(
|
| 225 |
+
fleet_drive_min=0.0, baseline_drive_min=0.0, savings_pct=0.0,
|
| 226 |
+
fleet_km=0.0, baseline_km=0.0, co2_kg_saved=0.0,
|
| 227 |
+
driver_utilization_pct=0.0, capacity_utilization_pct=0.0,
|
| 228 |
+
stops_per_van=[], feasible_vans=0, total_vans=fleet.num_vans,
|
| 229 |
+
),
|
| 230 |
+
warehouse_prep=[
|
| 231 |
+
"INFEASIBLE: no valid plan exists for this scenario. "
|
| 232 |
+
"Try relaxing time windows, adding a backup driver, or rescheduling stops."
|
| 233 |
+
],
|
| 234 |
+
firestore_written=0,
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
baseline_plan = solve_baseline(depot, fleet, drivers, stops, matrix)
|
| 238 |
+
|
| 239 |
+
stops_by_id = {s.id: s for s in stops}
|
| 240 |
+
graph = get_or_build_graph()
|
| 241 |
+
|
| 242 |
+
vans_out: list[VanOut] = []
|
| 243 |
+
for v in plan.vans:
|
| 244 |
+
sequence = ["DEPOT"] + [p.id for p in v.stops] + ["DEPOT"]
|
| 245 |
+
polyline = (
|
| 246 |
+
[Coords(lat=lat, lng=lng) for lat, lng in route_polyline(graph, matrix, sequence)]
|
| 247 |
+
if v.stops else []
|
| 248 |
+
)
|
| 249 |
+
prof = load_profile(v, stops_by_id)
|
| 250 |
+
vans_out.append(VanOut(
|
| 251 |
+
van_idx=v.van_idx,
|
| 252 |
+
driver_id=v.driver_id,
|
| 253 |
+
feasible=v.feasible,
|
| 254 |
+
travel_time_min=round(v.travel_s / 60, 2),
|
| 255 |
+
total_time_h=round(v.total_s / 3600, 3),
|
| 256 |
+
peak_cells=v.peak_cells,
|
| 257 |
+
peak_kg=v.peak_kg,
|
| 258 |
+
stops=[
|
| 259 |
+
StopOut(
|
| 260 |
+
sequence=p.sequence, id=p.id,
|
| 261 |
+
arrival_time=_hm(p.arrival_s),
|
| 262 |
+
coords=Coords(lat=stops_by_id[p.id].lat, lng=stops_by_id[p.id].lng),
|
| 263 |
+
)
|
| 264 |
+
for p in v.stops
|
| 265 |
+
],
|
| 266 |
+
polyline=polyline,
|
| 267 |
+
load_profile=[
|
| 268 |
+
LoadPointOut(after_stop=lp.after_stop, cells=lp.cells, kg=lp.kg)
|
| 269 |
+
for lp in prof
|
| 270 |
+
],
|
| 271 |
+
explanations=explain_van(v, stops_by_id, drivers[v.van_idx]),
|
| 272 |
+
))
|
| 273 |
+
|
| 274 |
+
kpis = compute_kpis(plan, baseline_plan, fleet, drivers, matrix)
|
| 275 |
+
prep = warehouse_prep(plan, body["stops"], stops_by_id)
|
| 276 |
+
|
| 277 |
+
written = 0
|
| 278 |
+
if write_firestore and fs_enabled():
|
| 279 |
+
service_times_s = {s.id: s.service_time_s for s in stops}
|
| 280 |
+
docs = [
|
| 281 |
+
build_route_doc(
|
| 282 |
+
driver_id=v.driver_id,
|
| 283 |
+
truck_id=f"T-{v.van_idx + 1:02d}",
|
| 284 |
+
van_type=body["fleet"]["van_type"],
|
| 285 |
+
depot=body["depot"],
|
| 286 |
+
request_stops=body["stops"],
|
| 287 |
+
van_plan=v,
|
| 288 |
+
service_times_s=service_times_s,
|
| 289 |
+
)
|
| 290 |
+
for v in plan.vans
|
| 291 |
+
]
|
| 292 |
+
written = write_routes(docs)
|
| 293 |
+
|
| 294 |
+
return OptimizeResponse(
|
| 295 |
+
request_id=body.get("request_id"),
|
| 296 |
+
fleet_drive_min=round(plan.drive_s / 60, 2),
|
| 297 |
+
fleet_total_h=round(plan.total_s / 3600, 3),
|
| 298 |
+
all_feasible=plan.all_feasible,
|
| 299 |
+
depot=DepotReq(**body["depot"]),
|
| 300 |
+
vans=vans_out,
|
| 301 |
+
kpis=KPIsOut(**kpis.__dict__),
|
| 302 |
+
warehouse_prep=prep,
|
| 303 |
+
firestore_written=written,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
# --- Endpoints ---------------------------------------------------------
|
| 308 |
+
|
| 309 |
+
@app.get("/health")
|
| 310 |
+
def health():
|
| 311 |
+
return {"status": "ok"}
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
@app.get("/sample-request", response_model=OptimizeRequest)
|
| 315 |
+
def sample_request():
|
| 316 |
+
if not SAMPLE.exists():
|
| 317 |
+
raise HTTPException(404, "sample_request.json missing")
|
| 318 |
+
return json.loads(SAMPLE.read_text(encoding="utf-8"))
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
@app.get("/products")
|
| 322 |
+
def products():
|
| 323 |
+
if not PRODUCTS.exists():
|
| 324 |
+
raise HTTPException(404, "products.json missing")
|
| 325 |
+
return json.loads(PRODUCTS.read_text(encoding="utf-8"))
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
@app.post("/optimize", response_model=OptimizeResponse)
|
| 329 |
+
def optimize(req: OptimizeRequest) -> OptimizeResponse:
|
| 330 |
+
return _run_pipeline(req.model_dump(), write_firestore=True)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
@app.post("/whatif", response_model=OptimizeResponse)
|
| 334 |
+
def whatif(req: WhatIfRequest) -> OptimizeResponse:
|
| 335 |
+
body = req.request.model_dump()
|
| 336 |
+
multiplier = 1.0
|
| 337 |
+
d = req.disruption
|
| 338 |
+
|
| 339 |
+
if d.type == "driver_unavailable":
|
| 340 |
+
if not d.driver_id:
|
| 341 |
+
raise HTTPException(422, "driver_id required for driver_unavailable")
|
| 342 |
+
body["drivers"] = [x for x in body["drivers"] if x["id"] != d.driver_id]
|
| 343 |
+
body["fleet"]["num_vans"] = len(body["drivers"])
|
| 344 |
+
elif d.type == "stop_cancelled":
|
| 345 |
+
if not d.stop_id:
|
| 346 |
+
raise HTTPException(422, "stop_id required for stop_cancelled")
|
| 347 |
+
body["stops"] = [s for s in body["stops"] if s["id"] != d.stop_id]
|
| 348 |
+
elif d.type == "traffic":
|
| 349 |
+
multiplier = float(d.multiplier or 1.3)
|
| 350 |
+
|
| 351 |
+
# What-ifs are exploratory; don't overwrite the real plan in Firestore.
|
| 352 |
+
# Larger time budget — disrupted scenarios are tighter to solve.
|
| 353 |
+
return _run_pipeline(
|
| 354 |
+
body, write_firestore=False, traffic_multiplier=multiplier, time_limit_s=15,
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
@app.post("/baseline", response_model=OptimizeResponse)
|
| 359 |
+
def baseline_endpoint(req: OptimizeRequest) -> OptimizeResponse:
|
| 360 |
+
"""Run only the naive baseline (for explicit comparison demos)."""
|
| 361 |
+
body = req.model_dump()
|
| 362 |
+
depot, fleet, drivers, stops = load(body)
|
| 363 |
+
matrix = build_travel_matrix(depot, stops)
|
| 364 |
+
plan = solve_baseline(depot, fleet, drivers, stops, matrix)
|
| 365 |
+
stops_by_id = {s.id: s for s in stops}
|
| 366 |
+
graph = get_or_build_graph()
|
| 367 |
+
|
| 368 |
+
vans_out: list[VanOut] = []
|
| 369 |
+
for v in plan.vans:
|
| 370 |
+
sequence = ["DEPOT"] + [p.id for p in v.stops] + ["DEPOT"]
|
| 371 |
+
polyline = (
|
| 372 |
+
[Coords(lat=lat, lng=lng) for lat, lng in route_polyline(graph, matrix, sequence)]
|
| 373 |
+
if v.stops else []
|
| 374 |
+
)
|
| 375 |
+
vans_out.append(VanOut(
|
| 376 |
+
van_idx=v.van_idx, driver_id=v.driver_id, feasible=v.feasible,
|
| 377 |
+
travel_time_min=round(v.travel_s / 60, 2),
|
| 378 |
+
total_time_h=round(v.total_s / 3600, 3),
|
| 379 |
+
peak_cells=v.peak_cells, peak_kg=v.peak_kg,
|
| 380 |
+
stops=[
|
| 381 |
+
StopOut(
|
| 382 |
+
sequence=p.sequence, id=p.id,
|
| 383 |
+
arrival_time=_hm(p.arrival_s),
|
| 384 |
+
coords=Coords(lat=stops_by_id[p.id].lat, lng=stops_by_id[p.id].lng),
|
| 385 |
+
)
|
| 386 |
+
for p in v.stops
|
| 387 |
+
],
|
| 388 |
+
polyline=polyline,
|
| 389 |
+
load_profile=[
|
| 390 |
+
LoadPointOut(after_stop=lp.after_stop, cells=lp.cells, kg=lp.kg)
|
| 391 |
+
for lp in load_profile(v, stops_by_id)
|
| 392 |
+
],
|
| 393 |
+
explanations=explain_van(v, stops_by_id, drivers[v.van_idx]),
|
| 394 |
+
))
|
| 395 |
+
|
| 396 |
+
return OptimizeResponse(
|
| 397 |
+
request_id=req.request_id,
|
| 398 |
+
fleet_drive_min=round(plan.drive_s / 60, 2),
|
| 399 |
+
fleet_total_h=round(plan.total_s / 3600, 3),
|
| 400 |
+
all_feasible=plan.all_feasible,
|
| 401 |
+
depot=req.depot,
|
| 402 |
+
vans=vans_out,
|
| 403 |
+
kpis=KPIsOut(
|
| 404 |
+
fleet_drive_min=round(plan.drive_s / 60, 2),
|
| 405 |
+
baseline_drive_min=round(plan.drive_s / 60, 2),
|
| 406 |
+
savings_pct=0.0,
|
| 407 |
+
fleet_km=0.0, baseline_km=0.0, co2_kg_saved=0.0,
|
| 408 |
+
driver_utilization_pct=0.0, capacity_utilization_pct=0.0,
|
| 409 |
+
stops_per_van=[len(v.stops) for v in plan.vans],
|
| 410 |
+
feasible_vans=sum(1 for v in plan.vans if v.feasible),
|
| 411 |
+
total_vans=len(plan.vans),
|
| 412 |
+
),
|
| 413 |
+
warehouse_prep=warehouse_prep(plan, body["stops"], stops_by_id),
|
| 414 |
+
firestore_written=0,
|
| 415 |
+
)
|
| 416 |
+
|
| 417 |
+
@app.post("/api/chat")
|
| 418 |
+
def chat_endpoint(req: ChatRequest):
|
| 419 |
+
api_key = os.environ.get("ANTHROPIC_API_KEY", "")
|
| 420 |
+
if not api_key:
|
| 421 |
+
raise HTTPException(500, "ANTHROPIC_API_KEY not configured")
|
| 422 |
+
client = Anthropic(api_key=api_key)
|
| 423 |
+
system = (
|
| 424 |
+
"You are a hands-free voice assistant for Damm Motion delivery drivers.\n\n"
|
| 425 |
+
"Current route status:\n" + req.context + "\n\n"
|
| 426 |
+
"Based on what the driver said, decide the best action and give a short spoken reply (max 2 sentences).\n"
|
| 427 |
+
"Respond in the same language the driver used. Be concise — this will be read aloud.\n"
|
| 428 |
+
"You MUST return ONLY valid JSON matching this schema:\n"
|
| 429 |
+
"{\n"
|
| 430 |
+
' "action": "mark_delivered" | "next_stop" | "navigate" | "status" | "unknown",\n'
|
| 431 |
+
' "response": "..."\n'
|
| 432 |
+
"}"
|
| 433 |
+
)
|
| 434 |
+
try:
|
| 435 |
+
response = client.messages.create(
|
| 436 |
+
model="claude-3-haiku-20240307",
|
| 437 |
+
max_tokens=200,
|
| 438 |
+
system=system,
|
| 439 |
+
messages=[{"role": "user", "content": req.transcript}]
|
| 440 |
+
)
|
| 441 |
+
return json.loads(response.content[0].text)
|
| 442 |
+
except Exception as e:
|
| 443 |
+
raise HTTPException(500, f"Anthropic API error: {e}")
|
| 444 |
+
|
| 445 |
+
@app.post("/api/tts")
|
| 446 |
+
def tts_endpoint(req: TTSRequest):
|
| 447 |
+
api_key = os.environ.get("ELEVENLABS_API_KEY", "")
|
| 448 |
+
voice_id = os.environ.get("ELEVENLABS_VOICE_ID", "21m00Tcm4TlvDq8ikWAM")
|
| 449 |
+
if not api_key:
|
| 450 |
+
raise HTTPException(500, "ELEVENLABS_API_KEY not configured")
|
| 451 |
+
url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
|
| 452 |
+
headers = {
|
| 453 |
+
"xi-api-key": api_key,
|
| 454 |
+
"Content-Type": "application/json"
|
| 455 |
+
}
|
| 456 |
+
payload = {
|
| 457 |
+
"text": req.text,
|
| 458 |
+
"model_id": "eleven_multilingual_v2",
|
| 459 |
+
"voice_settings": {"stability": 0.5, "similarity_boost": 0.75}
|
| 460 |
+
}
|
| 461 |
+
try:
|
| 462 |
+
with httpx.Client() as client:
|
| 463 |
+
resp = client.post(url, headers=headers, json=payload, timeout=10.0)
|
| 464 |
+
resp.raise_for_status()
|
| 465 |
+
return Response(content=resp.content, media_type="audio/mpeg")
|
| 466 |
+
except Exception as e:
|
| 467 |
+
raise HTTPException(500, f"ElevenLabs API error: {e}")
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
if __name__ == "__main__":
|
| 472 |
+
import uvicorn
|
| 473 |
+
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=False)
|
apt.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
libgeos-dev
|
| 2 |
+
libspatialindex-dev
|
| 3 |
+
libproj-dev
|
| 4 |
+
proj-bin
|
baseline.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Naive comparator: polar sweep + per-van nearest-neighbor.
|
| 2 |
+
|
| 3 |
+
Sort stops by angle from depot, round-robin assign to vans, then NN-order
|
| 4 |
+
each van's stops from the depot. No window or capacity guarantee — that's
|
| 5 |
+
the point. The OR-tools solver's "X% better" pitch number is computed
|
| 6 |
+
against this baseline.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
import math
|
| 12 |
+
|
| 13 |
+
from loader import Depot, Driver, Fleet, Stop
|
| 14 |
+
from solver import FleetPlan, StopPlan, VanPlan
|
| 15 |
+
from travel_time import TravelMatrix
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _polar(s: Stop, depot: Depot) -> float:
|
| 19 |
+
return math.atan2(s.lat - depot.lat, s.lng - depot.lng)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def solve_baseline(
|
| 23 |
+
depot: Depot,
|
| 24 |
+
fleet: Fleet,
|
| 25 |
+
drivers: list[Driver],
|
| 26 |
+
stops: list[Stop],
|
| 27 |
+
matrix: TravelMatrix,
|
| 28 |
+
) -> FleetPlan:
|
| 29 |
+
if not stops:
|
| 30 |
+
return FleetPlan([], 0, 0, True)
|
| 31 |
+
|
| 32 |
+
K = fleet.num_vans
|
| 33 |
+
swept = sorted(stops, key=lambda s: _polar(s, depot))
|
| 34 |
+
assigned: list[list[Stop]] = [[] for _ in range(K)]
|
| 35 |
+
for i, s in enumerate(swept):
|
| 36 |
+
assigned[i % K].append(s)
|
| 37 |
+
|
| 38 |
+
vans: list[VanPlan] = []
|
| 39 |
+
fleet_drive = fleet_total = 0
|
| 40 |
+
all_feas = True
|
| 41 |
+
depot_i = matrix.index_of("DEPOT")
|
| 42 |
+
|
| 43 |
+
for vid, van_stops in enumerate(assigned):
|
| 44 |
+
order: list[Stop] = []
|
| 45 |
+
remaining = {s.id: s for s in van_stops}
|
| 46 |
+
cur = depot_i
|
| 47 |
+
while remaining:
|
| 48 |
+
nxt = min(
|
| 49 |
+
remaining.values(),
|
| 50 |
+
key=lambda s: float(matrix.time_s[cur, matrix.index_of(s.id)]),
|
| 51 |
+
)
|
| 52 |
+
order.append(nxt)
|
| 53 |
+
cur = matrix.index_of(nxt.id)
|
| 54 |
+
del remaining[nxt.id]
|
| 55 |
+
|
| 56 |
+
# Simulate
|
| 57 |
+
t = float(drivers[vid].shift_start_s)
|
| 58 |
+
prev = depot_i
|
| 59 |
+
plan: list[StopPlan] = []
|
| 60 |
+
v_drive = cum_c = cum_k = peak_c = peak_k = 0
|
| 61 |
+
late = False
|
| 62 |
+
for seq, s in enumerate(order, start=1):
|
| 63 |
+
i = matrix.index_of(s.id)
|
| 64 |
+
travel = float(matrix.time_s[prev, i])
|
| 65 |
+
v_drive += int(travel)
|
| 66 |
+
t += travel
|
| 67 |
+
if t < s.t_open_s:
|
| 68 |
+
t = float(s.t_open_s)
|
| 69 |
+
if t > s.t_close_s:
|
| 70 |
+
late = True
|
| 71 |
+
plan.append(StopPlan(seq, s.id, int(t)))
|
| 72 |
+
t += s.service_time_s
|
| 73 |
+
cum_c += s.delivery_cells + s.pickup_cells
|
| 74 |
+
cum_k += int(round(s.delivery_kg + s.pickup_kg))
|
| 75 |
+
peak_c = max(peak_c, cum_c)
|
| 76 |
+
peak_k = max(peak_k, cum_k)
|
| 77 |
+
prev = i
|
| 78 |
+
ret = float(matrix.time_s[prev, depot_i])
|
| 79 |
+
v_drive += int(ret)
|
| 80 |
+
t += ret
|
| 81 |
+
v_total = int(t - drivers[vid].shift_start_s)
|
| 82 |
+
shift_ok = t <= drivers[vid].shift_end_s
|
| 83 |
+
cap_ok = peak_c <= fleet.capacity_cells and peak_k <= fleet.capacity_kg
|
| 84 |
+
feas = shift_ok and cap_ok and not late
|
| 85 |
+
all_feas &= feas
|
| 86 |
+
vans.append(VanPlan(
|
| 87 |
+
van_idx=vid, driver_id=drivers[vid].id, stops=plan,
|
| 88 |
+
travel_s=v_drive, total_s=v_total,
|
| 89 |
+
peak_cells=peak_c, peak_kg=peak_k, feasible=feas,
|
| 90 |
+
))
|
| 91 |
+
fleet_drive += v_drive
|
| 92 |
+
fleet_total += v_total
|
| 93 |
+
return FleetPlan(vans, fleet_drive, fleet_total, all_feas)
|
cache/090c41e37bdb93f0bcebd4b4bc1f40b7bab09935.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[{"place_id": 83153240, "licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright", "osm_type": "relation", "osm_id": 345553, "lat": "41.5393484", "lon": "2.2130934", "class": "boundary", "type": "administrative", "place_rank": 16, "importance": 0.5542014986669894, "addresstype": "town", "name": "Mollet del Vall\u00e8s", "display_name": "Mollet del Vall\u00e8s, Vall\u00e8s Oriental, Barcelona, Catalonia, 08100, Spain", "boundingbox": ["41.5238933", "41.5757548", "2.1874839", "2.2337129"], "geojson": {"type": "Polygon", "coordinates": [[[2.1874839, 41.5660264], [2.1886713, 41.5620372], [2.1890702, 41.5606969], [2.1895159, 41.5586331], [2.1897154, 41.557366], [2.18977, 41.5570191], [2.1899127, 41.5567065], [2.1909578, 41.5551533], [2.1912139, 41.5546129], [2.1916643, 41.5538977], [2.1918789, 41.5534257], [2.1919336, 41.5532754], [2.1922059, 41.55281], [2.1926189, 41.5521755], [2.1928236, 41.5518468], [2.1931857, 41.5514369], [2.1934456, 41.5511448], [2.1938335, 41.550452], [2.1938608, 41.5503604], [2.1940996, 41.5497779], [2.1941195, 41.5496825], [2.1941265, 41.5496488], [2.194155, 41.5495118], [2.1941668, 41.5493824], [2.1941733, 41.5493113], [2.1941512, 41.5488297], [2.1942058, 41.5486869], [2.1941492, 41.5482849], [2.1940406, 41.5479788], [2.1938735, 41.5476389], [2.1938537, 41.5474525], [2.1939048, 41.5472822], [2.1940436, 41.5469463], [2.1941827, 41.5467211], [2.1943431, 41.5465988], [2.1947454, 41.5463009], [2.1948424, 41.546237], [2.1956331, 41.5459515], [2.1958258, 41.5458819], [2.1963348, 41.5457439], [2.1965737, 41.5456657], [2.196733, 41.5456251], [2.1975926, 41.5452734], [2.1980398, 41.5451002], [2.1984434, 41.5450546], [2.198628, 41.5449761], [2.1989327, 41.5444729], [2.1990484, 41.5441577], [2.1990907, 41.5440423], [2.1989981, 41.5439172], [2.1991886, 41.5431925], [2.1993534, 41.54277], [2.1994988, 41.5421881], [2.1996249, 41.5417796], [2.1998888, 41.5410937], [2.2005018, 41.5406116], [2.2006493, 41.540267], [2.2005913, 41.5398086], [2.2006052, 41.5393455], [2.2006448, 41.5384216], [2.2006844, 41.5376637], [2.2007893, 41.5373485], [2.2008061, 41.5372979], [2.2010114, 41.5367214], [2.2014906, 41.5361235], [2.2023493, 41.5353283], [2.2031105, 41.5342134], [2.2039728, 41.5321216], [2.2044038, 41.5313728], [2.2051398, 41.5303941], [2.2052325, 41.5303221], [2.2056811, 41.5299734], [2.2060887, 41.5301259], [2.2064193, 41.530363], [2.2079124, 41.5294129], [2.2066527, 41.5284978], [2.2064068, 41.5281219], [2.2070978, 41.5277754], [2.2077063, 41.5272976], [2.2071596, 41.5264582], [2.207648, 41.5262739], [2.208583, 41.5261324], [2.2103007, 41.5255342], [2.2106341, 41.5254181], [2.2124621, 41.5252089], [2.2137528, 41.5245741], [2.215459, 41.5238933], [2.2168612, 41.525387], [2.2178829, 41.5263219], [2.2192584, 41.5283749], [2.2206246, 41.5312027], [2.220898, 41.5316224], [2.2222321, 41.5330111], [2.2258679, 41.5364916], [2.2266382, 41.5375729], [2.2277663, 41.5387819], [2.2291491, 41.5398802], [2.2308388, 41.5409195], [2.2308845, 41.5417817], [2.2309702, 41.5424164], [2.2311922, 41.5445495], [2.2318753, 41.5446984], [2.2321247, 41.5454094], [2.2332303, 41.5485619], [2.2336981, 41.550197], [2.2337129, 41.5515648], [2.2335562, 41.5521688], [2.2334562, 41.5525543], [2.2333137, 41.5531034], [2.2333926, 41.5536417], [2.2330531, 41.5537332], [2.2335131, 41.5546173], [2.2335454, 41.5546794], [2.2300447, 41.5550825], [2.2296885, 41.5551314], [2.2284825, 41.5553681], [2.2283125, 41.5554015], [2.2272597, 41.5559585], [2.226973, 41.5557707], [2.2267042, 41.555937], [2.2264898, 41.5556865], [2.2262913, 41.5557887], [2.2254175, 41.5558249], [2.2248152, 41.5558498], [2.221826, 41.5560463], [2.2194973, 41.5562047], [2.2184645, 41.556275], [2.2156455, 41.5563943], [2.2137293, 41.5565624], [2.2136854, 41.5565579], [2.2130041, 41.5564874], [2.2121678, 41.5566694], [2.2120332, 41.5570552], [2.2114083, 41.5588466], [2.2109127, 41.5599855], [2.2099535, 41.5611725], [2.208313, 41.5653613], [2.2046381, 41.5679589], [2.2042225, 41.568455], [2.2041598, 41.5687189], [2.2037502, 41.5700339], [2.2036045, 41.5709048], [2.2031298, 41.5714125], [2.2029418, 41.5722042], [2.2029666, 41.5745979], [2.202798, 41.5756319], [2.2008223, 41.5755144], [2.1997143, 41.5753036], [2.1988957, 41.5757098], [2.1982998, 41.5757548], [2.1976683, 41.5752073], [2.196818, 41.5752188], [2.1966193, 41.5749847], [2.1967541, 41.5745735], [2.1972111, 41.5739946], [2.1976747, 41.5731994], [2.1969374, 41.5731247], [2.1964843, 41.5728572], [2.1964892, 41.5724226], [2.1964902, 41.5723347], [2.1968795, 41.571957], [2.1970277, 41.5715631], [2.1967074, 41.5713076], [2.1959039, 41.5711549], [2.1951733, 41.5705668], [2.1944389, 41.570231], [2.1941699, 41.569568], [2.1936296, 41.5692593], [2.1933717, 41.5688839], [2.193307, 41.5674824], [2.1926531, 41.5672509], [2.1915684, 41.5670299], [2.1912425, 41.5674498], [2.1903819, 41.5671827], [2.1903964, 41.5663177], [2.1880644, 41.5660845], [2.1874839, 41.5660264]]]}}]
|
cache/2df67666927fdbbb503bc38e6fa304e5f7232647.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
cache/462dafbdff2cf3940ddb594f9d84d228a81a8662.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
cache/681ccfe17697053de5ea8ada83e18772e2e1b958.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
cache/8c72638f670e14db619bc7fb41926d3eda0b3a8c.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[{"place_id": 83242574, "licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright", "osm_type": "relation", "osm_id": 346608, "lat": "41.6079555", "lon": "2.2876008", "class": "boundary", "type": "administrative", "place_rank": 16, "importance": 0.5733043280218857, "addresstype": "town", "name": "Granollers", "display_name": "Granollers, Vall\u00e8s Oriental, Barcelona, Catalonia, Spain", "boundingbox": ["41.5642729", "41.6225765", "2.2564393", "2.3040077"], "geojson": {"type": "Polygon", "coordinates": [[[2.2564393, 41.5737252], [2.2587538, 41.5722597], [2.2620536, 41.5699386], [2.262792, 41.5694366], [2.264283, 41.5687559], [2.264199, 41.5681656], [2.2646466, 41.5675613], [2.2655354, 41.5663217], [2.2671605, 41.5652699], [2.2693722, 41.5642729], [2.2735497, 41.5672858], [2.2739517, 41.567692], [2.2743335, 41.5680779], [2.2777787, 41.5704851], [2.2786731, 41.5713082], [2.2793626, 41.5716771], [2.2817626, 41.5731735], [2.2820771, 41.5735011], [2.282756, 41.5737399], [2.2886329, 41.5720105], [2.2888842, 41.5727372], [2.2895614, 41.573102], [2.2897023, 41.5737977], [2.2904198, 41.5742148], [2.2914417, 41.5769764], [2.2929908, 41.5803901], [2.2931624, 41.5807681], [2.2922868, 41.582375], [2.2921375, 41.582895], [2.2923453, 41.5832366], [2.2925473, 41.5835063], [2.2931596, 41.5839552], [2.2930006, 41.5845027], [2.2939141, 41.5864321], [2.2946441, 41.5871456], [2.2947293, 41.5876008], [2.2944138, 41.5879935], [2.2949815, 41.5895067], [2.2946212, 41.5897934], [2.2945201, 41.5900231], [2.2946583, 41.5905389], [2.2951829, 41.5907938], [2.2951329, 41.5919394], [2.2953599, 41.5919289], [2.2957718, 41.5927111], [2.29579, 41.5930794], [2.2955562, 41.5937384], [2.2960116, 41.5943205], [2.2961114, 41.5950991], [2.295901, 41.5961892], [2.2962311, 41.5972903], [2.2962846, 41.5986742], [2.2963821, 41.6001551], [2.296633, 41.6001435], [2.2967069, 41.6001671], [2.2967367, 41.6002378], [2.2968181, 41.6007922], [2.296768, 41.6009115], [2.2967446, 41.6010656], [2.2966803, 41.6011586], [2.2966518, 41.60125], [2.2966126, 41.6015039], [2.2966766, 41.601699], [2.2968794, 41.6019777], [2.2971, 41.6023276], [2.2974932, 41.6031737], [2.2975865, 41.6032864], [2.2981109, 41.6051443], [2.2983273, 41.6057369], [2.2986272, 41.6066143], [2.3001881, 41.6064881], [2.3003503, 41.6064176], [2.3006672, 41.6061869], [2.3010124, 41.6060089], [2.3013203, 41.6058146], [2.301628, 41.6063024], [2.3018199, 41.6064937], [2.3027004, 41.6071371], [2.3030898, 41.6074972], [2.3040077, 41.6107857], [2.3034082, 41.6117251], [2.3031788, 41.6120845], [2.3030021, 41.6124167], [2.3025208, 41.6134203], [2.3025782, 41.6141198], [2.3008986, 41.6148578], [2.3004941, 41.6150355], [2.2976455, 41.6155543], [2.295706, 41.6160178], [2.292961, 41.6166754], [2.2933901, 41.618031], [2.2939909, 41.6197634], [2.2946454, 41.6204933], [2.295611, 41.6218207], [2.2955574, 41.6218167], [2.2941535, 41.6222695], [2.2930496, 41.6221225], [2.2924211, 41.6217734], [2.2918558, 41.6216104], [2.2911901, 41.6216861], [2.2906751, 41.621989], [2.289409, 41.6224976], [2.2884796, 41.6225765], [2.2868261, 41.6217255], [2.2865975, 41.6206917], [2.2865312, 41.6194434], [2.2861048, 41.6177525], [2.2857509, 41.6170936], [2.2847294, 41.6160364], [2.2836275, 41.6148959], [2.2829289, 41.6139828], [2.2778175, 41.6150164], [2.2767762, 41.6150334], [2.2762332, 41.6150407], [2.2749895, 41.6150464], [2.2734195, 41.6152039], [2.271548, 41.6150559], [2.2675402, 41.614898], [2.2658484, 41.6148947], [2.2653352, 41.614045], [2.2678884, 41.608139], [2.2664739, 41.6068266], [2.2653456, 41.6039033], [2.2653028, 41.6037925], [2.2650698, 41.602972], [2.2648486, 41.6021928], [2.2635621, 41.6003884], [2.2632215, 41.5991526], [2.2622215, 41.5983613], [2.2618772, 41.5963425], [2.2606652, 41.5935353], [2.2605276, 41.592922], [2.2597616, 41.5895075], [2.2587472, 41.5833783], [2.2586832, 41.5824449], [2.2584191, 41.5815568], [2.2564393, 41.5737252]]]}}]
|
challenge_description.txt
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Damm Smart Truck
|
| 2 |
+
Damm · Interhack BCN 2026
|
| 3 |
+
1. Context i motivació
|
| 4 |
+
Damm és una companyia amb una activitat logística d'alta complexitat, vinculada a la distribució de productes a múltiples canals i punts de venda. En aquest context, DDI (Distribució Directa Integral) té un paper clau com a xarxa de distribució del grup, amb un catàleg multiproducte de marques de primer nivell i presència a la Península Ibèrica, les Illes Balears i les Illes Canàries. DDI ofereix un servei àgil, flexible i personalitzat als seus clients, especialment en el canal hostaleria. Cada dia, els equips de distribució han de preparar, carregar i repartir comandes a diferents clients, amb rutes que poden incloure aproximadament entre 15 i 25 entregues per camió.
|
| 5 |
+
Actualment, la preparació de la càrrega es fa principalment agrupant els productes per referència. Aquest model facilita la preparació al magatzem i permet aprofitar millor l'espai disponible al camió. Tot i així, durant el repartiment, el xofer pot haver de buscar productes en diferents punts del vehicle per completar l'entrega de cada client, cosa que pot generar moviments addicionals, pèrdues de temps i ineficiències en la descàrrega.
|
| 6 |
+
A més, l'ordre de repartiment no està completament predefinit: sovint és el mateix xofer qui acaba decidint la ruta segons el seu coneixement del territori, els clients i les condicions del dia. Això obre una oportunitat per explorar com la tecnologia, les dades i l'optimització poden ajudar a prendre millors decisions combinant dues dimensions que habitualment es tracten per separat: la ruta de repartiment i la preparació física de la càrrega del camió.
|
| 7 |
+
El repte també ha de tenir en compte la realitat operativa de DDI: diferents tipologies i volums de producte, capacitat limitada del camió, restriccions de manipulació, accés lateral als palets mitjançant lones, horaris o preferències dels clients, i logística inversa. Aproximadament un 60% dels productes entregats són retornables, de manera que els camions també recullen caixes, envasos o barrils buits durant la ruta.
|
| 8 |
+
Aquest repte és rellevant perquè una millor coordinació entre ruta, càrrega i operativa pot generar impacte en eficiència, temps de repartiment, ergonomia, sostenibilitat, qualitat del servei i escalabilitat del model logístic.
|
| 9 |
+
|
| 10 |
+
2. Objectiu del repte
|
| 11 |
+
L'objectiu del repte és dissenyar una solució que permeti optimitzar de manera conjunta la ruta de repartiment i la configuració de la càrrega del camió, tenint en compte l'ordre d'entrega, el volum i tipologia dels productes, la capacitat del vehicle, la logística inversa i les restriccions operatives del procés actual.
|
| 12 |
+
La solució ideal hauria de ser capaç de proposar una ruta eficient i, alhora, recomanar com preparar físicament la càrrega perquè la descàrrega sigui més àgil i coherent amb l'ordre de repartiment, sense penalitzar excessivament l'aprofitament de l'espai ni la preparació al magatzem.
|
| 13 |
+
Es valoraran especialment les solucions que combinin un enfocament tècnic sòlid amb una aplicació realista al context logístic de Damm. No es tracta només de trobar la ruta més curta, sinó de trobar el millor equilibri entre eficiència de ruta, eficiència de càrrega, facilitat de descàrrega i viabilitat operativa.
|
| 14 |
+
|
| 15 |
+
3. Repte tècnic / requisits funcionals
|
| 16 |
+
La solució proposada hauria de cobrir, com a mínim, els següents elements:
|
| 17 |
+
* Optimització de ruta: Proposar un ordre de visita dels clients tenint en compte variables com distància, temps estimat de trajecte, número de clients, possibles franges horàries, restriccions d'entrega o criteris de prioritat.
|
| 18 |
+
* Optimització de càrrega: Recomanar una configuració física de la càrrega del camió tenint en compte els productes de cada client, el volum, la tipologia de producte, la capacitat del vehicle i l'ordre previst de descàrrega.
|
| 19 |
+
* Equilibri entre càrrega per referència i càrrega per client: Explorar si convé mantenir una càrrega agrupada per referències, passar a una càrrega agrupada per client o proposar un model híbrid que maximitzi l'eficiència global.
|
| 20 |
+
* Visualització de la càrrega: Incloure una representació visual, encara que sigui simplificada, de com quedaria carregat el camió: zones, palets, grups de producte, clients associats o ordre recomanat de descàrrega.
|
| 21 |
+
* Logística inversa: Incorporar la recollida de retornables com una variable del model. La solució hauria de considerar que el camió no només entrega productes, sinó que també pot anar recuperant caixes, envasos o barrils buits durant la ruta.
|
| 22 |
+
* Restriccions operatives reals: Tenir en compte que els camions disposen de lones laterals que permeten accedir als palets des del lateral, i que la càrrega ha de ser viable des del punt de vista de magatzem, manipulació, seguretat i temps de preparació.
|
| 23 |
+
* Layout del magatzem: Considerar, com a variable opcional o extensió del repte, si una disposició diferent del magatzem podria facilitar la preparació de càrregues més eficients segons ruta, client o tipologia de producte.
|
| 24 |
+
* Recomanacions automàtiques: Generar recomanacions accionables per als equips de logística, com ara: millor ordre de càrrega, clients que convé agrupar, productes crítics, punts de fricció esperats, o alertes sobre configuracions poc eficients.
|
| 25 |
+
* Explicabilitat de la solució: La proposta hauria d'explicar per què recomana una ruta o configuració determinada, quins criteris ha prioritzat i quins compromisos assumeix entre espai, temps, facilitat de descàrrega i eficiència global.
|
| 26 |
+
La solució pot adoptar diferents formes: algoritme d'optimització, prototip funcional, simulador, eina visual, dashboard interactiu, model de recomanació o combinació d'aquests elements.
|
| 27 |
+
|
| 28 |
+
4. Recursos i dades disponibles
|
| 29 |
+
Damm facilitarà als equips informació de context i, en la mesura del possible, dades de mostra o dades sintètiques per treballar el repte. Els recursos previstos són:
|
| 30 |
+
* Dades de mostra de comandes i repartiment de DDI: Exemples de comandes amb clients, referències de producte, quantitats, formats, volums o unitats logístiques aproximades.
|
| 31 |
+
* Exemples de rutes o assignació de clients: Mostres de com poden quedar assignats diferents clients a un xofer o camió en una jornada de repartiment.
|
| 32 |
+
* Informació sobre el funcionament actual de DDI: Explicació del procés actual de preparació de càrrega, criteris utilitzats, limitacions i punts de fricció identificats.
|
| 33 |
+
* Imatges o esquemes del magatzem: Material visual que ajudi a entendre com s'organitzen actualment les referències i com es prepara la càrrega.
|
| 34 |
+
* Imatges o esquemes de càrrega de camions: Exemples de com es distribueixen actualment els productes dins dels camions.
|
| 35 |
+
* Restriccions operatives simplificades: Capacitat estimada del camió, accés lateral, tipus de productes, manipulació, limitacions de volum o pes, i consideracions bàsiques de seguretat.
|
| 36 |
+
* Informació sobre logística inversa: Estimació del percentatge de productes retornables i exemples de retorns típics durant una ruta.
|
| 37 |
+
* Mentors o experts de Damm i DDI: Durant l'esdeveniment, Damm i DDI intentaran comptar amb persones de l'equip de Distribució i de Data & IA per resoldre dubtes operatius i tècnics dels participants.
|
| 38 |
+
En cas que no es puguin compartir dades reals per motius de confidencialitat, es facilitaran dades anonimitzades, simplificades o sintètiques que reprodueixin la lògica del problema.
|
| 39 |
+
|
| 40 |
+
5. Entregables esperats
|
| 41 |
+
Al final del repte, cada equip haurà de presentar una proposta que permeti entendre tant la solució tècnica com la seva aplicació pràctica al context de Damm.
|
| 42 |
+
Els entregables esperats són:
|
| 43 |
+
* Prototip funcional o demo conceptual: Una eina, model, simulador, script, dashboard o interfície que mostri com es podria optimitzar ruta i càrrega de manera conjunta.
|
| 44 |
+
* Algoritme o lògica de decisió: Explicació clara de l'enfocament utilitzat: criteris d'optimització, variables considerades, restriccions incorporades i prioritats del model.
|
| 45 |
+
* Visualització de la solució: Representació visual de la ruta proposada i de la configuració recomanada de la càrrega del camió.
|
| 46 |
+
* Aplicació a un cas d'ús concret: Exemple pràctic amb una ruta simulada o de mostra, indicant quins clients es visiten, en quin ordre, com es carrega el camió i quins beneficis s'esperen.
|
| 47 |
+
* Resultats i impacte esperat: Estimació qualitativa o quantitativa del possible impacte: reducció de temps de descarga, millor aprofitament del camió, menys moviments, menys quilòmetres, millor experiència del xofer o més eficiència operativa.
|
| 48 |
+
* Pitch final: Presentació breu amb el problema abordat, la solució proposada, els supòsits utilitzats, les limitacions, els resultats i els següents passos per portar-ho a un pilot real.
|
| 49 |
+
No és imprescindible que la solució estigui completament acabada, però sí que ha de ser comprensible, defensable i aplicable a un escenari logístic realista.
|
| 50 |
+
|
| 51 |
+
6. Criteris d'avaluació
|
| 52 |
+
Les propostes s'avaluaran segons els criteris següents:
|
| 53 |
+
* Aplicabilitat real al context de Damm (30%): La solució té en compte les restriccions operatives reals? És viable en un entorn de distribució com el de Damm? Entén bé el rol del magatzem, el xofer, la càrrega i la descàrrega?
|
| 54 |
+
* Qualitat tècnica de la solució (25%): L'enfocament d'optimització és sòlid? Les variables estan ben definides? La solució combina correctament ruta, càrrega i restriccions? El prototip o model és coherent?
|
| 55 |
+
* Impacte potencial (20%): La proposta pot generar millores rellevants en eficiència, temps, sostenibilitat, qualitat del servei, ergonomia o escalabilitat?
|
| 56 |
+
* Creativitat i originalitat (15%): L'equip proposa un enfocament diferencial? Explora solucions híbrides, visualitzacions útils, recomanacions intel·ligents o noves formes de prendre decisions?
|
| 57 |
+
* Claredat de comunicació i pitch (10%): La solució s'explica de manera clara? Els supòsits, resultats i limitacions són comprensibles? El pitch ajuda a imaginar com es podria portar la proposta a la pràctica?
|
| 58 |
+
Es valoraran especialment les propostes que no només optimitzin sobre el paper, sinó que demostrin sensibilitat pel funcionament real de l'operativa logística: preparació al magatzem, càrrega del camió, accés als productes, decisió del xofer, retornables i experiència de descàrrega al client.
|
cities_graph.graphml
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
cities_graph_dropoffs.png
ADDED
|
Git LFS Details
|
coords.csv
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
id,x,y
|
| 2 |
+
9100153533,2.2122023,41.5377822
|
| 3 |
+
9100423445,2.2139430,41.5373566
|
| 4 |
+
9100138634,2.2261548,41.5487678
|
| 5 |
+
9100228193,2.2109740,41.5379267
|
| 6 |
+
9100519725,2.2080131,41.5487496
|
| 7 |
+
9100519746,2.2199409,41.5483345
|
| 8 |
+
9100610435,2.2202430,41.5449153
|
| 9 |
+
9100621276,2.2076723,41.5407351
|
| 10 |
+
9100622784,2.2097200,41.5375000
|
| 11 |
+
9100623018,2.2133356,41.5387104
|
| 12 |
+
9100632834,2.2176727,41.5354334
|
| 13 |
+
9100639237,2.2081403,41.5329122
|
| 14 |
+
9100646795,2.2130934,41.5393484
|
| 15 |
+
9100653307,2.2187350,41.5471420
|
| 16 |
+
9100686742,2.2064397,41.5365029
|
| 17 |
+
9100688668,2.2076124,41.5479653
|
| 18 |
+
9100055962,2.2127912,41.5440412
|
| 19 |
+
9100654473,2.2185275,41.5364259
|
| 20 |
+
118470,2.2903800,41.6100748
|
| 21 |
+
118470,2.2903800,41.6100748
|
| 22 |
+
9100614413,2.2930703,41.6154062
|
| 23 |
+
9100626587,2.3021682,41.6098501
|
| 24 |
+
9100629896,2.2901386,41.5995054
|
| 25 |
+
9100630014,2.2899627,41.6000610
|
| 26 |
+
9100638648,2.2884292,41.5975345
|
| 27 |
+
9100639032,2.2902828,41.6091422
|
| 28 |
+
9100647947,2.2905456,41.6050949
|
| 29 |
+
9100702482,2.2902828,41.6091422
|
| 30 |
+
127151,2.2867282,41.5960953
|
| 31 |
+
127151,2.2867282,41.5960953
|
| 32 |
+
9100226319,2.2894172,41.6051624
|
| 33 |
+
9100621389,2.2884292,41.5975345
|
| 34 |
+
9100373615,2.2886546,41.6079491
|
| 35 |
+
9100042719,2.2867308,41.6078892
|
| 36 |
+
9100291002,2.2904018,41.6135165
|
| 37 |
+
9100469494,2.2844361,41.6076185
|
| 38 |
+
9100629126,2.2848378,41.6079349
|
| 39 |
+
9100642392,2.2809997,41.6098151
|
| 40 |
+
9100652617,2.2776200,41.6000581
|
| 41 |
+
9100041008,2.2869478,41.6129094
|
| 42 |
+
9100042975,2.2778844,41.6111420
|
| 43 |
+
9100054998,2.2128282,41.5376150
|
| 44 |
+
9100424060,2.2096754,41.5417587
|
| 45 |
+
9100424060,2.2096754,41.5417587
|
| 46 |
+
9100444038,2.2064101,41.5367615
|
| 47 |
+
9100350872,2.2194241,41.5479420
|
| 48 |
+
9100350872,2.2194241,41.5479420
|
| 49 |
+
9100660484,2.2119365,41.5369045
|
| 50 |
+
9100663784,2.2191655,41.5408673
|
| 51 |
+
9100686343,2.2120439,41.5373051
|
| 52 |
+
9100733360,2.2176096,41.5388602
|
| 53 |
+
9100745447,2.2172757,41.5433182
|
| 54 |
+
9100756398,2.2120439,41.5373051
|
| 55 |
+
9100762714,2.2059673,41.5364637
|
| 56 |
+
9100765744,2.2175419,41.5334748
|
| 57 |
+
9100767546,2.2175419,41.5334748
|
| 58 |
+
9100055977,2.2175419,41.5334748
|
| 59 |
+
9100055145,2.2161158,41.5428223
|
| 60 |
+
119751,2.2224956,41.5408211
|
| 61 |
+
119751,2.2224956,41.5408211
|
| 62 |
+
119751,2.2224956,41.5408211
|
| 63 |
+
119751,2.2224956,41.5408211
|
| 64 |
+
119751,2.2224956,41.5408211
|
| 65 |
+
119751,2.2224956,41.5408211
|
| 66 |
+
9100690441,2.2268314,41.5554911
|
| 67 |
+
9100433502,2.2072597,41.5403403
|
| 68 |
+
103154,2.2293485,41.5469926
|
| 69 |
+
103154,2.2293485,41.5469926
|
| 70 |
+
9100054573,2.2138550,41.5374979
|
| 71 |
+
9100054762,2.2127653,41.5353139
|
| 72 |
+
9100054848,2.2088394,41.5356284
|
| 73 |
+
9100054935,2.2135257,41.5367766
|
| 74 |
+
9100055101,2.2097139,41.5344906
|
| 75 |
+
9100055108,2.2146578,41.5348907
|
| 76 |
+
9100055221,2.2080238,41.5349415
|
| 77 |
+
9100055265,2.2118764,41.5399187
|
| 78 |
+
9100055505,2.2052385,41.5489980
|
| 79 |
+
9100055626,2.2115260,41.5365981
|
| 80 |
+
9100055679,2.2099163,41.5413828
|
| 81 |
+
9100056090,2.2108804,41.5393929
|
| 82 |
+
9100056113,2.2207985,41.5440801
|
| 83 |
+
9100056124,2.2117241,41.5370793
|
| 84 |
+
9100059602,2.2059673,41.5364637
|
| 85 |
+
9100076081,2.2078993,41.5349726
|
| 86 |
+
9100135772,2.2101021,41.5398093
|
| 87 |
+
9100150731,2.2122023,41.5377822
|
| 88 |
+
9100153532,2.2122023,41.5377822
|
| 89 |
+
9100178455,2.2130934,41.5393484
|
| 90 |
+
9100338332,2.2065665,41.5389553
|
| 91 |
+
9100413328,2.2146578,41.5348907
|
| 92 |
+
9100466233,2.2170676,41.5363800
|
| 93 |
+
9100470966,2.2173766,41.5385546
|
| 94 |
+
9100040775,2.2178880,41.5446079
|
| 95 |
+
9100054707,2.2069974,41.5458365
|
| 96 |
+
9100054708,2.2122200,41.5383466
|
| 97 |
+
9100054841,2.2179029,41.5395838
|
| 98 |
+
9100054856,2.2079370,41.5480260
|
| 99 |
+
9100054872,2.2172757,41.5433182
|
| 100 |
+
9100054896,2.2135294,41.5388501
|
| 101 |
+
9100054949,2.2080600,41.5467073
|
| 102 |
+
9100054994,2.2095811,41.5432985
|
| 103 |
+
9100055030,2.2077722,41.5484192
|
| 104 |
+
9100055144,2.2077425,41.5433782
|
| 105 |
+
9100055194,2.2066997,41.5465437
|
| 106 |
+
9100055251,2.2080746,41.5457859
|
| 107 |
+
9100055620,2.2133356,41.5387104
|
| 108 |
+
9100055707,2.2127664,41.5389993
|
| 109 |
+
9100055836,2.2133356,41.5387104
|
| 110 |
+
9100056034,2.2053181,41.5448735
|
| 111 |
+
9100075764,2.2059142,41.5473369
|
| 112 |
+
9100120690,2.2312937,41.5532633
|
| 113 |
+
9100121589,2.2194830,41.5463287
|
| 114 |
+
9100126935,2.2048761,41.5407251
|
| 115 |
+
9100129133,2.2229330,41.5416117
|
| 116 |
+
9100239655,2.2113491,41.5381651
|
| 117 |
+
9100246685,2.2064461,41.5449523
|
| 118 |
+
9100326765,2.2204354,41.5476472
|
| 119 |
+
9100353152,2.2149531,41.5450899
|
| 120 |
+
9100366443,2.2172619,41.5446492
|
| 121 |
+
9100371825,2.2154312,41.5425485
|
| 122 |
+
9100371825,2.2154312,41.5425485
|
| 123 |
+
9100467331,2.2179029,41.5395838
|
| 124 |
+
9100612100,2.2035472,41.5469903
|
| 125 |
+
9100617395,2.2212649,41.5429148
|
| 126 |
+
9100642781,2.2081578,41.5409278
|
| 127 |
+
9100653427,2.2056984,41.5450720
|
| 128 |
+
9100663743,2.2110669,41.5416294
|
| 129 |
+
9100665155,2.2076759,41.5475361
|
| 130 |
+
9100670399,2.2044719,41.5469943
|
| 131 |
+
9100677478,2.2041746,41.5407892
|
| 132 |
+
9100684101,2.2179750,41.5446936
|
| 133 |
+
9100684195,2.2141858,41.5380416
|
| 134 |
+
9100694798,2.2174068,41.5416023
|
| 135 |
+
9100695039,2.2073608,41.5416700
|
| 136 |
+
9100695182,2.2103770,41.5459864
|
| 137 |
+
9100698744,2.2143873,41.5387290
|
| 138 |
+
9100701166,2.2087202,41.5460003
|
| 139 |
+
9100702970,2.2117422,41.5402135
|
| 140 |
+
9100708130,2.2079690,41.5457329
|
| 141 |
+
9100721293,2.2312937,41.5532633
|
| 142 |
+
9100725280,2.2067478,41.5484516
|
| 143 |
+
9100727703,2.2099801,41.5396122
|
| 144 |
+
9100731896,2.2128990,41.5449562
|
| 145 |
+
9100733214,2.2146578,41.5348907
|
| 146 |
+
9100734979,2.2053939,41.5456206
|
| 147 |
+
9100738455,2.2037207,41.5472792
|
| 148 |
+
9100740330,2.2145252,41.5379929
|
| 149 |
+
9100742484,2.2074409,41.5483739
|
| 150 |
+
9100744307,2.2033610,41.5474286
|
| 151 |
+
9100746967,2.2047268,41.5448415
|
| 152 |
+
9100746984,2.2069067,41.5460519
|
| 153 |
+
9100750130,2.2172757,41.5433182
|
| 154 |
+
9100755962,2.2212189,41.5480125
|
| 155 |
+
9100760131,2.2140396,41.5411864
|
| 156 |
+
9100760596,2.2122023,41.5377822
|
| 157 |
+
9100764101,2.2103975,41.5358610
|
| 158 |
+
9100764969,2.2109429,41.5408021
|
| 159 |
+
9100055150,2.2102571,41.5430140
|
| 160 |
+
9100040930,2.2966772,41.6112168
|
| 161 |
+
9100044418,2.2962428,41.6016140
|
| 162 |
+
9100044461,2.2948382,41.6128906
|
| 163 |
+
9100219680,2.2908605,41.6029664
|
| 164 |
+
9100252903,2.2884246,41.6068851
|
| 165 |
+
9100743845,2.2858358,41.6081910
|
| 166 |
+
122790,2.2826119,41.6010176
|
| 167 |
+
122790,2.2826119,41.6010176
|
| 168 |
+
122790,2.2826119,41.6010176
|
| 169 |
+
122790,2.2826119,41.6010176
|
| 170 |
+
136720,2.2860180,41.6089721
|
| 171 |
+
136720,2.2860180,41.6089721
|
| 172 |
+
9100756793,2.2853158,41.6062754
|
| 173 |
+
9100041486,2.2848532,41.6022445
|
| 174 |
+
9100042386,2.2904212,41.6145934
|
| 175 |
+
9100043893,2.2864716,41.5979571
|
| 176 |
+
9100044282,2.2857091,41.6119638
|
| 177 |
+
9100044469,2.2848378,41.6079349
|
| 178 |
+
9100056059,2.2863715,41.6055199
|
| 179 |
+
9100087801,2.2848276,41.6067998
|
| 180 |
+
9100098069,2.2835448,41.6105436
|
| 181 |
+
9100145874,2.2844361,41.6076185
|
| 182 |
+
9100219758,2.2903800,41.6100748
|
| 183 |
+
9100229514,2.2844361,41.6076185
|
| 184 |
+
9100250393,2.2845818,41.6045777
|
| 185 |
+
9100253786,2.2849912,41.6008185
|
| 186 |
+
9100301896,2.2844361,41.6076185
|
| 187 |
+
9100302461,2.2826132,41.6044162
|
| 188 |
+
9100399265,2.2860180,41.6089721
|
| 189 |
+
9100399265,2.2860180,41.6089721
|
| 190 |
+
9100479079,2.2878827,41.6018011
|
| 191 |
+
9100523982,2.2862797,41.6090288
|
| 192 |
+
9100527198,2.2862953,41.6073341
|
| 193 |
+
9100658144,2.2895960,41.6137480
|
| 194 |
+
9100664303,2.2878566,41.6094543
|
| 195 |
+
9100677377,2.2907866,41.6147295
|
| 196 |
+
9100680026,2.2875639,41.6049348
|
| 197 |
+
9100681218,2.2839377,41.6047532
|
| 198 |
+
9100681231,2.2832281,41.6048713
|
| 199 |
+
9100682473,2.2835448,41.6105436
|
| 200 |
+
9100685322,2.2826119,41.6010176
|
| 201 |
+
9100695278,2.2856383,41.6078865
|
| 202 |
+
9100699710,2.2834923,41.6077058
|
| 203 |
+
9100701777,2.2809916,41.5863360
|
| 204 |
+
9100717340,2.2862049,41.6087026
|
| 205 |
+
9100723316,2.2836169,41.6004999
|
| 206 |
+
9100727750,2.2881422,41.6045572
|
| 207 |
+
9100735762,2.2882135,41.6103062
|
| 208 |
+
9100744232,2.2857910,41.6095412
|
| 209 |
+
9100747036,2.2881422,41.6045572
|
| 210 |
+
9100751859,2.2857091,41.6119638
|
| 211 |
+
9100751879,2.2868959,41.6125659
|
| 212 |
+
9100752730,2.2868135,41.6110212
|
| 213 |
+
9100756820,2.2893978,41.6135096
|
| 214 |
+
9100764052,2.2829522,41.6066781
|
| 215 |
+
9100764737,2.2857091,41.6119638
|
| 216 |
+
9100553805,2.2899627,41.6000610
|
| 217 |
+
9100041010,2.2906157,41.6014433
|
| 218 |
+
9100041103,2.2890758,41.6022669
|
| 219 |
+
9100043144,2.3021682,41.6098501
|
| 220 |
+
9100043853,2.2902828,41.6091422
|
| 221 |
+
9100044157,2.2879157,41.6000696
|
| 222 |
+
9100094835,2.2902828,41.6091422
|
| 223 |
+
9100120794,2.2902828,41.6091422
|
| 224 |
+
9100151073,2.2879157,41.6000696
|
| 225 |
+
9100238682,2.2894189,41.6076472
|
| 226 |
+
9100338195,2.2948382,41.6128906
|
| 227 |
+
9100340763,2.2946433,41.6212710
|
| 228 |
+
9100341135,2.2916653,41.6128008
|
| 229 |
+
9100348821,2.2880423,41.5908820
|
| 230 |
+
9100355289,2.2912334,41.5997034
|
| 231 |
+
9100503002,2.2903709,41.6081160
|
| 232 |
+
9100513352,2.2880423,41.5908820
|
| 233 |
+
9100616552,2.2870963,41.5997457
|
| 234 |
+
9100619658,2.2900023,41.6076959
|
| 235 |
+
9100658142,2.2902828,41.6091422
|
| 236 |
+
9100661498,2.3021682,41.6098501
|
| 237 |
+
9100678134,2.2902828,41.6091422
|
| 238 |
+
9100685852,2.2884292,41.5975345
|
| 239 |
+
9100688641,2.2962684,41.6075810
|
| 240 |
+
9100698912,2.2883573,41.5961028
|
| 241 |
+
9100699983,2.2930628,41.6183051
|
| 242 |
+
9100700301,2.2901218,41.5929397
|
| 243 |
+
9100703647,2.2900905,41.6066610
|
| 244 |
+
9100718706,2.2896298,41.6043855
|
| 245 |
+
9100723039,2.2962428,41.6016140
|
| 246 |
+
9100723531,2.2902828,41.6091422
|
| 247 |
+
9100729258,2.2905778,41.6068004
|
| 248 |
+
9100729405,2.2880423,41.5908820
|
| 249 |
+
9100730939,2.2912636,41.6042778
|
| 250 |
+
9100732766,2.2903800,41.6100748
|
| 251 |
+
9100736806,2.2912334,41.5997034
|
| 252 |
+
9100737231,2.2948382,41.6128906
|
| 253 |
+
9100738166,2.2948382,41.6128906
|
| 254 |
+
9100742644,2.2962428,41.6016140
|
| 255 |
+
9100742712,2.2877804,41.6032002
|
| 256 |
+
9100743172,2.2901218,41.5929397
|
| 257 |
+
9100743868,2.2883573,41.5961028
|
| 258 |
+
9100745043,2.2905456,41.6050949
|
| 259 |
+
9100747591,2.3021682,41.6098501
|
| 260 |
+
9100750645,2.2902828,41.6091422
|
| 261 |
+
9100755605,2.2948458,41.6150155
|
| 262 |
+
9100756365,2.2903800,41.6100748
|
| 263 |
+
9100762486,2.2887494,41.5936550
|
| 264 |
+
128003,2.2941506,41.6133433
|
| 265 |
+
9100733969,2.2466144,41.5683377
|
| 266 |
+
9100746992,2.2335471,41.5546655
|
| 267 |
+
9100659648,2.2778844,41.6111420
|
| 268 |
+
9100662674,2.2809997,41.6098151
|
| 269 |
+
9100681160,2.2678223,41.5946422
|
| 270 |
+
9100043170,2.2776200,41.6000581
|
| 271 |
+
9100422647,2.2765848,41.6138166
|
| 272 |
+
9100422647,2.2765848,41.6138166
|
| 273 |
+
9100190779,2.2128282,41.5376150
|
| 274 |
+
9100271543,2.2102490,41.5340160
|
| 275 |
+
9100305166,2.2063516,41.5319198
|
| 276 |
+
9100305167,2.2097139,41.5344906
|
| 277 |
+
9100354651,2.2116133,41.5368244
|
| 278 |
+
9100517197,2.2085748,41.5392350
|
| 279 |
+
9100519718,2.2128282,41.5376150
|
| 280 |
+
9100054838,2.2112416,41.5452446
|
| 281 |
+
9100055377,2.2129105,41.5377848
|
| 282 |
+
9100121732,2.2130934,41.5393484
|
| 283 |
+
9100281244,2.2190261,41.5431161
|
| 284 |
+
9100331748,2.2172619,41.5446492
|
| 285 |
+
9100336673,2.2052990,41.5487701
|
| 286 |
+
9100336673,2.2052990,41.5487701
|
| 287 |
+
9100341736,2.2054979,41.5472380
|
| 288 |
+
9100369998,2.2132852,41.5403600
|
| 289 |
+
9100444956,2.2204354,41.5476472
|
| 290 |
+
9100486027,2.2102612,41.5442036
|
| 291 |
+
9100514208,2.2110252,41.5414845
|
| 292 |
+
9100517477,2.2128282,41.5376150
|
| 293 |
+
9100520123,2.2126138,41.5375280
|
| 294 |
+
9100522192,2.2064397,41.5365029
|
| 295 |
+
9100536443,2.2082685,41.5403851
|
| 296 |
+
9100536466,2.2084332,41.5346950
|
| 297 |
+
9100536469,2.2081840,41.5346681
|
| 298 |
+
9100536481,2.2178009,41.5445223
|
| 299 |
+
9100497829,2.2179660,41.5455691
|
| 300 |
+
9100304652,2.2902828,41.6091422
|
| 301 |
+
9100365812,2.2902828,41.6091422
|
| 302 |
+
9100447620,2.2901386,41.5995054
|
| 303 |
+
9100517443,2.2966772,41.6112168
|
| 304 |
+
9100520022,2.2902828,41.6091422
|
| 305 |
+
9100480482,2.2874578,41.5918442
|
| 306 |
+
9100524060,2.2935300,41.6079447
|
| 307 |
+
9100495996,2.2900905,41.6066610
|
| 308 |
+
9100153969,2.2874201,41.6060897
|
| 309 |
+
9100227012,2.2836169,41.6004999
|
| 310 |
+
9100310143,2.2843711,41.6052993
|
| 311 |
+
9100338196,2.2848532,41.6022445
|
| 312 |
+
9100340260,2.2901218,41.5929397
|
| 313 |
+
9100420786,2.2849912,41.6008185
|
| 314 |
+
9100420786,2.2849912,41.6008185
|
| 315 |
+
9100445926,2.2862953,41.6073341
|
| 316 |
+
9100506888,2.2862277,41.6128208
|
| 317 |
+
9100507154,2.2868135,41.6110212
|
| 318 |
+
9100507921,2.2839377,41.6047532
|
| 319 |
+
9100513397,2.2856383,41.6078865
|
| 320 |
+
9100525754,2.2829522,41.6066781
|
| 321 |
+
9100151566,2.2857091,41.6119638
|
| 322 |
+
9100443296,2.2086125,41.5455598
|
| 323 |
+
9100443296,2.2086125,41.5455598
|
| 324 |
+
9100423767,2.2614494,41.5914669
|
| 325 |
+
9100420785,2.2787148,41.5864105
|
| 326 |
+
9100120799,2.2879189,41.6137279
|
| 327 |
+
9100122840,2.2899986,41.5978596
|
| 328 |
+
9100140860,2.2865995,41.5973528
|
| 329 |
+
9100283498,2.2928775,41.6052739
|
| 330 |
+
9100522267,2.2091974,41.5382145
|
| 331 |
+
9100054744,2.2175419,41.5334748
|
| 332 |
+
9100054778,2.2190209,41.5406662
|
| 333 |
+
9100054857,2.2086022,41.5342606
|
| 334 |
+
9100055224,2.2099801,41.5396122
|
| 335 |
+
9100055497,2.2181763,41.5394682
|
| 336 |
+
9100055508,2.2126637,41.5376026
|
| 337 |
+
9100056293,2.2116836,41.5373806
|
| 338 |
+
9100075499,2.2142802,41.5343759
|
| 339 |
+
9100403364,2.2099801,41.5396122
|
| 340 |
+
9100403364,2.2099801,41.5396122
|
| 341 |
+
9100043749,2.2127274,41.5440011
|
| 342 |
+
9100054481,2.2091222,41.5325847
|
| 343 |
+
9100054565,2.2167283,41.5442201
|
| 344 |
+
9100054732,2.2097257,41.5433826
|
| 345 |
+
9100054873,2.2091274,41.5439659
|
| 346 |
+
9100055568,2.2128459,41.5397838
|
| 347 |
+
9100184449,2.2133356,41.5387104
|
| 348 |
+
9100366732,2.2194830,41.5443121
|
| 349 |
+
9100366733,2.2189898,41.5439145
|
| 350 |
+
9100553669,2.2089742,41.5490650
|
| 351 |
+
9100629788,2.2158511,41.5358663
|
| 352 |
+
9100655002,2.2158262,41.5335224
|
| 353 |
+
9100655372,2.2193309,41.5479989
|
| 354 |
+
9100680393,2.2128798,41.5417561
|
| 355 |
+
9100530919,2.2190261,41.5431161
|
| 356 |
+
9100054639,2.2102836,41.5412876
|
| 357 |
+
9100054644,2.2136925,41.5396471
|
| 358 |
+
9100054973,2.2095811,41.5432985
|
| 359 |
+
9100056047,2.2041449,41.5462592
|
| 360 |
+
9100054513,2.2059673,41.5364637
|
| 361 |
+
9100040724,2.2884246,41.6068851
|
| 362 |
+
9100041355,2.2902828,41.6091422
|
| 363 |
+
9100041765,2.2883743,41.5974116
|
| 364 |
+
9100041774,2.2917028,41.6129399
|
| 365 |
+
9100041990,2.2916653,41.6128008
|
| 366 |
+
9100042037,2.2903973,41.6072964
|
| 367 |
+
9100042037,2.2903973,41.6072964
|
| 368 |
+
9100042491,2.2893517,41.6048363
|
| 369 |
+
9100043799,2.2932558,41.6132830
|
| 370 |
+
9100044409,2.2930628,41.6183051
|
| 371 |
+
9100076613,2.2893517,41.6048363
|
| 372 |
+
9100134890,2.2948382,41.6128906
|
| 373 |
+
9100264654,2.2905456,41.6050949
|
| 374 |
+
9100521595,2.2916653,41.6128008
|
| 375 |
+
9100521678,2.2745725,41.5999439
|
| 376 |
+
9100521678,2.2745725,41.5999439
|
| 377 |
+
9100633076,2.2879211,41.6034961
|
| 378 |
+
9100755674,2.2948382,41.6128906
|
| 379 |
+
9100761572,2.2884292,41.5975345
|
| 380 |
+
9100765851,2.2902072,41.5979909
|
| 381 |
+
9100366724,2.2083603,41.5479180
|
| 382 |
+
9100042829,2.2866014,41.6009244
|
| 383 |
+
9100042612,2.2895960,41.6137480
|
| 384 |
+
9100043364,2.2869413,41.6078514
|
| 385 |
+
9100043381,2.2848276,41.6067998
|
| 386 |
+
9100059282,2.2859478,41.6003820
|
| 387 |
+
9100121164,2.2836169,41.6004999
|
| 388 |
+
9100373422,2.2860180,41.6089721
|
| 389 |
+
9100041564,2.2900731,41.6120428
|
| 390 |
+
9100042249,2.2778844,41.6111420
|
| 391 |
+
9100042942,2.2830445,41.6063647
|
| 392 |
+
9100043866,2.2902828,41.6091422
|
| 393 |
+
9100110789,2.2778844,41.6111420
|
| 394 |
+
128051,2.2112807,41.5385814
|
| 395 |
+
9100498065,2.2113414,41.5358970
|
| 396 |
+
9100501076,2.2872104,41.6079613
|
| 397 |
+
9100502995,2.2941506,41.6133433
|
| 398 |
+
9100503205,2.2172619,41.5446492
|
| 399 |
+
9100529181,2.2204354,41.5476472
|
| 400 |
+
9100535260,2.2868135,41.6110212
|
| 401 |
+
9100536475,2.2099801,41.5396122
|
| 402 |
+
9100538933,2.2902828,41.6091422
|
| 403 |
+
9100539485,2.2113467,41.5382678
|
| 404 |
+
9100540782,2.2089661,41.5469214
|
| 405 |
+
9100542069,2.2127664,41.5389993
|
| 406 |
+
9100544203,2.2092940,41.5435383
|
| 407 |
+
9100544826,2.2037207,41.5472792
|
| 408 |
+
9100545628,2.2122023,41.5377822
|
| 409 |
+
9100550379,2.2836169,41.6004999
|
| 410 |
+
9100552330,2.2166876,41.5401113
|
| 411 |
+
9100553092,2.2921283,41.6122100
|
| 412 |
+
9100554598,2.2905456,41.6050949
|
| 413 |
+
9100556448,2.2172160,41.5347593
|
| 414 |
+
9100556449,2.2204354,41.5476472
|
| 415 |
+
9100558804,2.2893517,41.6048363
|
| 416 |
+
9100559291,2.2913767,41.5987086
|
| 417 |
+
9100566254,2.2103227,41.5390911
|
| 418 |
+
9100568067,2.2781179,41.6099253
|
| 419 |
+
9100568310,2.2107797,41.5358796
|
| 420 |
+
9100579782,2.2749209,41.6142376
|
| 421 |
+
9100582978,2.2094442,41.5404481
|
| 422 |
+
9100583212,2.2198141,41.5480364
|
| 423 |
+
9100591127,2.2128282,41.5376150
|
| 424 |
+
9100603628,2.2948382,41.6128906
|
| 425 |
+
9100607321,2.2048012,41.5475288
|
| 426 |
+
9100607527,2.2946433,41.6212710
|
| 427 |
+
9100495381,2.2890613,41.6082392
|
coords_original.csv
ADDED
|
@@ -0,0 +1,1369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
id,x,y
|
| 2 |
+
9100681586,2.2761414,41.9534888
|
| 3 |
+
9100673632,2.2664487,41.9339248
|
| 4 |
+
9100642823,2.2524996,41.9432633
|
| 5 |
+
9100684868,2.2515181,41.9427456
|
| 6 |
+
9100577691,2.2596812,41.9440200
|
| 7 |
+
9100579841,2.2699563,41.9468902
|
| 8 |
+
9100577732,2.2568159,41.9225347
|
| 9 |
+
9100215822,2.2534725,41.9344164
|
| 10 |
+
9100159640,2.2537880,41.9304920
|
| 11 |
+
9100661094,2.2503658,41.9325627
|
| 12 |
+
9100664901,2.2607560,41.9353285
|
| 13 |
+
9100723046,2.2530973,41.9387629
|
| 14 |
+
9100700207,2.2542440,41.9311873
|
| 15 |
+
9100592884,2.2580752,41.9432061
|
| 16 |
+
9100648495,2.2503642,41.9400424
|
| 17 |
+
9100669635,2.2427068,41.9339086
|
| 18 |
+
9100581337,2.2507540,41.9282378
|
| 19 |
+
9100399521,2.2464148,41.9257044
|
| 20 |
+
9100411152,2.2464148,41.9257044
|
| 21 |
+
9100642838,2.2521353,41.9282438
|
| 22 |
+
9100577728,2.2595161,41.9236118
|
| 23 |
+
9100416412,2.2576833,41.9469832
|
| 24 |
+
9100353302,2.2536660,41.9216704
|
| 25 |
+
9100159004,2.2535786,41.9232104
|
| 26 |
+
9100158975,2.2511652,41.9218291
|
| 27 |
+
9100158925,2.2511589,41.9209841
|
| 28 |
+
9100135277,2.2483197,41.9330738
|
| 29 |
+
9100582033,2.2601402,41.9369272
|
| 30 |
+
9100579835,2.2700673,41.9491580
|
| 31 |
+
9100582097,2.2601402,41.9369272
|
| 32 |
+
9100701789,2.2556061,41.9280651
|
| 33 |
+
9100059229,2.3040510,41.9147708
|
| 34 |
+
9100058850,2.2556935,41.9272415
|
| 35 |
+
9100689904,2.2524005,41.9266809
|
| 36 |
+
9100617812,2.2563649,41.9303739
|
| 37 |
+
9100563642,2.2531530,41.9316684
|
| 38 |
+
9100492927,2.2650848,41.9278044
|
| 39 |
+
9100374429,2.2552611,41.9299960
|
| 40 |
+
9100343446,2.2613364,41.9329919
|
| 41 |
+
9100253034,2.2433976,41.9288411
|
| 42 |
+
9100197987,2.2598956,41.9351571
|
| 43 |
+
9100192841,2.2591520,41.9298377
|
| 44 |
+
9100159002,2.2553725,41.9392044
|
| 45 |
+
9100158946,2.2532078,41.9311045
|
| 46 |
+
9100158856,2.2493603,41.9282167
|
| 47 |
+
9100158828,2.2605091,41.9496266
|
| 48 |
+
9100158696,2.2444589,41.9278558
|
| 49 |
+
9100152300,2.2468399,41.9259367
|
| 50 |
+
9100081422,2.2535593,41.9294084
|
| 51 |
+
9100059399,2.2581330,41.9307726
|
| 52 |
+
9100636961,2.2505314,41.9319171
|
| 53 |
+
9100389358,2.2565573,41.9292890
|
| 54 |
+
129963,2.2553725,41.9392044
|
| 55 |
+
129963,2.2553725,41.9392044
|
| 56 |
+
9100160118,2.2607705,41.9362066
|
| 57 |
+
9100579824,2.2613364,41.9329919
|
| 58 |
+
9100738149,2.2565573,41.9292890
|
| 59 |
+
9100058481,2.2511499,41.9336836
|
| 60 |
+
9100058670,2.2570623,41.9201837
|
| 61 |
+
9100554547,2.2490191,41.9261272
|
| 62 |
+
9100206312,2.2613364,41.9329919
|
| 63 |
+
9100649610,2.2602515,41.9356846
|
| 64 |
+
9100666666,2.2525867,41.9279680
|
| 65 |
+
9100678356,1.7238666,41.2247191
|
| 66 |
+
9100735783,2.2503642,41.9400424
|
| 67 |
+
9100737400,2.0130333,41.5749006
|
| 68 |
+
9100153533,2.2122023,41.5377822
|
| 69 |
+
9100423445,2.2139430,41.5373566
|
| 70 |
+
9100505447,2.2108199,41.5364182
|
| 71 |
+
9100138634,2.2261548,41.5487678
|
| 72 |
+
9100228193,2.2109740,41.5379267
|
| 73 |
+
9100519725,2.2080131,41.5487496
|
| 74 |
+
9100519746,2.2199409,41.5483345
|
| 75 |
+
9100610435,2.2202430,41.5449153
|
| 76 |
+
9100621276,2.2076723,41.5407351
|
| 77 |
+
9100622784,2.2097200,41.5375000
|
| 78 |
+
9100623018,2.2133356,41.5387104
|
| 79 |
+
9100632834,2.2176727,41.5354334
|
| 80 |
+
9100639237,2.2081403,41.5329122
|
| 81 |
+
9100646097,2.2134024,41.5385494
|
| 82 |
+
9100646795,2.2130934,41.5393484
|
| 83 |
+
9100653307,2.2187350,41.5471420
|
| 84 |
+
9100686742,2.2064397,41.5365029
|
| 85 |
+
9100688668,2.2076124,41.5479653
|
| 86 |
+
9100055962,2.2127912,41.5440412
|
| 87 |
+
9100654473,2.2185275,41.5364259
|
| 88 |
+
9100687896,2.0238222,41.5766663
|
| 89 |
+
9100646871,2.0238222,41.5766663
|
| 90 |
+
9100646871,2.0238222,41.5766663
|
| 91 |
+
9100565225,2.1588520,41.6373520
|
| 92 |
+
9100582098,2.2859297,42.0012394
|
| 93 |
+
9100582039,2.2553511,41.9338179
|
| 94 |
+
9100453364,2.2797769,42.0065077
|
| 95 |
+
9100582034,2.2859297,42.0012394
|
| 96 |
+
9100160021,2.2827129,42.0018981
|
| 97 |
+
9100158833,2.2841079,42.0000201
|
| 98 |
+
9100681243,2.2852099,42.0046441
|
| 99 |
+
9100494906,2.2859297,42.0012394
|
| 100 |
+
9100698675,2.2269093,41.4356616
|
| 101 |
+
118470,2.2903800,41.6100748
|
| 102 |
+
118470,2.2903800,41.6100748
|
| 103 |
+
9100614413,2.2930703,41.6154062
|
| 104 |
+
9100626587,2.3021682,41.6098501
|
| 105 |
+
9100629896,2.2901386,41.5995054
|
| 106 |
+
9100630014,2.2899627,41.6000610
|
| 107 |
+
9100638648,2.2884292,41.5975345
|
| 108 |
+
9100639032,2.2902828,41.6091422
|
| 109 |
+
9100647947,2.2905456,41.6050949
|
| 110 |
+
9100702482,2.2902828,41.6091422
|
| 111 |
+
127151,2.2867282,41.5960953
|
| 112 |
+
127151,2.2867282,41.5960953
|
| 113 |
+
9100226319,2.2894172,41.6051624
|
| 114 |
+
9100621389,2.2884292,41.5975345
|
| 115 |
+
9100651895,2.2919194,41.6066418
|
| 116 |
+
9100696143,2.1886164,41.4772111
|
| 117 |
+
9100772092,1.9778551,41.2715237
|
| 118 |
+
9100378510,2.3066306,41.6475034
|
| 119 |
+
9100432797,2.2968737,41.6362062
|
| 120 |
+
9100373615,2.2886546,41.6079491
|
| 121 |
+
9100042719,2.2867308,41.6078892
|
| 122 |
+
9100291002,2.2904018,41.6135165
|
| 123 |
+
9100469494,2.2844361,41.6076185
|
| 124 |
+
9100620561,2.2880019,41.6094653
|
| 125 |
+
9100629126,2.2848378,41.6079349
|
| 126 |
+
9100645312,2.2884352,41.6083389
|
| 127 |
+
9100645312,2.2884352,41.6083389
|
| 128 |
+
9100731938,2.2908170,42.0688515
|
| 129 |
+
9100694999,2.2626881,42.0499809
|
| 130 |
+
9100613076,2.2365300,41.5144672
|
| 131 |
+
9100615213,2.2387255,41.6168847
|
| 132 |
+
9100617121,2.2680755,41.5423391
|
| 133 |
+
9100622604,2.2639501,41.5425769
|
| 134 |
+
9100626406,2.2358483,41.5730106
|
| 135 |
+
9100641568,2.2428345,41.5274308
|
| 136 |
+
9100642392,2.2809997,41.6098151
|
| 137 |
+
9100643360,2.2392923,41.6091585
|
| 138 |
+
9100645053,2.2378567,41.5166869
|
| 139 |
+
9100650953,2.2657650,41.5424778
|
| 140 |
+
9100650976,2.2630204,41.5396186
|
| 141 |
+
9100651605,2.2422342,41.5522085
|
| 142 |
+
9100652617,2.2776200,41.6000581
|
| 143 |
+
9100669500,2.2272296,41.5650830
|
| 144 |
+
9100683728,2.2454707,41.5497584
|
| 145 |
+
9100700812,2.2288549,41.5610382
|
| 146 |
+
9100735784,2.2503758,41.9493593
|
| 147 |
+
9100658240,2.2911384,42.0706327
|
| 148 |
+
9100670575,2.2440361,42.0097548
|
| 149 |
+
9100742665,2.1654577,42.2991932
|
| 150 |
+
9100159110,2.2291876,41.8499396
|
| 151 |
+
9100121555,2.1889602,41.9103794
|
| 152 |
+
9100529186,2.2692686,42.0628895
|
| 153 |
+
9100528087,2.2388317,42.0577275
|
| 154 |
+
9100517311,2.2735167,42.0618766
|
| 155 |
+
9100513817,2.2588065,42.0472690
|
| 156 |
+
9100378362,2.2619260,42.0614029
|
| 157 |
+
9100369663,2.2176420,42.1014165
|
| 158 |
+
9100281570,2.2432687,42.0537348
|
| 159 |
+
9100159884,2.2176548,42.1015934
|
| 160 |
+
9100159296,2.2733199,42.0421397
|
| 161 |
+
9100126286,2.2523327,42.0509255
|
| 162 |
+
9100122181,2.2633591,42.0486753
|
| 163 |
+
9100110641,2.2558745,42.0606621
|
| 164 |
+
9100577666,2.3160025,41.9379237
|
| 165 |
+
9100577666,2.3160025,41.9379237
|
| 166 |
+
9100282736,2.2959777,42.0747765
|
| 167 |
+
9100572141,2.2988127,41.9290515
|
| 168 |
+
9100433466,2.2415844,42.0031722
|
| 169 |
+
9100220279,2.2844174,41.9240718
|
| 170 |
+
9100160124,2.3098611,41.9842883
|
| 171 |
+
9100397795,2.2187303,41.9604417
|
| 172 |
+
9100059032,2.2793945,41.9257923
|
| 173 |
+
9100122540,2.2965618,42.0751312
|
| 174 |
+
9100255064,2.1801599,41.9359546
|
| 175 |
+
9100058278,2.2668592,42.0455750
|
| 176 |
+
9100577693,2.2183972,41.8484694
|
| 177 |
+
9100041762,2.2338386,41.5731338
|
| 178 |
+
9100044050,2.2670557,41.5425450
|
| 179 |
+
9100054518,2.1963277,41.5629993
|
| 180 |
+
9100184803,2.2204658,41.6146683
|
| 181 |
+
9100601539,2.2480812,41.5515189
|
| 182 |
+
9100381913,2.3495103,41.5989840
|
| 183 |
+
9100579833,2.3244213,41.9231156
|
| 184 |
+
9100206314,2.2189237,41.7967600
|
| 185 |
+
9100648393,2.2420281,42.0186373
|
| 186 |
+
9100597817,2.2619851,42.0467040
|
| 187 |
+
9100652762,2.2605972,42.0471788
|
| 188 |
+
9100159533,2.2449807,42.0154462
|
| 189 |
+
9100041008,2.2869478,41.6129094
|
| 190 |
+
9100450636,2.2497544,41.5492483
|
| 191 |
+
9100468310,2.2105956,41.5852616
|
| 192 |
+
9100493210,2.2450245,41.5226144
|
| 193 |
+
9100299260,2.2318948,41.5356836
|
| 194 |
+
9100514207,2.2493274,41.5520324
|
| 195 |
+
9100055266,2.2280352,41.5275215
|
| 196 |
+
9100042975,2.2778844,41.6111420
|
| 197 |
+
9100281245,2.2335076,41.5712669
|
| 198 |
+
9100054533,2.2291023,41.5347619
|
| 199 |
+
9100268368,2.2474868,41.5774520
|
| 200 |
+
9100054998,2.2128282,41.5376150
|
| 201 |
+
9100424060,2.2096754,41.5417587
|
| 202 |
+
9100424060,2.2096754,41.5417587
|
| 203 |
+
9100444038,2.2064101,41.5367615
|
| 204 |
+
9100350872,2.2194241,41.5479420
|
| 205 |
+
9100350872,2.2194241,41.5479420
|
| 206 |
+
9100631930,2.2026414,41.5531014
|
| 207 |
+
9100660484,2.2119365,41.5369045
|
| 208 |
+
9100663784,2.2191655,41.5408673
|
| 209 |
+
9100686343,2.2120439,41.5373051
|
| 210 |
+
9100733360,2.2176096,41.5388602
|
| 211 |
+
9100745447,2.2172757,41.5433182
|
| 212 |
+
9100756398,2.2120439,41.5373051
|
| 213 |
+
9100762714,2.2059673,41.5364637
|
| 214 |
+
9100765744,2.2175419,41.5334748
|
| 215 |
+
9100767546,2.2175419,41.5334748
|
| 216 |
+
9100055977,2.2175419,41.5334748
|
| 217 |
+
9100055145,2.2161158,41.5428223
|
| 218 |
+
9100373406,2.1089413,41.8982599
|
| 219 |
+
119751,2.2224956,41.5408211
|
| 220 |
+
119751,2.2224956,41.5408211
|
| 221 |
+
119751,2.2224956,41.5408211
|
| 222 |
+
119751,2.2224956,41.5408211
|
| 223 |
+
119751,2.2224956,41.5408211
|
| 224 |
+
119751,2.2224956,41.5408211
|
| 225 |
+
9100111065,2.2243481,41.5397125
|
| 226 |
+
9100690441,2.2268314,41.5554911
|
| 227 |
+
9100433502,2.2072597,41.5403403
|
| 228 |
+
103154,2.2293485,41.5469926
|
| 229 |
+
103154,2.2293485,41.5469926
|
| 230 |
+
9100054573,2.2138550,41.5374979
|
| 231 |
+
9100054762,2.2127653,41.5353139
|
| 232 |
+
9100054848,2.2088394,41.5356284
|
| 233 |
+
9100054935,2.2135257,41.5367766
|
| 234 |
+
9100055101,2.2097139,41.5344906
|
| 235 |
+
9100055108,2.2146578,41.5348907
|
| 236 |
+
9100055221,2.2080238,41.5349415
|
| 237 |
+
9100055265,2.2118764,41.5399187
|
| 238 |
+
9100055505,2.2052385,41.5489980
|
| 239 |
+
9100055626,2.2115260,41.5365981
|
| 240 |
+
9100055679,2.2099163,41.5413828
|
| 241 |
+
9100056090,2.2108804,41.5393929
|
| 242 |
+
9100056113,2.2207985,41.5440801
|
| 243 |
+
9100056124,2.2117241,41.5370793
|
| 244 |
+
9100059602,2.2059673,41.5364637
|
| 245 |
+
9100076081,2.2078993,41.5349726
|
| 246 |
+
9100135772,2.2101021,41.5398093
|
| 247 |
+
9100150731,2.2122023,41.5377822
|
| 248 |
+
9100153532,2.2122023,41.5377822
|
| 249 |
+
9100178455,2.2130934,41.5393484
|
| 250 |
+
9100338332,2.2065665,41.5389553
|
| 251 |
+
9100413328,2.2146578,41.5348907
|
| 252 |
+
9100466233,2.2170676,41.5363800
|
| 253 |
+
9100470966,2.2173766,41.5385546
|
| 254 |
+
9100040775,2.2178880,41.5446079
|
| 255 |
+
9100054707,2.2069974,41.5458365
|
| 256 |
+
9100054708,2.2122200,41.5383466
|
| 257 |
+
9100054841,2.2179029,41.5395838
|
| 258 |
+
9100054856,2.2079370,41.5480260
|
| 259 |
+
9100054872,2.2172757,41.5433182
|
| 260 |
+
9100054896,2.2135294,41.5388501
|
| 261 |
+
9100054949,2.2080600,41.5467073
|
| 262 |
+
9100054994,2.2095811,41.5432985
|
| 263 |
+
9100055030,2.2077722,41.5484192
|
| 264 |
+
9100055144,2.2077425,41.5433782
|
| 265 |
+
9100055194,2.2066997,41.5465437
|
| 266 |
+
9100055251,2.2080746,41.5457859
|
| 267 |
+
9100055620,2.2133356,41.5387104
|
| 268 |
+
9100055707,2.2127664,41.5389993
|
| 269 |
+
9100055836,2.2133356,41.5387104
|
| 270 |
+
9100056034,2.2053181,41.5448735
|
| 271 |
+
9100075764,2.2059142,41.5473369
|
| 272 |
+
9100120690,2.2312937,41.5532633
|
| 273 |
+
9100121589,2.2194830,41.5463287
|
| 274 |
+
9100126935,2.2048761,41.5407251
|
| 275 |
+
9100129133,2.2229330,41.5416117
|
| 276 |
+
9100239655,2.2113491,41.5381651
|
| 277 |
+
9100246685,2.2064461,41.5449523
|
| 278 |
+
9100326765,2.2204354,41.5476472
|
| 279 |
+
9100353152,2.2149531,41.5450899
|
| 280 |
+
9100366443,2.2172619,41.5446492
|
| 281 |
+
9100371825,2.2154312,41.5425485
|
| 282 |
+
9100371825,2.2154312,41.5425485
|
| 283 |
+
9100467331,2.2179029,41.5395838
|
| 284 |
+
9100612100,2.2035472,41.5469903
|
| 285 |
+
9100617395,2.2212649,41.5429148
|
| 286 |
+
9100642781,2.2081578,41.5409278
|
| 287 |
+
9100653427,2.2056984,41.5450720
|
| 288 |
+
9100663743,2.2110669,41.5416294
|
| 289 |
+
9100665155,2.2076759,41.5475361
|
| 290 |
+
9100670399,2.2044719,41.5469943
|
| 291 |
+
9100677478,2.2041746,41.5407892
|
| 292 |
+
9100684101,2.2179750,41.5446936
|
| 293 |
+
9100684195,2.2141858,41.5380416
|
| 294 |
+
9100694798,2.2174068,41.5416023
|
| 295 |
+
9100695039,2.2073608,41.5416700
|
| 296 |
+
9100695182,2.2103770,41.5459864
|
| 297 |
+
9100698744,2.2143873,41.5387290
|
| 298 |
+
9100701166,2.2087202,41.5460003
|
| 299 |
+
9100702970,2.2117422,41.5402135
|
| 300 |
+
9100708130,2.2079690,41.5457329
|
| 301 |
+
9100721293,2.2312937,41.5532633
|
| 302 |
+
9100725280,2.2067478,41.5484516
|
| 303 |
+
9100727703,2.2099801,41.5396122
|
| 304 |
+
9100731896,2.2128990,41.5449562
|
| 305 |
+
9100733214,2.2146578,41.5348907
|
| 306 |
+
9100734979,2.2053939,41.5456206
|
| 307 |
+
9100738455,2.2037207,41.5472792
|
| 308 |
+
9100740330,2.2145252,41.5379929
|
| 309 |
+
9100742484,2.2074409,41.5483739
|
| 310 |
+
9100744307,2.2033610,41.5474286
|
| 311 |
+
9100746967,2.2047268,41.5448415
|
| 312 |
+
9100746984,2.2069067,41.5460519
|
| 313 |
+
9100750130,2.2172757,41.5433182
|
| 314 |
+
9100755962,2.2212189,41.5480125
|
| 315 |
+
9100760131,2.2140396,41.5411864
|
| 316 |
+
9100760596,2.2122023,41.5377822
|
| 317 |
+
9100764101,2.2103975,41.5358610
|
| 318 |
+
9100764969,2.2109429,41.5408021
|
| 319 |
+
9100767153,2.2166358,41.5549179
|
| 320 |
+
9100055150,2.2102571,41.5430140
|
| 321 |
+
9100389125,2.2600454,41.9242746
|
| 322 |
+
9100529116,2.2537958,41.9251994
|
| 323 |
+
9100503495,2.2568623,41.9290103
|
| 324 |
+
9100573746,2.2563649,41.9303739
|
| 325 |
+
9100566266,2.2506089,41.9308683
|
| 326 |
+
9100563102,2.2485742,41.9306405
|
| 327 |
+
9100453983,2.2464502,41.9254891
|
| 328 |
+
9100721688,2.2611937,41.9296040
|
| 329 |
+
9100567698,2.2639403,41.9303538
|
| 330 |
+
9100058499,2.2502340,41.9308194
|
| 331 |
+
9100056562,2.2538173,41.9304079
|
| 332 |
+
9100736216,2.2551163,41.9297448
|
| 333 |
+
9100739591,2.2535593,41.9294084
|
| 334 |
+
9100745478,2.2418950,41.9331258
|
| 335 |
+
9100745910,2.2567384,41.9218069
|
| 336 |
+
9100752144,2.2553725,41.9392044
|
| 337 |
+
9100767161,2.2531348,41.9305718
|
| 338 |
+
9100771401,2.2469843,41.9280873
|
| 339 |
+
9100446439,2.2353908,41.9229088
|
| 340 |
+
9100724322,2.2645658,41.9295598
|
| 341 |
+
9100717724,2.2427068,41.9339086
|
| 342 |
+
9100715856,2.2628750,41.9283142
|
| 343 |
+
9100703436,2.2555490,41.9300495
|
| 344 |
+
9100703234,2.2611937,41.9296040
|
| 345 |
+
9100701784,2.2500644,41.9331299
|
| 346 |
+
9100699144,2.2462133,41.9277790
|
| 347 |
+
9100691356,2.2531962,41.9296059
|
| 348 |
+
9100682040,2.2538241,41.9331083
|
| 349 |
+
9100668218,2.2511589,41.9209841
|
| 350 |
+
9100452581,2.2537958,41.9251994
|
| 351 |
+
9100423519,2.2530946,41.9390433
|
| 352 |
+
9100409671,2.2537958,41.9251994
|
| 353 |
+
9100374304,2.2507637,41.9205837
|
| 354 |
+
9100324575,2.2537958,41.9251994
|
| 355 |
+
9100127570,2.2518941,41.9210286
|
| 356 |
+
115740,2.3208874,41.6569665
|
| 357 |
+
115740,2.3208874,41.6569665
|
| 358 |
+
9100727009,2.2550951,41.9305350
|
| 359 |
+
9100576586,2.2566849,41.9228639
|
| 360 |
+
9100158659,2.2537958,41.9251994
|
| 361 |
+
9100262827,2.2587748,41.9246710
|
| 362 |
+
9100494258,2.2823467,41.9501585
|
| 363 |
+
9100588030,2.2396588,41.9324198
|
| 364 |
+
9100579836,0.2472249,41.2092917
|
| 365 |
+
9100540322,2.2647540,41.9305666
|
| 366 |
+
9100527263,2.2416303,41.9277987
|
| 367 |
+
9100523486,2.2613364,41.9329919
|
| 368 |
+
9100520726,2.2491750,41.9305658
|
| 369 |
+
9100517701,2.2554247,41.9310557
|
| 370 |
+
9100517366,2.2559144,41.9292412
|
| 371 |
+
9100515640,2.2498685,41.9317399
|
| 372 |
+
9100454403,2.2553725,41.9392044
|
| 373 |
+
9100419900,2.2547534,41.9297992
|
| 374 |
+
9100345891,2.2581703,41.9293424
|
| 375 |
+
9100345891,2.2581703,41.9293424
|
| 376 |
+
9100337367,2.2479662,41.9325803
|
| 377 |
+
9100294904,2.2521129,41.9283170
|
| 378 |
+
9100246993,2.2552323,41.9304438
|
| 379 |
+
9100166608,2.2585039,41.9315695
|
| 380 |
+
9100126373,2.2598956,41.9351571
|
| 381 |
+
9100541821,2.2465476,41.9302339
|
| 382 |
+
9100628369,2.2509464,41.9330609
|
| 383 |
+
128247,2.2596812,41.9440200
|
| 384 |
+
128247,2.2596812,41.9440200
|
| 385 |
+
128247,2.2596812,41.9440200
|
| 386 |
+
128247,2.2596812,41.9440200
|
| 387 |
+
118136,2.2556061,41.9280651
|
| 388 |
+
118136,2.2556061,41.9280651
|
| 389 |
+
9100471566,2.2521129,41.9283170
|
| 390 |
+
9100739798,2.2596812,41.9440200
|
| 391 |
+
9100577790,2.2532078,41.9311045
|
| 392 |
+
9100156149,2.2525130,41.9397640
|
| 393 |
+
9100057040,2.2553725,41.9392044
|
| 394 |
+
9100634160,2.2838474,41.9513914
|
| 395 |
+
9100058168,2.2474786,41.9324264
|
| 396 |
+
9100058399,2.2441904,41.9299944
|
| 397 |
+
9100058486,2.2516845,41.9239278
|
| 398 |
+
9100058727,2.2538173,41.9304079
|
| 399 |
+
9100058749,2.2614048,41.9309017
|
| 400 |
+
9100056384,2.2540399,41.9252838
|
| 401 |
+
9100056413,2.2535786,41.9232104
|
| 402 |
+
9100056689,2.2447874,41.9274571
|
| 403 |
+
9100056722,2.2683223,41.9266540
|
| 404 |
+
9100056774,2.2537958,41.9251994
|
| 405 |
+
9100056957,2.2598956,41.9351571
|
| 406 |
+
9100056959,2.2481155,41.9283036
|
| 407 |
+
9100056959,2.2481155,41.9283036
|
| 408 |
+
9100057171,2.2498685,41.9317399
|
| 409 |
+
9100057428,2.2563590,41.9329091
|
| 410 |
+
9100057605,1.7243645,41.2218848
|
| 411 |
+
9100057936,2.2592526,41.9308577
|
| 412 |
+
9100057963,2.2480296,41.9327785
|
| 413 |
+
9100736273,2.2509012,41.9395873
|
| 414 |
+
9100740503,2.2507637,41.9205837
|
| 415 |
+
9100742873,2.2666919,41.9352578
|
| 416 |
+
9100745490,2.2462504,41.9307633
|
| 417 |
+
9100746500,2.2563726,41.9293215
|
| 418 |
+
9100746832,2.2550430,41.9462891
|
| 419 |
+
9100748119,2.2563649,41.9303739
|
| 420 |
+
9100748120,2.2511499,41.9336836
|
| 421 |
+
9100748566,2.2577609,41.9317137
|
| 422 |
+
9100751056,2.2533756,41.9321566
|
| 423 |
+
9100752643,2.2608214,41.9359628
|
| 424 |
+
9100752651,2.2533756,41.9321566
|
| 425 |
+
9100752860,2.2506925,41.9243539
|
| 426 |
+
9100755818,2.2485905,41.9309900
|
| 427 |
+
9100760072,2.2563649,41.9303739
|
| 428 |
+
9100766575,2.2596852,41.9284721
|
| 429 |
+
9100767754,2.2468399,41.9259367
|
| 430 |
+
9100059144,2.2537958,41.9251994
|
| 431 |
+
9100521875,2.2553725,41.9392044
|
| 432 |
+
9100668715,2.2490191,41.9261272
|
| 433 |
+
9100723264,2.0358486,41.4810270
|
| 434 |
+
9100696849,2.1865562,41.3940428
|
| 435 |
+
9100761557,2.1855349,41.4839950
|
| 436 |
+
9100750914,2.1068011,41.3625095
|
| 437 |
+
9100040930,2.2966772,41.6112168
|
| 438 |
+
9100044418,2.2962428,41.6016140
|
| 439 |
+
9100044457,2.2872441,41.6080085
|
| 440 |
+
9100044461,2.2948382,41.6128906
|
| 441 |
+
9100219680,2.2908605,41.6029664
|
| 442 |
+
9100252903,2.2884246,41.6068851
|
| 443 |
+
9100743845,2.2858358,41.6081910
|
| 444 |
+
9100747490,2.0276735,41.5624896
|
| 445 |
+
9100770429,2.2356974,41.4268393
|
| 446 |
+
9100736601,2.2178629,41.4313373
|
| 447 |
+
9100627990,2.1448161,41.5172181
|
| 448 |
+
9100749745,2.4401437,41.5375440
|
| 449 |
+
9100474233,2.4332586,41.5369710
|
| 450 |
+
9100756802,2.0422074,41.3821845
|
| 451 |
+
122790,2.2826119,41.6010176
|
| 452 |
+
122790,2.2826119,41.6010176
|
| 453 |
+
122790,2.2826119,41.6010176
|
| 454 |
+
122790,2.2826119,41.6010176
|
| 455 |
+
136720,2.2860180,41.6089721
|
| 456 |
+
136720,2.2860180,41.6089721
|
| 457 |
+
9100570919,2.2878116,41.6088070
|
| 458 |
+
122348,2.2885371,41.6082737
|
| 459 |
+
122348,2.2885371,41.6082737
|
| 460 |
+
122348,2.2885371,41.6082737
|
| 461 |
+
122348,2.2885371,41.6082737
|
| 462 |
+
9100756793,2.2853158,41.6062754
|
| 463 |
+
9100041486,2.2848532,41.6022445
|
| 464 |
+
9100041725,2.2889012,41.6085949
|
| 465 |
+
9100042386,2.2904212,41.6145934
|
| 466 |
+
9100043893,2.2864716,41.5979571
|
| 467 |
+
9100044282,2.2857091,41.6119638
|
| 468 |
+
9100044469,2.2848378,41.6079349
|
| 469 |
+
9100056059,2.2863715,41.6055199
|
| 470 |
+
9100087801,2.2848276,41.6067998
|
| 471 |
+
9100098069,2.2835448,41.6105436
|
| 472 |
+
9100145874,2.2844361,41.6076185
|
| 473 |
+
9100169285,2.2873227,41.6080652
|
| 474 |
+
9100219758,2.2903800,41.6100748
|
| 475 |
+
9100229514,2.2844361,41.6076185
|
| 476 |
+
9100250393,2.2845818,41.6045777
|
| 477 |
+
9100253786,2.2849912,41.6008185
|
| 478 |
+
9100297849,2.2879758,41.6092099
|
| 479 |
+
9100301896,2.2844361,41.6076185
|
| 480 |
+
9100302461,2.2826132,41.6044162
|
| 481 |
+
9100399265,2.2860180,41.6089721
|
| 482 |
+
9100399265,2.2860180,41.6089721
|
| 483 |
+
9100479079,2.2878827,41.6018011
|
| 484 |
+
9100509767,2.2880953,41.6080570
|
| 485 |
+
9100523982,2.2862797,41.6090288
|
| 486 |
+
9100527198,2.2862953,41.6073341
|
| 487 |
+
9100658144,2.2895960,41.6137480
|
| 488 |
+
9100664303,2.2878566,41.6094543
|
| 489 |
+
9100677377,2.2907866,41.6147295
|
| 490 |
+
9100680026,2.2875639,41.6049348
|
| 491 |
+
9100681218,2.2839377,41.6047532
|
| 492 |
+
9100681231,2.2832281,41.6048713
|
| 493 |
+
9100682415,2.2889012,41.6085949
|
| 494 |
+
9100682473,2.2835448,41.6105436
|
| 495 |
+
9100685322,2.2826119,41.6010176
|
| 496 |
+
9100695278,2.2856383,41.6078865
|
| 497 |
+
9100699710,2.2834923,41.6077058
|
| 498 |
+
9100701777,2.2809916,41.5863360
|
| 499 |
+
9100717340,2.2862049,41.6087026
|
| 500 |
+
9100723316,2.2836169,41.6004999
|
| 501 |
+
9100727750,2.2881422,41.6045572
|
| 502 |
+
9100729954,2.1663673,41.3763722
|
| 503 |
+
9100735762,2.2882135,41.6103062
|
| 504 |
+
9100737822,2.1663673,41.3763722
|
| 505 |
+
9100744232,2.2857910,41.6095412
|
| 506 |
+
9100747036,2.2881422,41.6045572
|
| 507 |
+
9100751859,2.2857091,41.6119638
|
| 508 |
+
9100751879,2.2868959,41.6125659
|
| 509 |
+
9100752730,2.2868135,41.6110212
|
| 510 |
+
9100756820,2.2893978,41.6135096
|
| 511 |
+
9100764052,2.2829522,41.6066781
|
| 512 |
+
9100764737,2.2857091,41.6119638
|
| 513 |
+
118135,2.3065616,41.6137735
|
| 514 |
+
118135,2.3065616,41.6137735
|
| 515 |
+
9100553805,2.2899627,41.6000610
|
| 516 |
+
9100041010,2.2906157,41.6014433
|
| 517 |
+
9100041103,2.2890758,41.6022669
|
| 518 |
+
9100042964,2.2962490,41.6003384
|
| 519 |
+
9100043144,2.3021682,41.6098501
|
| 520 |
+
9100043853,2.2902828,41.6091422
|
| 521 |
+
9100044157,2.2879157,41.6000696
|
| 522 |
+
9100094835,2.2902828,41.6091422
|
| 523 |
+
9100120794,2.2902828,41.6091422
|
| 524 |
+
9100151073,2.2879157,41.6000696
|
| 525 |
+
9100238682,2.2894189,41.6076472
|
| 526 |
+
9100338195,2.2948382,41.6128906
|
| 527 |
+
9100340763,2.2946433,41.6212710
|
| 528 |
+
9100341135,2.2916653,41.6128008
|
| 529 |
+
9100348821,2.2880423,41.5908820
|
| 530 |
+
9100355289,2.2912334,41.5997034
|
| 531 |
+
9100503002,2.2903709,41.6081160
|
| 532 |
+
9100513352,2.2880423,41.5908820
|
| 533 |
+
9100616552,2.2870963,41.5997457
|
| 534 |
+
9100619658,2.2900023,41.6076959
|
| 535 |
+
9100658142,2.2902828,41.6091422
|
| 536 |
+
9100661498,2.3021682,41.6098501
|
| 537 |
+
9100678134,2.2902828,41.6091422
|
| 538 |
+
9100685852,2.2884292,41.5975345
|
| 539 |
+
9100688641,2.2962684,41.6075810
|
| 540 |
+
9100698912,2.2883573,41.5961028
|
| 541 |
+
9100699983,2.2930628,41.6183051
|
| 542 |
+
9100700301,2.2901218,41.5929397
|
| 543 |
+
9100700874,2.3029362,41.6099920
|
| 544 |
+
9100703647,2.2900905,41.6066610
|
| 545 |
+
9100718615,2.2915768,41.5891339
|
| 546 |
+
9100718706,2.2896298,41.6043855
|
| 547 |
+
9100723039,2.2962428,41.6016140
|
| 548 |
+
9100723531,2.2902828,41.6091422
|
| 549 |
+
9100729258,2.2905778,41.6068004
|
| 550 |
+
9100729405,2.2880423,41.5908820
|
| 551 |
+
9100730939,2.2912636,41.6042778
|
| 552 |
+
9100732766,2.2903800,41.6100748
|
| 553 |
+
9100736806,2.2912334,41.5997034
|
| 554 |
+
9100737231,2.2948382,41.6128906
|
| 555 |
+
9100738166,2.2948382,41.6128906
|
| 556 |
+
9100742644,2.2962428,41.6016140
|
| 557 |
+
9100742712,2.2877804,41.6032002
|
| 558 |
+
9100743172,2.2901218,41.5929397
|
| 559 |
+
9100743868,2.2883573,41.5961028
|
| 560 |
+
9100745043,2.2905456,41.6050949
|
| 561 |
+
9100747591,2.3021682,41.6098501
|
| 562 |
+
9100750645,2.2902828,41.6091422
|
| 563 |
+
9100755605,2.2948458,41.6150155
|
| 564 |
+
9100756365,2.2903800,41.6100748
|
| 565 |
+
9100758269,2.2962858,41.6137009
|
| 566 |
+
9100762486,2.2887494,41.5936550
|
| 567 |
+
128003,2.2941506,41.6133433
|
| 568 |
+
9100694685,2.2859271,42.0035278
|
| 569 |
+
9100691956,2.2841079,42.0000201
|
| 570 |
+
9100381861,2.2870159,42.0048200
|
| 571 |
+
9100722376,2.2901025,42.0058602
|
| 572 |
+
9100728312,2.2879627,42.0034372
|
| 573 |
+
9100641799,2.3019986,42.0093552
|
| 574 |
+
9100593250,2.2870159,42.0048200
|
| 575 |
+
9100577717,2.2834631,42.0015741
|
| 576 |
+
9100540106,2.2777747,42.0054042
|
| 577 |
+
9100536121,2.2792299,42.0049384
|
| 578 |
+
9100520242,2.2815043,41.9985521
|
| 579 |
+
9100494853,2.2851546,42.0080143
|
| 580 |
+
9100494288,2.2815043,41.9985521
|
| 581 |
+
9100487378,2.2870159,42.0048200
|
| 582 |
+
9100485303,2.2904072,42.0032136
|
| 583 |
+
9100479427,2.2829636,42.0003612
|
| 584 |
+
9100467000,2.2862494,42.0049256
|
| 585 |
+
9100420376,2.2883893,42.0103075
|
| 586 |
+
9100326018,2.2782625,42.0040915
|
| 587 |
+
9100301063,2.2882579,42.0000513
|
| 588 |
+
9100253099,2.2894170,42.0052225
|
| 589 |
+
9100206311,2.2960830,42.0042500
|
| 590 |
+
9100194884,2.2841159,41.9999811
|
| 591 |
+
9100169134,2.2780530,42.0084523
|
| 592 |
+
9100160354,2.2841079,42.0000201
|
| 593 |
+
9100159434,2.2859297,42.0012394
|
| 594 |
+
9100159298,2.2841079,42.0000201
|
| 595 |
+
9100158995,2.2916854,42.0034180
|
| 596 |
+
9100158872,2.2807645,42.0014402
|
| 597 |
+
9100158492,2.2841079,42.0000201
|
| 598 |
+
9100159254,2.2862494,42.0049256
|
| 599 |
+
9100058412,2.2859360,41.9971332
|
| 600 |
+
9100056427,2.2870159,42.0048200
|
| 601 |
+
9100057531,2.2840374,42.0058888
|
| 602 |
+
9100042378,2.2338386,41.5731338
|
| 603 |
+
9100042685,2.2396525,41.5859174
|
| 604 |
+
9100043482,2.2204694,41.5573546
|
| 605 |
+
9100044179,2.2196024,41.5584051
|
| 606 |
+
9100044290,2.2300571,41.5624950
|
| 607 |
+
9100227177,2.2216661,41.5601641
|
| 608 |
+
9100264438,2.2221133,41.5628482
|
| 609 |
+
9100366757,2.2338087,41.5729405
|
| 610 |
+
9100382635,2.2239254,41.5744778
|
| 611 |
+
9100382635,2.2239254,41.5744778
|
| 612 |
+
9100413240,2.2239254,41.5744778
|
| 613 |
+
9100413240,2.2239254,41.5744778
|
| 614 |
+
9100486030,2.2239254,41.5744778
|
| 615 |
+
9100058807,2.2852682,41.9998545
|
| 616 |
+
9100058807,2.2852682,41.9998545
|
| 617 |
+
9100617124,2.2222997,41.5621407
|
| 618 |
+
9100617475,2.2276058,41.5676536
|
| 619 |
+
9100643487,2.2362929,41.5720441
|
| 620 |
+
9100652615,2.2338386,41.5731338
|
| 621 |
+
9100661113,2.2335076,41.5712669
|
| 622 |
+
9100661227,2.2335076,41.5712669
|
| 623 |
+
9100670427,2.2300571,41.5624950
|
| 624 |
+
9100671265,2.2375085,41.5715166
|
| 625 |
+
9100692245,2.2335076,41.5712669
|
| 626 |
+
9100698420,2.2290519,41.5618454
|
| 627 |
+
9100701217,2.2199155,41.5572101
|
| 628 |
+
9100701291,2.2491640,41.5752938
|
| 629 |
+
9100733333,2.2358483,41.5730106
|
| 630 |
+
9100733969,2.2466144,41.5683377
|
| 631 |
+
9100738774,2.2852345,42.0069571
|
| 632 |
+
9100742503,2.2827129,42.0018981
|
| 633 |
+
9100746992,2.2335471,41.5546655
|
| 634 |
+
9100747472,2.2887829,42.0037921
|
| 635 |
+
9100747929,2.2599215,41.9357016
|
| 636 |
+
9100758135,2.2841079,42.0000201
|
| 637 |
+
9100759612,2.2365079,41.5727187
|
| 638 |
+
9100759982,2.2821017,42.0019216
|
| 639 |
+
9100761848,2.2188869,41.5580804
|
| 640 |
+
9100766615,2.2827129,42.0018981
|
| 641 |
+
9100751000,2.1384114,41.4082961
|
| 642 |
+
9100736471,2.0673480,41.3725772
|
| 643 |
+
9100736476,2.0762494,41.3707998
|
| 644 |
+
119717,2.2670557,41.5425450
|
| 645 |
+
119717,2.2670557,41.5425450
|
| 646 |
+
9100042801,2.2672342,41.5504818
|
| 647 |
+
9100055035,2.2657650,41.5424778
|
| 648 |
+
9100055275,2.2671006,41.5538145
|
| 649 |
+
9100055925,2.2634864,41.5404976
|
| 650 |
+
9100120688,2.2640731,41.5418444
|
| 651 |
+
9100125812,2.2670557,41.5425450
|
| 652 |
+
9100129120,2.2634864,41.5404976
|
| 653 |
+
9100154524,2.2680755,41.5423391
|
| 654 |
+
9100154591,2.2670557,41.5425450
|
| 655 |
+
9100198106,2.2656357,41.5418110
|
| 656 |
+
9100199450,2.2654430,41.5421621
|
| 657 |
+
9100296409,2.2617986,41.5457009
|
| 658 |
+
9100513843,2.2670557,41.5425450
|
| 659 |
+
9100636763,2.2654263,41.5434709
|
| 660 |
+
9100669904,2.2703077,41.5560713
|
| 661 |
+
9100702913,2.2662977,41.5415646
|
| 662 |
+
9100724298,2.2649124,41.5422434
|
| 663 |
+
9100725292,2.2638593,41.5444901
|
| 664 |
+
9100726583,2.2657650,41.5424778
|
| 665 |
+
9100728401,2.2654263,41.5434709
|
| 666 |
+
9100728505,2.2662468,41.5526721
|
| 667 |
+
9100729533,2.2670557,41.5425450
|
| 668 |
+
9100752742,2.2672594,41.5537186
|
| 669 |
+
9100752760,2.2716350,41.5394714
|
| 670 |
+
9100760817,2.2672594,41.5537186
|
| 671 |
+
9100676061,2.1858748,41.5305800
|
| 672 |
+
9100743597,2.0244030,41.5571448
|
| 673 |
+
9100743608,2.0200263,41.5572965
|
| 674 |
+
9100745932,2.0215891,41.5532104
|
| 675 |
+
9100727035,2.3168103,41.9262384
|
| 676 |
+
9100723081,2.2509899,41.8417656
|
| 677 |
+
9100666742,2.2356751,41.9399525
|
| 678 |
+
9100741999,2.2328323,41.5187520
|
| 679 |
+
9100736469,2.2189766,41.4559529
|
| 680 |
+
9100722593,2.3109883,41.9822194
|
| 681 |
+
9100703787,2.2827543,41.9254761
|
| 682 |
+
9100703223,2.2196544,42.1255155
|
| 683 |
+
9100691332,2.2157118,42.1027842
|
| 684 |
+
9100677847,2.2170289,42.1014260
|
| 685 |
+
9100695204,2.2981884,42.0736365
|
| 686 |
+
9100723139,2.2098212,41.6744354
|
| 687 |
+
9100737399,2.2250134,41.6545925
|
| 688 |
+
9100747990,2.1948878,42.1928844
|
| 689 |
+
9100723869,2.2574110,42.0547107
|
| 690 |
+
9100747927,2.2619708,41.6673066
|
| 691 |
+
9100682560,2.2109452,42.1093234
|
| 692 |
+
9100758280,2.2968517,41.6170269
|
| 693 |
+
9100628975,2.2391397,41.5863002
|
| 694 |
+
9100653354,2.2269124,41.6071575
|
| 695 |
+
9100653355,2.2698300,41.6172558
|
| 696 |
+
9100659648,2.2778844,41.6111420
|
| 697 |
+
9100662674,2.2809997,41.6098151
|
| 698 |
+
9100666589,2.2174176,41.6156392
|
| 699 |
+
9100677459,2.2499505,41.5899940
|
| 700 |
+
9100678295,2.2439866,41.5508441
|
| 701 |
+
9100679516,2.2389756,41.6135644
|
| 702 |
+
9100679898,2.2392923,41.6091585
|
| 703 |
+
9100679906,2.2328323,41.5187520
|
| 704 |
+
9100681160,2.2678223,41.5946422
|
| 705 |
+
9100683566,2.2369904,41.6380430
|
| 706 |
+
9100698329,2.2391397,41.5863002
|
| 707 |
+
9100717355,2.2401800,41.6075064
|
| 708 |
+
9100723356,2.2387255,41.6168847
|
| 709 |
+
9100726650,2.2392923,41.6091585
|
| 710 |
+
9100729132,2.2132365,41.6205599
|
| 711 |
+
9100729658,2.2351500,41.5746876
|
| 712 |
+
9100729943,2.2472440,41.5510434
|
| 713 |
+
9100733355,2.1848420,41.8749586
|
| 714 |
+
9100735422,2.2523414,41.5574271
|
| 715 |
+
9100735547,2.3066306,41.6475034
|
| 716 |
+
9100736215,2.1971859,42.2063069
|
| 717 |
+
9100736235,2.3255398,41.9568431
|
| 718 |
+
9100736259,2.3463586,41.9734211
|
| 719 |
+
9100736266,2.1610531,42.1119353
|
| 720 |
+
9100736272,2.2740658,42.0626012
|
| 721 |
+
9100736411,2.3646919,42.3135631
|
| 722 |
+
9100736486,2.2168660,41.4383458
|
| 723 |
+
9100736670,2.2955963,41.6166654
|
| 724 |
+
9100742280,2.2296498,41.6544007
|
| 725 |
+
9100742290,2.2988127,41.9290515
|
| 726 |
+
9100743554,2.2422342,41.5522085
|
| 727 |
+
9100743595,2.0754028,41.5411615
|
| 728 |
+
9100745325,2.2049229,41.5913140
|
| 729 |
+
9100745792,2.2422342,41.5522085
|
| 730 |
+
9100745929,2.0241617,41.5715591
|
| 731 |
+
9100750650,2.2581011,42.0509154
|
| 732 |
+
9100751813,1.9495564,41.5545761
|
| 733 |
+
9100756104,2.2080609,42.1090624
|
| 734 |
+
9100756188,2.2633625,42.0471980
|
| 735 |
+
9100756958,2.2373369,41.5820100
|
| 736 |
+
9100757467,2.2327977,41.5185077
|
| 737 |
+
9100757477,2.2327977,41.5185077
|
| 738 |
+
9100757707,2.2625690,42.0481713
|
| 739 |
+
9100759429,2.2630436,41.9547493
|
| 740 |
+
9100761780,2.2480812,41.5515189
|
| 741 |
+
9100761923,2.3105442,41.9852648
|
| 742 |
+
9100764980,2.2977082,41.6195559
|
| 743 |
+
9100767890,1.7234661,41.2189877
|
| 744 |
+
9100675584,2.2395108,41.6085173
|
| 745 |
+
9100759837,2.2832278,41.8696534
|
| 746 |
+
9100736222,2.3267062,41.9520556
|
| 747 |
+
9100669176,2.2728873,42.0416526
|
| 748 |
+
9100673284,2.2677302,42.0450403
|
| 749 |
+
9100736185,2.2803334,42.2352329
|
| 750 |
+
9100736219,2.5112633,41.8792429
|
| 751 |
+
9100736220,2.1654577,42.2991932
|
| 752 |
+
9100739178,2.2867627,42.2331282
|
| 753 |
+
9100736244,2.1669895,42.3052600
|
| 754 |
+
9100742664,2.1689682,42.2231128
|
| 755 |
+
9100736227,2.5130355,41.8154463
|
| 756 |
+
9100736228,2.3117865,42.3303463
|
| 757 |
+
9100764653,1.0117111,41.6254383
|
| 758 |
+
9100529900,2.3255097,41.9224731
|
| 759 |
+
9100527964,2.2469557,41.9407274
|
| 760 |
+
9100473659,2.2250151,41.8502701
|
| 761 |
+
9100451636,2.2391157,41.9464029
|
| 762 |
+
9100180416,2.2296902,41.8561191
|
| 763 |
+
9100129392,2.3233845,41.9235173
|
| 764 |
+
9100058963,2.2848502,41.8712847
|
| 765 |
+
9100059072,2.2825786,41.8994960
|
| 766 |
+
9100542720,2.2583026,42.0514464
|
| 767 |
+
9100515642,2.2480137,42.0394588
|
| 768 |
+
9100473327,2.2559207,42.1259284
|
| 769 |
+
9100246985,2.2946584,42.1019164
|
| 770 |
+
9100246985,2.2946584,42.1019164
|
| 771 |
+
9100136123,2.2544593,42.0502103
|
| 772 |
+
9100136123,2.2544593,42.0502103
|
| 773 |
+
9100112395,2.2633625,42.0471980
|
| 774 |
+
9100059171,2.2643391,42.0478304
|
| 775 |
+
9100059082,2.2176548,42.1015934
|
| 776 |
+
9100059021,2.2530000,42.0486940
|
| 777 |
+
9100058835,2.2981884,42.0736365
|
| 778 |
+
9100560156,2.2447652,41.9454852
|
| 779 |
+
9100159144,2.3160079,41.9380343
|
| 780 |
+
9100577716,2.3095312,41.9808584
|
| 781 |
+
9100522381,2.3089732,41.9799802
|
| 782 |
+
9100491082,2.2842557,41.9250675
|
| 783 |
+
9100383438,2.3098611,41.9842883
|
| 784 |
+
9100372136,2.3105442,41.9852648
|
| 785 |
+
9100350948,2.3057373,41.9856557
|
| 786 |
+
9100305726,2.2235582,41.9917247
|
| 787 |
+
9100247403,2.3098611,41.9842883
|
| 788 |
+
9100159423,2.2388559,42.0162050
|
| 789 |
+
9100159383,2.2446563,42.0134975
|
| 790 |
+
9100159311,2.2413588,42.0094536
|
| 791 |
+
9100158623,2.3098611,41.9842883
|
| 792 |
+
9100145307,2.2812777,41.9256656
|
| 793 |
+
9100448962,2.1764325,41.9738515
|
| 794 |
+
9100342026,2.2423079,41.9410056
|
| 795 |
+
9100270631,2.3105442,41.9852648
|
| 796 |
+
9100171360,2.2643391,42.0478304
|
| 797 |
+
9100095188,1.9707409,41.8630096
|
| 798 |
+
9100058012,2.2974841,41.8636348
|
| 799 |
+
9100058012,2.2974841,41.8636348
|
| 800 |
+
9100058180,2.3234385,41.9287430
|
| 801 |
+
9100058299,2.2511885,41.9522180
|
| 802 |
+
9100058446,2.2433019,41.9409869
|
| 803 |
+
9100058476,2.2433019,41.9409869
|
| 804 |
+
9100058613,2.2545759,42.0513068
|
| 805 |
+
9100058617,2.3043616,41.9764808
|
| 806 |
+
9100058730,2.2271777,41.8586828
|
| 807 |
+
9100056658,2.3022487,41.8833733
|
| 808 |
+
9100056827,2.3370278,41.8907569
|
| 809 |
+
9100056987,2.2657724,41.9603858
|
| 810 |
+
9100056988,2.2632097,42.0465438
|
| 811 |
+
9100057038,2.2648653,42.0474234
|
| 812 |
+
9100057483,2.2608422,42.0526285
|
| 813 |
+
9100057514,2.1433283,41.4233777
|
| 814 |
+
9100057776,2.2841514,41.9251146
|
| 815 |
+
9100057906,2.2633591,42.0486753
|
| 816 |
+
9100057926,2.2841431,41.9030309
|
| 817 |
+
9100058772,2.2530000,42.0486940
|
| 818 |
+
9100639954,2.2971256,41.9959638
|
| 819 |
+
9100618226,2.2503758,41.9493593
|
| 820 |
+
9100633779,2.2748592,42.0472965
|
| 821 |
+
9100600530,2.2723370,42.0630229
|
| 822 |
+
9100527972,2.0465281,41.5386925
|
| 823 |
+
9100058394,2.2519287,41.7622695
|
| 824 |
+
9100041503,2.2486437,41.5510530
|
| 825 |
+
9100042301,2.2189019,41.6699723
|
| 826 |
+
9100042414,2.2398562,41.5862431
|
| 827 |
+
9100042846,2.2398562,41.5862431
|
| 828 |
+
9100043170,2.2776200,41.6000581
|
| 829 |
+
9100043317,2.2395108,41.6085173
|
| 830 |
+
9100044116,2.2464762,41.5516216
|
| 831 |
+
9100044483,2.2415208,41.6141336
|
| 832 |
+
9100044483,2.2415208,41.6141336
|
| 833 |
+
9100055559,2.2546044,41.5198863
|
| 834 |
+
9100076545,2.2975420,41.6279324
|
| 835 |
+
9100112714,2.2302474,41.5265353
|
| 836 |
+
9100158437,2.1600571,41.6497686
|
| 837 |
+
9100238822,2.2313296,41.5277717
|
| 838 |
+
9100301605,2.2391397,41.5863002
|
| 839 |
+
9100371601,2.2132365,41.6205599
|
| 840 |
+
9100377532,2.2318948,41.5356836
|
| 841 |
+
9100377532,2.2318948,41.5356836
|
| 842 |
+
9100410532,2.2995973,41.6226037
|
| 843 |
+
9100411688,2.2995973,41.6226037
|
| 844 |
+
9100411688,2.2995973,41.6226037
|
| 845 |
+
9100414445,2.2048469,41.5897504
|
| 846 |
+
9100414445,2.2048469,41.5897504
|
| 847 |
+
9100422647,2.2765848,41.6138166
|
| 848 |
+
9100422647,2.2765848,41.6138166
|
| 849 |
+
9100424065,2.2472440,41.5510434
|
| 850 |
+
9100424065,2.2472440,41.5510434
|
| 851 |
+
9100511092,2.2374755,41.5285100
|
| 852 |
+
9100054889,2.2327977,41.5185077
|
| 853 |
+
9100055361,2.2218218,41.5257639
|
| 854 |
+
9100243079,2.2328323,41.5187520
|
| 855 |
+
9100447655,2.2252848,41.5267067
|
| 856 |
+
9100505409,2.2252848,41.5267067
|
| 857 |
+
9100528985,2.3560841,41.6431411
|
| 858 |
+
9100530921,2.2316623,41.5798670
|
| 859 |
+
9100558085,2.2497544,41.5492483
|
| 860 |
+
9100056389,2.2600926,42.0524792
|
| 861 |
+
9100044002,2.2391397,41.5863002
|
| 862 |
+
9100054923,2.2313296,41.5277717
|
| 863 |
+
9100070576,2.3066306,41.6475034
|
| 864 |
+
9100120656,2.2423002,41.5975692
|
| 865 |
+
9100120981,2.2828800,41.6173948
|
| 866 |
+
9100127929,2.2954051,41.6176777
|
| 867 |
+
9100134891,2.3260575,41.5873365
|
| 868 |
+
9100139142,2.2291023,41.5347619
|
| 869 |
+
9100151684,2.2422342,41.5522085
|
| 870 |
+
9100172700,2.3066306,41.6475034
|
| 871 |
+
9100264859,2.2222198,41.5814354
|
| 872 |
+
9100342550,2.2374755,41.5285100
|
| 873 |
+
9100054600,2.1744746,41.4251545
|
| 874 |
+
9100055177,2.2236567,41.5230355
|
| 875 |
+
9100252058,2.2252848,41.5267067
|
| 876 |
+
128916,2.2374755,41.5285100
|
| 877 |
+
126089,2.0838603,41.4743059
|
| 878 |
+
126089,2.0838603,41.4743059
|
| 879 |
+
119956,2.7298228,41.7922628
|
| 880 |
+
9100190779,2.2128282,41.5376150
|
| 881 |
+
9100271543,2.2102490,41.5340160
|
| 882 |
+
9100305166,2.2063516,41.5319198
|
| 883 |
+
9100305167,2.2097139,41.5344906
|
| 884 |
+
9100354651,2.2116133,41.5368244
|
| 885 |
+
9100517197,2.2085748,41.5392350
|
| 886 |
+
9100519718,2.2128282,41.5376150
|
| 887 |
+
9100054838,2.2112416,41.5452446
|
| 888 |
+
9100055377,2.2129105,41.5377848
|
| 889 |
+
9100121732,2.2130934,41.5393484
|
| 890 |
+
9100281244,2.2190261,41.5431161
|
| 891 |
+
9100296646,2.2134297,41.5385950
|
| 892 |
+
9100331748,2.2172619,41.5446492
|
| 893 |
+
9100336673,2.2052990,41.5487701
|
| 894 |
+
9100336673,2.2052990,41.5487701
|
| 895 |
+
9100341736,2.2054979,41.5472380
|
| 896 |
+
9100369998,2.2132852,41.5403600
|
| 897 |
+
9100444956,2.2204354,41.5476472
|
| 898 |
+
9100486027,2.2102612,41.5442036
|
| 899 |
+
9100514208,2.2110252,41.5414845
|
| 900 |
+
9100517477,2.2128282,41.5376150
|
| 901 |
+
9100520123,2.2126138,41.5375280
|
| 902 |
+
9100522192,2.2064397,41.5365029
|
| 903 |
+
9100536443,2.2082685,41.5403851
|
| 904 |
+
9100536466,2.2084332,41.5346950
|
| 905 |
+
9100536469,2.2081840,41.5346681
|
| 906 |
+
9100536481,2.2178009,41.5445223
|
| 907 |
+
9100541892,2.2275956,41.5525724
|
| 908 |
+
9100567861,2.2275956,41.5525724
|
| 909 |
+
9100568458,2.2275956,41.5525724
|
| 910 |
+
9100497829,2.2179660,41.5455691
|
| 911 |
+
9100602299,2.2519606,41.9242917
|
| 912 |
+
9100145802,2.2493603,41.9282167
|
| 913 |
+
9100636960,2.2581330,41.9307726
|
| 914 |
+
9100541928,2.2539458,41.9278688
|
| 915 |
+
9100436371,2.2493603,41.9282167
|
| 916 |
+
9100569086,2.2833157,41.9530229
|
| 917 |
+
9100294054,2.2560674,41.9247787
|
| 918 |
+
9100151045,2.2543655,41.9199827
|
| 919 |
+
9100135478,2.2490191,41.9261272
|
| 920 |
+
9100642662,2.2500644,41.9331299
|
| 921 |
+
9100632171,2.2563590,41.9329091
|
| 922 |
+
9100651724,2.2556935,41.9272415
|
| 923 |
+
9100522518,2.2296592,41.4469019
|
| 924 |
+
9100304652,2.2902828,41.6091422
|
| 925 |
+
9100365812,2.2902828,41.6091422
|
| 926 |
+
9100447620,2.2901386,41.5995054
|
| 927 |
+
9100517443,2.2966772,41.6112168
|
| 928 |
+
9100520022,2.2902828,41.6091422
|
| 929 |
+
9100087371,2.2915768,41.5891339
|
| 930 |
+
9100480482,2.2874578,41.5918442
|
| 931 |
+
9100524060,2.2935300,41.6079447
|
| 932 |
+
9100495996,2.2900905,41.6066610
|
| 933 |
+
9100153969,2.2874201,41.6060897
|
| 934 |
+
9100227012,2.2836169,41.6004999
|
| 935 |
+
9100310143,2.2843711,41.6052993
|
| 936 |
+
9100338196,2.2848532,41.6022445
|
| 937 |
+
9100340260,2.2901218,41.5929397
|
| 938 |
+
9100420786,2.2849912,41.6008185
|
| 939 |
+
9100420786,2.2849912,41.6008185
|
| 940 |
+
9100445926,2.2862953,41.6073341
|
| 941 |
+
9100506888,2.2862277,41.6128208
|
| 942 |
+
9100507154,2.2868135,41.6110212
|
| 943 |
+
9100507921,2.2839377,41.6047532
|
| 944 |
+
9100513397,2.2856383,41.6078865
|
| 945 |
+
9100525754,2.2829522,41.6066781
|
| 946 |
+
9100151566,2.2857091,41.6119638
|
| 947 |
+
9100239658,2.1848826,41.4913696
|
| 948 |
+
9100639016,2.2848487,42.0011466
|
| 949 |
+
9100637313,2.2830133,41.9996008
|
| 950 |
+
9100574086,2.2885774,42.0091833
|
| 951 |
+
9100562472,2.2841159,41.9999811
|
| 952 |
+
9100374147,2.2941505,42.0021369
|
| 953 |
+
9100332707,2.2841079,42.0000201
|
| 954 |
+
9100158102,2.2213060,41.5619295
|
| 955 |
+
9100158099,2.2239254,41.5744778
|
| 956 |
+
9100230870,2.2207326,41.5611489
|
| 957 |
+
9100239659,2.2110753,41.5641645
|
| 958 |
+
9100529011,2.2255470,41.5721753
|
| 959 |
+
9100169606,2.2633000,41.5403971
|
| 960 |
+
9100282679,2.2639501,41.5425769
|
| 961 |
+
9100334134,2.2672594,41.5537186
|
| 962 |
+
9100517175,2.2661559,41.5411377
|
| 963 |
+
9100536467,2.2654263,41.5434709
|
| 964 |
+
9100542820,2.2651306,41.5424644
|
| 965 |
+
9100535918,2.0346022,41.5688366
|
| 966 |
+
9100041161,2.2437190,41.5514771
|
| 967 |
+
9100041161,2.2437190,41.5514771
|
| 968 |
+
9100161621,2.2472440,41.5510434
|
| 969 |
+
9100161621,2.2472440,41.5510434
|
| 970 |
+
9100229412,2.2437190,41.5514771
|
| 971 |
+
9100443296,2.2086125,41.5455598
|
| 972 |
+
9100443296,2.2086125,41.5455598
|
| 973 |
+
9100512676,2.2472440,41.5510434
|
| 974 |
+
9100521616,2.1600571,41.6497686
|
| 975 |
+
9100565827,2.2787208,41.8119225
|
| 976 |
+
9100556955,2.2791434,42.0215909
|
| 977 |
+
9100126372,2.2173677,42.1008727
|
| 978 |
+
9100627695,2.3234385,41.9287430
|
| 979 |
+
9100572378,2.2389118,42.0581855
|
| 980 |
+
9100563070,2.2702684,42.0462150
|
| 981 |
+
9100640157,2.2399183,42.0151553
|
| 982 |
+
9100635212,2.2413588,42.0094536
|
| 983 |
+
9100634356,2.2402383,42.0089327
|
| 984 |
+
9100420327,2.2291117,41.5267673
|
| 985 |
+
9100075744,2.3260575,41.5873365
|
| 986 |
+
9100233605,2.2313296,41.5277717
|
| 987 |
+
9100423767,2.2614494,41.5914669
|
| 988 |
+
9100503009,2.1962827,41.5114490
|
| 989 |
+
9100512192,2.2494580,41.5265253
|
| 990 |
+
9100513427,2.2494580,41.5265253
|
| 991 |
+
9100514206,2.2377696,41.5945622
|
| 992 |
+
9100526119,2.2872441,41.6080085
|
| 993 |
+
9100536830,2.2249035,41.6026588
|
| 994 |
+
9100420785,2.2787148,41.5864105
|
| 995 |
+
9100497770,2.2944420,41.6172314
|
| 996 |
+
9100556483,2.3101328,41.9890385
|
| 997 |
+
9100300991,2.2394306,41.6108773
|
| 998 |
+
9100120799,2.2879189,41.6137279
|
| 999 |
+
9100122840,2.2899986,41.5978596
|
| 1000 |
+
9100139584,2.3260575,41.5873365
|
| 1001 |
+
9100140860,2.2865995,41.5973528
|
| 1002 |
+
9100283498,2.2928775,41.6052739
|
| 1003 |
+
9100522267,2.2091974,41.5382145
|
| 1004 |
+
9100054744,2.2175419,41.5334748
|
| 1005 |
+
9100054778,2.2190209,41.5406662
|
| 1006 |
+
9100054857,2.2086022,41.5342606
|
| 1007 |
+
9100055224,2.2099801,41.5396122
|
| 1008 |
+
9100055497,2.2181763,41.5394682
|
| 1009 |
+
9100055508,2.2126637,41.5376026
|
| 1010 |
+
9100056293,2.2116836,41.5373806
|
| 1011 |
+
9100075499,2.2142802,41.5343759
|
| 1012 |
+
9100403364,2.2099801,41.5396122
|
| 1013 |
+
9100403364,2.2099801,41.5396122
|
| 1014 |
+
9100043749,2.2127274,41.5440011
|
| 1015 |
+
9100054481,2.2091222,41.5325847
|
| 1016 |
+
9100054565,2.2167283,41.5442201
|
| 1017 |
+
9100054732,2.2097257,41.5433826
|
| 1018 |
+
9100054873,2.2091274,41.5439659
|
| 1019 |
+
9100055568,2.2128459,41.5397838
|
| 1020 |
+
9100184449,2.2133356,41.5387104
|
| 1021 |
+
9100366732,2.2194830,41.5443121
|
| 1022 |
+
9100366733,2.2189898,41.5439145
|
| 1023 |
+
9100553669,2.2089742,41.5490650
|
| 1024 |
+
9100629788,2.2158511,41.5358663
|
| 1025 |
+
9100655002,2.2158262,41.5335224
|
| 1026 |
+
9100655372,2.2193309,41.5479989
|
| 1027 |
+
9100680393,2.2128798,41.5417561
|
| 1028 |
+
9100530919,2.2190261,41.5431161
|
| 1029 |
+
9100054639,2.2102836,41.5412876
|
| 1030 |
+
9100054644,2.2136925,41.5396471
|
| 1031 |
+
9100054973,2.2095811,41.5432985
|
| 1032 |
+
9100056047,2.2041449,41.5462592
|
| 1033 |
+
9100733609,2.2166358,41.5549179
|
| 1034 |
+
9100054513,2.2059673,41.5364637
|
| 1035 |
+
9100609140,2.2567785,41.9272827
|
| 1036 |
+
9100730075,2.2547101,41.9284049
|
| 1037 |
+
9100259166,2.2595757,41.9461004
|
| 1038 |
+
9100443450,2.2463441,41.9284714
|
| 1039 |
+
9100523487,2.2537806,41.9416894
|
| 1040 |
+
9100755440,2.2446851,41.9314066
|
| 1041 |
+
9100759507,2.2538173,41.9304079
|
| 1042 |
+
9100678069,2.2454199,41.9334234
|
| 1043 |
+
9100678719,2.2572558,41.9306530
|
| 1044 |
+
9100610240,2.2599215,41.9357016
|
| 1045 |
+
9100627975,1.7201945,41.2281885
|
| 1046 |
+
9100564797,2.2532019,41.9300447
|
| 1047 |
+
9100466625,2.2562026,41.9309153
|
| 1048 |
+
9100134828,2.2535593,41.9294084
|
| 1049 |
+
9100119869,2.2541861,41.9283786
|
| 1050 |
+
9100119869,2.2541861,41.9283786
|
| 1051 |
+
9100127992,2.2653884,41.9258886
|
| 1052 |
+
136675,2.2556061,41.9280651
|
| 1053 |
+
136675,2.2556061,41.9280651
|
| 1054 |
+
9100040724,2.2884246,41.6068851
|
| 1055 |
+
9100041355,2.2902828,41.6091422
|
| 1056 |
+
9100041765,2.2883743,41.5974116
|
| 1057 |
+
9100041774,2.2917028,41.6129399
|
| 1058 |
+
9100041990,2.2916653,41.6128008
|
| 1059 |
+
9100042037,2.2903973,41.6072964
|
| 1060 |
+
9100042037,2.2903973,41.6072964
|
| 1061 |
+
9100042491,2.2893517,41.6048363
|
| 1062 |
+
9100043799,2.2932558,41.6132830
|
| 1063 |
+
9100044409,2.2930628,41.6183051
|
| 1064 |
+
9100076613,2.2893517,41.6048363
|
| 1065 |
+
9100134890,2.2948382,41.6128906
|
| 1066 |
+
9100264654,2.2905456,41.6050949
|
| 1067 |
+
9100521595,2.2916653,41.6128008
|
| 1068 |
+
9100521678,2.2745725,41.5999439
|
| 1069 |
+
9100521678,2.2745725,41.5999439
|
| 1070 |
+
9100633076,2.2879211,41.6034961
|
| 1071 |
+
9100755674,2.2948382,41.6128906
|
| 1072 |
+
9100761572,2.2884292,41.5975345
|
| 1073 |
+
9100765851,2.2902072,41.5979909
|
| 1074 |
+
9100042261,2.2617986,41.5457009
|
| 1075 |
+
9100041053,2.2297944,41.5623467
|
| 1076 |
+
9100041393,2.2338087,41.5729405
|
| 1077 |
+
9100041543,2.2335076,41.5712669
|
| 1078 |
+
9100041555,2.2338386,41.5731338
|
| 1079 |
+
9100041840,2.2338386,41.5731338
|
| 1080 |
+
9100054649,2.2339001,41.5635429
|
| 1081 |
+
9100150711,2.2250231,41.5658580
|
| 1082 |
+
9100366724,2.2083603,41.5479180
|
| 1083 |
+
9100373488,2.2286495,41.5673215
|
| 1084 |
+
9100756811,2.2286495,41.5673215
|
| 1085 |
+
9100043208,2.2319845,41.5564845
|
| 1086 |
+
9100042829,2.2866014,41.6009244
|
| 1087 |
+
9100042612,2.2895960,41.6137480
|
| 1088 |
+
9100043364,2.2869413,41.6078514
|
| 1089 |
+
9100043381,2.2848276,41.6067998
|
| 1090 |
+
9100059282,2.2859478,41.6003820
|
| 1091 |
+
9100121164,2.2836169,41.6004999
|
| 1092 |
+
9100373640,2.2889012,41.6085949
|
| 1093 |
+
9100373641,2.2889012,41.6085949
|
| 1094 |
+
9100047575,2.0799631,41.4612375
|
| 1095 |
+
9100043198,2.2634864,41.5404976
|
| 1096 |
+
9100043408,2.2687391,41.5451813
|
| 1097 |
+
9100043540,2.2670557,41.5425450
|
| 1098 |
+
9100043719,2.2703688,41.5544153
|
| 1099 |
+
9100044366,2.2672594,41.5537186
|
| 1100 |
+
9100055329,2.2670557,41.5425450
|
| 1101 |
+
9100366735,2.2639501,41.5425769
|
| 1102 |
+
9100468163,2.2670557,41.5425450
|
| 1103 |
+
9100044480,2.2721334,41.5650881
|
| 1104 |
+
9100563722,2.2634864,41.5404976
|
| 1105 |
+
9100725417,2.2680755,41.5423391
|
| 1106 |
+
9100750960,2.2672594,41.5537186
|
| 1107 |
+
9100373506,2.2679351,41.5401096
|
| 1108 |
+
9100373422,2.2860180,41.6089721
|
| 1109 |
+
9100040748,2.2982986,41.6325530
|
| 1110 |
+
9100040967,2.2423002,41.5975692
|
| 1111 |
+
9100041059,2.2946932,41.6182864
|
| 1112 |
+
9100041485,2.2496681,41.5504002
|
| 1113 |
+
9100041564,2.2900731,41.6120428
|
| 1114 |
+
9100041740,2.2389756,41.6135644
|
| 1115 |
+
9100042249,2.2778844,41.6111420
|
| 1116 |
+
9100042500,2.2880285,41.6084902
|
| 1117 |
+
9100042942,2.2830445,41.6063647
|
| 1118 |
+
9100043107,2.2391397,41.5863002
|
| 1119 |
+
9100043454,2.2877195,41.6087686
|
| 1120 |
+
9100043839,2.2574392,41.5687858
|
| 1121 |
+
9100043866,2.2902828,41.6091422
|
| 1122 |
+
9100044089,2.2314136,41.5799754
|
| 1123 |
+
9100044243,2.3260575,41.5873365
|
| 1124 |
+
9100044326,2.2995973,41.6226037
|
| 1125 |
+
9100044359,2.2108905,41.6057010
|
| 1126 |
+
9100044399,2.3066306,41.6475034
|
| 1127 |
+
9100044419,2.2302096,41.6514740
|
| 1128 |
+
9100044419,2.2302096,41.6514740
|
| 1129 |
+
9100041886,2.2862395,41.5544103
|
| 1130 |
+
9100044099,2.2571268,41.5617936
|
| 1131 |
+
9100110789,2.2778844,41.6111420
|
| 1132 |
+
9100150866,2.2541680,41.6005985
|
| 1133 |
+
9100614225,2.2818378,41.8990539
|
| 1134 |
+
9100597250,2.2975704,41.6367289
|
| 1135 |
+
9100590492,2.1884843,41.9112574
|
| 1136 |
+
9100169481,2.1897552,41.9111046
|
| 1137 |
+
9100130145,2.2433019,41.9409869
|
| 1138 |
+
9100473447,2.3370278,41.8907569
|
| 1139 |
+
9100627170,2.2229045,42.0949606
|
| 1140 |
+
9100568729,2.2907452,42.0704070
|
| 1141 |
+
9100461830,2.2565465,42.0507703
|
| 1142 |
+
9100252094,2.2702684,42.0462150
|
| 1143 |
+
9100139252,2.2633625,42.0471980
|
| 1144 |
+
9100129209,2.2733199,42.0421397
|
| 1145 |
+
9100126369,2.2643391,42.0478304
|
| 1146 |
+
9100410610,2.2619851,42.0467040
|
| 1147 |
+
9100615441,2.2864775,41.9979342
|
| 1148 |
+
9100606155,2.2870159,42.0048200
|
| 1149 |
+
9100160054,2.3169242,41.9907623
|
| 1150 |
+
9100609846,2.2558746,42.0502743
|
| 1151 |
+
9100058035,2.3117547,41.9368178
|
| 1152 |
+
9100056628,2.2509869,42.1236646
|
| 1153 |
+
9100043594,2.3387913,41.5917235
|
| 1154 |
+
9100420424,2.2242038,41.5902763
|
| 1155 |
+
9100536947,2.2384492,41.5945214
|
| 1156 |
+
9100540250,2.2480812,41.5515189
|
| 1157 |
+
9100540250,2.2480812,41.5515189
|
| 1158 |
+
9100542312,2.2407849,41.5137993
|
| 1159 |
+
9100547001,2.2387255,41.6168847
|
| 1160 |
+
9100563721,2.2472440,41.5510434
|
| 1161 |
+
9100574583,2.2394306,41.6108773
|
| 1162 |
+
9100576639,2.2394306,41.6108773
|
| 1163 |
+
9100576643,2.2056461,41.6002489
|
| 1164 |
+
9100373403,2.1916161,41.5145864
|
| 1165 |
+
9100373428,2.1958746,41.5133643
|
| 1166 |
+
9100041363,2.2480812,41.5515189
|
| 1167 |
+
9100041363,2.2480812,41.5515189
|
| 1168 |
+
9100041363,2.2480812,41.5515189
|
| 1169 |
+
9100041363,2.2480812,41.5515189
|
| 1170 |
+
9100242346,2.2821977,41.8998707
|
| 1171 |
+
9100041442,2.2085040,41.5911301
|
| 1172 |
+
9100041618,2.2387255,41.6168847
|
| 1173 |
+
9100044043,2.2110766,41.5734509
|
| 1174 |
+
9100044130,2.2872441,41.6080085
|
| 1175 |
+
9100044391,2.3066306,41.6475034
|
| 1176 |
+
9100120687,2.2333091,41.4972238
|
| 1177 |
+
9100043706,2.3260575,41.5873365
|
| 1178 |
+
9100716110,2.2720126,42.0450096
|
| 1179 |
+
9100687352,2.2631285,42.0480337
|
| 1180 |
+
9100744181,2.2389847,41.6057252
|
| 1181 |
+
9100661607,2.0599079,41.4800678
|
| 1182 |
+
9100627485,2.2419007,41.5880498
|
| 1183 |
+
9100740458,1.9999130,41.5581162
|
| 1184 |
+
9100749021,2.2486437,41.5510530
|
| 1185 |
+
9100755589,2.2365600,41.6391446
|
| 1186 |
+
9100758360,2.1990757,41.6023153
|
| 1187 |
+
9100667648,2.2828264,41.9254286
|
| 1188 |
+
9100681544,2.3066306,41.6475034
|
| 1189 |
+
9100743215,2.2748757,41.9131243
|
| 1190 |
+
9100736229,2.3677235,42.2645002
|
| 1191 |
+
9100736213,2.1852141,42.3170010
|
| 1192 |
+
119646,2.1728228,41.4047213
|
| 1193 |
+
119642,2.1728228,41.4047213
|
| 1194 |
+
9100097548,2.2650845,41.5423303
|
| 1195 |
+
9100385693,2.3100989,41.9378759
|
| 1196 |
+
100423,2.0342130,41.3312213
|
| 1197 |
+
100423,2.0342130,41.3312213
|
| 1198 |
+
100423,2.0342130,41.3312213
|
| 1199 |
+
100423,2.0342130,41.3312213
|
| 1200 |
+
100423,2.0342130,41.3312213
|
| 1201 |
+
100423,2.0342130,41.3312213
|
| 1202 |
+
100423,2.0342130,41.3312213
|
| 1203 |
+
100423,2.0342130,41.3312213
|
| 1204 |
+
100423,2.0342130,41.3312213
|
| 1205 |
+
100423,2.0342130,41.3312213
|
| 1206 |
+
100423,2.0342130,41.3312213
|
| 1207 |
+
100423,2.0342130,41.3312213
|
| 1208 |
+
100423,2.0342130,41.3312213
|
| 1209 |
+
100423,2.0342130,41.3312213
|
| 1210 |
+
100423,2.0342130,41.3312213
|
| 1211 |
+
100423,2.0342130,41.3312213
|
| 1212 |
+
100423,2.0342130,41.3312213
|
| 1213 |
+
100423,2.0342130,41.3312213
|
| 1214 |
+
100423,2.0342130,41.3312213
|
| 1215 |
+
100423,2.0342130,41.3312213
|
| 1216 |
+
100423,2.0342130,41.3312213
|
| 1217 |
+
100423,2.0342130,41.3312213
|
| 1218 |
+
100423,2.0342130,41.3312213
|
| 1219 |
+
100423,2.0342130,41.3312213
|
| 1220 |
+
100423,2.0342130,41.3312213
|
| 1221 |
+
100423,2.0342130,41.3312213
|
| 1222 |
+
100423,2.0342130,41.3312213
|
| 1223 |
+
100423,2.0342130,41.3312213
|
| 1224 |
+
100423,2.0342130,41.3312213
|
| 1225 |
+
100423,2.0342130,41.3312213
|
| 1226 |
+
100423,2.0342130,41.3312213
|
| 1227 |
+
100423,2.0342130,41.3312213
|
| 1228 |
+
100423,2.0342130,41.3312213
|
| 1229 |
+
100423,2.0342130,41.3312213
|
| 1230 |
+
100423,2.0342130,41.3312213
|
| 1231 |
+
100423,2.0342130,41.3312213
|
| 1232 |
+
100423,2.0342130,41.3312213
|
| 1233 |
+
100423,2.0342130,41.3312213
|
| 1234 |
+
100423,2.0342130,41.3312213
|
| 1235 |
+
100423,2.0342130,41.3312213
|
| 1236 |
+
100423,2.0342130,41.3312213
|
| 1237 |
+
100423,2.0342130,41.3312213
|
| 1238 |
+
100423,2.0342130,41.3312213
|
| 1239 |
+
100423,2.0342130,41.3312213
|
| 1240 |
+
100423,2.0342130,41.3312213
|
| 1241 |
+
100423,2.0342130,41.3312213
|
| 1242 |
+
100423,2.0342130,41.3312213
|
| 1243 |
+
100423,2.0342130,41.3312213
|
| 1244 |
+
100423,2.0342130,41.3312213
|
| 1245 |
+
100423,2.0342130,41.3312213
|
| 1246 |
+
100423,2.0342130,41.3312213
|
| 1247 |
+
100423,2.0342130,41.3312213
|
| 1248 |
+
100423,2.0342130,41.3312213
|
| 1249 |
+
100423,2.0342130,41.3312213
|
| 1250 |
+
100423,2.0342130,41.3312213
|
| 1251 |
+
128249,2.1781574,41.4090968
|
| 1252 |
+
128249,2.1781574,41.4090968
|
| 1253 |
+
128249,2.1781574,41.4090968
|
| 1254 |
+
128249,2.1781574,41.4090968
|
| 1255 |
+
128249,2.1781574,41.4090968
|
| 1256 |
+
128249,2.1781574,41.4090968
|
| 1257 |
+
128249,2.1781574,41.4090968
|
| 1258 |
+
128249,2.1781574,41.4090968
|
| 1259 |
+
128249,2.1781574,41.4090968
|
| 1260 |
+
128249,2.1781574,41.4090968
|
| 1261 |
+
128249,2.1781574,41.4090968
|
| 1262 |
+
128249,2.1781574,41.4090968
|
| 1263 |
+
128249,2.1781574,41.4090968
|
| 1264 |
+
128249,2.1781574,41.4090968
|
| 1265 |
+
128249,2.1781574,41.4090968
|
| 1266 |
+
128249,2.1781574,41.4090968
|
| 1267 |
+
128249,2.1781574,41.4090968
|
| 1268 |
+
128249,2.1781574,41.4090968
|
| 1269 |
+
128249,2.1781574,41.4090968
|
| 1270 |
+
128249,2.1781574,41.4090968
|
| 1271 |
+
128249,2.1781574,41.4090968
|
| 1272 |
+
128249,2.1781574,41.4090968
|
| 1273 |
+
128249,2.1781574,41.4090968
|
| 1274 |
+
128249,2.1781574,41.4090968
|
| 1275 |
+
128249,2.1781574,41.4090968
|
| 1276 |
+
128249,2.1781574,41.4090968
|
| 1277 |
+
128249,2.1781574,41.4090968
|
| 1278 |
+
128249,2.1781574,41.4090968
|
| 1279 |
+
128249,2.1781574,41.4090968
|
| 1280 |
+
128249,2.1781574,41.4090968
|
| 1281 |
+
128249,2.1781574,41.4090968
|
| 1282 |
+
128249,2.1781574,41.4090968
|
| 1283 |
+
128249,2.1781574,41.4090968
|
| 1284 |
+
128249,2.1781574,41.4090968
|
| 1285 |
+
128249,2.1781574,41.4090968
|
| 1286 |
+
128249,2.1781574,41.4090968
|
| 1287 |
+
128249,2.1781574,41.4090968
|
| 1288 |
+
128249,2.1781574,41.4090968
|
| 1289 |
+
128249,2.1781574,41.4090968
|
| 1290 |
+
128249,2.1781574,41.4090968
|
| 1291 |
+
128249,2.1781574,41.4090968
|
| 1292 |
+
128249,2.1781574,41.4090968
|
| 1293 |
+
128249,2.1781574,41.4090968
|
| 1294 |
+
128249,2.1781574,41.4090968
|
| 1295 |
+
128249,2.1781574,41.4090968
|
| 1296 |
+
128249,2.1781574,41.4090968
|
| 1297 |
+
128249,2.1781574,41.4090968
|
| 1298 |
+
128249,2.1781574,41.4090968
|
| 1299 |
+
128051,2.2112807,41.5385814
|
| 1300 |
+
9100498065,2.2113414,41.5358970
|
| 1301 |
+
9100501076,2.2872104,41.6079613
|
| 1302 |
+
9100502995,2.2941506,41.6133433
|
| 1303 |
+
9100503205,2.2172619,41.5446492
|
| 1304 |
+
9100503664,2.2394306,41.6108773
|
| 1305 |
+
9100529176,2.2949726,41.6171051
|
| 1306 |
+
9100529181,2.2204354,41.5476472
|
| 1307 |
+
9100529300,2.2656357,41.5418110
|
| 1308 |
+
9100535260,2.2868135,41.6110212
|
| 1309 |
+
9100536475,2.2099801,41.5396122
|
| 1310 |
+
9100536484,2.1827252,41.4620385
|
| 1311 |
+
9100536762,2.2657650,41.5424778
|
| 1312 |
+
9100537742,2.2472440,41.5510434
|
| 1313 |
+
9100538933,2.2902828,41.6091422
|
| 1314 |
+
9100539485,2.2113467,41.5382678
|
| 1315 |
+
9100540782,2.2089661,41.5469214
|
| 1316 |
+
9100541197,2.2348744,41.5309319
|
| 1317 |
+
9100542069,2.2127664,41.5389993
|
| 1318 |
+
9100544203,2.2092940,41.5435383
|
| 1319 |
+
9100544826,2.2037207,41.5472792
|
| 1320 |
+
9100545628,2.2122023,41.5377822
|
| 1321 |
+
9100547290,2.2178699,41.6131717
|
| 1322 |
+
9100550379,2.2836169,41.6004999
|
| 1323 |
+
9100552330,2.2166876,41.5401113
|
| 1324 |
+
9100553092,2.2921283,41.6122100
|
| 1325 |
+
9100553915,2.2965447,41.6248545
|
| 1326 |
+
9100554598,2.2905456,41.6050949
|
| 1327 |
+
9100556448,2.2172160,41.5347593
|
| 1328 |
+
9100556449,2.2204354,41.5476472
|
| 1329 |
+
9100558804,2.2893517,41.6048363
|
| 1330 |
+
9100559291,2.2913767,41.5987086
|
| 1331 |
+
9100560544,2.2319209,41.5754246
|
| 1332 |
+
9100562973,2.2827809,41.5921277
|
| 1333 |
+
9100564349,2.2318948,41.5356836
|
| 1334 |
+
9100566100,2.2318091,41.5712114
|
| 1335 |
+
9100566254,2.2103227,41.5390911
|
| 1336 |
+
9100568067,2.2781179,41.6099253
|
| 1337 |
+
9100568310,2.2107797,41.5358796
|
| 1338 |
+
9100569006,2.2276058,41.5676536
|
| 1339 |
+
9100574514,2.2338087,41.5729405
|
| 1340 |
+
9100576264,2.2026414,41.5531014
|
| 1341 |
+
9100579782,2.2749209,41.6142376
|
| 1342 |
+
9100580278,2.2872441,41.6080085
|
| 1343 |
+
9100582978,2.2094442,41.5404481
|
| 1344 |
+
9100583212,2.2198141,41.5480364
|
| 1345 |
+
9100591127,2.2128282,41.5376150
|
| 1346 |
+
9100591222,2.2392923,41.6091585
|
| 1347 |
+
9100591493,2.2347331,41.5640133
|
| 1348 |
+
9100591545,2.2391324,41.6166405
|
| 1349 |
+
9100596041,2.2134723,41.5386243
|
| 1350 |
+
9100596801,2.2275956,41.5525724
|
| 1351 |
+
9100597581,2.2391397,41.5863002
|
| 1352 |
+
9100601682,2.2634864,41.5404976
|
| 1353 |
+
9100601980,2.2884637,41.6083974
|
| 1354 |
+
9100602372,2.2001015,41.6276171
|
| 1355 |
+
9100603628,2.2948382,41.6128906
|
| 1356 |
+
9100607321,2.2048012,41.5475288
|
| 1357 |
+
9100607527,2.2946433,41.6212710
|
| 1358 |
+
9100645059,2.2485742,41.9306405
|
| 1359 |
+
9100645059,2.2485742,41.9306405
|
| 1360 |
+
9100250629,2.2238365,42.1018400
|
| 1361 |
+
9100495381,2.2890613,41.6082392
|
| 1362 |
+
9100264285,2.2094836,41.5280522
|
| 1363 |
+
9100264285,2.2094836,41.5280522
|
| 1364 |
+
9100264285,2.2094836,41.5280522
|
| 1365 |
+
9100264285,2.2094836,41.5280522
|
| 1366 |
+
9100663630,2.2533949,41.9288513
|
| 1367 |
+
9100722247,2.2859297,42.0012394
|
| 1368 |
+
9100569087,2.2833157,41.9530229
|
| 1369 |
+
9100749940,2.2289661,41.5267467
|
dirs.csv
ADDED
|
@@ -0,0 +1,1369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Cliente,Calle,CP,Poblaci�n
|
| 2 |
+
9100681586,CARRER SUSQUEDA 18,8500,VIC
|
| 3 |
+
9100673632,Carrer Torell� 57,8500,VIC
|
| 4 |
+
9100648495,CALLE MATARO S/N,8500,VIC
|
| 5 |
+
9100642823,CTRA. SANT HIP�LIT 61,8500,VIC
|
| 6 |
+
9100684868,Carrer de Figueres 14,8500,VIC
|
| 7 |
+
9100582033,Carrer de Francesc Pla el Vigat� 1,8500,VIC
|
| 8 |
+
9100579835,Carrer del Mas de la M�ra 9,8500,VIC
|
| 9 |
+
9100577691,Carrer Santiago Ramon y Cajal 93,8500,VIC
|
| 10 |
+
9100579841,Carrer de Lleida 35,8500,VIC
|
| 11 |
+
9100577732,Avinguda de Pius XII 11,8500,VIC
|
| 12 |
+
9100582097,Carrer de Francesc Pla el Vigat� 1,8500,VIC
|
| 13 |
+
9100669635,AVENIDA OLIMPIA S/N,8500,VIC
|
| 14 |
+
9100581337,CALLE DOCTOR JUNYENT 8,8500,VIC
|
| 15 |
+
9100399521,CALLE LA LLOTJA 9,8500,VIC
|
| 16 |
+
9100215822,Carrer del Pare Coll 2,8500,VIC
|
| 17 |
+
9100159640,Pla�a Major 13,8500,VIC
|
| 18 |
+
9100411152,CALLE LA LLOTJA 9,8500,VIC
|
| 19 |
+
9100701789,Carrer de Pruit S/N,8500,VIC
|
| 20 |
+
9100661094,Carrer del Bisbe Morgades 31,8500,VIC
|
| 21 |
+
9100642838,RAMBLA HOSPITAL 44,8500,VIC
|
| 22 |
+
9100664901,Avinguda de Sant Bernat Calb� 8,8500,VIC
|
| 23 |
+
9100723046,Carrer de Solsona 2,8500,VIC
|
| 24 |
+
9100700207,Rambla del Carme 35,8500,VIC
|
| 25 |
+
9100592884,MANLLEU 54,8500,VIC
|
| 26 |
+
9100577728,CALLE PIUS XII 20,8500,VIC
|
| 27 |
+
9100416412,CALLE RIPOLL 67,8500,VIC
|
| 28 |
+
9100353302,AVENIDA ESTADI 13,8500,VIC
|
| 29 |
+
9100159004,AVENIDA PIUS XII 18,8500,VIC
|
| 30 |
+
9100158975,CALLE RAIMON D'ABADAL 23,8500,VIC
|
| 31 |
+
9100158925,CALLE VIRREI AVILES 28,8500,VIC
|
| 32 |
+
9100135277,CALLE MIRAMARGES 1,8500,VIC
|
| 33 |
+
9100059229,CALLE SABADELL 04-jun,8500,VIC
|
| 34 |
+
9100058850,CALLE SOLEDAT 4,8500,VIC
|
| 35 |
+
9100689904,ESCOLA 12,8500,VIC
|
| 36 |
+
9100617812,RAMBLA PASSEIG 10,8500,VIC
|
| 37 |
+
9100563642,Carrer de Gurb 36,8500,VIC
|
| 38 |
+
9100492927,CALLE TORELLO 25,8500,VIC
|
| 39 |
+
9100374429,PLAZA PES 6,8500,VIC
|
| 40 |
+
9100343446,RODA 95,8500,VIC
|
| 41 |
+
9100253034,CALLE ERMITA DE SANT SIXT 11,8500,VIC
|
| 42 |
+
9100197987,AVENIDA SANT BERNAT CALBO 23,8500,VIC
|
| 43 |
+
9100192841,CALLE SANTA JOAQUIMA DE VEDRUNA 29,8500,VIC
|
| 44 |
+
9100159002,Carretera de Manlleu 7,8500,VIC
|
| 45 |
+
9100158946,RAMBLA DAVALLADES 21,8500,VIC
|
| 46 |
+
9100158856,CALLE DOCTOR JUNYENT S/N,8500,VIC
|
| 47 |
+
9100158828,CALLE MANLLEU 5,8500,VIC
|
| 48 |
+
9100158696,CARRETERA PRATS DE LLU�ANES 2,8500,VIC
|
| 49 |
+
9100152300,CALLE LA LLOTJA 3,8500,VIC
|
| 50 |
+
9100081422,CALLE RIERA 5,8500,VIC
|
| 51 |
+
9100059399,PLAZA SANTS MARTIRS 12,8500,VIC
|
| 52 |
+
9100636961,CALLE BISBE MORGADES 36,8500,VIC
|
| 53 |
+
9100389358,PLAZA PIETAT 8,8500,VIC
|
| 54 |
+
129963,Carretera de Manlleu 90,8500,VIC
|
| 55 |
+
129963,Carretera de Manlleu 90,8500,VIC
|
| 56 |
+
9100160118,CALLE FRANCESC PLA EL VIGATA 6,8500,VIC
|
| 57 |
+
9100579824,Carretera de Roda 61,8500,VIC
|
| 58 |
+
9100738149,Pla�a de la Pietat 1,8500,VIC
|
| 59 |
+
9100058481,RONDA FRANCESC CAMPRODON 5,8500,VIC
|
| 60 |
+
9100058670,CALLE TAGAMANENT 1,8500,VIC
|
| 61 |
+
9100554547,AVENIDA PAISOS CATALANS 82,8500,VIC
|
| 62 |
+
9100206312,Carretera de Roda 97,8500,VIC
|
| 63 |
+
9100649610,RAMON SALA I SA�ALA 15,8500,VIC
|
| 64 |
+
9100666666,Rambla de Sant Dom�nec 1,8500,VIC
|
| 65 |
+
9100678356,Carrer de Manuel Serra i Moret 4,8500,VIC
|
| 66 |
+
9100735783,Carrer de Matar� 5,8500,VIC
|
| 67 |
+
9100737400,Avinguda de l'Abat Marcet 251,8225,TERRASSA
|
| 68 |
+
9100153533,Carrer de Berenguer III S/N,8100,MOLLET DEL VALLES
|
| 69 |
+
9100423445,CALLE BERENGUER III 58,8100,MOLLET DEL VALL�S
|
| 70 |
+
9100505447,Pla�a Major 9,8100,MOLLET DEL VALLES
|
| 71 |
+
9100138634,CALLE REC DE DALT 21-23,8100,MOLLET DEL VALLES
|
| 72 |
+
9100228193,AVENIDA RAFAEL CASANOVA 8,8100,MOLLET DEL VALLES
|
| 73 |
+
9100519725,CALLE FRANCESC FERRER I GUARDIA 9,8100,MOLLET DEL VALL�S
|
| 74 |
+
9100519746,CALLE JOAQUIM MIR 49,8100,MOLLET DEL VALL�S
|
| 75 |
+
9100610435,Carrer de Can Flequer 17,8100,MOLLET DEL VALLES
|
| 76 |
+
9100621276,Via de Ronda 73,8100,MOLLET DEL VALL�S
|
| 77 |
+
9100622784,CALLE RAFAEL CASANOVAS 81,8100,MOLLET DEL VALLES
|
| 78 |
+
9100623018,PLAZA PRAT DE LA RIBA 21,8100,MOLLET DEL VALLES
|
| 79 |
+
9100632834,RAMBLA DE BALMES 16,8100,MOLLET DEL VALLES
|
| 80 |
+
9100639237,AVENIDA BURGOS 63,8100,MOLLET DEL VALLES
|
| 81 |
+
9100646097,PLAZA PRAT DE LA RIBA 20,8100,MOLLET DEL VALLES
|
| 82 |
+
9100646795,PLAZA CATALUNYA 9,8100,MOLLET DEL VALLES
|
| 83 |
+
9100653307,Carrer de Joaquim Mir 53,8100,MOLLET DEL VALLES
|
| 84 |
+
9100686742,Carrer de Joan Maragall 19,8100,MOLLET DEL VALLES
|
| 85 |
+
9100688668,Carrer de Gallecs 71,8100,MOLLET DEL VALLES
|
| 86 |
+
9100055962,RONDA 7,8100,MOLLET DEL VALLES
|
| 87 |
+
9100654473,CALLE CAN PARERA 22,8100,MOLLET DEL VALLES
|
| 88 |
+
9100687896,CALLE PROVEN�A 200,8226,TERRASSA
|
| 89 |
+
9100646871,CALLE PROVEN�A 200,8226,TERRASSA
|
| 90 |
+
9100646871,CALLE PROVEN�A 200,8226,TERRASSA
|
| 91 |
+
9100565225,CALLE LAS CREMADES S/N,8140,CALDES DE MONTBUI
|
| 92 |
+
9100582098,CARRETERA D'OLOT 7,8560,MANLLEU
|
| 93 |
+
9100582039,LUGAR BRUGUERA S/N,8560,MANLLEU
|
| 94 |
+
9100453364,Carrer de Do�a Blanca 168,8560,MANLLEU
|
| 95 |
+
9100582034,Carretera d'Olot 7,8560,MANLLEU
|
| 96 |
+
9100160021,CALLE DOCTOR FLEMING 20,8560,MANLLEU
|
| 97 |
+
9100158833,Pla�a de Fra Bernad� 32,8560,MANLLEU
|
| 98 |
+
9100681243,CARRER PINTOR GUARDIA 6,8560,MANLLEU
|
| 99 |
+
9100494906,Carretera d'Olot 50,8560,MANLLEU
|
| 100 |
+
9100698675,Carrer de V�lez Rubio 12,8918,BADALONA
|
| 101 |
+
118470,TORRES I BAGES 1-3,8402,GRANOLLERS
|
| 102 |
+
118470,TORRES I BAGES 1-3,8402,GRANOLLERS
|
| 103 |
+
9100614413,Carrer d'Apel�les Mestres 2,8402,GRANOLLERS
|
| 104 |
+
9100626587,CALLE MONTSENY 10,8402,GRANOLLERS
|
| 105 |
+
9100629896,CALLE SANT ROC 19,8402,GRANOLLERS
|
| 106 |
+
9100630014,PLAZA JOSEP SERRAT I BONASTRE S/N,8402,GRANOLLERS
|
| 107 |
+
9100638648,Carrer d'Emili Botey Alsina 22,8402,GRANOLLERS
|
| 108 |
+
9100639032,Carrer de Girona 90,8402,Granollers
|
| 109 |
+
9100647947,AVENIDA SANT ESTEVE 35,8402,GRANOLLERS
|
| 110 |
+
9100702482,Carrer de Girona 4,8402,GRANOLLERS
|
| 111 |
+
127151,CALLE FRANCESC DE MACIÀ I LLUSS 44,8402,GRANOLLERS
|
| 112 |
+
127151,CALLE FRANCESC DE MACIÀ I LLUSS 44,8402,GRANOLLERS
|
| 113 |
+
9100226319,CALLE CONESTABLE DE PORTUGAL 13,8402,GRANOLLERS
|
| 114 |
+
9100621389,CALLE EMILI BOTEY ALSINA 24,8402,GRANOLLERS
|
| 115 |
+
9100651895,Rambla Josep Tarradellas 5,8402,GRANOLLERS
|
| 116 |
+
9100696143,Carrer Llevant 2,8110,MONTCADA I REIXAC
|
| 117 |
+
9100772092,Passeig del Pitort 103,8860,CASTELLDEFELS
|
| 118 |
+
9100378510,ARAGON 12,8520,FRANQUESES DEL VALLES (LES)
|
| 119 |
+
9100432797,"CARRETERA DE RIBES, N-152 131",8520,LES FRANQUESES DEL VALL�S
|
| 120 |
+
9100373615,CALLE SANTA ESPERAN#A 11,8401,GRANOLLERS
|
| 121 |
+
9100042719,PLAZA PORXADA 39,8401,GRANOLLERS
|
| 122 |
+
9100291002,CALLE JOAN PRIM 177,8401,GRANOLLERS
|
| 123 |
+
9100469494,CALLE SANT JOSEP 5,8401,GRANOLLERS
|
| 124 |
+
9100620561,CALLE DE CORRO 192,8401,GRANOLLERS
|
| 125 |
+
9100629126,Carrer de Pompeu Fabra 15,8401,GRANOLLERS
|
| 126 |
+
9100645312,CALLE SANT ROC 29,8401,GRANOLLERS
|
| 127 |
+
9100645312,CALLE SANT ROC 29,8401,GRANOLLERS
|
| 128 |
+
9100731938,Avinguda del cam� de la fontsan S/N,8572,SANT PERE DE TORELL�
|
| 129 |
+
9100694999,Pla�a Sant Forti� 2,8570,TORELL�
|
| 130 |
+
9100613076,Carrer Joaquim Blume 13,8105,SANT FOST DE CAMPSENTELLES
|
| 131 |
+
9100615213,AVENIDA PAISOS CATALANS S/N,8186,LLI�� D'AMUNT
|
| 132 |
+
9100617121,PLAZA PAU PICASSO 9,8170,MONTORN�S DEL VALL�S
|
| 133 |
+
9100622604,Avinguda de l'Onze de Setembre 36,8170,MONTORN�S DEL VALL�S
|
| 134 |
+
9100626406,PLAZA MOSSEN PERE BATLLE 9,8150,PARETS DEL VALLES
|
| 135 |
+
9100641568,Carrer de Joan XXIII 4,8107,MARTORELLES
|
| 136 |
+
9100642392,Passeig de la Conca de Bes�s 33,8403,GRANOLLERS
|
| 137 |
+
9100643360,Carrer d'Anselm Clav� 70,8186,LLI�� D'AMUNT
|
| 138 |
+
9100645053,B-500 Km 6,8105,SANT FOST DE CAMPSENTELLE
|
| 139 |
+
9100650953,Carrer Major 64,8170,MONTORN�S DEL VALL�S
|
| 140 |
+
9100650976,Avinguda de la Mare de D�u de Mo 26,8170,MONTORN�S DEL VALL�S
|
| 141 |
+
9100651605,PLAZA MIL.LENARI 8,8160,MONTMEL�
|
| 142 |
+
9100652617,Avinguda de Sant Juli� 201,8403,GRANOLLERS
|
| 143 |
+
9100669500,Carrer de Mary Santpere 1,8150,PARETS DEL VALLES
|
| 144 |
+
9100683728,Carrer Llu�s Companys 4,8160,MONTMEL�
|
| 145 |
+
9100700812,Carrer de Conestable de Portugal 8,8150,PARETS DEL VALLES
|
| 146 |
+
9100735784,Avinguda de la Ribera 6,8503,GURB
|
| 147 |
+
9100658240,Carretera BV-5224 1,8572,SANT PERE DE TORELL�
|
| 148 |
+
9100670575,Carrer Matagalls 75,8508,LES MASIES DE VOLTREG�
|
| 149 |
+
9100742665,CARRETERA N260 KM.118 KM 118,17534,RIBES DE FRESER
|
| 150 |
+
9100159110,CALLE BARCELONA 10,8551,TONA
|
| 151 |
+
9100121555,CALLE NOU S/N,8505,SANTA EUL�LIA DE RIUPRIMER
|
| 152 |
+
9100529186,CALLE VILAMITJANA S/N,8571,SANT VICEN� DE TORELL�
|
| 153 |
+
9100528087,CALLE GRACIA 12,8573,OR�S
|
| 154 |
+
9100517311,PLAZA CAL FERRER 14,8571,SANT VICEN� DE TORELL�
|
| 155 |
+
9100513817,Carrer del Ter 172,8570,TORELL�
|
| 156 |
+
9100378362,CALLE MAJOR 4,8571,SANT VICEN# DE TORELLO
|
| 157 |
+
9100369663,CARRETERA VIDRA 2,8580,SANT QUIRZE DE BESORA
|
| 158 |
+
9100281570,CALLE CAN CASARRAMONA 26,8570,TORELL�
|
| 159 |
+
9100159884,CARRETERA BERGA 31,8580,SANT QUIRZE DE BESORA
|
| 160 |
+
9100159296,PLAZA LA SARDANA S/N,8570,TORELL�
|
| 161 |
+
9100126286,CALLE GIRONA 33,8570,TORELL�
|
| 162 |
+
9100122181,CALLE ENRIC PRAT DE LA RIBA 7,8570,TORELL�
|
| 163 |
+
9100110641,Carrer de Vila-Seca 12,8571,Vila-seca
|
| 164 |
+
9100577666,Avinguda de l'Atl�ntida 8,8519,FOLGUEROLES
|
| 165 |
+
9100577666,Avinguda de l'Atl�ntida 8,8519,FOLGUEROLES
|
| 166 |
+
9100282736,Carrer del Bisbe Castanyer 15,8572,SANT PERE DE TORELL�
|
| 167 |
+
9100572141,CARRETERA N-141 D S/N,8506,CALLDETENES
|
| 168 |
+
9100433466,CALLE MARQUES DE PALMEROLA 7,8508,LES MASIES DE VOLTREG�
|
| 169 |
+
9100220279,CALLE GRAN 1,8506,CALLDETENES
|
| 170 |
+
9100160124,Carrer d'en Bac de Roda 45,8510,RODA DE TER
|
| 171 |
+
9100397795,CALLE SANT ROC 29,8503,GURB
|
| 172 |
+
9100059032,CARRETERA SANT HILARI S/N,8506,CALLDETENES
|
| 173 |
+
9100122540,CALLE VERDAGUER 2,8572,SANT PERE DE TORELL�
|
| 174 |
+
9100255064,LUGAR HOSTAL NOU 1,8503,GURB
|
| 175 |
+
9100058278,CALLE SANT BARTOMEU 34,8570,TORELL�
|
| 176 |
+
9100577693,"AVENIDA BALNEARI, S/N S/N",8551,TONA
|
| 177 |
+
9100041762,MAJOR 1,8150,PARETS DEL VALLES
|
| 178 |
+
9100044050,MAJOR 70,8170,MONTORNES DEL VALLES
|
| 179 |
+
9100054518,"PORXO CAN XAMBRERS, S/N -",8104,GALLECS
|
| 180 |
+
9100184803,CALLE GARRAF 5,8185,LLI#A DE VALL
|
| 181 |
+
9100601539,CALLE GRAN VIAL 19-25,8160,MONTMEL�
|
| 182 |
+
9100381913,Centro Comercial La Roca Villag S/N,8430,SANTA AGNES DE MALANYANES
|
| 183 |
+
9100579833,CALLE NURIA 12,8504,SANT JULI� DE VILATORTA
|
| 184 |
+
9100206314,CALLE SOCOS 20,8540,CENTELLES
|
| 185 |
+
9100648393,Carrer de Sant Agust� 13,8512,SANT HIP�LIT DE VOLTREG�
|
| 186 |
+
9100597817,PLAZA VELLA 11,8570,TORELL�
|
| 187 |
+
9100652762,CALLE GES D'AVALL 4,8570,TORELL�
|
| 188 |
+
9100159533,Carrer Barcelona 1,8508,LES MASIES DE VOLTREG�
|
| 189 |
+
9100041008,Ponent 59,8400,GRANOLLERS
|
| 190 |
+
9100450636,CALLE FOLCH I TORRES S/N,8160,MONTMEL�
|
| 191 |
+
9100468310,CARRETERA DE PARETS A BIGUES I KM 8,8186,LLI�A DE MUNT
|
| 192 |
+
9100493210,BV-5006 5006,8106,SANTA MARIA DE MARTORELLES
|
| 193 |
+
9100299260,CALLE CARRERADA 1,8107,MARTORELLES
|
| 194 |
+
9100514207,CALLE DOCTOR FERRAN 3,8160,MONTMEL�
|
| 195 |
+
9100055266,MASIA CAN CARRANCA S/N -,8107,MARTORELLES
|
| 196 |
+
9100042975,JOANOT MARTORELL 32,8400,GRANOLLERS
|
| 197 |
+
9100281245,Avinguda de Catalunya 111,8150,PARETS DEL VALLES
|
| 198 |
+
9100054533,CAN FENOSA 1-2 -,8107,MARTORELLES
|
| 199 |
+
9100268368,CALLE TORRENT DE CAN NINOU 1,8150,PARETS DEL VALLES
|
| 200 |
+
9100054998,Avinguda de la Llibertat 30,8100,MOLLET DEL VALLES
|
| 201 |
+
9100424060,CALLE GAIETA VINZIA 76,8100,MOLLET DEL VALL�S
|
| 202 |
+
9100424060,CALLE GAIETA VINZIA 76,8100,MOLLET DEL VALL�S
|
| 203 |
+
9100444038,JOAN MARAGALL 17,8100,MOLLET DEL VALL�S
|
| 204 |
+
9100350872,CALLE JOAQUIM MIR 57,8100,MOLLET DEL VALLES
|
| 205 |
+
9100350872,CALLE JOAQUIM MIR 57,8100,MOLLET DEL VALLES
|
| 206 |
+
9100631930,CALLE GALLECS 50,8100,MOLLET DEL VALLES
|
| 207 |
+
9100660484,Avinguda de la Llibertat 12,8100,MOLLET DEL VALLES
|
| 208 |
+
9100663784,RAMBLA POMPEU FABRA 40,8100,MOLLET DEL VALLES
|
| 209 |
+
9100686343,Passatge Sant Miquel 10,8100,MOLLET DEL VALLES
|
| 210 |
+
9100733360,Rambla de Pompeu Fabra 14,8100,MOLLET DEL VALLES
|
| 211 |
+
9100745447,CALLE CAN PANTIQUET 22,8100,MOLLET DEL VALLES
|
| 212 |
+
9100756398,Passatge Sant Miquel 10,8100,MOLLET DEL VALLES
|
| 213 |
+
9100762714,CALLE JOAN MARAGALL S/N,8100,MOLLET DEL VALLES
|
| 214 |
+
9100765744,Ronda d'Orient 1,8100,MOLLET DEL VALLES
|
| 215 |
+
9100767546,Ronda d'Orient 1,8100,MOLLET DEL VALLES
|
| 216 |
+
9100055977,Ronda d'Orient 1,8100,MOLLET DEL VALLES
|
| 217 |
+
9100055145,AVENIDA JAUME I 166,8100,MOLLET DEL VALLES
|
| 218 |
+
9100373406,CARRETERA C-17 S/N,8100,MOLLET DEL VALLES
|
| 219 |
+
119751,AVENIDA RABASSAIRES 40,8100,MOLLET DEL VALL�S
|
| 220 |
+
119751,AVENIDA RABASSAIRES 40,8100,MOLLET DEL VALL�S
|
| 221 |
+
119751,AVENIDA RABASSAIRES 40,8100,MOLLET DEL VALL�S
|
| 222 |
+
119751,AVENIDA RABASSAIRES 40,8100,MOLLET DEL VALL�S
|
| 223 |
+
119751,AVENIDA RABASSAIRES 40,8100,MOLLET DEL VALL�S
|
| 224 |
+
119751,AVENIDA RABASSAIRES 40,8100,MOLLET DEL VALL�S
|
| 225 |
+
9100111065,Avinguda de Can Prat s/n,8100,MOLLET DEL VALLES
|
| 226 |
+
9100690441,CL QUARTER NORD 1,8100,MOLLET DEL VALLES
|
| 227 |
+
9100433502,CALLE FERRO 33,8100,MOLLET DEL VALLES
|
| 228 |
+
103154,"P.I. CAN MAGAROLA, CTRA. NAC 152, K",8100,MOLLET DEL VALLES
|
| 229 |
+
103154,"P.I. CAN MAGAROLA, CTRA. NAC 152, K",8100,MOLLET DEL VALLES
|
| 230 |
+
9100054573,CALLE ANSELM CLAVE 43,8100,MOLLET DEL VALLES
|
| 231 |
+
9100054762,AVENIDA CALDERO 48,8100,MOLLET DEL VALLES
|
| 232 |
+
9100054848,Carrer de Mari� Fortuny 38,8100,MOLLET DEL VALLES
|
| 233 |
+
9100054935,Carrer de Llu�s Duran 20,8100,MOLLET DEL VALLES
|
| 234 |
+
9100055101,Avinguda Badalona 18,8100,MOLLET DEL VALLES
|
| 235 |
+
9100055108,CALLE FRANCESC MACIA 51,8100,MOLLET DEL VALLES
|
| 236 |
+
9100055221,CALLE ONZE DE SETEMBRE 26,8100,MOLLET DEL VALLES
|
| 237 |
+
9100055265,CALLE GAIETA VINZIA 11,8100,MOLLET DEL VALLES
|
| 238 |
+
9100055505,NICARAGUA S/N -,8100,MOLLET DEL VALLES
|
| 239 |
+
9100055626,AVENIDA LLIBERTAT 14,8100,MOLLET DEL VALLES
|
| 240 |
+
9100055679,CALLE GAIETA VINZIA 81,8100,MOLLET DEL VALLES
|
| 241 |
+
9100056090,FRANCESC CAMBO 21 -,8100,MOLLET DEL VALLES
|
| 242 |
+
9100056113,CALLE MOSSOS D'ESQUADRA 4,8100,MOLLET DEL VALLES
|
| 243 |
+
9100056124,CALLE PAU CLARIS 19,8100,MOLLET DEL VALLES
|
| 244 |
+
9100059602,Carrer de Joan Brossa 7,8100,MOLLET DEL VALLES
|
| 245 |
+
9100076081,CALLE ONZE DE SETEMBRE 28,8100,MOLLET DEL VALLES
|
| 246 |
+
9100135772,Carrer de Saragossa 1,8100,MOLLET DEL VALLES
|
| 247 |
+
9100150731,CALLE BERENGUER III 57,8100,MOLLET DEL VALLES
|
| 248 |
+
9100153532,BERENGUER III 98,8100,MOLLET DEL VALLES
|
| 249 |
+
9100178455,ITALIA 46,8100,MOLLET DEL VALLES
|
| 250 |
+
9100338332,RONDA ORIENT 8,8100,MOLLET DEL VALLES
|
| 251 |
+
9100413328,CALLE FRANCESC MACIA 89,8100,MOLLET DEL VALL�S
|
| 252 |
+
9100466233,CALLE LA RIERA 13,8100,MOLLET DEL VALLES
|
| 253 |
+
9100470966,RAMBLA POMPEU FABRA 10,8100,MOLLET DEL VALLES
|
| 254 |
+
9100040775,Antoni Gaudi 10,8100,MOLLET DEL VALLES
|
| 255 |
+
9100054707,Carrer de Jaume Coll 3,8100,MOLLET DEL VALLES
|
| 256 |
+
9100054708,PASAJE LA PAU S/N,8100,MOLLET DEL VALLES
|
| 257 |
+
9100054841,RAMBLA POMPEU FABRA 7,8100,MOLLET DEL VALLES
|
| 258 |
+
9100054856,CALLE GALLECS 58,8100,MOLLET DEL VALLES
|
| 259 |
+
9100054872,CALLE CAN PANTIQUET 26,8100,MOLLET DEL VALLES
|
| 260 |
+
9100054896,Pla�a de Prat de la Riba 11,8100,MOLLET DEL VALLES
|
| 261 |
+
9100054949,Avinguda de Rivoli 24,8100,MOLLET DEL VALLES
|
| 262 |
+
9100054994,CERVANTES 19-23,8100,MOLLET DEL VALLES
|
| 263 |
+
9100055030,CALLE GALLECS 64,8100,MOLLET DEL VALLES
|
| 264 |
+
9100055144,AVENIDA CALDES DE MONTBUI 18,8100,MOLLET DEL VALLES
|
| 265 |
+
9100055194,CALLE JAUME COLL 9,8100,MOLLET DEL VALLES
|
| 266 |
+
9100055251,AGRICULTURA 9,8100,MOLLET DEL VALLES
|
| 267 |
+
9100055620,PRAT DE LA RIBA 13,8100,MOLLET DEL VALLES
|
| 268 |
+
9100055707,CALLE PERE RAMON 1,8100,MOLLET DEL VALLES
|
| 269 |
+
9100055836,Pla�a de Prat de la Riba 12,8100,MOLLET DEL VALLES
|
| 270 |
+
9100056034,CALLE AGRICULTURA 18,8100,MOLLET DEL VALLES
|
| 271 |
+
9100075764,CALLE SALVADOR ESPRIU 30,8100,MOLLET DEL VALLES
|
| 272 |
+
9100120690,CALLE CLAUDI ARA�O I ARA�O 17,8100,MOLLET DEL VALLES
|
| 273 |
+
9100121589,AVENIDA ANTONI GAUDI 30-32,8100,MOLLET DEL VALLES
|
| 274 |
+
9100126935,Passatge de l'Ombra 1,8100,MOLLET DEL VALL�S
|
| 275 |
+
9100129133,AVENIDA RABASSAIRES 46-50,8100,MOLLET DEL VALLES
|
| 276 |
+
9100239655,AVENIDA JAUME I 3-5,8100,MOLLET DEL VALLES
|
| 277 |
+
9100246685,CALLE AGRICULTURA 37,8100,MOLLET DEL VALLES
|
| 278 |
+
9100326765,AVENIDA ANTONI GAUDI 46,8100,MOLLET DEL VALLES
|
| 279 |
+
9100353152,CAN MOLLET 1,8100,MOLLET DEL VALLES
|
| 280 |
+
9100366443,CALLE ENRIC MORERA 37,8100,MOLLET DEL VALLES
|
| 281 |
+
9100371825,AVENIDA JAUME I 154,8100,MOLLET DEL VALLES
|
| 282 |
+
9100371825,AVENIDA JAUME I 154,8100,MOLLET DEL VALLES
|
| 283 |
+
9100467331,RAMBLA POMPEU FABRA 4,8100,MOLLET DEL VALL�S
|
| 284 |
+
9100612100,CALLE FRANCESC LAYRET 65,8100,MOLLET DEL VALLES
|
| 285 |
+
9100617395,Passeig de la Ronda 7,8100,MOLLET DEL VALLES
|
| 286 |
+
9100642781,Carrer de Sant Jordi 56,8100,MOLLET DEL VALLES
|
| 287 |
+
9100653427,Avinguda de Caldes de Montbui 17,8100,MOLLET DEL VALLES
|
| 288 |
+
9100663743,Carrer de Sant Lloren� 52,8100,MOLLET DEL VALLES
|
| 289 |
+
9100665155,Carrer de Salvador Espriu 7,8100,MOLLET DEL VALLES
|
| 290 |
+
9100670399,CALLE SALVADOR ESPRIU 38,8100,MOLLET DEL VALLES
|
| 291 |
+
9100677478,Carrer de Sant Ramon 36,8100,MOLLET DEL VALLES
|
| 292 |
+
9100684101,Avinguda d'Antoni Gaud� 12,8100,Mollet del Vall�s
|
| 293 |
+
9100684195,Carrer de la Pau 28,8100,MOLLET DEL VALLES
|
| 294 |
+
9100694798,Carrer de Gaiet� Ventall� 33,8100,MOLLET DEL VALLES
|
| 295 |
+
9100695039,Carrer del Ferrocarril 80,8100,MOLLET DEL VALLES
|
| 296 |
+
9100695182,CALLE PAU VILA 12,8100,MOLLET DEL VALLES
|
| 297 |
+
9100698744,Carrer d'Anselm Clav� 15,8100,MOLLET DEL VALLES
|
| 298 |
+
9100701166,Carrer de Gallecs 47,8100,MOLLET DEL VALLES
|
| 299 |
+
9100702970,Carrer de Gaiet� V�nzia 20,8100,MOLLET DEL VALLES
|
| 300 |
+
9100708130,Carrer de l'Agricultura 11,8100,MOLLET DEL VALLES
|
| 301 |
+
9100721293,Carrer de Claudi Ara�� i Ara�� 17,8100,MOLLET DEL VALLES
|
| 302 |
+
9100725280,Carrer de Francesc Ferrer i Gu�r 21,8100,MOLLET DEL VALLES
|
| 303 |
+
9100727703,Rambla Nova 93,8100,MOLLET DEL VALLES
|
| 304 |
+
9100731896,Avinguda del Parc 1,8100,MOLLET DEL VALLES
|
| 305 |
+
9100733214,Carrer de Francesc Maci� 62,8100,MOLLET DEL VALLES
|
| 306 |
+
9100734979,AVENIDA CALDES DE MONTBUI 21,8100,MOLLET DEL VALLES
|
| 307 |
+
9100738455,Carrer de Francesc Layret 28,8100,MOLLET DEL VALLES
|
| 308 |
+
9100740330,Carrer de la Pau 21,8100,MOLLET DEL VALLES
|
| 309 |
+
9100742484,Carrer de Gallecs 75,8100,MOLLET DEL VALLES
|
| 310 |
+
9100744307,Carrer de Francesc Layret 71,8100,MOLLET DEL VALLES
|
| 311 |
+
9100746967,Carrer de Francesc Layret 6,8100,MOLLET DEL VALLES
|
| 312 |
+
9100746984,Carrer Jaume Coll 5,8100,MOLLET DEL VALLES
|
| 313 |
+
9100750130,Carrer de Can Pantiquet S/N,8100,MOLLET DEL VALLES
|
| 314 |
+
9100755962,AVENIDA ANTONI GAUDI 62,8100,MOLLET DEL VALLES
|
| 315 |
+
9100760131,Avinguda de Jaume I 156,8100,MOLLET DEL VALLES
|
| 316 |
+
9100760596,Carrer de Berenguer III 156,8100,POLINY�
|
| 317 |
+
9100764101,Plaza mayor 5,8100,MOLLET DEL VALLES
|
| 318 |
+
9100764969,Carrer de Gaiet� V�nzia 44,8100,MOLLET DEL VALLES
|
| 319 |
+
9100767153,CAMINO CAN VILA S/N,8100,MOLLET DEL VALLES
|
| 320 |
+
9100055150,FERROCARRIL 52 -,8100,MOLLET DEL VALLES
|
| 321 |
+
9100389125,MENENDEZ I PELAYO 1,8500,VIC
|
| 322 |
+
9100529116,PASEO GENERALITAT 37,8500,VIC
|
| 323 |
+
9100503495,PLAZA PIETAT 2,8500,VIC
|
| 324 |
+
9100573746,RAMBLA PASSEIG 7,8500,VIC
|
| 325 |
+
9100566266,Carrer de Jacint Verdaguer 28,8500,VIC
|
| 326 |
+
9100563102,PLAZA ESTACIO S/N,8500,VIC
|
| 327 |
+
9100453983,CALLE LA LLOTJA 9,8500,VIC
|
| 328 |
+
9100721688,Carrer Sant Segimon 22,8500,VIC
|
| 329 |
+
9100567698,CALLE CANIGO 70,8500,VIC
|
| 330 |
+
9100058499,CALLE JACINT VERDAGUER 27,8500,VIC
|
| 331 |
+
9100056562,PLAZA MAJOR 21,8500,VIC
|
| 332 |
+
9100736216,CARRER DE LA CIUTAT S/N,8500,VIC
|
| 333 |
+
9100739591,Carrer de la Riera 30,8500,VIC
|
| 334 |
+
9100745478,Avinguda d'Ol�mpia 7,8500,VIC
|
| 335 |
+
9100745910,Carrer de la Provid�ncia 23,8500,VIC
|
| 336 |
+
9100752144,CARRETERA DE VIC 217,8500,VIC
|
| 337 |
+
9100767161,Rambla de les Davallades 8,8500,VIC
|
| 338 |
+
9100771401,Masia Cam� de Planes Bones S/N,8500,SENTFORES
|
| 339 |
+
9100446439,CALLE RIPOLL 53,8500,VIC
|
| 340 |
+
9100724322,Carrer de la Ind�stria 52,8500,VIC
|
| 341 |
+
9100717724,AVENIDA OLIMPIA S/N,8500,VIC
|
| 342 |
+
9100715856,Carrer de Montserrat 20,8500,VIC
|
| 343 |
+
9100703436,Carrer de les Carnisseries 34,8500,VIC
|
| 344 |
+
9100703234,Carrer Sant Segimon 14,8500,VIC
|
| 345 |
+
9100701784,Carrer del Pare Galliss� 4,8500,VIC
|
| 346 |
+
9100699144,C/RAMON D'ABADAL 21,8500,VIC
|
| 347 |
+
9100691356,Call Nou 9,8500,VIC
|
| 348 |
+
9100682040,Carrer del Pare Coll 2,8500,VIC
|
| 349 |
+
9100668218,CARRER DEL VIRREI AVIL�S 6,8500,VIC
|
| 350 |
+
9100452581,PASEO GENERALITAT 44,8500,VIC
|
| 351 |
+
9100423519,CALLE SOLSONA 4,8500,VIC
|
| 352 |
+
9100409671,PASEO GENERALITAT 15,8500,VIC
|
| 353 |
+
9100374304,CALLE ENRIC PRAT DE LA RIBA 49,8500,VIC
|
| 354 |
+
9100324575,PASEO GENERALITAT 8,8500,VIC
|
| 355 |
+
9100127570,PASAJE PLA DEL REMEI 11,8500,VIC
|
| 356 |
+
115740,CARRETERA BV-5213 KM 10,8500,VIC
|
| 357 |
+
115740,CARRETERA BV-5213 KM 10,8500,VIC
|
| 358 |
+
9100727009,Pla�a Major 36,8500,VIC
|
| 359 |
+
9100576586,CALLE MOSSEN LLUIS ROMEU 1,8500,VIC
|
| 360 |
+
9100158659,Passeig de la Generalitat 10,8500,VIC
|
| 361 |
+
9100262827,Carrer de Jaume Munmany 27,8500,VIC
|
| 362 |
+
9100494258,CALLE CABRERES 2,8500,VIC
|
| 363 |
+
9100588030,Avinguda d'Ol�mpia 15,8500,VIC
|
| 364 |
+
9100579836,CALLE SAU 9,8500,VIC
|
| 365 |
+
9100540322,CALLE CAN PAU RABA 1,8500,VIC
|
| 366 |
+
9100527263,CARRETERA PRATS DE LLU�ANES 14,8500,VIC
|
| 367 |
+
9100523486,Carretera de Roda 1,8500,VIC
|
| 368 |
+
9100520726,Carrer Moss�n Josep Gudiol 21,8500,VIC
|
| 369 |
+
9100517701,PLAZA PARE MILLAN S/N,8500,VIC
|
| 370 |
+
9100517366,Carrer dels Corretgers 4,8500,VIC
|
| 371 |
+
9100515640,CALLE ARQUEBISBE ALEMANY 24,8500,VIC
|
| 372 |
+
9100454403,CALLE MANLLEU 40,8500,VIC
|
| 373 |
+
9100419900,CALLE SANT MIQUEL DELS SANTS 1,8500,VIC
|
| 374 |
+
9100345891,CALLE DUES SOLES 8,8500,VIC
|
| 375 |
+
9100345891,CALLE DUES SOLES 8,8500,VIC
|
| 376 |
+
9100337367,CALLE MIRAMARGES 8,8500,VIC
|
| 377 |
+
9100294904,RAMBLA HOSPITAL 8,8500,VIC
|
| 378 |
+
9100246993,CALLE ARGENTERS 4,8500,VIC
|
| 379 |
+
9100166608,CALLE MISERICORDIA 4,8500,VIC
|
| 380 |
+
9100126373,AVENIDA SANT BERNAT CALBO 42,8500,VIC
|
| 381 |
+
9100541821,CALLE MIRAMARGES 7,8500,VIC
|
| 382 |
+
9100628369,CALLE GURB 103,8500,VIC
|
| 383 |
+
128247,"CALLE SANTIAGO RAM�N Y CAJAL, 87",8500,VIC
|
| 384 |
+
128247,"CALLE SANTIAGO RAM�N Y CAJAL, 87",8500,VIC
|
| 385 |
+
128247,"CALLE SANTIAGO RAM�N Y CAJAL, 87",8500,VIC
|
| 386 |
+
128247,"CALLE SANTIAGO RAM�N Y CAJAL, 87",8500,VIC
|
| 387 |
+
118136,MANLLEU 30-32,8500,VIC
|
| 388 |
+
118136,MANLLEU 30-32,8500,VIC
|
| 389 |
+
9100471566,RAMBLA HOSPITAL 31,8500,VIC
|
| 390 |
+
9100739798,CARRER SANTIAGO RAMON I CAJAL 48,8500,VIC
|
| 391 |
+
9100577790,Rambla de les Davallades 21,8500,VIC
|
| 392 |
+
9100156149,CALLE CERVERA 11,8500,VIC
|
| 393 |
+
9100057040,Carretera de Manlleu s/n,8500,VIC
|
| 394 |
+
9100634160,Carrer de Rupit 12,8500,VIC
|
| 395 |
+
9100058168,Carrer de Miramarges 10,8500,VIC
|
| 396 |
+
9100058399,CALLE EIX ONZE DE SETEMBRE 29,8500,VIC
|
| 397 |
+
9100058486,Rambla de Josep Tarradellas 11,8500,VIC
|
| 398 |
+
9100058727,Pla�a Major 28,8500,VIC
|
| 399 |
+
9100058749,Carrer de Sant Jordi 1,8500,VIC
|
| 400 |
+
9100056384,CALLE ANDREU FEBRER 1,8500,VIC
|
| 401 |
+
9100056413,AVENIDA PIUS XII 13,8500,VIC
|
| 402 |
+
9100056689,CALLE EIX ONZE DE SETEMBRE 11,8500,VIC
|
| 403 |
+
9100056722,CARRETERA SANT HILARI SACALM S/N,8500,VIC
|
| 404 |
+
9100056774,PASEO GENERALITAT 23,8500,VIC
|
| 405 |
+
9100056957,Avinguda de Sant Bernat Calb� 52,8500,VIC
|
| 406 |
+
9100056959,CALLE JOAN DE SERRALLONGA 3,8500,VIC
|
| 407 |
+
9100056959,CALLE JOAN DE SERRALLONGA 3,8500,VIC
|
| 408 |
+
9100057171,Carrer de l'Arquebisbe Alemany 21,8500,VIC
|
| 409 |
+
9100057428,CALLE JAUME I EL CONQUERIDOR 3,8500,VIC
|
| 410 |
+
9100057605,PLAZA ENRIC GONZALEZ I TORNES 1,8500,VIC
|
| 411 |
+
9100057936,Carrer de Sant Pau 39,8500,VIC
|
| 412 |
+
9100057963,CALLE MIRAMARGES 6,8500,VIC
|
| 413 |
+
9100736273,Carrer de Cervera 14,8500,VIC
|
| 414 |
+
9100740503,Carrer Enric Prat de la Riba 16,8500,VIC
|
| 415 |
+
9100742873,CARRER MARE DE DEU DELS MUTS 2,8500,VIC
|
| 416 |
+
9100745490,Carrer d�en Perot Rocaguinarda 2,8500,VIC
|
| 417 |
+
9100746500,Carrer de Sant Sadurn� 12,8500,VIC
|
| 418 |
+
9100746832,Carrer de Ripoll 33,8500,VIC
|
| 419 |
+
9100748119,Rambla del Passeig 65,8500,VIC
|
| 420 |
+
9100748120,Ronda de Francesc Camprodon 18,8500,VIC
|
| 421 |
+
9100748566,Carrer Nou 7,8500,VIC
|
| 422 |
+
9100751056,Carrer del Pla de Baleny� 9,8500,VIC
|
| 423 |
+
9100752643,Carrer de Ramon Sala i Sa�ala 13,8500,VIC
|
| 424 |
+
9100752651,Carrer del Pla de Baleny� 7,8500,VIC
|
| 425 |
+
9100752860,Carretera d la Guixa 10,8500,VIC
|
| 426 |
+
9100755818,Pla�a de l'Estaci� 1,8500,VIC
|
| 427 |
+
9100760072,Rambla del Passeig 39,8500,VIC
|
| 428 |
+
9100766575,Carrer de Francesc Maria Masferre 4,8500,VIC
|
| 429 |
+
9100767754,Carrer de la Llotja 7,8500,VIC
|
| 430 |
+
9100059144,PASEO GENERALITAT 58,8500,VIC
|
| 431 |
+
9100521875,CALLE MANLLEU 42,8500,VIC
|
| 432 |
+
9100668715,Avinguda dels Pa�sos Catalans 49,8500,VIC
|
| 433 |
+
9100723264,Carrer Pa�sos Catalans 7,8191,VILANOVA DEL VALL�S
|
| 434 |
+
9100696849,Avenida Meridiana 27,8018,BARCELONA
|
| 435 |
+
9100761557,"CARRETERA BV-5001 KM 8,8",8110,MONTCADA I REIXAC
|
| 436 |
+
9100750914,Carrer de les Esquadres 26,8901,L'HOSPITALET DE LLOBREGAT
|
| 437 |
+
9100040930,LLUIS VIVES 19,8400,GRANOLLERS
|
| 438 |
+
9100044418,JOSEP UMBERT 130,8400,GRANOLLERS
|
| 439 |
+
9100044457,GERONA 185,8400,GRANOLLERS
|
| 440 |
+
9100044461,PASEO MUNTANYA 139,8400,GRANOLLERS
|
| 441 |
+
9100219680,AMADEU CASTELLANOS 2,8400,GRANOLLERS
|
| 442 |
+
9100252903,ANSELM CLAVE 77,8400,GRANOLLERS
|
| 443 |
+
9100743845,PASSATGE SANT BARTOMEU 16,8400,GRANOLLERS
|
| 444 |
+
9100747490,Carrer de Wagner 2,8222,TERRASSA
|
| 445 |
+
9100770429,Avinguda de Catalunya S/N,8930,SANT ADRI� DE BES�S
|
| 446 |
+
9100736601,Avinguda de Catalunya 51,8930,BARCELONA
|
| 447 |
+
9100627990,CARRER CAN MAG� 10,8210,BARBER� DEL VALL�S
|
| 448 |
+
9100749745,Calle Cuba 38,8302,MATAR�
|
| 449 |
+
9100474233,RONDA FRANCESC MACIA 18,8302,MATAR�
|
| 450 |
+
9100756802,Carrer de Falguera 6,8980,SANT FELIU DE LLOBREGAT
|
| 451 |
+
122790,CALLE LLUIS COMPANYS 41,8401,GRANOLLERS
|
| 452 |
+
122790,CALLE LLUIS COMPANYS 41,8401,GRANOLLERS
|
| 453 |
+
122790,CALLE LLUIS COMPANYS 41,8401,GRANOLLERS
|
| 454 |
+
122790,CALLE LLUIS COMPANYS 41,8401,GRANOLLERS
|
| 455 |
+
136720,"PLA�A DE LA CASERNA, 3",8401,GRANOLLERS
|
| 456 |
+
136720,"PLA�A DE LA CASERNA, 3",8401,GRANOLLERS
|
| 457 |
+
9100570919,Carrer de Santa Elisabet 15,8401,GRANOLLERS
|
| 458 |
+
122348,SANT ROC 8,8401,GRANOLLERS
|
| 459 |
+
122348,SANT ROC 8,8401,GRANOLLERS
|
| 460 |
+
122348,SANT ROC 8,8401,GRANOLLERS
|
| 461 |
+
122348,SANT ROC 8,8401,GRANOLLERS
|
| 462 |
+
9100756793,Carrer Pr�ncep de Viana 25,8401,GRANOLLERS
|
| 463 |
+
9100041486,AVENIDA ENRIC PRAT DE LA RIBA 77,8401,GRANOLLERS
|
| 464 |
+
9100041725,PLAZA JOSEP MALUQUER I SALVADOR 17,8401,GRANOLLERS
|
| 465 |
+
9100042386,CALLE ROSSELLO 77,8401,GRANOLLERS
|
| 466 |
+
9100043893,CALLE FRANCESC MACIA 160,8401,GRANOLLERS
|
| 467 |
+
9100044282,CALLE RAFAEL CASANOVA 64,8401,GRANOLLERS
|
| 468 |
+
9100044469,CALLE POMPEU FABRA 15,8401,GRANOLLERS
|
| 469 |
+
9100056059,CALLE MIQUEL RICOMA 55,8401,GRANOLLERS
|
| 470 |
+
9100087801,CALLE ANNIBAL 26,8401,GRANOLLERS
|
| 471 |
+
9100098069,CALLE ROGER DE FLOR 45,8401,GRANOLLERS
|
| 472 |
+
9100145874,CALLE SANT JOSEP 1,8401,GRANOLLERS
|
| 473 |
+
9100169285,PLAZA PORXADA 32,8401,GRANOLLERS
|
| 474 |
+
9100219758,CALLE TORRES I BAGES 53,8401,GRANOLLERS
|
| 475 |
+
9100229514,CALLE SANT JOSEP 10,8401,GRANOLLERS
|
| 476 |
+
9100250393,VIC 6,8401,GRANOLLERS
|
| 477 |
+
9100253786,CALLE MARE DE DEU DE MONTSERRAT 58,8401,GRANOLLERS
|
| 478 |
+
9100297849,CALLE CORRO 2,8401,GRANOLLERS
|
| 479 |
+
9100301896,CALLE SANT JOSEP 14,8401,GRANOLLERS
|
| 480 |
+
9100302461,CALLE ROGER DE FLOR 1,8401,GRANOLLERS
|
| 481 |
+
9100399265,PLAZA CASERNA S/N,8401,GRANOLLERS
|
| 482 |
+
9100399265,PLAZA CASERNA S/N,8401,GRANOLLERS
|
| 483 |
+
9100479079,CALLE MUSEU 10-12,8401,GRANOLLERS
|
| 484 |
+
9100509767,PLAZA PORXADA 21,8401,GRANOLLERS
|
| 485 |
+
9100523982,CALLE TRAVESSERES 43,8401,GRANOLLERS
|
| 486 |
+
9100527198,CR MIQUEL RICOMA 137,8401,GRANOLLERS
|
| 487 |
+
9100658144,RAMON LLULL 9,8401,GRANOLLERS
|
| 488 |
+
9100664303,Carrer de Corr� 15,8401,GRANOLLERS
|
| 489 |
+
9100677377,Carrer de Joan Corominas 22,8401,GRANOLLERS
|
| 490 |
+
9100680026,Carrer Guayaquil 30,8401,GRANOLLERS
|
| 491 |
+
9100681218,Carrer d'Isabel de Villena 64,8401,GRANOLLERS
|
| 492 |
+
9100681231,Carrer de Roger de Flor 93,8401,GRANOLLERS
|
| 493 |
+
9100682415,Pla�a Josep Maluquer i Salvador 20,8401,GRANOLLERS
|
| 494 |
+
9100682473,Carrer de Roger de Flor 75,8401,GRANOLLERS
|
| 495 |
+
9100685322,CALLE LLUIS COMPANYS 60,8401,GRANOLLERS
|
| 496 |
+
9100695278,Carrer de Sant Jaume 46,8401,GRANOLLERS
|
| 497 |
+
9100699710,Carrer de Roger de Flor 61,8401,GRANOLLERS
|
| 498 |
+
9100701777,Passeig Francesc F�bregas 81,8401,GRANOLLERS
|
| 499 |
+
9100717340,SANTA ANA 25,8401,GRANOLLERS
|
| 500 |
+
9100723316,Carrer del Camp de les Moreres 3,8401,GRANOLLERS
|
| 501 |
+
9100727750,Carrer d'Alfons IV 48,8401,GRANOLLERS
|
| 502 |
+
9100729954,PLAZA JOSEP MARIA FOLCH I TORRES 12,8401,GRANOLLERS
|
| 503 |
+
9100735762,Carrer de Corr� 36,8401,GRANOLLERS
|
| 504 |
+
9100737822,PLAZA JOSEP MARIA FOLCH I TORRES 14,8401,GRANOLLERS
|
| 505 |
+
9100744232,CALLE PONENT 12,8401,GRANOLLERS
|
| 506 |
+
9100747036,Carrer d'Alfons IV 38,8401,GRANOLLERS
|
| 507 |
+
9100751859,Carrer Rafael Casanova 60,8401,GRANOLLERS
|
| 508 |
+
9100751879,Carrer de Ponent 55,8401,GRANOLLERS
|
| 509 |
+
9100752730,Carrer de Josep Torras I Bages 29,8401,GRANOLLERS
|
| 510 |
+
9100756820,Carrer de Joan Prim 7,8401,GRANOLLERS
|
| 511 |
+
9100764052,Carrer de Tres Torres 2,8401,GRANOLLERS
|
| 512 |
+
9100764737,Carrer Rafael Casanova 50,8401,GRANOLLERS
|
| 513 |
+
118135,"MATAGALLS, S/N (ESQUINA C/CAMI DEL",8402,GRANOLLERS
|
| 514 |
+
118135,"MATAGALLS, S/N (ESQUINA C/CAMI DEL",8402,GRANOLLERS
|
| 515 |
+
9100553805,PLAZA JOSEP SERRAT I BONASTRE 18,8402,GRANOLLERS
|
| 516 |
+
9100041010,AVENIDA SANT ESTEVE 73,8402,GRANOLLERS
|
| 517 |
+
9100041103,CALLE BARTOLOME ESTEBAN MURILLO 31,8402,GRANOLLERS
|
| 518 |
+
9100042964,Tur� d'en Bruguet 2,8402,GRANOLLERS
|
| 519 |
+
9100043144,CALLE MONTSENY 4,8402,GRANOLLERS
|
| 520 |
+
9100043853,GIRONA 3,8402,GRANOLLERS
|
| 521 |
+
9100044157,CALLE CRISTOFOL COLOM 23,8402,GRANOLLERS
|
| 522 |
+
9100094835,GIRONA 36,8402,GRANOLLERS
|
| 523 |
+
9100120794,GIRONA 49,8402,GRANOLLERS
|
| 524 |
+
9100151073,CALLE CRISTOFOL COLOM 16,8402,GRANOLLERS
|
| 525 |
+
9100238682,PLAZA JOSEP BARANGE 13,8402,GRANOLLERS
|
| 526 |
+
9100338195,PASSEIG MUNTANYA 66,8402,GRANOLLERS
|
| 527 |
+
9100340763,PLAZA CAN MONIC 7,8402,GRANOLLERS
|
| 528 |
+
9100341135,CALLE JOAN PRIM 4,8402,GRANOLLERS
|
| 529 |
+
9100348821,CALLE VALLESPIR 10,8402,GRANOLLERS
|
| 530 |
+
9100355289,CALLE ANGEL GUIMERA 12-14,8402,GRANOLLERS
|
| 531 |
+
9100503002,PLAZA PAU CASALS 4,8402,GRANOLLERS
|
| 532 |
+
9100513352,CALLE VALLESPIR 1,8402,GRANOLLERS
|
| 533 |
+
9100616552,CALLE FRANCESC MACIA 135,8402,GRANOLLERS
|
| 534 |
+
9100619658,PLAZA JOSEP BARANGE 2,8402,GRANOLLERS
|
| 535 |
+
9100658142,Carrer de Girona 161,8402,GRANOLLERS
|
| 536 |
+
9100661498,CALLE MONTSENY 1,8402,GRANOLLERS
|
| 537 |
+
9100678134,CALLE GIRONA 183,8402,GRANOLLERS
|
| 538 |
+
9100685852,Carrer d'Emili Botey Alsina 12,8402,GRANOLLERS
|
| 539 |
+
9100688641,CALLE COLOMBIA 14,8402,GRANOLLERS
|
| 540 |
+
9100698912,Carrer del pla de Baix 53,8402,GRANOLLERS
|
| 541 |
+
9100699983,Calle Girona 214,8402,GRANOLLERS
|
| 542 |
+
9100700301,Carrer Esteve Terrades 52,8402,GRANOLLERS
|
| 543 |
+
9100700874,Carrer Montseny 10,8402,GRANOLLERS
|
| 544 |
+
9100703647,Pla�a de Pau Casals 14,8402,GRANOLLERS
|
| 545 |
+
9100718615,PLAZA CAN BASSA 1,8402,GRANOLLERS
|
| 546 |
+
9100718706,CARRER SANT JOSEP DE CALA�ANS 119,8402,GRANOLLERS
|
| 547 |
+
9100723039,CARRER JOSEP UMBERT 42,8402,GRANOLLERS
|
| 548 |
+
9100723531,Carrer de Girona 321,8402,GRANOLLERS
|
| 549 |
+
9100729258,Avinguda del Parc 5,8402,GRANOLLERS
|
| 550 |
+
9100729405,CALLE VALLESPIR 16,8402,GRANOLLERS
|
| 551 |
+
9100730939,Carrer Francisco de Quevedo 11,8402,GRANOLLERS
|
| 552 |
+
9100732766,Pla�a Josep Torras i Bages 2,8402,GRANOLLERS
|
| 553 |
+
9100736806,Carrer de Vene�uela 86,8402,GRANOLLERS
|
| 554 |
+
9100737231,Passeig de la Muntanya 154,8402,GRANOLLERS
|
| 555 |
+
9100738166,Passeig de la Muntanya 145,8402,GRANOLLERS
|
| 556 |
+
9100742644,Calle Josep Umbert 130,8402,GRANOLLERS
|
| 557 |
+
9100742712,ENRIC PRAT DE LA RIBA 77,8402,GRANOLLERS
|
| 558 |
+
9100743172,Carrer Esteve Terrades 44,8402,GRANOLLERS
|
| 559 |
+
9100743868,Carrer del pla de Baix 51,8402,GRANOLLERS
|
| 560 |
+
9100745043,Avinguda Sant Esteve 96,8402,GRANOLLERS
|
| 561 |
+
9100747591,Carrer Montseny 4,8402,GRANOLLERS
|
| 562 |
+
9100750645,Carrer de Girona 122,8402,GRANOLLERS
|
| 563 |
+
9100755605,Carrer Bertran de Seva 1,8402,GRANOLLERS
|
| 564 |
+
9100756365,PLAZA JOSEP TORRAS I BAGES 2,8402,GRANOLLERS
|
| 565 |
+
9100758269,Carrer de l'Hospital 7,8402,GRANOLLERS
|
| 566 |
+
9100762486,Carrer de Montserrat Roig 19,8402,GRANOLLERS
|
| 567 |
+
128003,"CALLE FRANSCESC RIBAS, 64",8402,GRANOLLERS
|
| 568 |
+
9100694685,Carrer de Sant Pere 7,8560,MANLLEU
|
| 569 |
+
9100691956,Pla�a de Fra Bernad� 22,8560,MANLLEU
|
| 570 |
+
9100381861,AVENIDA ROMA 122,8560,MANLLEU
|
| 571 |
+
9100722376,Avinguda Bellmunt 25,8560,MANLLEU
|
| 572 |
+
9100728312,Carrer de Ripoll 20,8560,MANLLEU
|
| 573 |
+
9100641799,CALLE JOAN CODINA VIVET S/N,8560,MANLLEU
|
| 574 |
+
9100593250,AVENIDA ROMA 106,8560,MANLLEU
|
| 575 |
+
9100577717,Carrer del Montseny 98,8560,MANLLEU
|
| 576 |
+
9100540106,CALLE DO� BLANCA 114,8560,MANLLEU
|
| 577 |
+
9100536121,CALLE VILAMIROSA 92,8560,MANLLEU
|
| 578 |
+
9100520242,PASEO SANT JOAN 110,8560,MANLLEU
|
| 579 |
+
9100494853,AVENIDA PUIGMAL 100,8560,MANLLEU
|
| 580 |
+
9100494288,PASEO SANT JOAN 194,8560,MANLLEU
|
| 581 |
+
9100487378,AVENIDA ROMA 220,8560,MANLLEU
|
| 582 |
+
9100485303,CALLE LLEIDA 58,8560,MANLLEU
|
| 583 |
+
9100479427,CALLE ENRIC DELARIS 16,8560,MANLLEU
|
| 584 |
+
9100467000,CALLE SOGORB 73,8560,MANLLEU
|
| 585 |
+
9100420376,CALLE GARCIA ESTRADA 20,8560,MANLLEU
|
| 586 |
+
9100326018,Carrer de N�ria 24,8560,MANLLEU
|
| 587 |
+
9100301063,CALLE TORRENT MAGI 80,8560,MANLLEU
|
| 588 |
+
9100253099,PLAZA SARDANA 1,8560,MANLLEU
|
| 589 |
+
9100206311,Carrer Francesc Puget 2,8560,MANLLEU
|
| 590 |
+
9100194884,CARRETERA BV-5224 (TORELLO) KM 1,8560,MANLLEU
|
| 591 |
+
9100169134,AVENIDA PIRINEUS S/N,8560,MANLLEU
|
| 592 |
+
9100160354,Pla�a de Fra Bernad� 3,8560,MANLLEU
|
| 593 |
+
9100159434,CARRETERA OLOT 58,8560,MANLLEU
|
| 594 |
+
9100159298,PLAZA FRA BERNADI 15,8560,MANLLEU
|
| 595 |
+
9100158995,CARRETERA OLOT 77,8560,MANLLEU
|
| 596 |
+
9100158872,CALLE PUIGSACALM 15,8560,MANLLEU
|
| 597 |
+
9100158492,PLAZA FRA BERNADI 12,8560,MANLLEU
|
| 598 |
+
9100159254,CALLE SOGORB 79,8560,MANLLEU
|
| 599 |
+
9100058412,CALLE PONT 15,8560,MANLLEU
|
| 600 |
+
9100056427,AVENIDA ROMA 221,8560,MANLLEU
|
| 601 |
+
9100057531,CALLE PINTOR GUARDIA 6,8560,MANLLEU
|
| 602 |
+
9100042378,Carrer Major 31,8150,PARETS DEL VALLES
|
| 603 |
+
9100042685,Pla�a de la Vila 8,8150,LLI�� DE VALL
|
| 604 |
+
9100043482,CALLE ARAGO 13,8150,PARETS DEL VALLES
|
| 605 |
+
9100044179,CALLE L'EMPORDA 27,8150,PARETS DEL VALLES
|
| 606 |
+
9100044290,CALLE MONISTROL 1,8150,PARETS DEL VALLES
|
| 607 |
+
9100227177,CALLE INDEPENDENCIA 51,8150,PARETS DEL VALLES
|
| 608 |
+
9100264438,LLIBERTAT 2,8150,PARETS DEL VALLES
|
| 609 |
+
9100366757,PLAZA VILA S/N,8150,PARETS DEL VALLES
|
| 610 |
+
9100382635,CALLE VICTOR CATALA 2,8150,PARETS DEL VALLES
|
| 611 |
+
9100382635,CALLE VICTOR CATALA 2,8150,PARETS DEL VALLES
|
| 612 |
+
9100413240,CALLE VICTOR CATALA 9,8150,PARETS DEL VALL�S
|
| 613 |
+
9100413240,CALLE VICTOR CATALA 9,8150,PARETS DEL VALL�S
|
| 614 |
+
9100486030,Carrer de V�ctor Catal� 18,8150,PARETS DEL VALLES
|
| 615 |
+
9100058807,CALLE SANT JORDI 8,8560,MANLLEU
|
| 616 |
+
9100058807,CALLE SANT JORDI 8,8560,MANLLEU
|
| 617 |
+
9100617124,CARRER SANT JAUME 134,8150,PARETS DEL VALLES
|
| 618 |
+
9100617475,AVENIDA PEDRA DEL DIABLE 28,8150,PARETS DEL VALLES
|
| 619 |
+
9100643487,Avinguda de Francesc Maci� 11,8150,PARETS DEL VALLES
|
| 620 |
+
9100652615,Carrer del Raval 1,8150,PARETS DEL VALLES
|
| 621 |
+
9100661113,Avinguda de Catalunya 102,8150,PARETS DEL VALLES
|
| 622 |
+
9100661227,Avinguda de Catalunya 129,8150,PARETS DEL VALLES
|
| 623 |
+
9100670427,Carrer de Monistrol 1,8150,PARETS DEL VALLES
|
| 624 |
+
9100671265,Avinguda de Francesc Maci� 12,8150,PARETS DEL VALLES
|
| 625 |
+
9100692245,Avinguda de Catalunya 133,8150,PARETS DEL VALLES
|
| 626 |
+
9100698420,Carrer de la Mina 8,8150,PARETS DEL VALLES
|
| 627 |
+
9100701217,CALLE GARBI 12,8150,PARETS DEL VALLES
|
| 628 |
+
9100701291,CALLE DE LA BASSA S/N,8150,PARETS DEL VALLES
|
| 629 |
+
9100733333,PLAZA MOSSEN PERE BATLLE 1,8150,PARETS DEL VALLES
|
| 630 |
+
9100733969,CALLE XALOC 1,8150,PARETS DEL VALLES
|
| 631 |
+
9100738774,Carrer del Pla dels Estudiants 40,8560,MANLLEU
|
| 632 |
+
9100742503,Carrer Doctor Fleming 46,8560,MANLLEU
|
| 633 |
+
9100746992,CALLE CONGOST 1,8150,PARETS DEL VALLES
|
| 634 |
+
9100747472,Carrer de Tarragona 50,8560,MANLLEU
|
| 635 |
+
9100747929,Carrer d'en Santiago Rusi�ol 54,8560,MANLLEU
|
| 636 |
+
9100758135,Pla�a de Fra Bernad� 23,8560,MANLLEU
|
| 637 |
+
9100759612,Carrer Salvador Espriu 6,8150,PARETS DEL VALL�S
|
| 638 |
+
9100759982,Parc d'�ngela Roca 7,8560,MANLLEU
|
| 639 |
+
9100761848,Carrer d'Arag� 11,8150,PARETS DEL VALL�S
|
| 640 |
+
9100766615,Carrer Doctor Fleming 53,8560,MANLLEU
|
| 641 |
+
9100751000,Pla�a de Narcisa Freixas 2,8022,BARCELONA
|
| 642 |
+
9100736471,Carrer de Fructu�s Gelabert 1,8970,SANT JOAN DESPI
|
| 643 |
+
9100736476,Avinguda de Barcelona 64,8970,SANT JOAN DESPI
|
| 644 |
+
119717,"CTRA. SANT ADRIA A LA ROCA, KM. 15,",8170,MONTORN�S DEL VALL�S
|
| 645 |
+
119717,"CTRA. SANT ADRIA A LA ROCA, KM. 15,",8170,MONTORN�S DEL VALL�S
|
| 646 |
+
9100042801,Avinguda del Riu Mogent 1,8170,MONTORN�S DEL VALL�S
|
| 647 |
+
9100055035,CALLE MAJOR 61,8170,MONTORNES DEL VALLES
|
| 648 |
+
9100055275,CALLE FEDERICO GARCIA LORCA 3,8170,MONTORNES DEL VALLES
|
| 649 |
+
9100055925,BLAS INFANTE 2,8170,MONTORNES DEL VALLES
|
| 650 |
+
9100120688,PLAZA LA FONT 1,8170,MONTORNES DEL VALLES
|
| 651 |
+
9100125812,JOSEP TERRADELLAS SN,8170,MONTORNES DEL VALLES
|
| 652 |
+
9100129120,CALLE BLAS INFANTE 8,8170,MONTORNES DEL VALLES
|
| 653 |
+
9100154524,PLAZA PAU PICASSO 1,8170,MONTORNES DEL VALLES
|
| 654 |
+
9100154591,CALLE VILANOVA S/N,8170,MONTORNES DEL VALLES
|
| 655 |
+
9100198106,CALLE PALAU D'AMETLLA 14,8170,MONTORNES DEL VALLES
|
| 656 |
+
9100199450,CALLE L'ESTRELLA 12,8170,MONTORNES DEL VALLES
|
| 657 |
+
9100296409,Carrer del Gran Vial 3,8170,MONTORN�S DEL VALL�S
|
| 658 |
+
9100513843,CARRETERA SANT ADRIA DEL BESOS-L 17,8170,MONTORN�S DEL VALL�S
|
| 659 |
+
9100636763,Carrer de Sant Isidre 13,8170,Montorn�s del Vall�s
|
| 660 |
+
9100669904,Carrer de Carles Riba 3,8170,MONTORN�S DEL VALL�S
|
| 661 |
+
9100702913,Carrer del Sol 3,8170,MONTORN�S DEL VALL�S
|
| 662 |
+
9100724298,Carrer de Jacint Verdaguer 9,8170,MONTORN�S DEL VALL�S
|
| 663 |
+
9100725292,Carrer la Pau 10,8170,MONTORN�S DEL VALL�S
|
| 664 |
+
9100726583,Carrer Major 57,8170,MONTORN�S DEL VALL�S
|
| 665 |
+
9100728401,Carrer de Sant Isidre 16,8170,MONTORN�S DEL VALL�S
|
| 666 |
+
9100728505,Pla�a del Poble 14,8170,MONTORN�S DEL VALL�S
|
| 667 |
+
9100729533,BV-5001 32-34,8170,MONTORN�S DEL VALL�S
|
| 668 |
+
9100752742,Carrer de Federico Garc�a Lorca 2,8170,MONTORN�S DEL VALL�S
|
| 669 |
+
9100752760,CALLE CONSELL DE CENT 5,8170,MONTORN�S DEL VALL�S
|
| 670 |
+
9100760817,Carrer de Federico Garc�a Lorca 23,8170,MONTORN�S DEL VALL�S
|
| 671 |
+
9100676061,PASSEIG DE LA FLORIDA 15,8130,SANTA PERP�TUA DE MOGODA
|
| 672 |
+
9100743597,CARRER COLOM 209,8223,TERRASSA
|
| 673 |
+
9100743608,CARRETERA MONTCADA 148,8223,TERRASSA
|
| 674 |
+
9100745932,Carrer del Germ� Joaquim 55,8223,TERRASSA
|
| 675 |
+
9100727035,Avinguda Nostra Senyora de Monts 22,8504,SANT JULI� DE VILATORTA
|
| 676 |
+
9100723081,Carretera Seva 2,8554,SANT MIQUEL DE BALENYA
|
| 677 |
+
9100666742,Carrer del Ti?ler 27,8503,GURB
|
| 678 |
+
9100741999,Avinguda Catalunya 3,8105,SANT FOST DE CAMPSENTELLES
|
| 679 |
+
9100736469,Carrer de Mil� 22,8917,BADALONA
|
| 680 |
+
9100722593,Carrer Ramon Pujols Claveras 42,8510,RODA DE TER
|
| 681 |
+
9100703787,Carrer Gran 15,8506,CALLDETENES
|
| 682 |
+
9100703223,CASA NOVA DEL CASTELL S/N,8585,MONTESQUIU
|
| 683 |
+
9100691332,Carrer del Mestre Quer 12,8580,SANT QUIRZE DE BESORA
|
| 684 |
+
9100677847,AUTOVIA C-17 S/N,8580,SANT QUIRZE DE BESORA
|
| 685 |
+
9100695204,Carrer Josep Vilanova 27,8572,SANT PERE DE TORELL�
|
| 686 |
+
9100723139,CALLE DEL CASAL 1,8415,BIGUES I RIELLS
|
| 687 |
+
9100737399,CALLE DE LA FABRICA 26,8187,SANTA EULALIA DE RON�ANA
|
| 688 |
+
9100747990,Carrer de Concepci� Ducloux s/n,17500,RIPOLL
|
| 689 |
+
9100723869,AVINGUDA DEL CASTELL 18,8570,TORELLO
|
| 690 |
+
9100747927,Passeig de l'Ametlla 19,8480,L'AMETLLA DEL VALL�S
|
| 691 |
+
9100682560,PASEO TER S/N,8585,MONTESQUIU
|
| 692 |
+
9100758280,Carrer Rossell� 22,8521,BELLAVISTA
|
| 693 |
+
9100628975,PASAJE MARTI S/N,8185,LLI�� DE VALL
|
| 694 |
+
9100653354,Cam� de la Pineda 6,8186,LLI�� D'AMUNT
|
| 695 |
+
9100653355,Passeig l'Alzina 1,8420,CANOVELLES
|
| 696 |
+
9100659648,Carrer de Joanot Martorell 34,8403,GRANOLLERS
|
| 697 |
+
9100662674,Passeig de la Conca de Bes�s 45,8403,GRANOLLERS
|
| 698 |
+
9100666589,Carrer de la Ribera d'Ebre 56,8186,LLI�� D'AMUNT
|
| 699 |
+
9100677459,Carrer Can Nadal 12,8185,LLI�� DE VALL
|
| 700 |
+
9100678295,Passeig de les Corts Catalanes 7,8160,MONTMEL�
|
| 701 |
+
9100679516,Carrer de Folch i Torres 102,8186,LLI�� D'AMUNT
|
| 702 |
+
9100679898,Carrer d'Anselm Clav� 60,8186,LLI�� D'AMUNT
|
| 703 |
+
9100679906,Carrer de Barcelona 136,8105,SANT FOST DE CAMPSENTELLES
|
| 704 |
+
9100681160,CALLE GALILEU GALILEI 16,8403,GRANOLLERS
|
| 705 |
+
9100683566,Cam� del Gual 11,8187,SANTA EUL�LIA DE RON�ANA
|
| 706 |
+
9100698329,PASAJE MARTI S/N,8185,LLI�� DE VALL
|
| 707 |
+
9100717355,CALLE RAFAEL CASANOVA 6,8186,LLI�� D'AMUNT
|
| 708 |
+
9100723356,Avinguda dels Pa�sos Catalans 90,8186,LLI�� D'AMUNT
|
| 709 |
+
9100726650,CALLE ANSELM CLAVE 70,8186,LLI�� D'AMUNT
|
| 710 |
+
9100729132,CALLE MATARRANYA 2,8186,LLI�� D'AMUNT
|
| 711 |
+
9100729658,Carrer Raval 5,8185,PARETS DEL VALLES
|
| 712 |
+
9100729943,Carrer Major 27,8160,MONTMEL�
|
| 713 |
+
9100733355,Carrer Barcelona 60,8521,BELLAVISTA
|
| 714 |
+
9100735422,CALLE VIC 7,8160,MONTMEL�
|
| 715 |
+
9100735547,Pasaje Extremadura S/N,8521,LES FRANQUESES DEL VALLES
|
| 716 |
+
9100736215,C-17 17,17500,RIPOLL
|
| 717 |
+
9100736235,Carrer Pirineu S/N,8519,TAVERNOLES
|
| 718 |
+
9100736259,Ctra. Parador Km. 7,8519,TAV�RNOLES
|
| 719 |
+
9100736266,BP-4654 Km 1.2,8588,SORA
|
| 720 |
+
9100736272,CARRER AJUNTAMENT 5,8571,SAN VICEN� DE TORELLO
|
| 721 |
+
9100736411,Pla�a del Dr. Robert 1,17867,CAMPRODON
|
| 722 |
+
9100736486,Carrer d'Europa 1,8913,BADALONA
|
| 723 |
+
9100736670,Carrer Proven�a 29,8521,BELLAVISTA
|
| 724 |
+
9100742280,CARRER MARE DE DEU DE LA SALUT 3,8187,SANTA EUL�LIA DE RON�ANA
|
| 725 |
+
9100742290,CARRETERA N-141 D S/N,8506,CALLDETENES
|
| 726 |
+
9100743554,Avinguda del Mil�lenari s/n,8160,MONTMEL�
|
| 727 |
+
9100743595,CARRER DE L'ANOIA 7,8192,SANT QUIRZE DEL VALL�S
|
| 728 |
+
9100745325,Carrer de Palaud�ries 88,8186,LLI�� D'AMUNT
|
| 729 |
+
9100745792,AVENIDA MIL.LENARI 12,8160,MONTMEL�
|
| 730 |
+
9100745929,CALLE PINTOR HUGUET 62,8226,TERRASSA
|
| 731 |
+
9100750650,CARRER DE VOLTREG� 15,8570,TORELL�
|
| 732 |
+
9100751813,CARRETERA OLESA S/N,8232,VILADECAVALLS
|
| 733 |
+
9100756104,Carretera de Ribes 21,8585,MONTESQUIU
|
| 734 |
+
9100756188,Pla�a Nova 8,8570,TORELL�
|
| 735 |
+
9100756958,Avinguda de Catalunya 61,8185,LLI�� DE VALL
|
| 736 |
+
9100757467,CARRETERA LA ROCA S/N,8105,SANT FOST DE CAMPSENTELLE
|
| 737 |
+
9100757477,Pla�a la Vila 2,8105,SANT FOST DE CAMPSENTELLES
|
| 738 |
+
9100757707,Carrer del Pont 31,8570,TORELL�
|
| 739 |
+
9100759429,Carretera de Manlleu km 4,8503,GURB
|
| 740 |
+
9100761780,Pla�a Sant Isidre S/N,8160,MONTMEL�
|
| 741 |
+
9100761923,Avinguda Tecla Sala 40,8510,RODA DE TER
|
| 742 |
+
9100764980,Passeig Andalusia 5,8521,BELLAVISTA
|
| 743 |
+
9100767890,Pla�a Mossen Cinto Verdaguer 3,8519,FOLGUEROLES
|
| 744 |
+
9100675584,CALLE METALURGIA S/N,8186,LLI�A D�AMUNT
|
| 745 |
+
9100759837,Carretera de Baleny� 237,8552,TARADELL
|
| 746 |
+
9100736222,Carrer Montseny 15,8519,TAV�RNOLES
|
| 747 |
+
9100669176,Carrer Sant Roc 68,8570,TORELL�
|
| 748 |
+
9100673284,Carrer de Manlleu 7,8570,TORELLO
|
| 749 |
+
9100736185,N-260 104,17860,SANT JOAN DE LES ABADESSES
|
| 750 |
+
9100736219,Carrer Lleida 7,17403,SANT HILARI SACALM
|
| 751 |
+
9100736220,PLAZA CONSTITUCIO S/N,17534,RIBES DE FRESER
|
| 752 |
+
9100739178,Carrer de l'Alcalde Pere Rovira 2,17860,SANT JOAN DE LES ABEDESSES
|
| 753 |
+
9100736244,Carrer de Sant Quinti 45,17534,RIBES DE FRESER
|
| 754 |
+
9100742664,PLAZA ANSELM CLAVE 3,17530,CAMPDEV�NOL
|
| 755 |
+
9100736227,CALLE SANT JAUME 25,17401,ARB�CIES
|
| 756 |
+
9100736228,Carrer de la Canal 9,17869,VILALLONGA DE TER
|
| 757 |
+
9100764653,N-2 494,25250,BELLPUIG
|
| 758 |
+
9100529900,PLAZA MARQUES DE LA QUADRA 11,8504,SANT JULI� DE VILATORTA
|
| 759 |
+
9100527964,CALLE BELLMUNT 12,8503,GURB
|
| 760 |
+
9100473659,CARRETERA C-17 S/N,8551,TONA
|
| 761 |
+
9100451636,CALLE JACINT VERDAGUER 20,8503,GURB
|
| 762 |
+
9100180416,CALLE DOCTOR BAYES 2,8551,TONA
|
| 763 |
+
9100129392,CALLE NURIA 27,8504,SANT JULI� DE VILATORTA
|
| 764 |
+
9100058963,"CARRETERA BALENYA KM 1,3",8552,TARADELL
|
| 765 |
+
9100059072,Carrer Montserrat 20,8507,SANTA EUG�NIA DE BERGA
|
| 766 |
+
9100542720,CALLE PUIG-ROVI 31,8570,TORELL�
|
| 767 |
+
9100515642,AVENIDA MONTSERRAT 7,8570,TORELL�
|
| 768 |
+
9100473327,PASEO PLA DE TEIA 38,8584,SANTA MARIA DE BESORA
|
| 769 |
+
9100246985,LUGAR SANTUARI DE BELLMUNT 1,8572,SANT PERE DE TORELL�
|
| 770 |
+
9100246985,LUGAR SANTUARI DE BELLMUNT 1,8572,SANT PERE DE TORELL�
|
| 771 |
+
9100136123,CALLE MONTESQUIU 41,8570,TORELL�
|
| 772 |
+
9100136123,CALLE MONTESQUIU 41,8570,TORELL�
|
| 773 |
+
9100112395,PLAZA NOVA 2,8570,TORELL�
|
| 774 |
+
9100059171,CALLE EL CANIGO 68,8570,TORELL�
|
| 775 |
+
9100059082,CARRETERA BERGA 6,8580,SANT QUIRZE DE BESORA
|
| 776 |
+
9100059021,CALLE TER 66,8570,TORELL�
|
| 777 |
+
9100058835,CALLE JOSEP VILANOVA 6,8572,SANT PERE DE TORELL�
|
| 778 |
+
9100560156,AVENIDA LA SEU 2,8503,GURB
|
| 779 |
+
9100159144,CALLE MAJOR 12,8519,FOLGUEROLES
|
| 780 |
+
9100577716,PLA�A MAJOR 12,8510,RODA DE TER
|
| 781 |
+
9100522381,CALLE SANT PERE 32,8510,RODA DE TER
|
| 782 |
+
9100491082,PLAZA ONZE DE SETEMBRE 1,8506,CALLDETENES
|
| 783 |
+
9100383438,CALLE BAC DE RODA 40,8510,RODA DE TER
|
| 784 |
+
9100372136,AVENIDA TECLA SALA S/N,8510,RODA DE TER
|
| 785 |
+
9100350948,CALLE TORRENT 3,8510,RODA DE TER
|
| 786 |
+
9100305726,Lugar Pairot de Baix s/n,8509,SANTA CECILIA DE VOLTREG�
|
| 787 |
+
9100247403,CALLE BAC DE RODA 45,8510,RODA DE TER
|
| 788 |
+
9100159423,CALLE BATLLE SERRALLONGA 11,8512,SANT HIP�LIT DE VOLTREG�
|
| 789 |
+
9100159383,Avinguda Gaud� 34,8512,SANT HIP�LIT DE VOLTREG�
|
| 790 |
+
9100159311,Carrer Barcelona 28,8508,LES MASIES DE VOLTREG�
|
| 791 |
+
9100158623,CALLE BAC DE RODA 44,8510,RODA DE TER
|
| 792 |
+
9100145307,CALLE GRAN 9,8506,CALLDETENES
|
| 793 |
+
9100448962,AVENIDA SANT BARTOMEU DEL GRAU S/N,8503,GURB
|
| 794 |
+
9100342026,CALLE SANT JORDI 9,8503,GURB
|
| 795 |
+
9100270631,Avinguda Tecla Sala 42,8510,RODA DE TER
|
| 796 |
+
9100171360,CALLE MOSSEN PARASSOLS 1,8570,TORELL�
|
| 797 |
+
9100095188,"Carretera de Prats de Llu�a Km 53,5",8279,AVINY�
|
| 798 |
+
9100058012,Cam� la Vallmitjana s/n,8552,TARADELL
|
| 799 |
+
9100058012,Cam� la Vallmitjana s/n,8552,TARADELL
|
| 800 |
+
9100058180,Avinguda de Sant Lloren� 68,8504,SANT JULI� DE VILATORTA
|
| 801 |
+
9100058299,CARRETERA B-521 (SANT HIPOLIT) KM 4,8503,GURB
|
| 802 |
+
9100058446,"CARRETERA C-25 EIX TRANSVE KM 174,6",8503,GURB
|
| 803 |
+
9100058476,"CARRETERA C-25 EIX TRANSVE KM 174,6",8503,GURB
|
| 804 |
+
9100058613,Carrer N�ria 103,8570,TORELL�
|
| 805 |
+
9100058617,Carrer de Barcelona 71,8510,RODA DE TER
|
| 806 |
+
9100058730,Passeig de la Su�ssa s/n,8551,TONA
|
| 807 |
+
9100056658,CALLE SANTA FE 2,8552,TARADELL
|
| 808 |
+
9100056827,Carrer de Puig-l'agulla s/n,8504,SANT JULI� DE VILATORTA
|
| 809 |
+
9100056987,CARRETERA B-522 VIC-MANLLEU KM 2,8503,GURB
|
| 810 |
+
9100056988,Carrer de Sant Feliu 19,8570,TORELL�
|
| 811 |
+
9100057038,Carrer dels Estudis 6,8570,TORELL�
|
| 812 |
+
9100057483,Pla�a els Pa�sos Catalans 1,8570,TORELL�
|
| 813 |
+
9100057514,CALLE BARCELONA 13,8503,GURB
|
| 814 |
+
9100057776,Avinguda Pau Casals 22,8506,CALLDETENES
|
| 815 |
+
9100057906,Calle de Enrique Prat de la Riba 15,8570,TORELL�
|
| 816 |
+
9100057926,CALLE PIRINEU 22,8507,SANTA EUG�NIA DE BERGA
|
| 817 |
+
9100058772,MATABOSCH C DEL TER 200,8570,TORELL�
|
| 818 |
+
9100639954,CALLE PIRINEU 8,8510,LES MASIES DE RODA
|
| 819 |
+
9100618226,AVENIDA LA RIBERA 5,8503,GURB
|
| 820 |
+
9100633779,C/ ESQUIROL S/N,8570,TORELL�
|
| 821 |
+
9100600530,CARRETERA BV-5626 7,8571,ST. VINCEN� DE TORELLO
|
| 822 |
+
9100527972,CALLE FRAN�A 16,8228,TERRASSA
|
| 823 |
+
9100058394,AUTOVIA C-17 KM 762,8573,OR�S
|
| 824 |
+
9100041503,AVENIDA POMPEU FABRA 3,8160,MONTMELO
|
| 825 |
+
9100042301,Prat de la Riba 74,8415,BIGUES I RIELLS
|
| 826 |
+
9100042414,Pla�a de la Vila S/N,8185,LLI�� DE VALL
|
| 827 |
+
9100042846,PLAZA VILA 15,8185,LLI#A DE VALL
|
| 828 |
+
9100043170,Avinguda de Sant Juli� 217,8403,GRANOLLERS
|
| 829 |
+
9100043317,CARRETERA CALDES KM 14-15,8186,LLI�� D'AMUNT
|
| 830 |
+
9100044116,CALLE DIPUTACIO 10,8160,MONTMELO
|
| 831 |
+
9100044483,Carrer Tenes 23,8186,LLI�� D'AMUNT
|
| 832 |
+
9100044483,Carrer Tenes 23,8186,LLI�� D'AMUNT
|
| 833 |
+
9100055559,ANSELM CLAVE 3,8106,SANTA MARIA DE MARTORELLES
|
| 834 |
+
9100076545,CALLE CELLECS 6,8520,LES FRANQUESES DEL VALLES
|
| 835 |
+
9100112714,CALLE LA PRIMAVERA 10,8107,MARTORELLES
|
| 836 |
+
9100158437,CALLE SANTIAGO RUSI�OL,8160,MONTMELO
|
| 837 |
+
9100238822,AVENIDA PIERA 33,8107,MARTORELLES
|
| 838 |
+
9100301605,AVENIDA CATALUNYA 63,8185,LLI�� DE VALL
|
| 839 |
+
9100371601,CALLE MATARRANYA 6,8186,LLI�A D'AMUNT
|
| 840 |
+
9100377532,CALLE CARRERADA 11,8107,MARTORELLES
|
| 841 |
+
9100377532,CALLE CARRERADA 11,8107,MARTORELLES
|
| 842 |
+
9100410532,PASEO ANDALUSIA 24,8521,BELLAVISTA
|
| 843 |
+
9100411688,PASEO ANDALUSIA 9,8521,LES FRANQUESES DEL VALL�S
|
| 844 |
+
9100411688,PASEO ANDALUSIA 9,8521,LES FRANQUESES DEL VALL�S
|
| 845 |
+
9100414445,CARRETERA PALAUDARIES 79,8186,LLI�� D'AMUNT
|
| 846 |
+
9100414445,CARRETERA PALAUDARIES 79,8186,LLI�� D'AMUNT
|
| 847 |
+
9100422647,CARRETERA CALDES 57,8403,GRANOLLERS
|
| 848 |
+
9100422647,CARRETERA CALDES 57,8403,GRANOLLERS
|
| 849 |
+
9100424065,CALLE MAJOR 53,8160,MONTMEL�
|
| 850 |
+
9100424065,CALLE MAJOR 53,8160,MONTMEL�
|
| 851 |
+
9100511092,CALLE LA VERNEDA 66-70,8107,MARTORELLES
|
| 852 |
+
9100054889,PLAZA LA VILA 4,8105,SANT FOST DE CAMPSENTELLES
|
| 853 |
+
9100055361,CALLE POMPEU FABRA 41,8105,SANT FOST DE CAMPSENTELLES
|
| 854 |
+
9100243079,CALLE BARCELONA 74,8105,SANT FOST DE CAMPSENTELLES
|
| 855 |
+
9100447655,AVENIDA SANT JERONI 4,8105,SANT FOST DE CAMPSENTELLES
|
| 856 |
+
9100505409,AVENIDA SANT JERONI 7,8105,SANT FOST
|
| 857 |
+
9100528985,Carretera Cardedeu 1,8520,BARCELONA
|
| 858 |
+
9100530921,AVENIDA CANTALLOPS S/N,8185,LLI�� DE VALL
|
| 859 |
+
9100558085,Carrer Folch i Torras 1,8160,MONTMEL�
|
| 860 |
+
9100056389,PIRINEO 97,8570,TORELL�
|
| 861 |
+
9100044002,SABADELL A GRANOLLERS KM 10.5,8185,LLI�A DE VALL
|
| 862 |
+
9100054923,AVENIDA PIERA 45,8107,MARTORELLES
|
| 863 |
+
9100070576,CALLE BARCELONA 62,8521,LES FRANQUESES DEL VALLES
|
| 864 |
+
9100120656,MONTSERRAT 41-43,8185,LLI�A DE VALL
|
| 865 |
+
9100120981,AVENIDA CANOVELLES 86,8420,CANOVELLES
|
| 866 |
+
9100127929,CALLE GIRONA 300,8521,BELLAVISTA
|
| 867 |
+
9100134891,NAVARRA 43,8430,ROCA DEL VALLES (LA)
|
| 868 |
+
9100139142,CAN FENOSA 21,8107,MARTORELLES
|
| 869 |
+
9100151684,AVENIDA MIL.LENARI 15,8160,MONTMELO
|
| 870 |
+
9100172700,ANDALUCIA 4,8520,LES FRANQUESES DEL VALL�S
|
| 871 |
+
9100264859,CALLE DOCTOR FLEMING 6,8185,LLI�� DE VALL
|
| 872 |
+
9100342550,CALLE FRANCESC LAYRET 19-21,8107,MARTORELLES
|
| 873 |
+
9100054600,CALLE L'HORTA 18,8107,MARTORELLES
|
| 874 |
+
9100055177,AVENIDA SANT JERONI 13,8105,SANT FOST DE CAMPSENTELLES
|
| 875 |
+
9100252058,AVENIDA SANT JERONI 3,8105,SANT FOST DE CAMPSENTELLES
|
| 876 |
+
128916,"CALLE VERNEDA 66-70, LOCAL 2",8107,MARTORELLES
|
| 877 |
+
126089,Pla�a Augusta 3,8173,SANT CUGAT DEL VALL�S
|
| 878 |
+
126089,Pla�a Augusta 3,8173,SANT CUGAT DEL VALL�S
|
| 879 |
+
119956,POL. IND. PUIGTIO CARRER RIUDELLOTS,17412,MA�ANET DE LA SELVA
|
| 880 |
+
9100190779,Avinguda de la Llibertat 39,8100,MOLLET DEL VALLES
|
| 881 |
+
9100271543,AVENIDA LLIBERTAT 28,8100,MOLLET DEL VALLES
|
| 882 |
+
9100305166,AVENIDA BURGOS 76,8100,MOLLET DEL VALLES
|
| 883 |
+
9100305167,AVENIDA BADALONA 8,8100,MOLLET DEL VALLES
|
| 884 |
+
9100354651,CALLE MERCAT 7,8100,MOLLET DEL VALLES
|
| 885 |
+
9100517197,CALLE ITALIA 28-30,8100,MOLLET DEL VALLES
|
| 886 |
+
9100519718,AVENIDA LLIBERTAT 24,8100,MOLLET DEL VALL�S
|
| 887 |
+
9100054838,Carrer de Magallanes 16,8100,MOLLET DEL VALLES
|
| 888 |
+
9100055377,RAMBLA BALMES 2,8100,MOLLET DEL VALLES
|
| 889 |
+
9100121732,CALLE ANSELM CLAVE 19-21,8100,MOLLET DEL VALLES
|
| 890 |
+
9100281244,Carrer Can Magre 23,8100,MOLLET DEL VALLES
|
| 891 |
+
9100296646,Pla�a de Prat de la Riba 19,8100,MOLLET DEL VALLES
|
| 892 |
+
9100331748,CALLE ENRIC MORERA 42,8100,MOLLET DEL VALLES
|
| 893 |
+
9100336673,CALLE NICARAGUA 5,8100,MOLLET DEL VALLES
|
| 894 |
+
9100336673,CALLE NICARAGUA 5,8100,MOLLET DEL VALLES
|
| 895 |
+
9100341736,SALVADOR ESPRIU 32,8100,MOLLET DEL VALLES
|
| 896 |
+
9100369998,Avinguda de Jaume I 77,8100,MOLLET DEL VALLES
|
| 897 |
+
9100444956,ANTONI GAUDI 9,8100,MOLLET DEL VALL�S
|
| 898 |
+
9100486027,CALLE GALLECS 17,8100,MOLLET DEL VALL�S
|
| 899 |
+
9100514208,CALLE DE LA JUSTADA 18,8100,MOLLET DEL VALL�S
|
| 900 |
+
9100517477,AVENIDA LLIBERTAT 45,8100,MOLLET DEL VALL�S
|
| 901 |
+
9100520123,AVENIDA LLIBERTAT 4,8100,MOLLET DEL VALL�S
|
| 902 |
+
9100522192,CALLE JOAN MARAGALL 19,8100,MOLLET DEL VALL�S
|
| 903 |
+
9100536443,SANT JORDI 49,8100,MOLLET DEL VALL�S
|
| 904 |
+
9100536466,CALLE ONZE DE SETEMBRE 9-13,8100,MOLLET DEL VALL�S
|
| 905 |
+
9100536469,CALLE SANT VALENTI 1,8100,MOLLET DEL VALL�S
|
| 906 |
+
9100536481,AVENIDA ANTONI GAUDI 8,8100,MOLLET DEL VALL�S
|
| 907 |
+
9100541892,CALLE MOLI DE CAN BASSA S/N,8100,MOLLET DEL VALL�S
|
| 908 |
+
9100567861,CALLE MOLI DE CAN BASSA S/N,8100,MOLLET DEL VALL�S
|
| 909 |
+
9100568458,CALLE MOLI DE CAN BASSA S/N,8100,MOLLET DEL VALL�S
|
| 910 |
+
9100497829,AVENIDA ANTONI GAUDI 19,8100,MOLLET DEL VALL�S
|
| 911 |
+
9100602299,RAMBLA JOSEP TARRADELLAS 1-3,8500,VIC
|
| 912 |
+
9100145802,CALLE DOCTOR JUNYENT 10,8500,VIC
|
| 913 |
+
9100636960,PLAZA SANTS MARTIRS 26,8500,VIC
|
| 914 |
+
9100541928,CALLE SANT ANTONI MARIA CLARET 1,8500,VIC
|
| 915 |
+
9100436371,CALLE DOCTOR JUNYENT S/N,8500,VIC
|
| 916 |
+
9100569086,CALLE PRUIT S/N,8500,VIC
|
| 917 |
+
9100294054,AVENIDA MARTI GENIS I AGUILAR 27,8500,VIC
|
| 918 |
+
9100151045,CALLE MARE DE DEU DE LORDA 2,8500,VIC
|
| 919 |
+
9100135478,AVENIDA PAISOS CATALANS 95,8500,VIC
|
| 920 |
+
9100642662,CALLE PARE GALLISSA 5,8500,VIC
|
| 921 |
+
9100632171,CALLE JAUME I EL CONQUERIDOR 1,8500,VIC
|
| 922 |
+
9100651724,CALLE SOLEDAT 13,8500,VIC
|
| 923 |
+
9100522518,CALLE PERE MARTELL 80,8917,BADALONA
|
| 924 |
+
9100304652,CALLE GIRONA 337,8402,GRANOLLERS
|
| 925 |
+
9100365812,CALLE GIRONA 14,8402,GRANOLLERS
|
| 926 |
+
9100447620,PLAZA JAUME I 15,8402,GRANOLLERS
|
| 927 |
+
9100517443,CALLE LLUIS VIVES 45,8402,GRANOLLERS
|
| 928 |
+
9100520022,"GIRONA, 52",8402,GRANOLLERS
|
| 929 |
+
9100087371,PLAZA CAN BASSA 2,8402,GRANOLLERS
|
| 930 |
+
9100480482,CALLE ESTEVE GARRELL 3,8402,GRANOLLERS
|
| 931 |
+
9100524060,PLAZA FONT VERDA 1,8402,GRANOLLERS
|
| 932 |
+
9100495996,PLAZA PAU CASALS 14,8402,GRANOLLERS
|
| 933 |
+
9100153969,CALLE PRINCEP DE VIANA 8,8401,GRANOLLERS
|
| 934 |
+
9100227012,CALLE CAMP DE LES MORERES 5,8401,GRANOLLERS
|
| 935 |
+
9100310143,CALLE JOAN PERE FONTANELLA 41,8401,GRANOLLERS
|
| 936 |
+
9100338196,AVENIDA ENRIC PRAT DE LA RIBA 77,8401,GRANOLLERS
|
| 937 |
+
9100340260,ESTEVE TERRADES 38,8401,GRANOLLERS
|
| 938 |
+
9100420786,CALLE MARE DE DEU DE MONTSERRAT 56,8401,GRANOLLERS
|
| 939 |
+
9100420786,CALLE MARE DE DEU DE MONTSERRAT 56,8401,GRANOLLERS
|
| 940 |
+
9100445926,CALLE MIQUEL RICOMA 92,8401,GRANOLLERS
|
| 941 |
+
9100506888,CALLE RAFAEL CASANOVA 56,8401,GRANOLLERS
|
| 942 |
+
9100507154,CALLE JOSEP TORRAS I BAGES 50,8401,GRANOLLERS
|
| 943 |
+
9100507921,CALLE ISABEL DE VILLENA 35,8401,GRANOLLERS
|
| 944 |
+
9100513397,CALLE SANT JAUME 32,8401,GRANOLLERS
|
| 945 |
+
9100525754,CALLE TRES TORRES 16,8401,GRANOLLERS
|
| 946 |
+
9100151566,RAFAEL CASANOVAS 50,8401,GRANOLLERS
|
| 947 |
+
9100239658,RAMBLA PAISOS CATALANS 77,8110,MONTCADA I REIXAC
|
| 948 |
+
9100639016,PLAZA DALT VILA 12,8560,MANLLEU
|
| 949 |
+
9100637313,ENRIC DELARIS 28,8560,MANLLEU
|
| 950 |
+
9100574086,CALLE SANTA MARIA DE CORCO 23,8560,MANLLEU
|
| 951 |
+
9100562472,CALLE SOGORB 35-37,8560,MANLLEU
|
| 952 |
+
9100374147,CARRETERA RODA 18,8560,MANLLEU
|
| 953 |
+
9100332707,PLAZA FRA BERNADI 27,8560,MANLLEU
|
| 954 |
+
9100158102,CALLE BUTJOSA 58,8150,PARETS DEL VALLES
|
| 955 |
+
9100158099,CALLE VICTOR CATALA 15,8150,PARETS DEL VALLES
|
| 956 |
+
9100230870,LA SALUT 37 A,8150,PARETS DEL VALLES
|
| 957 |
+
9100239659,D'EN GEGANT 10,8150,PARETS DEL VALLES
|
| 958 |
+
9100529011,CALLE POESIA 7,8150,PARETS DEL VALL�S
|
| 959 |
+
9100169606,PLAZA PAISOS CATALANS 1,8170,MONTORNES DEL VALLES
|
| 960 |
+
9100282679,Avinguda de l'Onze de Setembre 48,8170,MONTORN�S DEL VALL�S
|
| 961 |
+
9100334134,Carrer de Federico Garc�a Lorca 12,8170,MONTORNES DEL VALLES
|
| 962 |
+
9100517175,CALLE GENERAL PRIM 6,8170,MONTORN�S DEL VALL�S
|
| 963 |
+
9100536467,CALLE SANT ISIDRE 16,8170,MONTORN�S DEL VALL�S
|
| 964 |
+
9100542820,Carrer de Jacint Verdaguer 6,8170,MONTORN�S DEL VALL�S
|
| 965 |
+
9100535918,AVENIDA VALLES 546,8227,TERRASSA
|
| 966 |
+
9100041161,CALLE JOSEP TARRADELLAS 28,8160,MONTMELO
|
| 967 |
+
9100041161,CALLE JOSEP TARRADELLAS 28,8160,MONTMELO
|
| 968 |
+
9100161621,Carrer Major 16,8160,MONTMEL�
|
| 969 |
+
9100161621,Carrer Major 16,8160,MONTMEL�
|
| 970 |
+
9100229412,JOSEP TARRADELLAS 16,8160,MONTMELO
|
| 971 |
+
9100443296,PLAZA SANT ISIDRE 11,8160,MONTMELO
|
| 972 |
+
9100443296,PLAZA SANT ISIDRE 11,8160,MONTMELO
|
| 973 |
+
9100512676,CALLE MAJOR 11,8160,MONTMELO
|
| 974 |
+
9100521616,CALLE SANTIAGO RUSI?OL 9,8160,MONTMEL�
|
| 975 |
+
9100565827,CALLE L'ESTANYOL S/N,8559,EL BRULL
|
| 976 |
+
9100556955,PLAZA LA VILA 2,8570,TORELL�
|
| 977 |
+
9100126372,PLAZA BISAURA 2,8580,SANT QUIRZE DE BESORA
|
| 978 |
+
9100627695,AVENIDA SANT LLOREN� S/N,8504,SANT JULI� DE VILATORTA
|
| 979 |
+
9100572378,CALLE GRACIA 4,8573,OR�S
|
| 980 |
+
9100563070,PLAZA JOANOT MARTORELL 1,8570,TORELL�
|
| 981 |
+
9100640157,Carrer dels Marquesos de Palmerol 2,8512,SANT HIP�LIT DE VOLTREG�
|
| 982 |
+
9100635212,CALLE ONZE DE SETEMBRE 29,8508,LES MASIES DE VOLTREG�
|
| 983 |
+
9100634356,CALLE SANT MARTI XIC 6,8508,LES MASIES DE VOLTREG�
|
| 984 |
+
9100420327,CALLE SANT ISIDRE 7,8107,MARTORELLES
|
| 985 |
+
9100075744,CALLE SANT SADURNI 37-39,8430,LA ROCA DEL VALLES
|
| 986 |
+
9100233605,PIERA 53,8107,MARTORELLES
|
| 987 |
+
9100423767,GERONA 191,8400,GRANOLLERS
|
| 988 |
+
9100503009,PASEO PINTOR SERT 19,8120,LA LLAGOSTA
|
| 989 |
+
9100512192,CALLE MATAGALLS 16,8107,MARTORELLES
|
| 990 |
+
9100513427,CALLE MATAGALLS 16,8107,MARTORELLES
|
| 991 |
+
9100514206,AVENIDA SANT MORITZ 15,8185,LLI?A DE VALL
|
| 992 |
+
9100526119,DE LA MUNTANYA 100,8400,GRANOLLERS
|
| 993 |
+
9100536830,CALLE CARGOLS 30,8185,LLI�� DE VALL
|
| 994 |
+
9100420785,AVENIDA SANT JULIA 198,8403,GRANOLLERS
|
| 995 |
+
9100497770,CALLE GIRONA 34,8521,LES FRANQUESES DEL VALLES
|
| 996 |
+
9100556483,CALLE JAUME I 22,8510,RODA DE TER
|
| 997 |
+
9100300991,L'ALIAN�A 20,8186,LLI�A D'AMUNT
|
| 998 |
+
9100120799,CALLE RAMON LLULL 17,8400,GRANOLLERS
|
| 999 |
+
9100122840,SANT ESTEVE 59,8400,GRANOLLERS
|
| 1000 |
+
9100139584,BAILEN 31,8430,ROCA DEL VALLES (LA)
|
| 1001 |
+
9100140860,CR FRANCESC MACIA 261 -,8400,GRANOLLERS
|
| 1002 |
+
9100283498,PASEO MUNTANYA 13-15,8400,GRANOLLERS
|
| 1003 |
+
9100522267,AVENIDA RAFAEL CASANOVA 12,8100,MOLLET DEL VALL�S
|
| 1004 |
+
9100054744,RONDA ORIENT 14,8100,MOLLET DEL VALLES
|
| 1005 |
+
9100054778,RAMBLA POMPEU FABRA 38,8100,MOLLET DEL VALLES
|
| 1006 |
+
9100054857,Avinguda de Burgos 66,8100,MOLLET DEL VALLES
|
| 1007 |
+
9100055224,RAMBLA NOVA 71,8100,MOLLET DEL VALLES
|
| 1008 |
+
9100055497,RAMBLA POMPEU FABRA 20,8100,MOLLET DEL VALLES
|
| 1009 |
+
9100055508,Avinguda de la Llibertat 2,8100,MOLLET DEL VALLES
|
| 1010 |
+
9100056293,PASAJE SANT MIQUEL 16,8100,MOLLET DEL VALLES
|
| 1011 |
+
9100075499,Carrer d'Alexander Fleming 2,8100,MOLLET DEL VALLES
|
| 1012 |
+
9100403364,RAMBLA NOVA 93,8100,MOLLET DEL VALLES
|
| 1013 |
+
9100403364,RAMBLA NOVA 93,8100,MOLLET DEL VALLES
|
| 1014 |
+
9100043749,RONDA 9,8100,MOLLET DEL VALLES
|
| 1015 |
+
9100054481,CALLE CARME 22-24,8100,MOLLET DEL VALLES
|
| 1016 |
+
9100054565,AVENIDA JAUME I 207,8100,MOLLET DEL VALLES
|
| 1017 |
+
9100054732,Carrer Miguel de Cervantes 19,8100,MOLLET DEL VALLES
|
| 1018 |
+
9100054873,Carrer de Sant Joan 26,8100,MOLLET DEL VALLES
|
| 1019 |
+
9100055568,Avinguda de Jaume I 57,8100,MOLLET DEL VALLES
|
| 1020 |
+
9100184449,PLAZA PRAT DE LA RIBA 3,8100,MOLLET DEL VALLES
|
| 1021 |
+
9100366732,Carrer de Can Pantiquet 36,8100,MOLLET DEL VALLES
|
| 1022 |
+
9100366733,CALLE CAN MOLLET 30,8100,MOLLET DEL VALLES
|
| 1023 |
+
9100553669,Carrer de Francesc Ferrer i Gu�r 15,8100,MOLLET DEL VALLES
|
| 1024 |
+
9100629788,CALLE BERENGUER III 118,8100,MOLLET DEL VALLES
|
| 1025 |
+
9100655002,CALLE PINEDA FOSCA 2,8100,MOLLET DEL VALLES
|
| 1026 |
+
9100655372,Carrer de Can Pantiquet 18,8100,MOLLET DEL VALLES
|
| 1027 |
+
9100680393,Carrer de Ramon Casas 9,8100,MOLLET DEL VALLES
|
| 1028 |
+
9100530919,CAN MAGRE 23,8100,MOLLET DEL VALL�S
|
| 1029 |
+
9100054639,CALLE GAIETA VINZIA 60,8100,MOLLET DEL VALLES
|
| 1030 |
+
9100054644,CALLE ESGLESIA 9,8100,MOLLET DEL VALLES
|
| 1031 |
+
9100054973,CALLE MIGUEL DE CERVANTES 19-23,8100,MOLLET DEL VALLES
|
| 1032 |
+
9100056047,CALLE FRANCESC LAYRET 18,8100,MOLLET DEL VALLES
|
| 1033 |
+
9100733609,CAMINO CAN VILA S/N,8100,MOLLET DEL VALLES
|
| 1034 |
+
9100054513,CALLE JOAN MARAGALL S/N,8100,MOLLET DEL VALLES
|
| 1035 |
+
9100609140,CALLE SANT FRANCESC 110,8500,VIC
|
| 1036 |
+
9100730075,Pla�a de la Catedral 6,8500,VIC
|
| 1037 |
+
9100259166,CALLE LLEIDA S/N,8500,VIC
|
| 1038 |
+
9100443450,CALLE PEROT ROCAGUINARDA 5,8500,VIC
|
| 1039 |
+
9100523487,CARRETERA B-521 6,8500,VIC
|
| 1040 |
+
9100755440,Carrer Eix Onze de Setembre 11,8500,VIC
|
| 1041 |
+
9100759507,PLA�A MAJOR 1,8500,VIC
|
| 1042 |
+
9100678069,Carrer d�en Perot Rocaguinarda 29,8500,VIC
|
| 1043 |
+
9100678719,Carrer de la Fusina 34,8500,VIC
|
| 1044 |
+
9100610240,CALLE SANTIAGO RUSI#OL 1,8500,VIC
|
| 1045 |
+
9100627975,CALLE NORD 1,8500,VIC
|
| 1046 |
+
9100564797,CALLE JACINT VERDAGUER 5,8500,VIC
|
| 1047 |
+
9100466625,CALLE SANT FIDEL 3,8500,VIC
|
| 1048 |
+
9100134828,CALLE RIERA 13,8500,VIC
|
| 1049 |
+
9100119869,CALLE RAMADA 4,8500,VIC
|
| 1050 |
+
9100119869,CALLE RAMADA 4,8500,VIC
|
| 1051 |
+
9100127992,CALLE PRAT D'EN GALLINERS S/N,8500,VIC
|
| 1052 |
+
136675,"CTRA. DE SANT HIP�LIT, 53 (NAU 4)",8500,VIC
|
| 1053 |
+
136675,"CTRA. DE SANT HIP�LIT, 53 (NAU 4)",8500,VIC
|
| 1054 |
+
9100040724,CALLE ANSELM CLAVE 1,8402,GRANOLLERS
|
| 1055 |
+
9100041355,CALLE GIRONA 301,8402,GRANOLLERS
|
| 1056 |
+
9100041765,CALLE EMILI BOTEY ALSINA 14,8402,GRANOLLERS
|
| 1057 |
+
9100041774,Girona 114,8402,GRANOLLERS
|
| 1058 |
+
9100041990,CALLE JOAN PRIM 114,8402,GRANOLLERS
|
| 1059 |
+
9100042037,AVENIDA PARC S/N,8402,GRANOLLERS
|
| 1060 |
+
9100042037,AVENIDA PARC S/N,8402,GRANOLLERS
|
| 1061 |
+
9100042491,PLAZA CORONA 14,8402,GRANOLLERS
|
| 1062 |
+
9100043799,CALLE FRANCESC RIBAS 1,8402,GRANOLLERS
|
| 1063 |
+
9100044409,GERONA 21,8402,GRANOLLERS
|
| 1064 |
+
9100076613,PLAZA CORONA 9,8402,GRANOLLERS
|
| 1065 |
+
9100134890,PASEO MUNTANYA 70,8402,GRANOLLERS
|
| 1066 |
+
9100264654,AVENIDA SANT ESTEVE 59,8402,GRANOLLERS
|
| 1067 |
+
9100521595,CALLE JOAN PRIM 182,8402,GRANOLLERS
|
| 1068 |
+
9100521678,CALLE JOANOT MARTORELL 272,8402,GRANOLLERS
|
| 1069 |
+
9100521678,CALLE JOANOT MARTORELL 272,8402,GRANOLLERS
|
| 1070 |
+
9100633076,CALLE ALFONS IV 85,8402,GRANOLLERS
|
| 1071 |
+
9100755674,Passeig de la Muntanya 17,8402,GRANOLLERS
|
| 1072 |
+
9100761572,Carrer d'Emili Botey Alsina 22,8402,GRANOLLERS
|
| 1073 |
+
9100765851,Carrer d' Esteve Garrell 8,8402,GRANOLLERS
|
| 1074 |
+
9100042261,CL GARBI N�M. 3 -,8150,PARETS DEL VALLES
|
| 1075 |
+
9100041053,Carrer de Prat de la Riba 31,8150,PARETS DEL VALLES
|
| 1076 |
+
9100041393,CATALUNYA 102,8150,PARETS DEL VALLES
|
| 1077 |
+
9100041543,Avinguda de Catalunya 92,8150,PARETS DEL VALLES
|
| 1078 |
+
9100041555,Carrer del Garb� 13,8150,PARETS DEL VALLES
|
| 1079 |
+
9100041840,Carrer d'Arag� 2,8150,PARETS DEL VALLES
|
| 1080 |
+
9100054649,TENES S/N PARC LEVANT -,8150,PARETS DEL VALLES
|
| 1081 |
+
9100150711,PAU CASALS 33,8150,PARETS DEL VALLES
|
| 1082 |
+
9100366724,CALLE SALVADOR ESPRIU 10,8150,PARETS DEL VALLES
|
| 1083 |
+
9100373488,AVENIDA PEDRA DEL DIABLE 9,8150,PARETS DEL VALLES
|
| 1084 |
+
9100756811,Avinguda de la Pedra del Diable 9,8150,PARETS DEL VALL�S
|
| 1085 |
+
9100043208,CL LLOBREGAT 2 -,8150,PARETS DEL VALLES
|
| 1086 |
+
9100042829,CALLE MALLORCA 10,8401,GRANOLLERS
|
| 1087 |
+
9100042612,RAMON LLULL 18,8401,GRANOLLERS
|
| 1088 |
+
9100043364,MANEL MUNTANYA 5,8401,GRANOLLERS
|
| 1089 |
+
9100043381,CALLE ANNIBAL 59,8401,GRANOLLERS
|
| 1090 |
+
9100059282,MAGALLANES SN,8401,GRANOLLERS
|
| 1091 |
+
9100121164,CAMP DE LES MORERES 13,8401,GRANOLLERS
|
| 1092 |
+
9100373640,PLAZA JOSEP MALUQUER I SALVADOR 17,8401,GRANOLLERS
|
| 1093 |
+
9100373641,PLAZA JOSEP MALUQUER I SALVADOR 17,8401,GRANOLLERS
|
| 1094 |
+
9100047575,Carrer de Vill� 79,8173,SANT CUGAT DEL VALL�S
|
| 1095 |
+
9100043198,BLAS INFANTE 14-16,8170,MONTORNES DEL VALLES
|
| 1096 |
+
9100043408,ONCE DE SEPTIEMBRE 51,8170,MONTORNES DEL VALLES
|
| 1097 |
+
9100043540,MAJOR 28,8170,MONTORNES DEL VALLES
|
| 1098 |
+
9100043719,LLIBERTAT 12,8170,MONTORNES DEL VALLES
|
| 1099 |
+
9100044366,FEDERICO GARCIA LORCA 21,8170,MONTORNES DEL VALLES
|
| 1100 |
+
9100055329,SUR 5,8170,MONTORNES DEL VALLES
|
| 1101 |
+
9100366735,AVENIDA L'ONZE DE SETEMBRE 29,8170,MONTORNES DEL VALLES
|
| 1102 |
+
9100468163,MAJOR 45,8170,MONTORN�S DEL VALL�S
|
| 1103 |
+
9100044480,CAN PARELLADA SN,8170,MONTORN�S DEL VALL�S
|
| 1104 |
+
9100563722,BLAS INFANTE 22,8170,MONTORN�S DEL VALL�S
|
| 1105 |
+
9100725417,Pla�a de Pau Picasso 1,8170,MONTORN�S DEL VALL�S
|
| 1106 |
+
9100750960,Carrer de Federico Garc�a Lorca 25,8170,MONTORN�S DEL VALL�S
|
| 1107 |
+
9100373506,PLAZA JOAQUIM MIR 1,8170,MONTORNES DEL VALLES
|
| 1108 |
+
9100373422,PLAZA DE LA CASERNA S/N,8400,GRANOLLERS
|
| 1109 |
+
9100040748,BARCELONES 9,8520,CORRO D�AVALL
|
| 1110 |
+
9100040967,MONTSERRAT 36,8185,LLI�A DE VALL
|
| 1111 |
+
9100041059,VALLES 1,8520,CORRO D�AVALL
|
| 1112 |
+
9100041485,Carrer del Pintor Mir 2,8160,MONTMEL�
|
| 1113 |
+
9100041564,"CR JOAN PRIM, 90 -",8400,GRANOLLERS
|
| 1114 |
+
9100041740,FOLCH I TORRES 95,8186,LLI�A D�AMUNT
|
| 1115 |
+
9100042249,JOANOT MARTORELL 1,8400,GRANOLLERS
|
| 1116 |
+
9100042500,"CR SANT ROC, 5 -",8400,GRANOLLERS
|
| 1117 |
+
9100042942,DE LES HORTES 2,8400,GRANOLLERS
|
| 1118 |
+
9100043107,CR GRANOLLERS SABADELL KM. 13 -,8185,LLI�A DE VALL
|
| 1119 |
+
9100043454,CALLE SANTA ELISABETH 9,8400,GRANOLLERS
|
| 1120 |
+
9100043839,GRAN 3,8160,MONTMELO
|
| 1121 |
+
9100043866,GIRONA 210,8400,GRANOLLERS
|
| 1122 |
+
9100044089,Avinguda Cantallops 14,8185,LLI�� DE VALL
|
| 1123 |
+
9100044243,VALENCIA 60,8430,ROCA DEL VALLES (LA)
|
| 1124 |
+
9100044326,PASEO ANDALUSIA 103,8521,BELLAVISTA
|
| 1125 |
+
9100044359,Passeig de Sant Valeri� 171,8186,LLI�� D'AMUNT
|
| 1126 |
+
9100044399,ESPANYA 8,8520,FRANQUESES DEL VALLES (LES)
|
| 1127 |
+
9100044419,Carretera Parets a Bigues 8,8186,LLI�� D'AMUNT
|
| 1128 |
+
9100044419,Carretera Parets a Bigues 8,8186,LLI�� D'AMUNT
|
| 1129 |
+
9100041886,CALLE JAUME GINESTI 1,8410,VILANOVA DEL VALLES
|
| 1130 |
+
9100044099,CARRETERA MONTMELO 120,8403,GRANOLLERS
|
| 1131 |
+
9100110789,Carrer de Joanot Martorell 121,8403,GRANOLLERS
|
| 1132 |
+
9100150866,MASIA CAN MALE S/N,8186,LLI�A D'AMUNT
|
| 1133 |
+
9100614225,JAUME I 14,8507,SANTA EUG�NIA DE BERGA
|
| 1134 |
+
9100597250,PLAZA AJUNTAMENT S/N,8522,MALLA
|
| 1135 |
+
9100590492,CALLE PUIG 2,8505,SANTA EUL�LIA DE RIUPRIMER
|
| 1136 |
+
9100169481,CALLE MAJOR 33,8505,SANTA EUL�LIA DE RIUPRIMER
|
| 1137 |
+
9100130145,CALLE ELS PIRINEUS 21,8503,GURB
|
| 1138 |
+
9100473447,CARRETERA PUIG-L'AGULLA S/N,8504,SANT JULI� DE VILATORTA
|
| 1139 |
+
9100627170,AUTOVIA C-17 KM 81,8580,SANT QUIRZE DE BESORA
|
| 1140 |
+
9100568729,AVENIDA JOAN MARAGALL 2,8572,SANT PERE DE TORELL�
|
| 1141 |
+
9100461830,Carrer de Canig� 41,8570,TORELL�
|
| 1142 |
+
9100252094,PLAZA JOANOT MARTORELL 19,8570,TORELL�
|
| 1143 |
+
9100139252,PLAZA NOVA 14,8570,TORELL�
|
| 1144 |
+
9100129209,PLAZA LA SARDANA 6,8570,TORELL�
|
| 1145 |
+
9100126369,CALLE ELS ESTUDIS 13,8570,TORELL�
|
| 1146 |
+
9100410610,PLAZA VELLA 3,8570,TORELL�
|
| 1147 |
+
9100615441,Passeig de Sant Joan 66,8560,MANLLEU
|
| 1148 |
+
9100606155,AVENIDA ROMA 104,8560,MANLLEU
|
| 1149 |
+
9100160054,CALLE SANT JOSEP 2,8510,LES MASIES DE RODA
|
| 1150 |
+
9100609846,CALLE NURIA 14,8570,TORELL�
|
| 1151 |
+
9100058035,CALLE REC D'ACUMULADA 9,8519,FOLGUEROLES
|
| 1152 |
+
9100056628,CARRETERA VIDRA KM 1,8584,SANTA MARIA DE BESORA
|
| 1153 |
+
9100043594,MATARO A GRANOLLERS S/N,8430,LA ROCA DEL VALL�S
|
| 1154 |
+
9100420424,DEL PILAR 4,8185,LLI�� DE VALL
|
| 1155 |
+
9100536947,AVENIDA SANT MORITZ 15,8185,LLI�� DE VALL
|
| 1156 |
+
9100540250,CALLE MAS MORENETA S/N,8160,MONTMEL�
|
| 1157 |
+
9100540250,CALLE MAS MORENETA S/N,8160,MONTMEL�
|
| 1158 |
+
9100542312,CALLE MIQUEL BIADA 1,8105,SANT FOST DE CAMPSENTELLES
|
| 1159 |
+
9100547001,PAISOS CATALANS SN,8186,LLI�A D�AMUNT
|
| 1160 |
+
9100563721,MAJOR 1,8160,MONTMEL�
|
| 1161 |
+
9100574583,Carrer l'Alian�a 17,8186,LLI�� D'AMUNT
|
| 1162 |
+
9100576639,Carrer l'Alian�a 17,8186,LLI�� D'AMUNT
|
| 1163 |
+
9100576643,CAN ROURE 18,8186,LLI�A D�AMUNT
|
| 1164 |
+
9100373403,CALLE LA FLORIDA 22,8129,LA LLAGOSTA
|
| 1165 |
+
9100373428,CALLE GIRONA 20,8120,LA LLAGOSTA
|
| 1166 |
+
9100041363,CAMI MAS MORENETA SN,8160,MONTMELO
|
| 1167 |
+
9100041363,CAMI MAS MORENETA SN,8160,MONTMELO
|
| 1168 |
+
9100041363,CAMI MAS MORENETA SN,8160,MONTMELO
|
| 1169 |
+
9100041363,CAMI MAS MORENETA SN,8160,MONTMELO
|
| 1170 |
+
9100242346,CALLE CANIGO 1,8507,SANTA EUG�NIA DE BERGA
|
| 1171 |
+
9100041442,Passeig de Can Salgot 29,8186,LLI�� D'AMUNT
|
| 1172 |
+
9100041618,PAISOS CATALANS SN,8186,LLI�A D�AMUNT
|
| 1173 |
+
9100044043,CARRETERA C-155 KM 14,8185,LLI�A DE VALL
|
| 1174 |
+
9100044130,ENGINYER 7-9,8400,GRANOLLERS
|
| 1175 |
+
9100044391,BARCELONA 110,8521,LES FRANQUESES DEL VALLES
|
| 1176 |
+
9100120687,CAMINO ELS CASTANYERS S/N,8105,SANT FOST DE CAMPSENTELLES
|
| 1177 |
+
9100043706,DE LA NORIA 15,8430,"ROCA DEL VALLES, LA"
|
| 1178 |
+
9100716110,Carrer de Cabrera 1,8570,TORELL�
|
| 1179 |
+
9100687352,Carrer de Sant Miquel 1,8570,TORELL�
|
| 1180 |
+
9100744181,Carrer Baronia de Montbui 42,8186,LLI�� D'AMUNT
|
| 1181 |
+
9100661607,CARRETERA RUBI 76-78,8190,SANT CUGAT DEL VALL�S
|
| 1182 |
+
9100627485,Avinguda Montserrat 12,8185,LLI�� DE VALL
|
| 1183 |
+
9100740458,AVINGUDA D'ANGEL SALLENT 146,8224,TERRASSA
|
| 1184 |
+
9100749021,Avinguda de Pompeu Fabra 24,8160,MONTMEL�
|
| 1185 |
+
9100755589,Carrer Josep Tarradellas 31,8187,SANTA EUL�LIA DE RON�ANA
|
| 1186 |
+
9100758360,Carrer de l'Alzina 29,8186,LLI�� D'AMUNT
|
| 1187 |
+
9100667648,Carrer Gran 20,8506,CALLDETENES
|
| 1188 |
+
9100681544,PASEO ANDALUSIA 15-17,8521,LES FRANQUESES DEL VALLES
|
| 1189 |
+
9100743215,Carretera d'Arb�cies 21,8507,SANTA EUG�NIA DE BERGA
|
| 1190 |
+
9100736229,Carrer del Freixa 1,17864,SANT PAU DE SEG�RIES
|
| 1191 |
+
9100736213,CARRETERA GIV-5262 S/N,17534,RIBES DE FRESER
|
| 1192 |
+
119646,"ROSELLON, 515",8025,BARCELONA
|
| 1193 |
+
119642,"ROSELLON, 515",8025,BARCELONA
|
| 1194 |
+
9100097548,CALLE JACINT VERDAGUER 3,8170,MONTORNES DEL VALLES
|
| 1195 |
+
9100385693,Carrer Cam� Vell de Vic 16,8519,FOLGUEROLES
|
| 1196 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1197 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1198 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1199 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1200 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1201 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1202 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1203 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1204 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1205 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1206 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1207 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1208 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1209 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1210 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1211 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1212 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1213 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1214 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1215 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1216 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1217 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1218 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1219 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1220 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1221 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1222 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1223 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1224 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1225 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1226 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1227 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1228 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1229 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1230 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1231 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1232 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1233 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1234 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1235 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1236 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1237 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1238 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1239 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1240 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1241 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1242 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1243 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1244 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1245 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1246 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1247 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1248 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1249 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1250 |
+
100423,"C/ PRAT DE LA RIBA, 13",8830,SANT BOI DE LLOBREGAT
|
| 1251 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1252 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1253 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1254 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1255 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1256 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1257 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1258 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1259 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1260 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1261 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1262 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1263 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1264 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1265 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1266 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1267 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1268 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1269 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1270 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1271 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1272 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1273 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1274 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1275 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1276 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1277 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1278 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1279 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1280 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1281 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1282 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1283 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1284 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1285 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1286 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1287 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1288 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1289 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1290 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1291 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1292 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1293 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1294 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1295 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1296 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1297 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1298 |
+
128249,CALLE ROSSELLO 515,8025,BARCELONA
|
| 1299 |
+
128051,RAMBLA NOVA 10,8100,MOLLET DEL VALL�S
|
| 1300 |
+
9100498065,Avinguda de la Llibertat 20,8100,MOLLET DEL VALLES
|
| 1301 |
+
9100501076,PLAZA PORXADA 3,8401,GRANOLLERS
|
| 1302 |
+
9100502995,CALLE FRANSCESC RIBAS 64,8402,GRANOLLERS
|
| 1303 |
+
9100503205,CALLE ENRIC MORERA 27,8100,MOLLET DEL VALL�S
|
| 1304 |
+
9100503664,CALLE L'ALIAN�A 7,8186,LLI�� D'AMUNT
|
| 1305 |
+
9100529176,PLAZA ESPANYA 10,8521,BELLAVISTA
|
| 1306 |
+
9100529181,Avinguda d'Antoni Gaud� 3,8100,MOLLET DEL VALLES
|
| 1307 |
+
9100529300,CALLE PALAU D'AMETLLA 15,8170,MONTORN�S DEL VALL�S
|
| 1308 |
+
9100535260,CALLE JOSEP TORRAS I BAGES 50,8401,GRANOLLERS
|
| 1309 |
+
9100536475,RAMBLA NOVA 33-35,8100,MOLLET DEL VALL�S
|
| 1310 |
+
9100536484,AVENIDA L'ALZINAR 11,8033,BARCELONA
|
| 1311 |
+
9100536762,CALLE MAJOR 66,8170,MONTORN�S DEL VALL�S
|
| 1312 |
+
9100537742,Carrer Major 51,8160,MONTMEL�
|
| 1313 |
+
9100538933,CALLE GIRONA 12,8402,GRANOLLERS
|
| 1314 |
+
9100539485,Rambla Nova 3,8100,MOLLET DEL VALLES
|
| 1315 |
+
9100540782,AVENIDA RIVOLI 22,8100,MOLLET DEL VALL�S
|
| 1316 |
+
9100541197,Carrer de Can Puig S/N,8107,MARTORELLES
|
| 1317 |
+
9100542069,PERE RAMON S/N (DARRERA MERCAT S/N,8100,MOLLET DEL VALL�S
|
| 1318 |
+
9100544203,Carrer de Borrell 16,8100,MOLLET DEL VALLES
|
| 1319 |
+
9100544826,CALLE FRANCESC LAYRET 28,8100,MOLLET DEL VALL�S
|
| 1320 |
+
9100545628,BERENGUER III 71,8100,MOLLET DEL VALL�S
|
| 1321 |
+
9100547290,CALLE RIBERA D'EBRE 81,8186,LLI�� D'AMUNT
|
| 1322 |
+
9100550379,CALLE CAMP DE LES MORERES 2,8401,GRANOLLERS
|
| 1323 |
+
9100552330,Carrer d'Anselm Clav� 13,8100,MOLLET DEL VALLES
|
| 1324 |
+
9100553092,Carrer de Minetes 29,8402,GRANOLLERS
|
| 1325 |
+
9100553915,ZONA ESPORTIVA SN,8520,FRANQUESES DEL VALLES (LES)
|
| 1326 |
+
9100554598,AVENIDA SANT ESTEVE 3,8402,GRANOLLERS
|
| 1327 |
+
9100556448,CALLE BERENGUER III 168,8100,MOLLET DEL VALLES
|
| 1328 |
+
9100556449,AVENIDA ANTONI GAUDI 57,8100,MOLLET DEL VALL�S
|
| 1329 |
+
9100558804,PLAZA CORONA 4,8402,GRANOLLERS
|
| 1330 |
+
9100559291,CALLE CASTELLTER�OL 1-3,8401,GRANOLLERS
|
| 1331 |
+
9100560544,PASEO JOAN MIRO 8,8185,PARETS DEL VALLES
|
| 1332 |
+
9100562973,CALLE CASTELLTER�OL 15,8401,GRANOLLERS
|
| 1333 |
+
9100564349,Carrer de la Carrerada 5,8107,MARTORELLES
|
| 1334 |
+
9100566100,AVENIDA LLUIS COMPANYS 39,8150,PARETS DEL VALL�S
|
| 1335 |
+
9100566254,RAMBLA NOVA 29-31,8100,MOLLET DEL VALL�S
|
| 1336 |
+
9100568067,CALLE MATARO 6,8403,GRANOLLERS
|
| 1337 |
+
9100568310,PLAZA MAJOR 24,8100,MOLLET DEL VALLES
|
| 1338 |
+
9100569006,AVENIDA PEDRA DEL DIABLE 6,8150,PARETS DEL VALLES
|
| 1339 |
+
9100574514,AVENIDA CATALUNYA 24,8150,PARETS DEL VALL�S
|
| 1340 |
+
9100576264,CALLE GALLECS 50,8100,MOLLET DEL VALL�S
|
| 1341 |
+
9100579782,CALLE MAS ESTAPER 8,8400,GRANOLLERS
|
| 1342 |
+
9100580278,GERONA 29,8400,GRANOLLERS
|
| 1343 |
+
9100582978,"RAMBLA NOVA, 69 -",8100,MOLLET DEL VALL�S
|
| 1344 |
+
9100583212,CALLE JOAQUIM MIR 88,8100,MOLLET DEL VALLES
|
| 1345 |
+
9100591127,AVENIDA DE LA LLIBERTAD 53,8100,MOLLET DEL VALL�S
|
| 1346 |
+
9100591222,CALLE ANSELM CLAVE 2,8186,LLI#A D'AMUNT
|
| 1347 |
+
9100591493,PASEO FLUVIAL S/N,8150,PARETS DEL VALL�S
|
| 1348 |
+
9100591545,PLAZA LA FONT 2,8186,LLI�� D'AMUNT
|
| 1349 |
+
9100596041,Pla�a de Prat de la Riba 18,8100,MOLLET DEL VALLES
|
| 1350 |
+
9100596801,CALLE MOLI DE CAN BASSA S/N,8100,MOLLET DEL VALL�S
|
| 1351 |
+
9100597581,AVENIDA CATALUNYA 14,8185,LLI�A DE VALL
|
| 1352 |
+
9100601682,BLAS INFANTE 15,8170,MONTORN�S DEL VALL�S
|
| 1353 |
+
9100601980,CALLE SANT ROC 17,8401,GRANOLLERS
|
| 1354 |
+
9100602372,CARRETERA CALDES 151,8186,LLI�A D'AMUNT
|
| 1355 |
+
9100603628,PASEO MUNTANYA 90,8402,GRANOLLERS
|
| 1356 |
+
9100607321,AVENIDA CALDES 27,8100,MOLLET DEL VALLES
|
| 1357 |
+
9100607527,PLAZA CAN MONIC 6,8402,GRANOLLERS
|
| 1358 |
+
9100645059,ESTACIO 2,8500,VIC
|
| 1359 |
+
9100645059,ESTACIO 2,8500,VIC
|
| 1360 |
+
9100250629,CALLE GOMBAU DE BESORA 1,8580,SANT QUIRZE DE BESORA
|
| 1361 |
+
9100495381,AVENIDA EUROPA 21,8401,GRANOLLERS
|
| 1362 |
+
9100264285,DEL CALDERI S/N,8100,MOLLET DEL VALLES
|
| 1363 |
+
9100264285,DEL CALDERI S/N,8100,MOLLET DEL VALLES
|
| 1364 |
+
9100264285,DEL CALDERI S/N,8100,MOLLET DEL VALLES
|
| 1365 |
+
9100264285,DEL CALDERI S/N,8100,MOLLET DEL VALLES
|
| 1366 |
+
9100663630,Carrer de la Riera 25,8500,VIC
|
| 1367 |
+
9100722247,Carretera d'Olot 140,8560,MANLLEU
|
| 1368 |
+
9100569087,CALLE PRUIT S/N,8500,VIC
|
| 1369 |
+
9100749940,CALLE HORTA 11,8107,MARTORELLES
|
firestore_writer.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Push solver output into Firestore so the frontend reads it live.
|
| 2 |
+
|
| 3 |
+
Document shape:
|
| 4 |
+
|
| 5 |
+
routes/{driverId}
|
| 6 |
+
driver_id, truck_id,
|
| 7 |
+
truck_layout {rows, cols} pallet floor grid
|
| 8 |
+
item_grid {L, W, H} lattice cells (cube_size_m per van)
|
| 9 |
+
items [{position {x,y,z},
|
| 10 |
+
shape {w_x, w_y, w_z},
|
| 11 |
+
stop_index, product_id, is_returnable}]
|
| 12 |
+
points [{lat, lng, address?}] ordered route (depot at index 0)
|
| 13 |
+
pallets [{row, col, products[]}] legacy floor-only view
|
| 14 |
+
deliveries [{pallet_positions[]}] aligned with points
|
| 15 |
+
windows [{start, end}]
|
| 16 |
+
service_times [number] minutes per stop
|
| 17 |
+
delivery_status, status
|
| 18 |
+
|
| 19 |
+
The Firebase Admin app is initialised lazily so the FastAPI process starts
|
| 20 |
+
fine without credentials (e.g. local solver-only dev). When the
|
| 21 |
+
`seed/service-account.json` is missing we log a warning and noop on writes.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import json
|
| 27 |
+
import os
|
| 28 |
+
from pathlib import Path
|
| 29 |
+
from typing import Optional
|
| 30 |
+
|
| 31 |
+
_BASE = Path(__file__).resolve().parent
|
| 32 |
+
_REPO = _BASE.parent
|
| 33 |
+
_DEFAULT_CRED = _REPO / "seed" / "service-account.json"
|
| 34 |
+
|
| 35 |
+
_db = None
|
| 36 |
+
_init_attempted = False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _try_init() -> None:
|
| 40 |
+
"""Lazy init. Credential precedence:
|
| 41 |
+
1. FIREBASE_SERVICE_ACCOUNT_JSON — full JSON inline (best for serverless secrets)
|
| 42 |
+
2. FIREBASE_SERVICE_ACCOUNT — path to a service-account.json on disk
|
| 43 |
+
3. seed/service-account.json — legacy default for local dev
|
| 44 |
+
"""
|
| 45 |
+
global _db, _init_attempted
|
| 46 |
+
if _init_attempted:
|
| 47 |
+
return
|
| 48 |
+
_init_attempted = True
|
| 49 |
+
|
| 50 |
+
try:
|
| 51 |
+
import firebase_admin
|
| 52 |
+
from firebase_admin import credentials, firestore
|
| 53 |
+
except ImportError as exc:
|
| 54 |
+
print(f"[firestore_writer] firebase_admin missing: {exc}")
|
| 55 |
+
return
|
| 56 |
+
|
| 57 |
+
cred = None
|
| 58 |
+
inline = os.environ.get("FIREBASE_SERVICE_ACCOUNT_JSON")
|
| 59 |
+
if inline:
|
| 60 |
+
try:
|
| 61 |
+
cred = credentials.Certificate(json.loads(inline))
|
| 62 |
+
print("[firestore_writer] loaded credentials from FIREBASE_SERVICE_ACCOUNT_JSON")
|
| 63 |
+
except Exception as exc: # noqa: BLE001
|
| 64 |
+
print(f"[firestore_writer] FIREBASE_SERVICE_ACCOUNT_JSON parse failed: {exc}")
|
| 65 |
+
|
| 66 |
+
if cred is None:
|
| 67 |
+
cred_path = Path(os.environ.get("FIREBASE_SERVICE_ACCOUNT", _DEFAULT_CRED))
|
| 68 |
+
if not cred_path.exists():
|
| 69 |
+
print(f"[firestore_writer] no service account (env or {cred_path}) — writes disabled")
|
| 70 |
+
return
|
| 71 |
+
try:
|
| 72 |
+
cred = credentials.Certificate(str(cred_path))
|
| 73 |
+
print(f"[firestore_writer] loaded credentials from {cred_path}")
|
| 74 |
+
except Exception as exc: # noqa: BLE001
|
| 75 |
+
print(f"[firestore_writer] cert load failed: {exc}")
|
| 76 |
+
return
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
if not firebase_admin._apps:
|
| 80 |
+
firebase_admin.initialize_app(cred)
|
| 81 |
+
_db = firestore.client()
|
| 82 |
+
print("[firestore_writer] firestore client initialised")
|
| 83 |
+
except Exception as exc: # noqa: BLE001
|
| 84 |
+
print(f"[firestore_writer] init failed: {exc}")
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def is_enabled() -> bool:
|
| 88 |
+
_try_init()
|
| 89 |
+
return _db is not None
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def _hm(s: int) -> str:
|
| 93 |
+
h, m = divmod(int(s) // 60, 60)
|
| 94 |
+
return f"{h:02d}:{m:02d}"
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def _van_layout(van_type: str) -> dict:
|
| 98 |
+
"""Pallet floor layout (rows × cols) for the visual grid."""
|
| 99 |
+
return {"6_pallets": {"rows": 2, "cols": 3}, "8_pallets": {"rows": 2, "cols": 4}}.get(
|
| 100 |
+
van_type, {"rows": 2, "cols": 3}
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _van_lattice(van_type: str) -> tuple[int, int, int]:
|
| 105 |
+
"""Lattice dimensions in cells: each axis is van_dim_m / cube_size_m.
|
| 106 |
+
|
| 107 |
+
cube_size_m and physical interior dimensions both live in vans.json — keep
|
| 108 |
+
the source of truth there. Returns (L, W, H) where L is along the truck
|
| 109 |
+
length, W its depth, H its stacking height.
|
| 110 |
+
"""
|
| 111 |
+
data = json.loads((_BASE / "vans.json").read_text(encoding="utf-8"))
|
| 112 |
+
spec = next((v for v in data["van_types"] if v["type"] == van_type), None)
|
| 113 |
+
if spec is None:
|
| 114 |
+
raise KeyError(f"unknown van_type: {van_type}")
|
| 115 |
+
cube = spec["cube_size_m"]
|
| 116 |
+
return (
|
| 117 |
+
int(round(spec["length_m"] / cube)),
|
| 118 |
+
int(round(spec["width_m"] / cube)),
|
| 119 |
+
int(round(spec["height_m"] / cube)),
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def _compute_item_layout(
|
| 124 |
+
L: int, W: int, H: int,
|
| 125 |
+
deliveries_per_stop: list[tuple[int, list[str]]],
|
| 126 |
+
products: dict,
|
| 127 |
+
) -> tuple[list[dict], dict]:
|
| 128 |
+
"""Run SmartTruckOptimizer3D with real product shapes. Returns one entry
|
| 129 |
+
per *item* (not per cell): each carries an anchor `position` + box `shape`
|
| 130 |
+
so the frontend can render multi-cell products as single boxes.
|
| 131 |
+
|
| 132 |
+
`deliveries_per_stop` is an ordered list of (stop_index, [product_id per
|
| 133 |
+
unit]); the optimizer respects that route order so earlier stops are
|
| 134 |
+
extractable first. Every unit of an `is_returnable` product is created as
|
| 135 |
+
a return-type instance so the optimizer's EMPTY_KEG ablation models the
|
| 136 |
+
space empties leave behind.
|
| 137 |
+
"""
|
| 138 |
+
counts_per_stop: dict[int, dict[str, int]] = {}
|
| 139 |
+
for stop_idx, units in deliveries_per_stop:
|
| 140 |
+
if not units:
|
| 141 |
+
continue
|
| 142 |
+
bucket: dict[str, int] = {}
|
| 143 |
+
for pid in units:
|
| 144 |
+
bucket[pid] = bucket.get(pid, 0) + 1
|
| 145 |
+
counts_per_stop[stop_idx] = bucket
|
| 146 |
+
|
| 147 |
+
if not counts_per_stop:
|
| 148 |
+
return [], {"L": L, "W": W, "H": H}
|
| 149 |
+
|
| 150 |
+
needed_pids = {pid for counts in counts_per_stop.values() for pid in counts}
|
| 151 |
+
item_shapes = {
|
| 152 |
+
pid: (
|
| 153 |
+
int(products[pid]["length_cells"]),
|
| 154 |
+
int(products[pid]["width_cells"]),
|
| 155 |
+
int(products[pid]["height_cells"]),
|
| 156 |
+
)
|
| 157 |
+
for pid in needed_pids
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
capacity = L * W * H
|
| 161 |
+
total_cells = sum(
|
| 162 |
+
item_shapes[pid][0] * item_shapes[pid][1] * item_shapes[pid][2] * cnt
|
| 163 |
+
for counts in counts_per_stop.values()
|
| 164 |
+
for pid, cnt in counts.items()
|
| 165 |
+
)
|
| 166 |
+
if total_cells > capacity:
|
| 167 |
+
# OR-tools VRP capacity already enforces this; defensive scaling so the
|
| 168 |
+
# lattice optimizer doesn't deadlock if a request slips through.
|
| 169 |
+
scale = capacity / total_cells
|
| 170 |
+
counts_per_stop = {
|
| 171 |
+
stop_idx: {pid: max(1, int(cnt * scale)) for pid, cnt in counts.items()}
|
| 172 |
+
for stop_idx, counts in counts_per_stop.items()
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
returns_per_stop = {
|
| 176 |
+
stop_idx: {pid: cnt for pid, cnt in counts.items() if products[pid]["is_returnable"]}
|
| 177 |
+
for stop_idx, counts in counts_per_stop.items()
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
route_ids = [stop_idx for stop_idx, _ in deliveries_per_stop if stop_idx in counts_per_stop]
|
| 181 |
+
|
| 182 |
+
import numpy as np # noqa: F401 (used by SmartTruckOptimizer3D)
|
| 183 |
+
from optimize_box import SmartTruckOptimizer3D
|
| 184 |
+
|
| 185 |
+
opt = SmartTruckOptimizer3D(L, W, H, route_ids, item_shapes=item_shapes)
|
| 186 |
+
initial = opt.generate_initial_state(counts_per_stop, returns_per_stop)
|
| 187 |
+
final, _, _ = opt.optimize(initial, steps=2000)
|
| 188 |
+
opt._sync_instances_to_state(final)
|
| 189 |
+
|
| 190 |
+
items: list[dict] = []
|
| 191 |
+
for inst in opt._instances.values():
|
| 192 |
+
x, y, z = inst.anchor
|
| 193 |
+
lx, ly, lz = inst.shape
|
| 194 |
+
items.append({
|
| 195 |
+
"position": {"x": int(x), "y": int(y), "z": int(z)},
|
| 196 |
+
"shape": {"w_x": int(lx), "w_y": int(ly), "w_z": int(lz)},
|
| 197 |
+
"stop_index": int(inst.client),
|
| 198 |
+
"product_id": inst.item_type,
|
| 199 |
+
"is_returnable": bool(products[inst.item_type]["is_returnable"]),
|
| 200 |
+
})
|
| 201 |
+
return items, {"L": L, "W": W, "H": H}
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def _pallets_for_stop(stop: dict, products: dict, capacity_cells: int = 9) -> list[list[dict]]:
|
| 205 |
+
"""Greedy packer: group a stop's deliveries into pallets capped at
|
| 206 |
+
`capacity_cells` cube-units each. One pallet's `products` list is in the
|
| 207 |
+
same shape the frontend already consumes."""
|
| 208 |
+
items: list[tuple[str, int]] = []
|
| 209 |
+
for line in stop.get("deliveries", []):
|
| 210 |
+
pid = line["product_id"]
|
| 211 |
+
cells = (
|
| 212 |
+
products[pid]["length_cells"]
|
| 213 |
+
* products[pid]["width_cells"]
|
| 214 |
+
* products[pid]["height_cells"]
|
| 215 |
+
)
|
| 216 |
+
items.extend([(pid, cells)] * line["qty"])
|
| 217 |
+
|
| 218 |
+
pallets: list[list[dict]] = []
|
| 219 |
+
current: dict[str, int] = {}
|
| 220 |
+
used = 0
|
| 221 |
+
for pid, cells in items:
|
| 222 |
+
if used + cells > capacity_cells and current:
|
| 223 |
+
pallets.append([{"product_id": p, "quantity": q} for p, q in current.items()])
|
| 224 |
+
current, used = {}, 0
|
| 225 |
+
current[pid] = current.get(pid, 0) + 1
|
| 226 |
+
used += cells
|
| 227 |
+
if current:
|
| 228 |
+
pallets.append([{"product_id": p, "quantity": q} for p, q in current.items()])
|
| 229 |
+
return pallets
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def build_route_doc(
|
| 233 |
+
*,
|
| 234 |
+
driver_id: str,
|
| 235 |
+
truck_id: str,
|
| 236 |
+
van_type: str,
|
| 237 |
+
depot: dict,
|
| 238 |
+
request_stops: list[dict],
|
| 239 |
+
van_plan,
|
| 240 |
+
service_times_s: dict[str, float],
|
| 241 |
+
) -> dict:
|
| 242 |
+
"""Translate one VanPlan into the Firestore route document shape."""
|
| 243 |
+
layout = _van_layout(van_type)
|
| 244 |
+
rows, cols = layout["rows"], layout["cols"]
|
| 245 |
+
L, W, H = _van_lattice(van_type)
|
| 246 |
+
products = {p["id"]: p for p in json.loads((_BASE / "products.json").read_text())["products"]}
|
| 247 |
+
by_id = {s["id"]: s for s in request_stops}
|
| 248 |
+
|
| 249 |
+
points: list[dict] = [
|
| 250 |
+
{"lat": depot["coords"]["lat"], "lng": depot["coords"]["lng"], "address": depot.get("id", "Depot")}
|
| 251 |
+
]
|
| 252 |
+
windows: list[dict] = [{"start": depot["open"], "end": depot["close"]}]
|
| 253 |
+
service_times_min: list[float] = [0]
|
| 254 |
+
pallets: list[dict] = []
|
| 255 |
+
deliveries: list[dict] = [{"pallet_positions": []}]
|
| 256 |
+
|
| 257 |
+
next_slot = 0 # row-major fill of the truck floor
|
| 258 |
+
deliveries_per_stop: list[tuple[int, list[str]]] = []
|
| 259 |
+
|
| 260 |
+
for stop_idx, sp in enumerate(van_plan.stops, start=1):
|
| 261 |
+
s = by_id[sp.id]
|
| 262 |
+
points.append({
|
| 263 |
+
"lat": s["coords"]["lat"],
|
| 264 |
+
"lng": s["coords"]["lng"],
|
| 265 |
+
"address": s.get("address", s["id"]),
|
| 266 |
+
})
|
| 267 |
+
windows.append({"start": s["time_window"]["open"], "end": s["time_window"]["close"]})
|
| 268 |
+
service_times_min.append(round(service_times_s.get(sp.id, 0) / 60, 1))
|
| 269 |
+
|
| 270 |
+
stop_pallets = _pallets_for_stop(s, products)
|
| 271 |
+
positions: list[dict] = []
|
| 272 |
+
for pdata in stop_pallets:
|
| 273 |
+
if next_slot >= rows * cols:
|
| 274 |
+
break
|
| 275 |
+
r, c = divmod(next_slot, cols)
|
| 276 |
+
pallets.append({"row": r, "col": c, "products": pdata})
|
| 277 |
+
positions.append({"row": r, "col": c})
|
| 278 |
+
next_slot += 1
|
| 279 |
+
deliveries.append({"pallet_positions": positions})
|
| 280 |
+
|
| 281 |
+
units: list[str] = []
|
| 282 |
+
for line in s.get("deliveries", []):
|
| 283 |
+
units.extend([line["product_id"]] * line["qty"])
|
| 284 |
+
deliveries_per_stop.append((stop_idx, units))
|
| 285 |
+
|
| 286 |
+
items, item_grid = _compute_item_layout(L, W, H, deliveries_per_stop, products)
|
| 287 |
+
|
| 288 |
+
return {
|
| 289 |
+
"driver_id": driver_id,
|
| 290 |
+
"truck_id": truck_id,
|
| 291 |
+
"truck_layout": layout,
|
| 292 |
+
"item_grid": item_grid,
|
| 293 |
+
"items": items,
|
| 294 |
+
"points": points,
|
| 295 |
+
"pallets": pallets,
|
| 296 |
+
"deliveries": deliveries,
|
| 297 |
+
"windows": windows,
|
| 298 |
+
"service_times": service_times_min,
|
| 299 |
+
"delivery_status": ["pending"] * len(points),
|
| 300 |
+
"status": "pending",
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def write_routes(docs: list[dict]) -> int:
|
| 305 |
+
"""Write a batch of route docs to `routes/{driver_id}`. Returns count
|
| 306 |
+
written; 0 when Firestore is disabled."""
|
| 307 |
+
if not is_enabled():
|
| 308 |
+
return 0
|
| 309 |
+
written = 0
|
| 310 |
+
for doc in docs:
|
| 311 |
+
_db.collection("routes").document(doc["driver_id"]).set(doc)
|
| 312 |
+
written += 1
|
| 313 |
+
return written
|
geometry.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Trace each van's route along actual OSM roads, returning a polyline.
|
| 2 |
+
|
| 3 |
+
Used to render routes on the frontend map as curves following the streets,
|
| 4 |
+
not as straight chord lines between stops.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import networkx as nx
|
| 10 |
+
|
| 11 |
+
from travel_time import TravelMatrix
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _edge_coords(graph: nx.MultiDiGraph, u: int, v: int) -> list[tuple[float, float]]:
|
| 15 |
+
"""Coords along the (u, v) edge. Uses curve geometry when present."""
|
| 16 |
+
data = graph.get_edge_data(u, v)
|
| 17 |
+
if data:
|
| 18 |
+
first = next(iter(data.values()))
|
| 19 |
+
geom = first.get("geometry")
|
| 20 |
+
if geom is not None:
|
| 21 |
+
xs, ys = geom.xy
|
| 22 |
+
return [(float(y), float(x)) for x, y in zip(xs, ys)]
|
| 23 |
+
return [
|
| 24 |
+
(float(graph.nodes[u]["y"]), float(graph.nodes[u]["x"])),
|
| 25 |
+
(float(graph.nodes[v]["y"]), float(graph.nodes[v]["x"])),
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def route_polyline(
|
| 30 |
+
graph: nx.MultiDiGraph,
|
| 31 |
+
matrix: TravelMatrix,
|
| 32 |
+
stop_sequence: list[str],
|
| 33 |
+
) -> list[tuple[float, float]]:
|
| 34 |
+
"""Concatenate OSM-traced legs into one (lat, lng) polyline.
|
| 35 |
+
|
| 36 |
+
`stop_sequence` typically starts and ends with "DEPOT", e.g.
|
| 37 |
+
["DEPOT", "S005", "S007", "DEPOT"].
|
| 38 |
+
"""
|
| 39 |
+
out: list[tuple[float, float]] = []
|
| 40 |
+
for src_id, dst_id in zip(stop_sequence, stop_sequence[1:]):
|
| 41 |
+
src_node = matrix.node_ids[matrix.index_of(src_id)]
|
| 42 |
+
dst_node = matrix.node_ids[matrix.index_of(dst_id)]
|
| 43 |
+
try:
|
| 44 |
+
path = nx.shortest_path(graph, src_node, dst_node, weight="travel_time")
|
| 45 |
+
except nx.NetworkXNoPath:
|
| 46 |
+
continue
|
| 47 |
+
for u, v in zip(path, path[1:]):
|
| 48 |
+
seg = _edge_coords(graph, u, v)
|
| 49 |
+
# Avoid duplicating the joint node at the boundary between legs.
|
| 50 |
+
if out and seg and out[-1] == seg[0]:
|
| 51 |
+
out.extend(seg[1:])
|
| 52 |
+
else:
|
| 53 |
+
out.extend(seg)
|
| 54 |
+
return out
|
graph_manager.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Build and persist a road graph covering Granollers and Mollet del Vallès.
|
| 2 |
+
|
| 3 |
+
Within each city we keep the full drivable network so deliveries can be routed
|
| 4 |
+
to any address. Between the cities we only keep major roads (motorway, trunk,
|
| 5 |
+
primary) since the truck just needs the fastest corridor from one to the other.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import csv
|
| 11 |
+
import json
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
|
| 14 |
+
import matplotlib.pyplot as plt
|
| 15 |
+
import networkx as nx
|
| 16 |
+
import osmnx as ox
|
| 17 |
+
from shapely.ops import unary_union
|
| 18 |
+
|
| 19 |
+
GRANOLLERS = "Granollers, Catalunya, Spain"
|
| 20 |
+
MOLLET_DEL_VALLES = "Mollet del Vallès, Catalunya, Spain"
|
| 21 |
+
|
| 22 |
+
NETWORK_TYPE = "drive"
|
| 23 |
+
|
| 24 |
+
# Inter-city corridor: only fast roads. Includes link ramps so the corridor
|
| 25 |
+
# actually connects to the local networks at on/off-ramps.
|
| 26 |
+
MAJOR_ROAD_FILTER = (
|
| 27 |
+
'["highway"~"motorway|trunk|primary|'
|
| 28 |
+
'motorway_link|trunk_link|primary_link"]'
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
DEFAULT_GRAPH_PATH = Path(__file__).with_name("cities_graph.graphml")
|
| 32 |
+
DEFAULT_COORDS_PATH = Path(__file__).with_name("coords.csv")
|
| 33 |
+
DEFAULT_REQUEST_PATH = Path(__file__).with_name("data") / "sample_request.json"
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def load_depot_coords(path: Path | str = DEFAULT_REQUEST_PATH) -> tuple[float, float] | None:
|
| 37 |
+
"""Return (lat, lng) of the depot if `sample_request.json` exists, else None."""
|
| 38 |
+
path = Path(path)
|
| 39 |
+
if not path.exists():
|
| 40 |
+
return None
|
| 41 |
+
req = json.loads(path.read_text(encoding="utf-8"))
|
| 42 |
+
c = req.get("depot", {}).get("coords")
|
| 43 |
+
if not c:
|
| 44 |
+
return None
|
| 45 |
+
return float(c["lat"]), float(c["lng"])
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def _draw_depot(ax, depot: tuple[float, float] | None) -> None:
|
| 49 |
+
if depot is None:
|
| 50 |
+
return
|
| 51 |
+
lat, lng = depot
|
| 52 |
+
ax.scatter(
|
| 53 |
+
[lng], [lat],
|
| 54 |
+
s=160, c="#d62728", marker="*", zorder=10,
|
| 55 |
+
edgecolors="black", linewidth=1.0,
|
| 56 |
+
label="Warehouse",
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _city_graph(place: str) -> nx.MultiDiGraph:
|
| 61 |
+
return ox.graph_from_place(place, network_type=NETWORK_TYPE)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _corridor_graph(places: list[str]) -> nx.MultiDiGraph:
|
| 65 |
+
"""Major-roads-only graph spanning the bounding box of the given places."""
|
| 66 |
+
boundaries = [ox.geocode_to_gdf(p).geometry.iloc[0] for p in places]
|
| 67 |
+
minx, miny, maxx, maxy = unary_union(boundaries).bounds
|
| 68 |
+
return ox.graph_from_bbox(
|
| 69 |
+
bbox=(minx, miny, maxx, maxy),
|
| 70 |
+
custom_filter=MAJOR_ROAD_FILTER,
|
| 71 |
+
truncate_by_edge=True,
|
| 72 |
+
retain_all=False,
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def build_combined_graph() -> nx.MultiDiGraph:
|
| 77 |
+
"""Return a single graph with both cities plus the minimal corridor between."""
|
| 78 |
+
g_granollers = _city_graph(GRANOLLERS)
|
| 79 |
+
g_mollet = _city_graph(MOLLET_DEL_VALLES)
|
| 80 |
+
g_corridor = _corridor_graph([GRANOLLERS, MOLLET_DEL_VALLES])
|
| 81 |
+
|
| 82 |
+
combined = nx.compose_all([g_granollers, g_mollet, g_corridor])
|
| 83 |
+
# Preserve graph-level metadata (CRS) that compose_all drops.
|
| 84 |
+
combined.graph.update(g_granollers.graph)
|
| 85 |
+
|
| 86 |
+
# Drop disconnected fragments left over after composition.
|
| 87 |
+
combined = ox.truncate.largest_component(combined, strongly=True)
|
| 88 |
+
|
| 89 |
+
# Enrich edges with speed_kph + travel_time (seconds) so downstream
|
| 90 |
+
# routing can use realistic times instead of raw distance.
|
| 91 |
+
combined = ox.routing.add_edge_speeds(combined)
|
| 92 |
+
combined = ox.routing.add_edge_travel_times(combined)
|
| 93 |
+
return combined
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def save_graph(graph: nx.MultiDiGraph, path: Path | str = DEFAULT_GRAPH_PATH) -> Path:
|
| 97 |
+
path = Path(path)
|
| 98 |
+
ox.save_graphml(graph, path)
|
| 99 |
+
return path
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def load_graph(path: Path | str = DEFAULT_GRAPH_PATH) -> nx.MultiDiGraph:
|
| 103 |
+
return ox.load_graphml(Path(path))
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def get_or_build_graph(path: Path | str = DEFAULT_GRAPH_PATH) -> nx.MultiDiGraph:
|
| 107 |
+
path = Path(path)
|
| 108 |
+
if path.exists():
|
| 109 |
+
return load_graph(path)
|
| 110 |
+
graph = build_combined_graph()
|
| 111 |
+
save_graph(graph, path)
|
| 112 |
+
return graph
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def load_dropoff_coords(path: Path | str = DEFAULT_COORDS_PATH) -> list[tuple[float, float]]:
|
| 116 |
+
"""Read drop-off coordinates from a CSV with columns `id, x, y` (x=lng, y=lat)."""
|
| 117 |
+
points: list[tuple[float, float]] = []
|
| 118 |
+
with open(path, newline="", encoding="utf-8") as f:
|
| 119 |
+
reader = csv.DictReader(f)
|
| 120 |
+
for row in reader:
|
| 121 |
+
points.append((float(row["y"]), float(row["x"])))
|
| 122 |
+
return points
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def _edge_colors(graph: nx.MultiDiGraph) -> list[str]:
|
| 126 |
+
keywords = ("motorway", "trunk", "primary")
|
| 127 |
+
# Iterate with keys=True so the list aligns 1:1 with osmnx's internal
|
| 128 |
+
# edge ordering in MultiDiGraph plotting.
|
| 129 |
+
return [
|
| 130 |
+
"#d62728" if any(k in str(data.get("highway", "")) for k in keywords) else "#444444"
|
| 131 |
+
for _, _, _, data in graph.edges(keys=True, data=True)
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def plot_graph(
|
| 136 |
+
graph: nx.MultiDiGraph,
|
| 137 |
+
save_path: Path | str | None = None,
|
| 138 |
+
depot: tuple[float, float] | None = None,
|
| 139 |
+
) -> Path | None:
|
| 140 |
+
"""Show the graph with city streets in grey and the inter-city corridor in red.
|
| 141 |
+
|
| 142 |
+
Always writes a PNG (default `cities_graph.png` next to this file) because the
|
| 143 |
+
interactive tkagg window can fail to render edges on some Windows setups —
|
| 144 |
+
the file is the source of truth.
|
| 145 |
+
"""
|
| 146 |
+
fig, ax = ox.plot_graph(
|
| 147 |
+
graph,
|
| 148 |
+
figsize=(12, 12),
|
| 149 |
+
edge_color=_edge_colors(graph),
|
| 150 |
+
edge_linewidth=1.2,
|
| 151 |
+
edge_alpha=1.0,
|
| 152 |
+
node_size=3,
|
| 153 |
+
node_color="#1f77b4",
|
| 154 |
+
bgcolor="white",
|
| 155 |
+
show=False,
|
| 156 |
+
close=False,
|
| 157 |
+
)
|
| 158 |
+
_draw_depot(ax, depot)
|
| 159 |
+
if depot is not None:
|
| 160 |
+
ax.legend(loc="upper right", framealpha=0.9)
|
| 161 |
+
ax.set_title(
|
| 162 |
+
f"Granollers + Mollet del Vallès "
|
| 163 |
+
f"({graph.number_of_nodes()} nodes, {graph.number_of_edges()} edges)"
|
| 164 |
+
)
|
| 165 |
+
out = Path(save_path) if save_path else Path(__file__).with_name("cities_graph.png")
|
| 166 |
+
fig.savefig(out, dpi=120, bbox_inches="tight")
|
| 167 |
+
plt.show()
|
| 168 |
+
return out
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def plot_graph_with_dropoffs(
|
| 172 |
+
graph: nx.MultiDiGraph,
|
| 173 |
+
coords: list[tuple[float, float]],
|
| 174 |
+
title: str | None = None,
|
| 175 |
+
save_path: Path | str | None = None,
|
| 176 |
+
depot: tuple[float, float] | None = None,
|
| 177 |
+
) -> Path | None:
|
| 178 |
+
"""Overlay drop-off coordinates on the road graph.
|
| 179 |
+
|
| 180 |
+
`coords` is a list of (lat, lng) pairs (matches `load_dropoff_coords`).
|
| 181 |
+
Axis limits expand to include any drop-offs that fall outside the graph
|
| 182 |
+
extent so off-area points are still visible (they may indicate the graph
|
| 183 |
+
needs to be rebuilt over a different region).
|
| 184 |
+
"""
|
| 185 |
+
fig, ax = ox.plot_graph(
|
| 186 |
+
graph,
|
| 187 |
+
figsize=(12, 12),
|
| 188 |
+
edge_color=_edge_colors(graph),
|
| 189 |
+
edge_linewidth=1.2,
|
| 190 |
+
edge_alpha=1.0,
|
| 191 |
+
node_size=3,
|
| 192 |
+
node_color="#1f77b4",
|
| 193 |
+
bgcolor="white",
|
| 194 |
+
show=False,
|
| 195 |
+
close=False,
|
| 196 |
+
)
|
| 197 |
+
pad = 0.005
|
| 198 |
+
node_lngs = [graph.nodes[n]["x"] for n in graph.nodes()]
|
| 199 |
+
node_lats = [graph.nodes[n]["y"] for n in graph.nodes()]
|
| 200 |
+
xmin, xmax = min(node_lngs) - pad, max(node_lngs) + pad
|
| 201 |
+
ymin, ymax = min(node_lats) - pad, max(node_lats) + pad
|
| 202 |
+
|
| 203 |
+
inside = [(lat, lng) for lat, lng in coords if xmin <= lng <= xmax and ymin <= lat <= ymax]
|
| 204 |
+
outside_count = len(coords) - len(inside)
|
| 205 |
+
if outside_count:
|
| 206 |
+
print(
|
| 207 |
+
f"warning: {outside_count}/{len(coords)} drop-offs fall outside the "
|
| 208 |
+
f"graph extent and were cropped from the plot"
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
lats = [c[0] for c in inside]
|
| 212 |
+
lngs = [c[1] for c in inside]
|
| 213 |
+
ax.scatter(
|
| 214 |
+
lngs, lats,
|
| 215 |
+
s=10, c="#2ca02c", alpha=0.7, zorder=5,
|
| 216 |
+
edgecolors="white", linewidth=0.3,
|
| 217 |
+
label=f"{len(inside)} drop-offs (in extent)",
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
_draw_depot(ax, depot)
|
| 221 |
+
|
| 222 |
+
ax.set_xlim(xmin, xmax)
|
| 223 |
+
ax.set_ylim(ymin, ymax)
|
| 224 |
+
ax.legend(loc="upper right", framealpha=0.9)
|
| 225 |
+
ax.set_title(
|
| 226 |
+
title
|
| 227 |
+
or f"Graph + {len(coords)} drop-offs "
|
| 228 |
+
f"({graph.number_of_nodes()} nodes, {graph.number_of_edges()} edges)"
|
| 229 |
+
)
|
| 230 |
+
out = Path(save_path) if save_path else Path(__file__).with_name("cities_graph_dropoffs.png")
|
| 231 |
+
fig.savefig(out, dpi=120, bbox_inches="tight")
|
| 232 |
+
plt.show()
|
| 233 |
+
return out
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
if __name__ == "__main__":
|
| 237 |
+
g = build_combined_graph()
|
| 238 |
+
print(f"Nodes: {g.number_of_nodes()} Edges: {g.number_of_edges()}")
|
| 239 |
+
out = save_graph(g)
|
| 240 |
+
print(f"Saved to {out}")
|
| 241 |
+
|
| 242 |
+
depot = load_depot_coords()
|
| 243 |
+
if depot is not None:
|
| 244 |
+
print(f"Depot at lat={depot[0]:.4f}, lng={depot[1]:.4f}")
|
| 245 |
+
if DEFAULT_COORDS_PATH.exists():
|
| 246 |
+
coords = load_dropoff_coords(DEFAULT_COORDS_PATH)
|
| 247 |
+
print(f"Loaded {len(coords)} drop-off points from {DEFAULT_COORDS_PATH.name}")
|
| 248 |
+
png = plot_graph_with_dropoffs(g, coords, depot=depot)
|
| 249 |
+
else:
|
| 250 |
+
png = plot_graph(g, depot=depot)
|
| 251 |
+
print(f"Saved plot to {png}")
|
interhack26.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c96049f4b662f1bd4a438ff514cb706de41b77ae4af9282e0e1a321b1fb5b9cb
|
| 3 |
+
size 154643
|
loader.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Parse a request dict + product/van catalogues into solver inputs.
|
| 2 |
+
|
| 3 |
+
The shape of the request dict matches `sample_request.json`. The catalogues
|
| 4 |
+
(`products.json`, `vans.json`) are read from disk lazily — keeps startup
|
| 5 |
+
cheap and avoids globals.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import json
|
| 11 |
+
from dataclasses import dataclass
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
|
| 14 |
+
_BASE = Path(__file__).parent
|
| 15 |
+
DEFAULT_PRODUCTS = _BASE / "products.json"
|
| 16 |
+
DEFAULT_VANS = _BASE / "vans.json"
|
| 17 |
+
|
| 18 |
+
SERVICE_BASE_S = 180.0 # parking + paperwork buffer per stop
|
| 19 |
+
SERVICE_PER_CELL_S = 10.0 # crate-handling time per unit cell
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass
|
| 23 |
+
class Depot:
|
| 24 |
+
id: str
|
| 25 |
+
lat: float
|
| 26 |
+
lng: float
|
| 27 |
+
open_s: int
|
| 28 |
+
close_s: int
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@dataclass
|
| 32 |
+
class Driver:
|
| 33 |
+
id: str
|
| 34 |
+
shift_start_s: int
|
| 35 |
+
shift_end_s: int
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@dataclass
|
| 39 |
+
class Fleet:
|
| 40 |
+
num_vans: int
|
| 41 |
+
capacity_kg: float
|
| 42 |
+
capacity_cells: int
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@dataclass
|
| 46 |
+
class Stop:
|
| 47 |
+
id: str
|
| 48 |
+
lat: float
|
| 49 |
+
lng: float
|
| 50 |
+
t_open_s: int
|
| 51 |
+
t_close_s: int
|
| 52 |
+
delivery_cells: int
|
| 53 |
+
delivery_kg: float
|
| 54 |
+
pickup_cells: int
|
| 55 |
+
pickup_kg: float
|
| 56 |
+
service_time_s: float
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _hms(s: str) -> int:
|
| 60 |
+
h, m = s.split(":")
|
| 61 |
+
return int(h) * 3600 + int(m) * 60
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _van_capacity_cells(spec: dict) -> int:
|
| 65 |
+
cube = spec["cube_size_m"]
|
| 66 |
+
return (
|
| 67 |
+
int(round(spec["length_m"] / cube))
|
| 68 |
+
* int(round(spec["width_m"] / cube))
|
| 69 |
+
* int(round(spec["height_m"] / cube))
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def _van_spec(vans_path: Path, van_type: str) -> dict:
|
| 74 |
+
data = json.loads(vans_path.read_text(encoding="utf-8"))
|
| 75 |
+
for v in data["van_types"]:
|
| 76 |
+
if v["type"] == van_type:
|
| 77 |
+
return v
|
| 78 |
+
raise KeyError(f"unknown van_type: {van_type}")
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _line_cells(p: dict, qty: int) -> int:
|
| 82 |
+
return p["length_cells"] * p["width_cells"] * p["height_cells"] * qty
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def load(
|
| 86 |
+
req: dict,
|
| 87 |
+
products_path: Path = DEFAULT_PRODUCTS,
|
| 88 |
+
vans_path: Path = DEFAULT_VANS,
|
| 89 |
+
) -> tuple[Depot, Fleet, list[Driver], list[Stop]]:
|
| 90 |
+
products = {
|
| 91 |
+
p["id"]: p
|
| 92 |
+
for p in json.loads(products_path.read_text(encoding="utf-8"))["products"]
|
| 93 |
+
}
|
| 94 |
+
spec = _van_spec(vans_path, req["fleet"]["van_type"])
|
| 95 |
+
|
| 96 |
+
depot = Depot(
|
| 97 |
+
id=req["depot"]["id"],
|
| 98 |
+
lat=req["depot"]["coords"]["lat"],
|
| 99 |
+
lng=req["depot"]["coords"]["lng"],
|
| 100 |
+
open_s=_hms(req["depot"]["open"]),
|
| 101 |
+
close_s=_hms(req["depot"]["close"]),
|
| 102 |
+
)
|
| 103 |
+
fleet = Fleet(
|
| 104 |
+
num_vans=req["fleet"]["num_vans"],
|
| 105 |
+
capacity_kg=float(spec["max_payload_kg"]),
|
| 106 |
+
capacity_cells=_van_capacity_cells(spec),
|
| 107 |
+
)
|
| 108 |
+
drivers = [
|
| 109 |
+
Driver(d["id"], _hms(d["shift_start"]), _hms(d["shift_end"]))
|
| 110 |
+
for d in req["drivers"]
|
| 111 |
+
]
|
| 112 |
+
|
| 113 |
+
stops: list[Stop] = []
|
| 114 |
+
for s in req["stops"]:
|
| 115 |
+
d_cells = sum(_line_cells(products[ln["product_id"]], ln["qty"]) for ln in s["deliveries"])
|
| 116 |
+
d_kg = sum(products[ln["product_id"]]["weight_kg"] * ln["qty"] for ln in s["deliveries"])
|
| 117 |
+
p_cells = sum(_line_cells(products[ln["product_id"]], ln["qty"]) for ln in s.get("pickups", []))
|
| 118 |
+
p_kg = sum(products[ln["product_id"]]["weight_kg"] * ln["qty"] for ln in s.get("pickups", []))
|
| 119 |
+
stops.append(Stop(
|
| 120 |
+
id=s["id"],
|
| 121 |
+
lat=s["coords"]["lat"], lng=s["coords"]["lng"],
|
| 122 |
+
t_open_s=_hms(s["time_window"]["open"]),
|
| 123 |
+
t_close_s=_hms(s["time_window"]["close"]),
|
| 124 |
+
delivery_cells=d_cells, delivery_kg=d_kg,
|
| 125 |
+
pickup_cells=p_cells, pickup_kg=p_kg,
|
| 126 |
+
service_time_s=SERVICE_BASE_S + SERVICE_PER_CELL_S * (d_cells + p_cells),
|
| 127 |
+
))
|
| 128 |
+
return depot, fleet, drivers, stops
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def build_travel_matrix(depot: Depot, stops: list[Stop]):
|
| 132 |
+
"""Travel-time + distance matrix scoped to depot + the request's stops."""
|
| 133 |
+
from travel_time import build_matrix
|
| 134 |
+
from graph_manager import get_or_build_graph
|
| 135 |
+
points = [("DEPOT", depot.lat, depot.lng)] + [(s.id, s.lat, s.lng) for s in stops]
|
| 136 |
+
return build_matrix(get_or_build_graph(), points)
|
optimize_box.py
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import random
|
| 3 |
+
import math
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class _Instance:
|
| 7 |
+
"""A multi-cell rectangular item placed in the truck lattice."""
|
| 8 |
+
__slots__ = ("id", "client", "item_type", "is_return", "anchor", "shape")
|
| 9 |
+
|
| 10 |
+
def __init__(self, instance_id, client, item_type, is_return, anchor, shape):
|
| 11 |
+
self.id = instance_id
|
| 12 |
+
self.client = client
|
| 13 |
+
self.item_type = item_type
|
| 14 |
+
self.is_return = is_return
|
| 15 |
+
self.anchor = anchor # (x, y, z) min corner
|
| 16 |
+
self.shape = shape # (lx, ly, lz)
|
| 17 |
+
|
| 18 |
+
def cells_at(self, anchor):
|
| 19 |
+
x0, y0, z0 = anchor
|
| 20 |
+
lx, ly, lz = self.shape
|
| 21 |
+
out = []
|
| 22 |
+
for i in range(lx):
|
| 23 |
+
for j in range(ly):
|
| 24 |
+
for k in range(lz):
|
| 25 |
+
out.append((x0 + i, y0 + j, z0 + k))
|
| 26 |
+
return out
|
| 27 |
+
|
| 28 |
+
@property
|
| 29 |
+
def cells(self):
|
| 30 |
+
return self.cells_at(self.anchor)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class SmartTruckOptimizer3D:
|
| 34 |
+
EMPTY_KEG = -1 # post-return solid block; structural support but always blocks extraction
|
| 35 |
+
|
| 36 |
+
def __init__(self, length_bays, width_pallets, height_layers, route, item_shapes=None):
|
| 37 |
+
"""
|
| 38 |
+
length_bays: lateral bays (X-axis)
|
| 39 |
+
width_pallets: depth of each bay (Y-axis); both Y-walls are open along the full length
|
| 40 |
+
height_layers: max stacking height (Z-axis)
|
| 41 |
+
route: client IDs in delivery order
|
| 42 |
+
item_shapes: dict[item_type, (lx, ly, lz)] of rectangular box dimensions.
|
| 43 |
+
Defaults to {"keg": (1, 1, 1)} for backward compatibility.
|
| 44 |
+
|
| 45 |
+
Removability rule: an instance is removable iff at least one of its three sides
|
| 46 |
+
— top, left (-Y), right (+Y) — is fully clear of blockers (other-client items
|
| 47 |
+
or empty kegs). The truck has no front/back access.
|
| 48 |
+
"""
|
| 49 |
+
self.L = length_bays
|
| 50 |
+
self.W = width_pallets
|
| 51 |
+
self.H = height_layers
|
| 52 |
+
self.route = list(route)
|
| 53 |
+
|
| 54 |
+
shapes = item_shapes if item_shapes is not None else {"keg": (1, 1, 1)}
|
| 55 |
+
self.item_shapes = {}
|
| 56 |
+
for name, dims in shapes.items():
|
| 57 |
+
dims = tuple(int(d) for d in dims)
|
| 58 |
+
if len(dims) != 3 or any(d < 1 for d in dims):
|
| 59 |
+
raise ValueError(f"Invalid shape for item '{name}': {dims}")
|
| 60 |
+
if dims[0] > self.L or dims[1] > self.W or dims[2] > self.H:
|
| 61 |
+
raise ValueError(f"Item '{name}' shape {dims} exceeds grid {(self.L, self.W, self.H)}.")
|
| 62 |
+
self.item_shapes[name] = dims
|
| 63 |
+
|
| 64 |
+
self._instances = {}
|
| 65 |
+
self._next_id = 1
|
| 66 |
+
|
| 67 |
+
# Work caches (variance over cell-x; multi-cell items contribute each cell once)
|
| 68 |
+
self._x_sum = {}
|
| 69 |
+
self._x_sq_sum = {}
|
| 70 |
+
self._counts = {}
|
| 71 |
+
self._max_var = max(((self.L - 1) ** 2) / 4.0, 1e-9)
|
| 72 |
+
|
| 73 |
+
# ---------- helpers ----------
|
| 74 |
+
|
| 75 |
+
def _is_blocker(self, value, client):
|
| 76 |
+
"""True iff a cell with `value` blocks `client` from extracting through it."""
|
| 77 |
+
if value == 0:
|
| 78 |
+
return False
|
| 79 |
+
if value == self.EMPTY_KEG:
|
| 80 |
+
return True
|
| 81 |
+
return self._instances[value].client != client
|
| 82 |
+
|
| 83 |
+
# ---------- initial state ----------
|
| 84 |
+
|
| 85 |
+
def generate_initial_state(self, client_item_counts, client_returns):
|
| 86 |
+
"""
|
| 87 |
+
client_item_counts: {client_id: {item_type: total_count}}
|
| 88 |
+
client_returns: {client_id: {item_type: returns_count}}
|
| 89 |
+
|
| 90 |
+
For each (client, item_type) we create `total - returns` pure-delivery instances
|
| 91 |
+
and `returns` substitution instances. Instances are placed at random valid
|
| 92 |
+
anchors; raises if the grid is too crowded.
|
| 93 |
+
"""
|
| 94 |
+
self._instances = {}
|
| 95 |
+
self._next_id = 1
|
| 96 |
+
state = np.zeros((self.L, self.W, self.H), dtype=int)
|
| 97 |
+
|
| 98 |
+
pending = [] # (client, item_type, is_return, shape)
|
| 99 |
+
for client, items in client_item_counts.items():
|
| 100 |
+
returns_for_client = client_returns.get(client, {})
|
| 101 |
+
for item_type, count in items.items():
|
| 102 |
+
if item_type not in self.item_shapes:
|
| 103 |
+
raise KeyError(f"Unknown item_type '{item_type}' (not in item_shapes).")
|
| 104 |
+
num_returns = returns_for_client.get(item_type, 0)
|
| 105 |
+
num_pure = count - num_returns
|
| 106 |
+
if num_pure < 0:
|
| 107 |
+
raise ValueError(
|
| 108 |
+
f"Client {client} item '{item_type}': returns ({num_returns}) "
|
| 109 |
+
f"exceed total ({count})."
|
| 110 |
+
)
|
| 111 |
+
shape = self.item_shapes[item_type]
|
| 112 |
+
for _ in range(num_pure):
|
| 113 |
+
pending.append((client, item_type, False, shape))
|
| 114 |
+
for _ in range(num_returns):
|
| 115 |
+
pending.append((client, item_type, True, shape))
|
| 116 |
+
|
| 117 |
+
total_cells_needed = sum(s[0] * s[1] * s[2] for *_, s in pending)
|
| 118 |
+
capacity = self.L * self.W * self.H
|
| 119 |
+
if total_cells_needed > capacity:
|
| 120 |
+
raise ValueError(f"Items need {total_cells_needed} cells; grid holds {capacity}.")
|
| 121 |
+
|
| 122 |
+
random.shuffle(pending)
|
| 123 |
+
for client, item_type, is_return, shape in pending:
|
| 124 |
+
anchor = self._place_random(state, shape)
|
| 125 |
+
if anchor is None:
|
| 126 |
+
raise RuntimeError(
|
| 127 |
+
f"Could not place item '{item_type}' for client {client}; grid too crowded."
|
| 128 |
+
)
|
| 129 |
+
inst_id = self._next_id
|
| 130 |
+
self._next_id += 1
|
| 131 |
+
inst = _Instance(inst_id, client, item_type, is_return, anchor, shape)
|
| 132 |
+
self._instances[inst_id] = inst
|
| 133 |
+
for (x, y, z) in inst.cells:
|
| 134 |
+
state[x, y, z] = inst_id
|
| 135 |
+
return state
|
| 136 |
+
|
| 137 |
+
def _place_random(self, state, shape, max_tries=500):
|
| 138 |
+
lx, ly, lz = shape
|
| 139 |
+
for _ in range(max_tries):
|
| 140 |
+
x = random.randint(0, self.L - lx)
|
| 141 |
+
y = random.randint(0, self.W - ly)
|
| 142 |
+
z = random.randint(0, self.H - lz)
|
| 143 |
+
if self._cells_clear(state, x, y, z, lx, ly, lz):
|
| 144 |
+
return (x, y, z)
|
| 145 |
+
# Deterministic fallback scan
|
| 146 |
+
for x in range(self.L - lx + 1):
|
| 147 |
+
for y in range(self.W - ly + 1):
|
| 148 |
+
for z in range(self.H - lz + 1):
|
| 149 |
+
if self._cells_clear(state, x, y, z, lx, ly, lz):
|
| 150 |
+
return (x, y, z)
|
| 151 |
+
return None
|
| 152 |
+
|
| 153 |
+
def _cells_clear(self, state, x, y, z, lx, ly, lz, allow_id=0):
|
| 154 |
+
"""True iff the box at (x,y,z) of size (lx,ly,lz) contains only 0 or `allow_id`."""
|
| 155 |
+
for i in range(lx):
|
| 156 |
+
for j in range(ly):
|
| 157 |
+
for k in range(lz):
|
| 158 |
+
v = state[x + i, y + j, z + k]
|
| 159 |
+
if v != 0 and v != allow_id:
|
| 160 |
+
return False
|
| 161 |
+
return True
|
| 162 |
+
|
| 163 |
+
# ---------- penalty (full eval) ----------
|
| 164 |
+
|
| 165 |
+
def physical_penalty(self, state):
|
| 166 |
+
"""
|
| 167 |
+
Sum of:
|
| 168 |
+
1. Initial gravity violations (cells whose support is void)
|
| 169 |
+
2. For each client in route order:
|
| 170 |
+
a. Extraction violations: instances with no fully-clear side
|
| 171 |
+
b. Post-ablation gravity violations
|
| 172 |
+
"""
|
| 173 |
+
penalty = 0
|
| 174 |
+
truck = np.copy(state)
|
| 175 |
+
|
| 176 |
+
penalty += self._gravity_violations(truck)
|
| 177 |
+
|
| 178 |
+
# Group instances by client once
|
| 179 |
+
by_client = {c: [] for c in self.route}
|
| 180 |
+
for inst in self._instances.values():
|
| 181 |
+
if inst.client in by_client:
|
| 182 |
+
by_client[inst.client].append(inst)
|
| 183 |
+
|
| 184 |
+
for client in self.route:
|
| 185 |
+
for inst in by_client[client]:
|
| 186 |
+
if self._instance_blocked(truck, inst):
|
| 187 |
+
penalty += 1
|
| 188 |
+
for inst in by_client[client]:
|
| 189 |
+
fill = self.EMPTY_KEG if inst.is_return else 0
|
| 190 |
+
for (x, y, z) in inst.cells:
|
| 191 |
+
truck[x, y, z] = fill
|
| 192 |
+
penalty += self._gravity_violations(truck)
|
| 193 |
+
|
| 194 |
+
return penalty
|
| 195 |
+
|
| 196 |
+
def _gravity_violations(self, truck):
|
| 197 |
+
if self.H < 2:
|
| 198 |
+
return 0
|
| 199 |
+
upper = truck[:, :, 1:]
|
| 200 |
+
lower = truck[:, :, :-1]
|
| 201 |
+
return int(np.sum((upper > 0) & (lower == 0)))
|
| 202 |
+
|
| 203 |
+
def _instance_blocked(self, truck, inst):
|
| 204 |
+
"""True iff none of {top, left, right} sides is fully clear of blockers."""
|
| 205 |
+
x0, y0, z0 = inst.anchor
|
| 206 |
+
lx, ly, lz = inst.shape
|
| 207 |
+
c = inst.client
|
| 208 |
+
|
| 209 |
+
# Top side: above the item
|
| 210 |
+
if z0 + lz >= self.H:
|
| 211 |
+
return False # extends to roof, top is open by definition
|
| 212 |
+
if self._region_clear(truck, x0, x0 + lx, y0, y0 + ly, z0 + lz, self.H, c):
|
| 213 |
+
return False
|
| 214 |
+
|
| 215 |
+
# Left side
|
| 216 |
+
if y0 == 0:
|
| 217 |
+
return False
|
| 218 |
+
if self._region_clear(truck, x0, x0 + lx, 0, y0, z0, z0 + lz, c):
|
| 219 |
+
return False
|
| 220 |
+
|
| 221 |
+
# Right side
|
| 222 |
+
if y0 + ly >= self.W:
|
| 223 |
+
return False
|
| 224 |
+
if self._region_clear(truck, x0, x0 + lx, y0 + ly, self.W, z0, z0 + lz, c):
|
| 225 |
+
return False
|
| 226 |
+
|
| 227 |
+
return True
|
| 228 |
+
|
| 229 |
+
def _region_clear(self, truck, x_lo, x_hi, y_lo, y_hi, z_lo, z_hi, client):
|
| 230 |
+
for x in range(x_lo, x_hi):
|
| 231 |
+
for y in range(y_lo, y_hi):
|
| 232 |
+
for z in range(z_lo, z_hi):
|
| 233 |
+
if self._is_blocker(truck[x, y, z], client):
|
| 234 |
+
return False
|
| 235 |
+
return True
|
| 236 |
+
|
| 237 |
+
# ---------- delta penalty ----------
|
| 238 |
+
|
| 239 |
+
def _instances_reading_cells(self, changed_cells):
|
| 240 |
+
"""Set of instance IDs whose blocking computation reads any of `changed_cells`."""
|
| 241 |
+
affected = set()
|
| 242 |
+
for inst_id, inst in self._instances.items():
|
| 243 |
+
x0, y0, z0 = inst.anchor
|
| 244 |
+
lx, ly, lz = inst.shape
|
| 245 |
+
for (cx, cy, cz) in changed_cells:
|
| 246 |
+
in_x = x0 <= cx < x0 + lx
|
| 247 |
+
in_y = y0 <= cy < y0 + ly
|
| 248 |
+
in_z = z0 <= cz < z0 + lz
|
| 249 |
+
if in_x and (
|
| 250 |
+
(in_y and cz >= z0 + lz) or # top column
|
| 251 |
+
(cy < y0 and in_z) or # left row
|
| 252 |
+
(cy >= y0 + ly and in_z) or # right row
|
| 253 |
+
(in_y and in_z) # inside (shouldn't happen unless self)
|
| 254 |
+
):
|
| 255 |
+
affected.add(inst_id)
|
| 256 |
+
break
|
| 257 |
+
return affected
|
| 258 |
+
|
| 259 |
+
def _evaluate_local_penalty(self, state, changed_cells, affected_ids):
|
| 260 |
+
"""
|
| 261 |
+
Sum penalty contributions whose values can differ between current and proposed
|
| 262 |
+
states. Cells outside `changed_cells` and instances outside `affected_ids` give
|
| 263 |
+
identical contributions in both, so they cancel in the delta.
|
| 264 |
+
|
| 265 |
+
The cascade still mutates a full-state copy (ablation is global), but only the
|
| 266 |
+
listed instances' extraction blocks and only the gravity neighbors of changed
|
| 267 |
+
cells contribute to the returned sum.
|
| 268 |
+
"""
|
| 269 |
+
truck = np.copy(state)
|
| 270 |
+
|
| 271 |
+
gravity_cells = set()
|
| 272 |
+
for (x, y, z) in changed_cells:
|
| 273 |
+
gravity_cells.add((x, y, z))
|
| 274 |
+
if z + 1 < self.H:
|
| 275 |
+
gravity_cells.add((x, y, z + 1))
|
| 276 |
+
|
| 277 |
+
penalty = 0
|
| 278 |
+
for (x, y, z) in gravity_cells:
|
| 279 |
+
if z >= 1 and truck[x, y, z] > 0 and truck[x, y, z - 1] == 0:
|
| 280 |
+
penalty += 1
|
| 281 |
+
|
| 282 |
+
by_client = {c: [] for c in self.route}
|
| 283 |
+
for inst in self._instances.values():
|
| 284 |
+
if inst.client in by_client:
|
| 285 |
+
by_client[inst.client].append(inst)
|
| 286 |
+
|
| 287 |
+
for client in self.route:
|
| 288 |
+
for inst in by_client[client]:
|
| 289 |
+
if inst.id in affected_ids and self._instance_blocked(truck, inst):
|
| 290 |
+
penalty += 1
|
| 291 |
+
for inst in by_client[client]:
|
| 292 |
+
fill = self.EMPTY_KEG if inst.is_return else 0
|
| 293 |
+
for (x, y, z) in inst.cells:
|
| 294 |
+
truck[x, y, z] = fill
|
| 295 |
+
for (x, y, z) in gravity_cells:
|
| 296 |
+
if z >= 1 and truck[x, y, z] > 0 and truck[x, y, z - 1] == 0:
|
| 297 |
+
penalty += 1
|
| 298 |
+
|
| 299 |
+
return penalty
|
| 300 |
+
|
| 301 |
+
# ---------- work ----------
|
| 302 |
+
|
| 303 |
+
def spatial_work(self, state):
|
| 304 |
+
"""
|
| 305 |
+
Mean-normalized variance of x-coords per client, in roughly [0, 1].
|
| 306 |
+
Each cell of a multi-cell item contributes its x once; rewards keeping a
|
| 307 |
+
client's items clustered along the truck length so the driver walks less
|
| 308 |
+
per stop. Translation-invariant (cluster *position* doesn't matter — both
|
| 309 |
+
Y-sides are open, see project notes).
|
| 310 |
+
"""
|
| 311 |
+
if not self.route:
|
| 312 |
+
return 0.0
|
| 313 |
+
total = 0.0
|
| 314 |
+
for client in self.route:
|
| 315 |
+
xs = []
|
| 316 |
+
for inst in self._instances.values():
|
| 317 |
+
if inst.client == client:
|
| 318 |
+
x0, _, _ = inst.anchor
|
| 319 |
+
lx, ly, lz = inst.shape
|
| 320 |
+
for i in range(lx):
|
| 321 |
+
xs.extend([x0 + i] * (ly * lz))
|
| 322 |
+
if len(xs) > 1:
|
| 323 |
+
total += float(np.var(xs))
|
| 324 |
+
return total / (self._max_var * len(self.route))
|
| 325 |
+
|
| 326 |
+
def _init_work_cache(self, state):
|
| 327 |
+
self._x_sum = {c: 0 for c in self.route}
|
| 328 |
+
self._x_sq_sum = {c: 0 for c in self.route}
|
| 329 |
+
self._counts = {c: 0 for c in self.route}
|
| 330 |
+
for inst in self._instances.values():
|
| 331 |
+
if inst.client not in self._counts:
|
| 332 |
+
continue
|
| 333 |
+
for (x, _, _) in inst.cells:
|
| 334 |
+
self._x_sum[inst.client] += x
|
| 335 |
+
self._x_sq_sum[inst.client] += x * x
|
| 336 |
+
self._counts[inst.client] += 1
|
| 337 |
+
|
| 338 |
+
def _work_from_cache(self):
|
| 339 |
+
if not self.route:
|
| 340 |
+
return 0.0
|
| 341 |
+
total = 0.0
|
| 342 |
+
for client in self.route:
|
| 343 |
+
n = self._counts.get(client, 0)
|
| 344 |
+
if n > 1:
|
| 345 |
+
mean = self._x_sum[client] / n
|
| 346 |
+
total += self._x_sq_sum[client] / n - mean * mean
|
| 347 |
+
return total / (self._max_var * len(self.route))
|
| 348 |
+
|
| 349 |
+
def _update_work_cache_for_move(self, inst, old_anchor, new_anchor):
|
| 350 |
+
"""Reflect that `inst` translated from old_anchor to new_anchor. O(|cells|)."""
|
| 351 |
+
if inst.client not in self._counts:
|
| 352 |
+
return
|
| 353 |
+
for (x, _, _) in inst.cells_at(old_anchor):
|
| 354 |
+
self._x_sum[inst.client] -= x
|
| 355 |
+
self._x_sq_sum[inst.client] -= x * x
|
| 356 |
+
for (x, _, _) in inst.cells_at(new_anchor):
|
| 357 |
+
self._x_sum[inst.client] += x
|
| 358 |
+
self._x_sq_sum[inst.client] += x * x
|
| 359 |
+
# counts unchanged
|
| 360 |
+
|
| 361 |
+
# ---------- optimize ----------
|
| 362 |
+
|
| 363 |
+
def optimize(self, initial_state, steps=40000, seed=None,
|
| 364 |
+
T_0=1.0, T_min=1e-4, gamma_0=0.01, gamma_max=100.0):
|
| 365 |
+
"""
|
| 366 |
+
SA with single-instance translation proposals + delta penalty evaluation.
|
| 367 |
+
|
| 368 |
+
Each step picks a random instance, picks a random in-bounds anchor, and
|
| 369 |
+
accepts/rejects a translation by Metropolis criterion on H = W + γP. Same-anchor
|
| 370 |
+
proposals and proposals that overlap other items are skipped without scoring.
|
| 371 |
+
"""
|
| 372 |
+
if seed is not None:
|
| 373 |
+
random.seed(seed)
|
| 374 |
+
np.random.seed(seed)
|
| 375 |
+
|
| 376 |
+
current_state = np.copy(initial_state)
|
| 377 |
+
self._init_work_cache(current_state)
|
| 378 |
+
current_P = self.physical_penalty(current_state)
|
| 379 |
+
current_W = self._work_from_cache()
|
| 380 |
+
current_H = current_W + gamma_max * current_P
|
| 381 |
+
|
| 382 |
+
best_state = np.copy(current_state)
|
| 383 |
+
best_H = current_H
|
| 384 |
+
best_feasible_state = None
|
| 385 |
+
best_feasible_W = float('inf')
|
| 386 |
+
|
| 387 |
+
instance_ids = list(self._instances.keys())
|
| 388 |
+
if not instance_ids:
|
| 389 |
+
return current_state, current_P, current_W
|
| 390 |
+
|
| 391 |
+
for step in range(steps):
|
| 392 |
+
fraction = step / float(steps)
|
| 393 |
+
T = T_0 * ((T_min / T_0) ** fraction)
|
| 394 |
+
gamma = gamma_0 + (gamma_max - gamma_0) * (fraction ** 4)
|
| 395 |
+
|
| 396 |
+
inst_id = random.choice(instance_ids)
|
| 397 |
+
inst = self._instances[inst_id]
|
| 398 |
+
lx, ly, lz = inst.shape
|
| 399 |
+
|
| 400 |
+
x_new = random.randint(0, self.L - lx)
|
| 401 |
+
y_new = random.randint(0, self.W - ly)
|
| 402 |
+
z_new = random.randint(0, self.H - lz)
|
| 403 |
+
new_anchor = (x_new, y_new, z_new)
|
| 404 |
+
old_anchor = inst.anchor
|
| 405 |
+
|
| 406 |
+
if new_anchor == old_anchor:
|
| 407 |
+
continue
|
| 408 |
+
|
| 409 |
+
# Validate: new footprint is void or self-owned
|
| 410 |
+
if not self._cells_clear(current_state, x_new, y_new, z_new, lx, ly, lz, allow_id=inst_id):
|
| 411 |
+
continue
|
| 412 |
+
|
| 413 |
+
old_cells = inst.cells_at(old_anchor)
|
| 414 |
+
new_cells = inst.cells_at(new_anchor)
|
| 415 |
+
changed_cells = set(old_cells) | set(new_cells)
|
| 416 |
+
affected_ids = self._instances_reading_cells(changed_cells)
|
| 417 |
+
affected_ids.add(inst_id)
|
| 418 |
+
|
| 419 |
+
# Score before
|
| 420 |
+
before_local = self._evaluate_local_penalty(current_state, changed_cells, affected_ids)
|
| 421 |
+
|
| 422 |
+
# Apply move
|
| 423 |
+
for (x, y, z) in old_cells:
|
| 424 |
+
current_state[x, y, z] = 0
|
| 425 |
+
for (x, y, z) in new_cells:
|
| 426 |
+
current_state[x, y, z] = inst_id
|
| 427 |
+
inst.anchor = new_anchor
|
| 428 |
+
self._update_work_cache_for_move(inst, old_anchor, new_anchor)
|
| 429 |
+
|
| 430 |
+
# Score after (note: instance.anchor is now new_anchor, so its blocking is
|
| 431 |
+
# evaluated at the new position; affected_ids set doesn't change because it
|
| 432 |
+
# already included this instance.)
|
| 433 |
+
after_local = self._evaluate_local_penalty(current_state, changed_cells, affected_ids)
|
| 434 |
+
delta_P = after_local - before_local
|
| 435 |
+
proposed_P = current_P + delta_P
|
| 436 |
+
proposed_W = self._work_from_cache()
|
| 437 |
+
delta_W = proposed_W - current_W
|
| 438 |
+
delta_H = delta_W + gamma * delta_P
|
| 439 |
+
|
| 440 |
+
if delta_H < 0 or random.random() < math.exp(-delta_H / max(T, 1e-12)):
|
| 441 |
+
current_P = proposed_P
|
| 442 |
+
current_W = proposed_W
|
| 443 |
+
current_H = current_W + gamma_max * current_P
|
| 444 |
+
if current_H < best_H:
|
| 445 |
+
best_H = current_H
|
| 446 |
+
best_state = np.copy(current_state)
|
| 447 |
+
if current_P == 0 and current_W < best_feasible_W:
|
| 448 |
+
best_feasible_W = current_W
|
| 449 |
+
best_feasible_state = np.copy(current_state)
|
| 450 |
+
else:
|
| 451 |
+
# Revert
|
| 452 |
+
for (x, y, z) in new_cells:
|
| 453 |
+
current_state[x, y, z] = 0
|
| 454 |
+
for (x, y, z) in old_cells:
|
| 455 |
+
current_state[x, y, z] = inst_id
|
| 456 |
+
inst.anchor = old_anchor
|
| 457 |
+
self._update_work_cache_for_move(inst, new_anchor, old_anchor)
|
| 458 |
+
|
| 459 |
+
result_state = best_feasible_state if best_feasible_state is not None else best_state
|
| 460 |
+
# Sync instance anchors to the returned state — best_state was np.copy'd at peak
|
| 461 |
+
# quality, but self._instances has been moving along with current_state since.
|
| 462 |
+
self._sync_instances_to_state(result_state)
|
| 463 |
+
return result_state, self.physical_penalty(result_state), self.spatial_work(result_state)
|
| 464 |
+
|
| 465 |
+
# ---------- result inspection ----------
|
| 466 |
+
|
| 467 |
+
def _sync_instances_to_state(self, state):
|
| 468 |
+
"""Set each instance's anchor to its min-corner in `state`. Drops instances
|
| 469 |
+
whose cells aren't found (shouldn't happen for an internally produced state)."""
|
| 470 |
+
missing = []
|
| 471 |
+
for inst_id, inst in self._instances.items():
|
| 472 |
+
cells = np.argwhere(state == inst_id)
|
| 473 |
+
if len(cells) == 0:
|
| 474 |
+
missing.append(inst_id)
|
| 475 |
+
continue
|
| 476 |
+
mn = cells.min(axis=0)
|
| 477 |
+
inst.anchor = (int(mn[0]), int(mn[1]), int(mn[2]))
|
| 478 |
+
for inst_id in missing:
|
| 479 |
+
del self._instances[inst_id]
|
| 480 |
+
|
| 481 |
+
def get_layout(self, state=None):
|
| 482 |
+
"""
|
| 483 |
+
Return the optimized placement as a list of dicts, one per instance:
|
| 484 |
+
{id, client, item_type, is_return, anchor, shape, cells}
|
| 485 |
+
|
| 486 |
+
With no argument, reads the current synced anchors (set by optimize()).
|
| 487 |
+
Pass a state array to derive positions directly from it instead.
|
| 488 |
+
"""
|
| 489 |
+
layout = []
|
| 490 |
+
if state is None:
|
| 491 |
+
for inst in self._instances.values():
|
| 492 |
+
layout.append({
|
| 493 |
+
"id": inst.id,
|
| 494 |
+
"client": inst.client,
|
| 495 |
+
"item_type": inst.item_type,
|
| 496 |
+
"is_return": inst.is_return,
|
| 497 |
+
"anchor": inst.anchor,
|
| 498 |
+
"shape": inst.shape,
|
| 499 |
+
"cells": inst.cells,
|
| 500 |
+
})
|
| 501 |
+
else:
|
| 502 |
+
for inst in self._instances.values():
|
| 503 |
+
cells = np.argwhere(state == inst.id)
|
| 504 |
+
if len(cells) == 0:
|
| 505 |
+
continue
|
| 506 |
+
anchor = tuple(int(v) for v in cells.min(axis=0))
|
| 507 |
+
layout.append({
|
| 508 |
+
"id": inst.id,
|
| 509 |
+
"client": inst.client,
|
| 510 |
+
"item_type": inst.item_type,
|
| 511 |
+
"is_return": inst.is_return,
|
| 512 |
+
"anchor": anchor,
|
| 513 |
+
"shape": inst.shape,
|
| 514 |
+
"cells": [tuple(int(v) for v in c) for c in cells],
|
| 515 |
+
})
|
| 516 |
+
return layout
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
# --- EXECUTION ---
|
| 520 |
+
if __name__ == "__main__":
|
| 521 |
+
optimizer = SmartTruckOptimizer3D(
|
| 522 |
+
length_bays=8, width_pallets=4, height_layers=3, route=[1, 2, 3],
|
| 523 |
+
item_shapes={
|
| 524 |
+
"keg": (1, 1, 1),
|
| 525 |
+
"crate": (2, 1, 1),
|
| 526 |
+
"tower": (1, 1, 2),
|
| 527 |
+
},
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
# {client: {item_type: total_count}}
|
| 531 |
+
orders = {
|
| 532 |
+
1: {"keg": 12, "crate": 4},
|
| 533 |
+
2: {"keg": 18, "crate": 6},
|
| 534 |
+
3: {"keg": 15, "tower": 4},
|
| 535 |
+
}
|
| 536 |
+
# {client: {item_type: returns_count}} — must be ≤ total per (client, item)
|
| 537 |
+
returns = {
|
| 538 |
+
1: {"keg": 8},
|
| 539 |
+
2: {"keg": 5},
|
| 540 |
+
3: {"keg": 10},
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
initial = optimizer.generate_initial_state(orders, returns)
|
| 544 |
+
print("Initial 3D Penalty: ", optimizer.physical_penalty(initial))
|
| 545 |
+
print(f"Initial Work (normalized): {optimizer.spatial_work(initial):.4f}")
|
| 546 |
+
|
| 547 |
+
final_state, final_P, final_W = optimizer.optimize(initial, steps=30000)
|
| 548 |
+
|
| 549 |
+
print(f"\nOptimization Complete.")
|
| 550 |
+
print(f"Final Physical Violations: {final_P}")
|
| 551 |
+
print(f"Final Work Metric: {final_W:.4f}")
|
| 552 |
+
|
| 553 |
+
if final_P == 0:
|
| 554 |
+
print("\nValid 3D Lattice Layout generated successfully.")
|
| 555 |
+
else:
|
| 556 |
+
print("\nOptimizer trapped in local minimum. Try increasing 'steps'.")
|
| 557 |
+
|
| 558 |
+
layout = optimizer.get_layout()
|
| 559 |
+
print(f"\nLayout: {len(layout)} items")
|
| 560 |
+
for item in sorted(layout, key=lambda i: (i["client"], i["anchor"])):
|
| 561 |
+
kind = "return" if item["is_return"] else "delivery"
|
| 562 |
+
print(f" client={item['client']} {item['item_type']:<6} {kind:<8} "
|
| 563 |
+
f"anchor={item['anchor']} shape={item['shape']}")
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
|
| 567 |
+
|
| 568 |
+
'''
|
| 569 |
+
Input format for generate_initial_state:
|
| 570 |
+
orders = {client_id: {item_type: total_count}}
|
| 571 |
+
returns = {client_id: {item_type: returns_count}}
|
| 572 |
+
'''
|
products.json
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"products": [
|
| 3 |
+
{
|
| 4 |
+
"id": "1_not_returnable_brand1",
|
| 5 |
+
"length_cells": 1,
|
| 6 |
+
"width_cells": 1,
|
| 7 |
+
"height_cells": 1,
|
| 8 |
+
"weight_kg": 10.0,
|
| 9 |
+
"is_returnable": false,
|
| 10 |
+
"brand": "brand1"
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"id": "2_not_returnable_brand1",
|
| 14 |
+
"length_cells": 1,
|
| 15 |
+
"width_cells": 1,
|
| 16 |
+
"height_cells": 2,
|
| 17 |
+
"weight_kg": 13.0,
|
| 18 |
+
"is_returnable": false,
|
| 19 |
+
"brand": "brand1"
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"id": "1_returnable_brand1",
|
| 23 |
+
"length_cells": 1,
|
| 24 |
+
"width_cells": 1,
|
| 25 |
+
"height_cells": 1,
|
| 26 |
+
"weight_kg": 15.0,
|
| 27 |
+
"is_returnable": true,
|
| 28 |
+
"brand": "brand1"
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"id": "2_returnable_brand1",
|
| 32 |
+
"length_cells": 1,
|
| 33 |
+
"width_cells": 1,
|
| 34 |
+
"height_cells": 2,
|
| 35 |
+
"weight_kg": 18.0,
|
| 36 |
+
"is_returnable": true,
|
| 37 |
+
"brand": "brand1"
|
| 38 |
+
},
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
{
|
| 44 |
+
"id": "1_not_returnable_brand2",
|
| 45 |
+
"length_cells": 1,
|
| 46 |
+
"width_cells": 1,
|
| 47 |
+
"height_cells": 1,
|
| 48 |
+
"weight_kg": 10.0,
|
| 49 |
+
"is_returnable": false,
|
| 50 |
+
"brand": "brand2"
|
| 51 |
+
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"id": "2_not_returnable_brand2",
|
| 55 |
+
"length_cells": 1,
|
| 56 |
+
"width_cells": 1,
|
| 57 |
+
"height_cells": 2,
|
| 58 |
+
"weight_kg": 13.0,
|
| 59 |
+
"is_returnable": false,
|
| 60 |
+
"brand": "brand2"
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"id": "1_returnable_brand2",
|
| 64 |
+
"length_cells": 1,
|
| 65 |
+
"width_cells": 1,
|
| 66 |
+
"height_cells": 1,
|
| 67 |
+
"weight_kg": 15.0,
|
| 68 |
+
"is_returnable": true,
|
| 69 |
+
"brand": "brand2"
|
| 70 |
+
},
|
| 71 |
+
{
|
| 72 |
+
"id": "2_returnable_brand2",
|
| 73 |
+
"length_cells": 1,
|
| 74 |
+
"width_cells": 1,
|
| 75 |
+
"height_cells": 2,
|
| 76 |
+
"weight_kg": 18.0,
|
| 77 |
+
"is_returnable": true,
|
| 78 |
+
"brand": "brand2"
|
| 79 |
+
},
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
{
|
| 87 |
+
"id": "1_not_returnable_brand3",
|
| 88 |
+
"length_cells": 1,
|
| 89 |
+
"width_cells": 1,
|
| 90 |
+
"height_cells": 1,
|
| 91 |
+
"weight_kg": 10.0,
|
| 92 |
+
"is_returnable": false,
|
| 93 |
+
"brand": "brand3"
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"id": "2_not_returnable_brand3",
|
| 97 |
+
"length_cells": 1,
|
| 98 |
+
"width_cells": 1,
|
| 99 |
+
"height_cells": 2,
|
| 100 |
+
"weight_kg": 13.0,
|
| 101 |
+
"is_returnable": false,
|
| 102 |
+
"brand": "brand3"
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"id": "1_returnable_brand3",
|
| 106 |
+
"length_cells": 1,
|
| 107 |
+
"width_cells": 1,
|
| 108 |
+
"height_cells": 1,
|
| 109 |
+
"weight_kg": 15.0,
|
| 110 |
+
"is_returnable": true,
|
| 111 |
+
"brand": "brand3"
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"id": "2_returnable_brand3",
|
| 115 |
+
"length_cells": 1,
|
| 116 |
+
"width_cells": 1,
|
| 117 |
+
"height_cells": 2,
|
| 118 |
+
"weight_kg": 18.0,
|
| 119 |
+
"is_returnable": true,
|
| 120 |
+
"brand": "brand3"
|
| 121 |
+
}
|
| 122 |
+
]
|
| 123 |
+
}
|
render.yaml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
- type: web
|
| 3 |
+
name: damm-truck-api
|
| 4 |
+
runtime: python
|
| 5 |
+
region: frankfurt # closest Render region to Barcelona
|
| 6 |
+
plan: standard # 2 GB RAM. Starter (512 MB) risks OOM on graph load.
|
| 7 |
+
rootDir: backend # FastAPI app lives in /backend in the repo
|
| 8 |
+
pythonVersion: "3.12"
|
| 9 |
+
buildCommand: pip install -r requirements.txt
|
| 10 |
+
startCommand: uvicorn app:app --host 0.0.0.0 --port $PORT --workers 1
|
| 11 |
+
healthCheckPath: /health
|
| 12 |
+
autoDeploy: true
|
| 13 |
+
envVars:
|
| 14 |
+
- key: MPLBACKEND
|
| 15 |
+
value: Agg
|
| 16 |
+
- key: PYTHONUNBUFFERED
|
| 17 |
+
value: "1"
|
requirements.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
osmnx>=2.0
|
| 2 |
+
networkx>=3.0
|
| 3 |
+
shapely>=2.0
|
| 4 |
+
matplotlib>=3.7
|
| 5 |
+
pydantic>=2.5
|
| 6 |
+
numpy>=1.26
|
| 7 |
+
scikit-learn>=1.2
|
| 8 |
+
pyproj>=3.6
|
| 9 |
+
fastapi>=0.115
|
| 10 |
+
uvicorn[standard]>=0.30
|
| 11 |
+
pyproj>=3.6
|
| 12 |
+
ortools>=9.10
|
| 13 |
+
firebase-admin>=6.5
|
| 14 |
+
anthropic>=0.40.0
|
| 15 |
+
httpx>=0.27.2
|
sample_request.json
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"request_id": "REQ-2026-05-10-001",
|
| 3 |
+
"date": "2026-05-10",
|
| 4 |
+
|
| 5 |
+
"depot": {
|
| 6 |
+
"id": "WH-GR",
|
| 7 |
+
"coords": { "lat": 41.5750, "lng": 2.2500 },
|
| 8 |
+
"open": "07:00",
|
| 9 |
+
"close": "20:00"
|
| 10 |
+
},
|
| 11 |
+
|
| 12 |
+
"fleet": {
|
| 13 |
+
"num_vans": 3,
|
| 14 |
+
"van_type": "8_pallets",
|
| 15 |
+
"vans_ref": "vans.json",
|
| 16 |
+
"products_ref": "products.json"
|
| 17 |
+
},
|
| 18 |
+
|
| 19 |
+
"drivers": [
|
| 20 |
+
{ "id": "D01", "shift_start": "07:00", "shift_end": "15:00" },
|
| 21 |
+
{ "id": "D02", "shift_start": "07:00", "shift_end": "15:00" },
|
| 22 |
+
{ "id": "D03", "shift_start": "07:30", "shift_end": "15:30" }
|
| 23 |
+
],
|
| 24 |
+
|
| 25 |
+
"stops": [
|
| 26 |
+
{
|
| 27 |
+
"id": "S001",
|
| 28 |
+
"coords": { "lat": 41.6052739, "lng": 2.2928775 },
|
| 29 |
+
"time_window": { "open": "08:00", "close": "12:00" },
|
| 30 |
+
"deliveries": [
|
| 31 |
+
{ "product_id": "1_not_returnable_brand1", "qty": 24 },
|
| 32 |
+
{ "product_id": "2_not_returnable_brand2", "qty": 12 },
|
| 33 |
+
{ "product_id": "1_returnable_brand1", "qty": 18 }
|
| 34 |
+
],
|
| 35 |
+
"pickups": [
|
| 36 |
+
{ "product_id": "1_returnable_brand1", "qty": 18 },
|
| 37 |
+
{ "product_id": "2_returnable_brand2", "qty": 6 }
|
| 38 |
+
]
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"id": "S002",
|
| 42 |
+
"coords": { "lat": 41.5975345, "lng": 2.2884292 },
|
| 43 |
+
"time_window": { "open": "08:30", "close": "10:30" },
|
| 44 |
+
"deliveries": [
|
| 45 |
+
{ "product_id": "2_not_returnable_brand1", "qty": 8 },
|
| 46 |
+
{ "product_id": "2_returnable_brand2", "qty": 16 }
|
| 47 |
+
],
|
| 48 |
+
"pickups": [
|
| 49 |
+
{ "product_id": "2_returnable_brand2", "qty": 16 }
|
| 50 |
+
]
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"id": "S003",
|
| 54 |
+
"coords": { "lat": 41.5428223, "lng": 2.2161158 },
|
| 55 |
+
"time_window": { "open": "09:00", "close": "13:00" },
|
| 56 |
+
"deliveries": [
|
| 57 |
+
{ "product_id": "1_not_returnable_brand3", "qty": 30 },
|
| 58 |
+
{ "product_id": "1_returnable_brand1", "qty": 20 }
|
| 59 |
+
],
|
| 60 |
+
"pickups": [
|
| 61 |
+
{ "product_id": "1_returnable_brand1", "qty": 25 }
|
| 62 |
+
]
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"id": "S004",
|
| 66 |
+
"coords": { "lat": 41.5393484, "lng": 2.2130934 },
|
| 67 |
+
"time_window": { "open": "09:00", "close": "11:00" },
|
| 68 |
+
"deliveries": [
|
| 69 |
+
{ "product_id": "2_not_returnable_brand2", "qty": 14 },
|
| 70 |
+
{ "product_id": "2_returnable_brand3", "qty": 10 }
|
| 71 |
+
],
|
| 72 |
+
"pickups": [
|
| 73 |
+
{ "product_id": "2_returnable_brand3", "qty": 8 }
|
| 74 |
+
]
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"id": "S005",
|
| 78 |
+
"coords": { "lat": 41.5479180, "lng": 2.2083603 },
|
| 79 |
+
"time_window": { "open": "10:00", "close": "14:00" },
|
| 80 |
+
"deliveries": [
|
| 81 |
+
{ "product_id": "1_not_returnable_brand2", "qty": 18 },
|
| 82 |
+
{ "product_id": "2_not_returnable_brand2", "qty": 6 }
|
| 83 |
+
],
|
| 84 |
+
"pickups": []
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"id": "S006",
|
| 88 |
+
"coords": { "lat": 41.5484516, "lng": 2.2067478 },
|
| 89 |
+
"time_window": { "open": "08:00", "close": "11:30" },
|
| 90 |
+
"deliveries": [
|
| 91 |
+
{ "product_id": "1_returnable_brand3", "qty": 36 }
|
| 92 |
+
],
|
| 93 |
+
"pickups": [
|
| 94 |
+
{ "product_id": "1_returnable_brand3", "qty": 30 }
|
| 95 |
+
]
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"id": "S007",
|
| 99 |
+
"coords": { "lat": 41.5450720, "lng": 2.2056984 },
|
| 100 |
+
"time_window": { "open": "11:00", "close": "13:00" },
|
| 101 |
+
"deliveries": [
|
| 102 |
+
{ "product_id": "1_not_returnable_brand1", "qty": 10 },
|
| 103 |
+
{ "product_id": "2_returnable_brand1", "qty": 14 }
|
| 104 |
+
],
|
| 105 |
+
"pickups": [
|
| 106 |
+
{ "product_id": "2_returnable_brand1", "qty": 12 }
|
| 107 |
+
]
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"id": "S008",
|
| 111 |
+
"coords": { "lat": 41.5381651, "lng": 2.2113491 },
|
| 112 |
+
"time_window": { "open": "08:30", "close": "12:30" },
|
| 113 |
+
"deliveries": [
|
| 114 |
+
{ "product_id": "2_not_returnable_brand3", "qty": 20 },
|
| 115 |
+
{ "product_id": "1_returnable_brand2", "qty": 10 }
|
| 116 |
+
],
|
| 117 |
+
"pickups": [
|
| 118 |
+
{ "product_id": "1_returnable_brand2", "qty": 8 }
|
| 119 |
+
]
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"id": "S009",
|
| 123 |
+
"coords": { "lat": 41.5367766, "lng": 2.2135257 },
|
| 124 |
+
"time_window": { "open": "09:30", "close": "13:30" },
|
| 125 |
+
"deliveries": [
|
| 126 |
+
{ "product_id": "1_not_returnable_brand2", "qty": 22 },
|
| 127 |
+
{ "product_id": "2_not_returnable_brand1", "qty": 8 },
|
| 128 |
+
{ "product_id": "2_returnable_brand2", "qty": 12 }
|
| 129 |
+
],
|
| 130 |
+
"pickups": [
|
| 131 |
+
{ "product_id": "2_returnable_brand2", "qty": 10 }
|
| 132 |
+
]
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"id": "S010",
|
| 136 |
+
"coords": { "lat": 41.5443121, "lng": 2.2194830 },
|
| 137 |
+
"time_window": { "open": "10:30", "close": "14:30" },
|
| 138 |
+
"deliveries": [
|
| 139 |
+
{ "product_id": "1_returnable_brand1", "qty": 14 },
|
| 140 |
+
{ "product_id": "2_returnable_brand3", "qty": 8 }
|
| 141 |
+
],
|
| 142 |
+
"pickups": [
|
| 143 |
+
{ "product_id": "1_returnable_brand1", "qty": 12 },
|
| 144 |
+
{ "product_id": "2_returnable_brand3", "qty": 6 }
|
| 145 |
+
]
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"id": "S011",
|
| 149 |
+
"coords": { "lat": 41.5377848, "lng": 2.2129105 },
|
| 150 |
+
"time_window": { "open": "08:00", "close": "10:00" },
|
| 151 |
+
"deliveries": [
|
| 152 |
+
{ "product_id": "1_not_returnable_brand3", "qty": 16 }
|
| 153 |
+
],
|
| 154 |
+
"pickups": []
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
"id": "S012",
|
| 158 |
+
"coords": { "lat": 41.5373051, "lng": 2.2120439 },
|
| 159 |
+
"time_window": { "open": "11:30", "close": "14:00" },
|
| 160 |
+
"deliveries": [
|
| 161 |
+
{ "product_id": "2_not_returnable_brand1", "qty": 10 },
|
| 162 |
+
{ "product_id": "1_returnable_brand2", "qty": 24 }
|
| 163 |
+
],
|
| 164 |
+
"pickups": [
|
| 165 |
+
{ "product_id": "1_returnable_brand2", "qty": 20 }
|
| 166 |
+
]
|
| 167 |
+
}
|
| 168 |
+
]
|
| 169 |
+
}
|
solver.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""OR-tools VRPSPD-TW solver.
|
| 2 |
+
|
| 3 |
+
One-shot solve: capacity (cells + kg), per-stop time windows, per-driver
|
| 4 |
+
shifts, simultaneous pickup-delivery, single depot.
|
| 5 |
+
|
| 6 |
+
Capacity model is the conservative VRPSPD bound:
|
| 7 |
+
sum(delivery_cells + pickup_cells on route) <= van_capacity_cells
|
| 8 |
+
sum(delivery_kg + pickup_kg on route) <= van_capacity_kg
|
| 9 |
+
|
| 10 |
+
This bounds the *peak* in-truck load by the *sum* of inflow + outflow on the
|
| 11 |
+
route. Slightly suboptimal vs. the exact load-profile model but correct
|
| 12 |
+
(never accepts an infeasible plan) and fast.
|
| 13 |
+
|
| 14 |
+
Objective: minimize total travel time across the fleet.
|
| 15 |
+
Time window + shift constraints are HARD (set via SetRange).
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
from __future__ import annotations
|
| 19 |
+
|
| 20 |
+
from dataclasses import dataclass, field
|
| 21 |
+
|
| 22 |
+
from ortools.constraint_solver import pywrapcp, routing_enums_pb2
|
| 23 |
+
|
| 24 |
+
from loader import Depot, Driver, Fleet, Stop
|
| 25 |
+
from travel_time import TravelMatrix
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@dataclass
|
| 29 |
+
class StopPlan:
|
| 30 |
+
sequence: int
|
| 31 |
+
id: str
|
| 32 |
+
arrival_s: int
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@dataclass
|
| 36 |
+
class VanPlan:
|
| 37 |
+
van_idx: int
|
| 38 |
+
driver_id: str
|
| 39 |
+
stops: list[StopPlan]
|
| 40 |
+
travel_s: int
|
| 41 |
+
total_s: int # depot-to-depot wall clock incl. waits + service
|
| 42 |
+
peak_cells: int
|
| 43 |
+
peak_kg: int
|
| 44 |
+
feasible: bool
|
| 45 |
+
violations: list[str] = field(default_factory=list)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@dataclass
|
| 49 |
+
class FleetPlan:
|
| 50 |
+
vans: list[VanPlan]
|
| 51 |
+
drive_s: int
|
| 52 |
+
total_s: int
|
| 53 |
+
all_feasible: bool
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def solve_vrp(
|
| 57 |
+
depot: Depot,
|
| 58 |
+
fleet: Fleet,
|
| 59 |
+
drivers: list[Driver],
|
| 60 |
+
stops: list[Stop],
|
| 61 |
+
matrix: TravelMatrix,
|
| 62 |
+
time_limit_s: int = 5,
|
| 63 |
+
) -> FleetPlan:
|
| 64 |
+
"""Index 0 = depot, 1..n = stops in input order."""
|
| 65 |
+
n = len(stops) + 1
|
| 66 |
+
K = fleet.num_vans
|
| 67 |
+
H = 24 * 3600
|
| 68 |
+
|
| 69 |
+
manager = pywrapcp.RoutingIndexManager(n, K, 0)
|
| 70 |
+
routing = pywrapcp.RoutingModel(manager)
|
| 71 |
+
|
| 72 |
+
# Arc cost = pure travel time (objective).
|
| 73 |
+
def arc_cb(fi, ti):
|
| 74 |
+
i, j = manager.IndexToNode(fi), manager.IndexToNode(ti)
|
| 75 |
+
return int(matrix.time_s[i, j])
|
| 76 |
+
routing.SetArcCostEvaluatorOfAllVehicles(routing.RegisterTransitCallback(arc_cb))
|
| 77 |
+
|
| 78 |
+
# Time dimension = travel + service at "from" node.
|
| 79 |
+
def time_cb(fi, ti):
|
| 80 |
+
i, j = manager.IndexToNode(fi), manager.IndexToNode(ti)
|
| 81 |
+
s = stops[i - 1].service_time_s if i >= 1 else 0
|
| 82 |
+
return int(matrix.time_s[i, j] + s)
|
| 83 |
+
time_idx = routing.RegisterTransitCallback(time_cb)
|
| 84 |
+
routing.AddDimension(time_idx, H, H, False, "Time")
|
| 85 |
+
time_dim = routing.GetDimensionOrDie("Time")
|
| 86 |
+
|
| 87 |
+
for vid, d in enumerate(drivers):
|
| 88 |
+
time_dim.CumulVar(routing.Start(vid)).SetRange(d.shift_start_s, d.shift_end_s)
|
| 89 |
+
time_dim.CumulVar(routing.End(vid)).SetRange(d.shift_start_s, d.shift_end_s)
|
| 90 |
+
for i, s in enumerate(stops):
|
| 91 |
+
time_dim.CumulVar(manager.NodeToIndex(i + 1)).SetRange(s.t_open_s, s.t_close_s)
|
| 92 |
+
|
| 93 |
+
# Capacity: cells.
|
| 94 |
+
def cells_cb(fi):
|
| 95 |
+
i = manager.IndexToNode(fi)
|
| 96 |
+
return 0 if i == 0 else stops[i - 1].delivery_cells + stops[i - 1].pickup_cells
|
| 97 |
+
routing.AddDimensionWithVehicleCapacity(
|
| 98 |
+
routing.RegisterUnaryTransitCallback(cells_cb),
|
| 99 |
+
0, [fleet.capacity_cells] * K, True, "Cells",
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
# Capacity: kg.
|
| 103 |
+
def kg_cb(fi):
|
| 104 |
+
i = manager.IndexToNode(fi)
|
| 105 |
+
return 0 if i == 0 else int(round(stops[i - 1].delivery_kg + stops[i - 1].pickup_kg))
|
| 106 |
+
routing.AddDimensionWithVehicleCapacity(
|
| 107 |
+
routing.RegisterUnaryTransitCallback(kg_cb),
|
| 108 |
+
0, [int(fleet.capacity_kg)] * K, True, "Kg",
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
sp = pywrapcp.DefaultRoutingSearchParameters()
|
| 112 |
+
sp.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
|
| 113 |
+
sp.local_search_metaheuristic = routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
|
| 114 |
+
sp.time_limit.seconds = int(time_limit_s)
|
| 115 |
+
|
| 116 |
+
sol = routing.SolveWithParameters(sp)
|
| 117 |
+
if sol is None:
|
| 118 |
+
return FleetPlan([], 0, 0, False)
|
| 119 |
+
|
| 120 |
+
vans: list[VanPlan] = []
|
| 121 |
+
fleet_drive = fleet_total = 0
|
| 122 |
+
all_feas = True
|
| 123 |
+
for vid in range(K):
|
| 124 |
+
idx = routing.Start(vid)
|
| 125 |
+
plan: list[StopPlan] = []
|
| 126 |
+
seq = peak_c = peak_k = cum_c = cum_k = v_drive = 0
|
| 127 |
+
while not routing.IsEnd(idx):
|
| 128 |
+
node = manager.IndexToNode(idx)
|
| 129 |
+
if node != 0:
|
| 130 |
+
seq += 1
|
| 131 |
+
arr = sol.Value(time_dim.CumulVar(idx))
|
| 132 |
+
s = stops[node - 1]
|
| 133 |
+
cum_c += s.delivery_cells + s.pickup_cells
|
| 134 |
+
cum_k += int(round(s.delivery_kg + s.pickup_kg))
|
| 135 |
+
peak_c = max(peak_c, cum_c)
|
| 136 |
+
peak_k = max(peak_k, cum_k)
|
| 137 |
+
plan.append(StopPlan(seq, s.id, int(arr)))
|
| 138 |
+
nxt = sol.Value(routing.NextVar(idx))
|
| 139 |
+
v_drive += int(matrix.time_s[manager.IndexToNode(idx), manager.IndexToNode(nxt)])
|
| 140 |
+
idx = nxt
|
| 141 |
+
end_t = sol.Value(time_dim.CumulVar(routing.End(vid)))
|
| 142 |
+
v_total = end_t - drivers[vid].shift_start_s
|
| 143 |
+
feas = v_total <= drivers[vid].shift_end_s - drivers[vid].shift_start_s
|
| 144 |
+
vans.append(VanPlan(
|
| 145 |
+
van_idx=vid,
|
| 146 |
+
driver_id=drivers[vid].id,
|
| 147 |
+
stops=plan,
|
| 148 |
+
travel_s=v_drive,
|
| 149 |
+
total_s=v_total,
|
| 150 |
+
peak_cells=peak_c,
|
| 151 |
+
peak_kg=peak_k,
|
| 152 |
+
feasible=feas,
|
| 153 |
+
))
|
| 154 |
+
fleet_drive += v_drive
|
| 155 |
+
fleet_total += v_total
|
| 156 |
+
all_feas &= feas
|
| 157 |
+
return FleetPlan(vans, fleet_drive, fleet_total, all_feas)
|
travel_time.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:361d146d55bef6afbbb6275213cd05167636f6248ddfc7c4e44a94851f64c250
|
| 3 |
+
size 752764
|
travel_time.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""All-pairs travel-time + distance matrix between snapped OSM road nodes.
|
| 2 |
+
|
| 3 |
+
Snaps each (lat, lng) to its nearest road node, then runs one Dijkstra per
|
| 4 |
+
unique source on the *directed* graph (one-ways respected). The matrix is
|
| 5 |
+
asymmetric and dense — every fitness evaluation downstream is now an O(1)
|
| 6 |
+
numpy lookup.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
import csv
|
| 12 |
+
import json
|
| 13 |
+
import time
|
| 14 |
+
from dataclasses import dataclass
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from dataclasses import dataclass
|
| 17 |
+
|
| 18 |
+
import networkx as nx
|
| 19 |
+
import numpy as np
|
| 20 |
+
import osmnx as ox
|
| 21 |
+
import pyproj
|
| 22 |
+
|
| 23 |
+
from graph_manager import DEFAULT_COORDS_PATH, get_or_build_graph
|
| 24 |
+
|
| 25 |
+
DEFAULT_MATRIX_PATH = Path(__file__).with_name("travel_time.npz")
|
| 26 |
+
|
| 27 |
+
@dataclass
|
| 28 |
+
class TravelMatrix:
|
| 29 |
+
point_ids: list[str]
|
| 30 |
+
node_ids: list[int]
|
| 31 |
+
time_s: np.ndarray
|
| 32 |
+
dist_m: np.ndarray
|
| 33 |
+
|
| 34 |
+
def index_of(self, point_id: str) -> int:
|
| 35 |
+
return self.point_ids.index(point_id)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _snap(graph: nx.MultiDiGraph, points: list[tuple[float, float]]) -> list[int]:
|
| 39 |
+
proj = ox.projection.project_graph(graph)
|
| 40 |
+
transformer = pyproj.Transformer.from_crs(
|
| 41 |
+
graph.graph["crs"], proj.graph["crs"], always_xy=True
|
| 42 |
+
)
|
| 43 |
+
lats = [p[0] for p in points]
|
| 44 |
+
lngs = [p[1] for p in points]
|
| 45 |
+
proj_x, proj_y = transformer.transform(lngs, lats)
|
| 46 |
+
return list(ox.distance.nearest_nodes(proj, X=list(proj_x), Y=list(proj_y)))
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def build_matrix(
|
| 50 |
+
graph: nx.MultiDiGraph,
|
| 51 |
+
points: list[tuple[str, float, float]],
|
| 52 |
+
) -> TravelMatrix:
|
| 53 |
+
point_ids = [p[0] for p in points]
|
| 54 |
+
node_ids = _snap(graph, [(p[1], p[2]) for p in points])
|
| 55 |
+
|
| 56 |
+
n = len(points)
|
| 57 |
+
time_s = np.full((n, n), np.inf, dtype=np.float32)
|
| 58 |
+
dist_m = np.full((n, n), np.inf, dtype=np.float32)
|
| 59 |
+
|
| 60 |
+
by_node: dict[int, list[int]] = {}
|
| 61 |
+
for i, nd in enumerate(node_ids):
|
| 62 |
+
by_node.setdefault(nd, []).append(i)
|
| 63 |
+
targets = set(node_ids)
|
| 64 |
+
|
| 65 |
+
for src_node, src_rows in by_node.items():
|
| 66 |
+
t_to = nx.single_source_dijkstra_path_length(graph, src_node, weight="travel_time")
|
| 67 |
+
d_to = nx.single_source_dijkstra_path_length(graph, src_node, weight="length")
|
| 68 |
+
for tgt in targets:
|
| 69 |
+
t = t_to.get(tgt, np.inf)
|
| 70 |
+
d = d_to.get(tgt, np.inf)
|
| 71 |
+
for j in by_node[tgt]:
|
| 72 |
+
for i in src_rows:
|
| 73 |
+
time_s[i, j] = t
|
| 74 |
+
dist_m[i, j] = d
|
| 75 |
+
np.fill_diagonal(time_s, 0.0)
|
| 76 |
+
np.fill_diagonal(dist_m, 0.0)
|
| 77 |
+
return TravelMatrix(point_ids, node_ids, time_s, dist_m)
|
travel_time_request.npz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b4b79abedec4c14ebad35c31033265bbdefe5b6667a59e43fac901d2a5ebf4c2
|
| 3 |
+
size 2318
|
vans.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_reference": "Mitsubishi Fuso Canter 515 Wide Cab Pantech (8_pallets); 6_pallets is a shorter chassis variant.",
|
| 3 |
+
"_units": {
|
| 4 |
+
"length_m": "interior length",
|
| 5 |
+
"width_m": "interior width",
|
| 6 |
+
"height_m": "interior height",
|
| 7 |
+
"cube_size_m": "discretization step for the lattice loader",
|
| 8 |
+
"pallet_length_m": "EUR pallet long side",
|
| 9 |
+
"pallet_width_m": "EUR pallet short side",
|
| 10 |
+
"pallet_height_m": "empty pallet board thickness"
|
| 11 |
+
},
|
| 12 |
+
"van_types": [
|
| 13 |
+
{
|
| 14 |
+
"type": "8_pallets",
|
| 15 |
+
"length_m": 4.8,
|
| 16 |
+
"width_m": 2.4,
|
| 17 |
+
"height_m": 2.1,
|
| 18 |
+
"cube_size_m": 0.4,
|
| 19 |
+
"pallet_length_m": 1.2,
|
| 20 |
+
"pallet_width_m": 1.2,
|
| 21 |
+
"pallet_height_m": 0.15,
|
| 22 |
+
"max_payload_kg": 3000
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"type": "6_pallets",
|
| 26 |
+
"length_m": 3.6,
|
| 27 |
+
"width_m": 2.4,
|
| 28 |
+
"height_m": 2.1,
|
| 29 |
+
"cube_size_m": 0.4,
|
| 30 |
+
"pallet_length_m": 1.2,
|
| 31 |
+
"pallet_width_m": 1.2,
|
| 32 |
+
"pallet_height_m": 0.15,
|
| 33 |
+
"max_payload_kg": 2200
|
| 34 |
+
}
|
| 35 |
+
],
|
| 36 |
+
"comments": "cube size = 0.4 / 5, Input format for generate_initial_state: orders = {client_id: {item_type: total_count}}, returns = {client_id: {item_type: returns_count}}"
|
| 37 |
+
}
|