danvancea commited on
Commit
4e22ad8
·
verified ·
1 Parent(s): c168162

Upload 34 files

Browse files
.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

  • SHA256: d93985e744189a295aebf5a341ec16537db158a83fab1a68aa76e2509a04dd40
  • Pointer size: 131 Bytes
  • Size of remote file: 242 kB
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
+ }