Add the geo trees for SE and US as linked tables, and build each locale's address on them #20
@@ -1,3 +1,4 @@
|
|||||||
.claude
|
.claude
|
||||||
*.out
|
*.out
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
data-import/cache/
|
||||||
|
|||||||
@@ -2,9 +2,16 @@
|
|||||||
|
|
||||||
Every shipped dataset, its source, its licence and the attribution it asks for. A
|
Every shipped dataset, its source, its licence and the attribution it asks for. A
|
||||||
`data-import/` script rebuilds each sourced table; a curated one is hand-written.
|
`data-import/` script rebuilds each sourced table; a curated one is hand-written.
|
||||||
|
The scripts run through `docker compose run --rm data-import data-import/<name>.py`
|
||||||
|
and cache their downloads under `data-import/cache/`; `geo-se.py` needs a
|
||||||
|
Trafikverket API key, free at [data.trafikverket.se](https://data.trafikverket.se/),
|
||||||
|
in `TRAFIKVERKET_API_KEY` or a `--key-file`.
|
||||||
|
|
||||||
| Table | Source | Licence | Attribution | Rebuild |
|
| Table | Source | Licence | Attribution | Rebuild |
|
||||||
|-------|--------|---------|-------------|---------|
|
|-------|--------|---------|-------------|---------|
|
||||||
|
| `geo/SE/locality.tsv`, `postal-code.tsv` | [GeoNames](https://www.geonames.org/) postal codes for SE; populations from SCB tätorter 2023 | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/); CC0 1.0 | "Postal codes from GeoNames, www.geonames.org" | `data-import/geo-se.py` |
|
||||||
|
| `geo/SE/region.tsv`, `municipality.tsv` | [SCB](https://www.scb.se/) län and kommun codes 2026 and population 2024 | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | none required | `data-import/geo-se.py` |
|
||||||
|
| `geo/SE/street.tsv` | [Trafikverket NVDB](https://www.trafikverket.se/) Gatunamn, through the open API | CC0 1.0 | none required | `data-import/geo-se.py` |
|
||||||
| `misc/country.tsv` | [datasets/country-codes](https://github.com/datasets/country-codes) | [PDDL 1.0](https://opendatacommons.org/licenses/pddl/1-0/) | none required | `data-import/country.py` |
|
| `misc/country.tsv` | [datasets/country-codes](https://github.com/datasets/country-codes) | [PDDL 1.0](https://opendatacommons.org/licenses/pddl/1-0/) | none required | `data-import/country.py` |
|
||||||
| `misc/currency.tsv` | [datasets/currency-codes](https://github.com/datasets/currency-codes); symbols from [Unicode CLDR](https://github.com/unicode-org/cldr) `en.xml` and `root.xml` | PDDL 1.0; [Unicode License v3](https://www.unicode.org/license.txt) | CLDR: "Copyright © 1991-2025 Unicode, Inc. Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the United States and other countries." | `data-import/currency.py` |
|
| `misc/currency.tsv` | [datasets/currency-codes](https://github.com/datasets/currency-codes); symbols from [Unicode CLDR](https://github.com/unicode-org/cldr) `en.xml` and `root.xml` | PDDL 1.0; [Unicode License v3](https://www.unicode.org/license.txt) | CLDR: "Copyright © 1991-2025 Unicode, Inc. Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the United States and other countries." | `data-import/currency.py` |
|
||||||
| `misc/httpstatus.tsv` | curated (IANA HTTP status codes are facts) | — | — | — |
|
| `misc/httpstatus.tsv` | curated (IANA HTTP status codes are facts) | — | — | — |
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Rebuild data/geo/SE/*.tsv from SCB (CC0), GeoNames (CC BY 4.0) and Trafikverket NVDB (CC0).
|
||||||
|
|
||||||
|
TRAFIKVERKET_API_KEY=… data-import/geo-se.py [--key-file FILE] [--cache DIR] [--streets-per-locality N] [--out DIR]
|
||||||
|
|
||||||
|
A locality is a GeoNames postort, placed in the municipality it names, else of its
|
||||||
|
tätort, else of most of its codes, and weighted by its tätort's population, else its
|
||||||
|
municipality's, else 200. Box codes are dropped by the digit after the postort's own
|
||||||
|
prefix. Each NVDB street segment goes to the nearest postal code centroid; a locality
|
||||||
|
keeps the N names with most segments.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import collections
|
||||||
|
import csv
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
CODES = "https://www.scb.se/contentassets/7a89e48960f741e08918e489ea36354a/kommunlankod-2026.xlsx"
|
||||||
|
POPULATION = "https://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101A/BefolkningNy"
|
||||||
|
POPULATION_QUERY = {
|
||||||
|
"query": [
|
||||||
|
{"code": "Region", "selection": {"filter": "all", "values": ["*"]}},
|
||||||
|
{"code": "ContentsCode", "selection": {"filter": "item", "values": ["BE0101N1"]}},
|
||||||
|
{"code": "Tid", "selection": {"filter": "top", "values": ["1"]}},
|
||||||
|
],
|
||||||
|
"response": {"format": "json"},
|
||||||
|
}
|
||||||
|
TATORTER = "https://geodata.scb.se/geoserver/stat/wfs?service=WFS&version=2.0.0&request=GetFeature&typeNames=stat:Tatorter_2023&outputFormat=csv&propertyName=tatort,kommun,bef"
|
||||||
|
POSTAL_CODES = "https://download.geonames.org/export/zip/SE.zip"
|
||||||
|
NVDB = "https://api.trafikinfo.trafikverket.se/v2/data.json"
|
||||||
|
NVDB_PAGE = 50000
|
||||||
|
OUT = Path(__file__).resolve().parent.parent / "data" / "geo" / "SE"
|
||||||
|
CACHE = Path(__file__).resolve().parent / "cache"
|
||||||
|
TIMEZONE = "Europe/Stockholm"
|
||||||
|
ONE_POSITION = {"Stockholm", "Göteborg", "Malmö"}
|
||||||
|
UNMATCHED_POPULATION = 200
|
||||||
|
XLSX_NS = {"m": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch(url, cache, name, data=None, headers=None):
|
||||||
|
path = cache / name
|
||||||
|
if not path.exists():
|
||||||
|
req = urllib.request.Request(url, data=data, headers=headers or {})
|
||||||
|
with urllib.request.urlopen(req, timeout=300) as r:
|
||||||
|
path.write_bytes(r.read())
|
||||||
|
return path.read_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
def xlsx_rows(data):
|
||||||
|
z = zipfile.ZipFile(io.BytesIO(data))
|
||||||
|
strings = ["".join(t.text or "" for t in si.iter("{%s}t" % XLSX_NS["m"])) for si in ET.fromstring(z.read("xl/sharedStrings.xml")).findall("m:si", XLSX_NS)]
|
||||||
|
sheet = ET.fromstring(z.read("xl/worksheets/sheet1.xml"))
|
||||||
|
for row in sheet.findall(".//m:row", XLSX_NS):
|
||||||
|
cells = []
|
||||||
|
for c in row.findall("m:c", XLSX_NS):
|
||||||
|
v = c.find("m:v", XLSX_NS)
|
||||||
|
cells.append("" if v is None else strings[int(v.text)] if c.get("t") == "s" else v.text)
|
||||||
|
yield cells
|
||||||
|
|
||||||
|
|
||||||
|
def scb_codes(cache):
|
||||||
|
regions, municipalities = {}, {}
|
||||||
|
for cells in xlsx_rows(fetch(CODES, cache, "kommunlankod.xlsx")):
|
||||||
|
if len(cells) < 2 or not re.fullmatch(r"\d{2}|\d{4}", cells[0]):
|
||||||
|
continue
|
||||||
|
(regions if len(cells[0]) == 2 else municipalities)[cells[0]] = cells[1].strip()
|
||||||
|
return regions, municipalities
|
||||||
|
|
||||||
|
|
||||||
|
def scb_population(cache):
|
||||||
|
body = json.dumps(POPULATION_QUERY).encode()
|
||||||
|
data = fetch(POPULATION, cache, "befolkning.json", data=body, headers={"Content-Type": "application/json"})
|
||||||
|
return {row["key"][0]: row["values"][0] for row in json.loads(data.decode("utf-8-sig"))["data"]}
|
||||||
|
|
||||||
|
|
||||||
|
def scb_tatorter(cache):
|
||||||
|
text = fetch(TATORTER, cache, "tatorter.csv").decode("utf-8")
|
||||||
|
by_name = collections.defaultdict(list)
|
||||||
|
for r in csv.DictReader(io.StringIO(text)):
|
||||||
|
by_name[r["tatort"]].append((r["kommun"], int(r["bef"])))
|
||||||
|
return by_name
|
||||||
|
|
||||||
|
|
||||||
|
def geonames(cache):
|
||||||
|
z = zipfile.ZipFile(io.BytesIO(fetch(POSTAL_CODES, cache, "SE.zip")))
|
||||||
|
rows = []
|
||||||
|
for line in z.read("SE.txt").decode("utf-8").splitlines():
|
||||||
|
f = line.split("\t")
|
||||||
|
lat, lon = (float(f[9]), float(f[10])) if f[9] and f[10] else (None, None)
|
||||||
|
rows.append({"code": f[1], "locality": f[2], "municipality": f[6], "lat": lat, "lon": lon})
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def nvdb_segments(cache, key):
|
||||||
|
path = cache / "nvdb-gatunamn.tsv"
|
||||||
|
if not path.exists():
|
||||||
|
with open(path, "w", encoding="utf-8") as out:
|
||||||
|
change = "0"
|
||||||
|
while True:
|
||||||
|
query = (
|
||||||
|
f'<REQUEST><LOGIN authenticationkey="{key}"/>'
|
||||||
|
f'<QUERY objecttype="Gatunamn" namespace="vägdata.nvdb_dk_o" schemaversion="1.0" limit="{NVDB_PAGE}" changeid="{change}">'
|
||||||
|
"<FILTER><EQ name=\"Deleted\" value=\"false\"/></FILTER>"
|
||||||
|
"<INCLUDE>Namn</INCLUDE><INCLUDE>Geometry.WKT-WGS84-3D</INCLUDE></QUERY></REQUEST>"
|
||||||
|
)
|
||||||
|
req = urllib.request.Request(NVDB, data=query.encode(), headers={"Content-Type": "text/xml"})
|
||||||
|
with urllib.request.urlopen(req, timeout=600) as r:
|
||||||
|
result = json.load(r)["RESPONSE"]["RESULT"][0]
|
||||||
|
rows = result.get("Gatunamn", [])
|
||||||
|
for row in rows:
|
||||||
|
m = re.match(r"LINESTRING Z \(([-\d.]+) ([-\d.]+) ", row.get("Geometry", {}).get("WKT-WGS84-3D", ""))
|
||||||
|
name = " ".join(row.get("Namn", "").split())
|
||||||
|
if m and name:
|
||||||
|
out.write(f"{name}\t{m.group(1)}\t{m.group(2)}\n")
|
||||||
|
change = result["INFO"]["LASTCHANGEID"]
|
||||||
|
if len(rows) < NVDB_PAGE:
|
||||||
|
break
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
name, lon, lat = line.split("\t")
|
||||||
|
yield name, float(lat), float(lon)
|
||||||
|
|
||||||
|
|
||||||
|
class Nearest:
|
||||||
|
"""Nearest point by an equirectangular distance, over a degree grid."""
|
||||||
|
|
||||||
|
def __init__(self, points, cell=0.05):
|
||||||
|
self.cell = cell
|
||||||
|
self.grid = collections.defaultdict(list)
|
||||||
|
for lat, lon, value in points:
|
||||||
|
self.grid[(int(lat // cell), int(lon // cell))].append((lat, lon, value))
|
||||||
|
|
||||||
|
def find(self, lat, lon):
|
||||||
|
ci, cj = int(lat // self.cell), int(lon // self.cell)
|
||||||
|
best, best_d = None, math.inf
|
||||||
|
ring = 0
|
||||||
|
while ring < 400:
|
||||||
|
for i in range(ci - ring, ci + ring + 1):
|
||||||
|
for j in range(cj - ring, cj + ring + 1):
|
||||||
|
if max(abs(i - ci), abs(j - cj)) != ring:
|
||||||
|
continue
|
||||||
|
for plat, plon, value in self.grid.get((i, j), ()):
|
||||||
|
d = (plat - lat) ** 2 + ((plon - lon) * math.cos(math.radians(lat))) ** 2
|
||||||
|
if d < best_d:
|
||||||
|
best, best_d = value, d
|
||||||
|
if best is not None and math.sqrt(best_d) < ring * self.cell * math.cos(math.radians(lat)):
|
||||||
|
return best
|
||||||
|
ring += 1
|
||||||
|
return best
|
||||||
|
|
||||||
|
|
||||||
|
def street_delivery(name, codes):
|
||||||
|
"""The codes delivered to a street: the digit after the postort's own prefix says box, company or reply."""
|
||||||
|
if name in ONE_POSITION:
|
||||||
|
return [c for c in codes if c[1] != "0"]
|
||||||
|
largest = collections.Counter(c[:3] for c in codes).most_common(1)[0][1]
|
||||||
|
length = 3 if largest * 2 >= len(codes) else 2
|
||||||
|
return [c for c in codes if c[length] not in "018"]
|
||||||
|
|
||||||
|
|
||||||
|
def municipality_of(name, rows, tatorter, municipalities):
|
||||||
|
named = [code for code, n in municipalities.items() if n == name]
|
||||||
|
if named:
|
||||||
|
return named[0], "kommun"
|
||||||
|
voted = collections.Counter(r["municipality"] for r in rows if r["municipality"] in municipalities)
|
||||||
|
matches = tatorter.get(name, [])
|
||||||
|
if matches:
|
||||||
|
in_vote = [m for m in matches if voted and m[0] == voted.most_common(1)[0][0]]
|
||||||
|
return max(in_vote or matches, key=lambda m: m[1])[0], "tatort"
|
||||||
|
if voted:
|
||||||
|
return voted.most_common(1)[0][0], "codes"
|
||||||
|
return None, "unplaced"
|
||||||
|
|
||||||
|
|
||||||
|
def population_of(name, municipality, tatorter, municipalities, population):
|
||||||
|
matches = [m for m in tatorter.get(name, []) if m[0] == municipality]
|
||||||
|
if matches:
|
||||||
|
return str(max(m[1] for m in matches))
|
||||||
|
return population[municipality] if municipalities[municipality] == name else str(UNMATCHED_POPULATION)
|
||||||
|
|
||||||
|
|
||||||
|
def well_cased(name):
|
||||||
|
return all(part[:1].isupper() and (len(part) == 1 or not part.isupper()) for part in re.split(r"[ -]", name))
|
||||||
|
|
||||||
|
|
||||||
|
def write(path, columns, rows):
|
||||||
|
lines = ["\t".join(columns)]
|
||||||
|
for row in rows:
|
||||||
|
cells = [str(row[c]) for c in columns]
|
||||||
|
assert not any(re.search(r"[\t\n{}]", c) for c in cells), row
|
||||||
|
lines.append("\t".join(cells))
|
||||||
|
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
print(f"{path}: {len(rows)} rows", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||||
|
p.add_argument("--cache", default=str(CACHE))
|
||||||
|
p.add_argument("--key-file", help="file holding the Trafikverket API key; TRAFIKVERKET_API_KEY otherwise")
|
||||||
|
p.add_argument("--out", default=str(OUT))
|
||||||
|
p.add_argument("--streets-per-locality", type=int, default=10)
|
||||||
|
a = p.parse_args()
|
||||||
|
key = Path(a.key_file).read_text().strip() if a.key_file else os.environ.get("TRAFIKVERKET_API_KEY")
|
||||||
|
if not key:
|
||||||
|
sys.exit("set TRAFIKVERKET_API_KEY or pass --key-file")
|
||||||
|
cache, out = Path(a.cache), Path(a.out)
|
||||||
|
cache.mkdir(parents=True, exist_ok=True)
|
||||||
|
out.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
regions, municipalities = scb_codes(cache)
|
||||||
|
population = scb_population(cache)
|
||||||
|
tatorter = scb_tatorter(cache)
|
||||||
|
codes = geonames(cache)
|
||||||
|
|
||||||
|
by_locality = collections.defaultdict(list)
|
||||||
|
for r in codes:
|
||||||
|
by_locality[r["locality"]].append(r)
|
||||||
|
localities, how = {}, collections.Counter()
|
||||||
|
for name, rows in by_locality.items():
|
||||||
|
municipality, method = municipality_of(name, rows, tatorter, municipalities)
|
||||||
|
how[method] += 1
|
||||||
|
kept = street_delivery(name, [r["code"].replace(" ", "") for r in rows])
|
||||||
|
with_point = [r for r in rows if r["lat"] is not None]
|
||||||
|
if municipality is None or not kept or not with_point or not well_cased(name):
|
||||||
|
continue
|
||||||
|
lat = sum(r["lat"] for r in with_point) / len(with_point)
|
||||||
|
lon = sum(r["lon"] for r in with_point) / len(with_point)
|
||||||
|
localities[name] = {"name": name, "municipality": municipality, "population": population_of(name, municipality, tatorter, municipalities, population), "lat": f"{lat:.4f}", "lon": f"{lon:.4f}", "codes": kept}
|
||||||
|
print(f"municipality by {dict(how)}; {len(by_locality) - len(localities)} postorter dropped", file=sys.stderr)
|
||||||
|
|
||||||
|
centroids = [(r["lat"], r["lon"], r["locality"]) for r in codes if r["lat"] is not None and r["locality"] in localities]
|
||||||
|
nearest_locality = Nearest(centroids)
|
||||||
|
segments = collections.Counter()
|
||||||
|
for name, lat, lon in nvdb_segments(cache, key):
|
||||||
|
segments[(nearest_locality.find(lat, lon), name)] += 1
|
||||||
|
streets_of = collections.defaultdict(list)
|
||||||
|
for (locality, name), n in segments.items():
|
||||||
|
streets_of[locality].append((n, name))
|
||||||
|
streets = []
|
||||||
|
for locality in sorted(streets_of):
|
||||||
|
for n, name in sorted(streets_of[locality], key=lambda s: (-s[0], s[1]))[: a.streets_per_locality]:
|
||||||
|
streets.append({"name": name, "locality": locality, "segments": n})
|
||||||
|
for name in [l for l in localities if l not in streets_of]:
|
||||||
|
del localities[name]
|
||||||
|
|
||||||
|
empty = sorted(m for m in municipalities if not any(l["municipality"] == m for l in localities.values()))
|
||||||
|
if empty:
|
||||||
|
sys.exit(f"municipalities without a locality: {empty}")
|
||||||
|
write(out / "region.tsv", ["code", "name", "population", "timezone"], [{"code": c, "name": n, "population": population[c], "timezone": TIMEZONE} for c, n in sorted(regions.items())])
|
||||||
|
write(out / "municipality.tsv", ["code", "name", "region", "population"], [{"code": c, "name": n, "region": c[:2], "population": population[c]} for c, n in sorted(municipalities.items())])
|
||||||
|
write(out / "locality.tsv", ["name", "municipality", "population", "lat", "lon"], [l for _, l in sorted(localities.items())])
|
||||||
|
write(out / "postal-code.tsv", ["code", "locality"], sorted(({"code": f"{c[:3]} {c[3:]}", "locality": l["name"]} for l in localities.values() for c in l["codes"]), key=lambda r: r["code"]))
|
||||||
|
write(out / "street.tsv", ["name", "locality", "segments"], streets)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"format": "{street} {number}\n{postal-code} {locality}",
|
||||||
|
"street": "{.street.name}",
|
||||||
|
"number": [
|
||||||
|
"{int(1,9)}",
|
||||||
|
"{int(10,99)}",
|
||||||
|
{ "format": "{int(100,999)}", "weight": 0.2 },
|
||||||
|
{ "format": "{int(1,9)}{upper(1)}", "weight": 0.2 },
|
||||||
|
{ "format": "{int(10,99)}{upper(1)}", "weight": 0.1 },
|
||||||
|
{ "format": "{int(100,999)}{upper(1)}", "weight": 0.05 }
|
||||||
|
],
|
||||||
|
"postal-code": "{.postal-code.code}",
|
||||||
|
"locality": "{.locality.name}"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{ "format": "{name}", "rows": "locality.tsv", "key": "name", "parent": "municipality", "weight": "population" }
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
{ "format": "{name}", "rows": "municipality.tsv", "key": "code", "name": "name", "parent": "region", "weight": "population" }
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
code name region population
|
||||||
|
0114 Upplands Väsby 01 50323
|
||||||
|
0115 Vallentuna 01 35119
|
||||||
|
0117 Österåker 01 49787
|
||||||
|
0120 Värmdö 01 46635
|
||||||
|
0123 Järfälla 01 88950
|
||||||
|
0125 Ekerö 01 28910
|
||||||
|
0126 Huddinge 01 114304
|
||||||
|
0127 Botkyrka 01 95905
|
||||||
|
0128 Salem 01 17507
|
||||||
|
0136 Haninge 01 100895
|
||||||
|
0138 Tyresö 01 49179
|
||||||
|
0139 Upplands-Bro 01 32868
|
||||||
|
0140 Nykvarn 01 12342
|
||||||
|
0160 Täby 01 77744
|
||||||
|
0162 Danderyd 01 32425
|
||||||
|
0163 Sollentuna 01 77624
|
||||||
|
0180 Stockholm 01 995574
|
||||||
|
0181 Södertälje 01 102911
|
||||||
|
0182 Nacka 01 112112
|
||||||
|
0183 Sundbyberg 01 56274
|
||||||
|
0184 Solna 01 85789
|
||||||
|
0186 Lidingö 01 48377
|
||||||
|
0187 Vaxholm 01 11822
|
||||||
|
0188 Norrtälje 01 66585
|
||||||
|
0191 Sigtuna 01 52767
|
||||||
|
0192 Nynäshamn 01 30579
|
||||||
|
0305 Håbo 03 22973
|
||||||
|
0319 Älvkarleby 03 9552
|
||||||
|
0330 Knivsta 03 21193
|
||||||
|
0331 Heby 03 14345
|
||||||
|
0360 Tierp 03 21104
|
||||||
|
0380 Uppsala 03 248016
|
||||||
|
0381 Enköping 03 48591
|
||||||
|
0382 Östhammar 03 22138
|
||||||
|
0428 Vingåker 04 8750
|
||||||
|
0461 Gnesta 04 11458
|
||||||
|
0480 Nyköping 04 58344
|
||||||
|
0481 Oxelösund 04 12031
|
||||||
|
0482 Flen 04 15362
|
||||||
|
0483 Katrineholm 04 34154
|
||||||
|
0484 Eskilstuna 04 107203
|
||||||
|
0486 Strängnäs 04 39313
|
||||||
|
0488 Trosa 04 14927
|
||||||
|
0509 Ödeshög 05 5237
|
||||||
|
0512 Ydre 05 3626
|
||||||
|
0513 Kinda 05 9957
|
||||||
|
0560 Boxholm 05 5517
|
||||||
|
0561 Åtvidaberg 05 11467
|
||||||
|
0562 Finspång 05 21623
|
||||||
|
0563 Valdemarsvik 05 7525
|
||||||
|
0580 Linköping 05 168035
|
||||||
|
0581 Norrköping 05 144980
|
||||||
|
0582 Söderköping 05 14789
|
||||||
|
0583 Motala 05 43505
|
||||||
|
0584 Vadstena 05 7490
|
||||||
|
0586 Mjölby 05 28695
|
||||||
|
0604 Aneby 06 6797
|
||||||
|
0617 Gnosjö 06 9131
|
||||||
|
0642 Mullsjö 06 7594
|
||||||
|
0643 Habo 06 13456
|
||||||
|
0662 Gislaved 06 28936
|
||||||
|
0665 Vaggeryd 06 14825
|
||||||
|
0680 Jönköping 06 147654
|
||||||
|
0682 Nässjö 06 31587
|
||||||
|
0683 Värnamo 06 34542
|
||||||
|
0684 Sävsjö 06 11563
|
||||||
|
0685 Vetlanda 06 27528
|
||||||
|
0686 Eksjö 06 17792
|
||||||
|
0687 Tranås 06 18604
|
||||||
|
0760 Uppvidinge 07 9061
|
||||||
|
0761 Lessebo 07 8289
|
||||||
|
0763 Tingsryd 07 11966
|
||||||
|
0764 Alvesta 07 19830
|
||||||
|
0765 Älmhult 07 17653
|
||||||
|
0767 Markaryd 07 9938
|
||||||
|
0780 Växjö 07 98334
|
||||||
|
0781 Ljungby 07 28280
|
||||||
|
0821 Högsby 08 5321
|
||||||
|
0834 Torsås 08 6984
|
||||||
|
0840 Mörbylånga 08 16224
|
||||||
|
0860 Hultsfred 08 13673
|
||||||
|
0861 Mönsterås 08 13069
|
||||||
|
0862 Emmaboda 08 9006
|
||||||
|
0880 Kalmar 08 72704
|
||||||
|
0881 Nybro 08 19951
|
||||||
|
0882 Oskarshamn 08 26923
|
||||||
|
0883 Västervik 08 36447
|
||||||
|
0884 Vimmerby 08 15384
|
||||||
|
0885 Borgholm 08 10666
|
||||||
|
0980 Gotland 09 60971
|
||||||
|
1060 Olofström 10 13000
|
||||||
|
1080 Karlskrona 10 66301
|
||||||
|
1081 Ronneby 10 28741
|
||||||
|
1082 Karlshamn 10 31751
|
||||||
|
1083 Sölvesborg 10 17430
|
||||||
|
1214 Svalöv 12 14543
|
||||||
|
1230 Staffanstorp 12 27303
|
||||||
|
1231 Burlöv 12 20101
|
||||||
|
1233 Vellinge 12 37816
|
||||||
|
1256 Östra Göinge 12 13978
|
||||||
|
1257 Örkelljunga 12 10277
|
||||||
|
1260 Bjuv 12 15985
|
||||||
|
1261 Kävlinge 12 32477
|
||||||
|
1262 Lomma 12 24715
|
||||||
|
1263 Svedala 12 23581
|
||||||
|
1264 Skurup 12 17099
|
||||||
|
1265 Sjöbo 12 19337
|
||||||
|
1266 Hörby 12 15562
|
||||||
|
1267 Höör 12 17518
|
||||||
|
1270 Tomelilla 12 13639
|
||||||
|
1272 Bromölla 12 12470
|
||||||
|
1273 Osby 12 12947
|
||||||
|
1275 Perstorp 12 7235
|
||||||
|
1276 Klippan 12 17714
|
||||||
|
1277 Åstorp 12 16449
|
||||||
|
1278 Båstad 12 16026
|
||||||
|
1280 Malmö 12 365644
|
||||||
|
1281 Lund 12 131590
|
||||||
|
1282 Landskrona 12 47309
|
||||||
|
1283 Helsingborg 12 152091
|
||||||
|
1284 Höganäs 12 28430
|
||||||
|
1285 Eslöv 12 34922
|
||||||
|
1286 Ystad 12 32106
|
||||||
|
1287 Trelleborg 12 47269
|
||||||
|
1290 Kristianstad 12 86379
|
||||||
|
1291 Simrishamn 12 18890
|
||||||
|
1292 Ängelholm 12 45110
|
||||||
|
1293 Hässleholm 12 52114
|
||||||
|
1315 Hylte 13 10196
|
||||||
|
1380 Halmstad 13 106084
|
||||||
|
1381 Laholm 13 26595
|
||||||
|
1382 Falkenberg 13 47337
|
||||||
|
1383 Varberg 13 69070
|
||||||
|
1384 Kungsbacka 13 85792
|
||||||
|
1401 Härryda 14 40003
|
||||||
|
1402 Partille 14 41060
|
||||||
|
1407 Öckerö 14 12771
|
||||||
|
1415 Stenungsund 14 27851
|
||||||
|
1419 Tjörn 14 16092
|
||||||
|
1421 Orust 14 15352
|
||||||
|
1427 Sotenäs 14 9104
|
||||||
|
1430 Munkedal 14 10354
|
||||||
|
1435 Tanum 14 12773
|
||||||
|
1438 Dals-Ed 14 4606
|
||||||
|
1439 Färgelanda 14 6376
|
||||||
|
1440 Ale 14 32576
|
||||||
|
1441 Lerum 14 43570
|
||||||
|
1442 Vårgårda 14 12474
|
||||||
|
1443 Bollebygd 14 9802
|
||||||
|
1444 Grästorp 14 5555
|
||||||
|
1445 Essunga 14 5560
|
||||||
|
1446 Karlsborg 14 7023
|
||||||
|
1447 Gullspång 14 5031
|
||||||
|
1452 Tranemo 14 11839
|
||||||
|
1460 Bengtsfors 14 9076
|
||||||
|
1461 Mellerud 14 9052
|
||||||
|
1462 Lilla Edet 14 14442
|
||||||
|
1463 Mark 14 35155
|
||||||
|
1465 Svenljunga 14 10747
|
||||||
|
1466 Herrljunga 14 9497
|
||||||
|
1470 Vara 14 16088
|
||||||
|
1471 Götene 14 13286
|
||||||
|
1472 Tibro 14 11338
|
||||||
|
1473 Töreboda 14 9043
|
||||||
|
1480 Göteborg 14 608993
|
||||||
|
1481 Mölndal 14 71420
|
||||||
|
1482 Kungälv 14 50313
|
||||||
|
1484 Lysekil 14 13907
|
||||||
|
1485 Uddevalla 14 57010
|
||||||
|
1486 Strömstad 14 13482
|
||||||
|
1487 Vänersborg 14 40041
|
||||||
|
1488 Trollhättan 14 59003
|
||||||
|
1489 Alingsås 14 42722
|
||||||
|
1490 Borås 14 114872
|
||||||
|
1491 Ulricehamn 14 24985
|
||||||
|
1492 Åmål 14 11906
|
||||||
|
1493 Mariestad 14 24583
|
||||||
|
1494 Lidköping 14 40425
|
||||||
|
1495 Skara 14 18707
|
||||||
|
1496 Skövde 14 57995
|
||||||
|
1497 Hjo 14 9350
|
||||||
|
1498 Tidaholm 14 12805
|
||||||
|
1499 Falköping 14 32806
|
||||||
|
1715 Kil 17 12061
|
||||||
|
1730 Eda 17 8412
|
||||||
|
1737 Torsby 17 11321
|
||||||
|
1760 Storfors 17 3788
|
||||||
|
1761 Hammarö 17 16992
|
||||||
|
1762 Munkfors 17 3625
|
||||||
|
1763 Forshaga 17 11520
|
||||||
|
1764 Grums 17 9004
|
||||||
|
1765 Årjäng 17 9825
|
||||||
|
1766 Sunne 17 13356
|
||||||
|
1780 Karlstad 17 98084
|
||||||
|
1781 Kristinehamn 17 23756
|
||||||
|
1782 Filipstad 17 9776
|
||||||
|
1783 Hagfors 17 11418
|
||||||
|
1784 Arvika 17 25547
|
||||||
|
1785 Säffle 17 14899
|
||||||
|
1814 Lekeberg 18 8606
|
||||||
|
1860 Laxå 18 5423
|
||||||
|
1861 Hallsberg 18 16120
|
||||||
|
1862 Degerfors 18 9278
|
||||||
|
1863 Hällefors 18 6321
|
||||||
|
1864 Ljusnarsberg 18 4369
|
||||||
|
1880 Örebro 18 160140
|
||||||
|
1881 Kumla 18 22681
|
||||||
|
1882 Askersund 18 11477
|
||||||
|
1883 Karlskoga 18 30180
|
||||||
|
1884 Nora 18 10639
|
||||||
|
1885 Lindesberg 18 23141
|
||||||
|
1904 Skinnskatteberg 19 4256
|
||||||
|
1907 Surahammar 19 9845
|
||||||
|
1960 Kungsör 19 8694
|
||||||
|
1961 Hallstahammar 19 16653
|
||||||
|
1962 Norberg 19 5452
|
||||||
|
1980 Västerås 19 160634
|
||||||
|
1981 Sala 19 22843
|
||||||
|
1982 Fagersta 19 13072
|
||||||
|
1983 Köping 19 25729
|
||||||
|
1984 Arboga 19 13980
|
||||||
|
2021 Vansbro 20 6752
|
||||||
|
2023 Malung-Sälen 20 10254
|
||||||
|
2026 Gagnef 20 10384
|
||||||
|
2029 Leksand 20 16137
|
||||||
|
2031 Rättvik 20 10998
|
||||||
|
2034 Orsa 20 6851
|
||||||
|
2039 Älvdalen 20 6882
|
||||||
|
2061 Smedjebacken 20 10823
|
||||||
|
2062 Mora 20 20540
|
||||||
|
2080 Falun 20 59945
|
||||||
|
2081 Borlänge 20 51425
|
||||||
|
2082 Säter 20 11223
|
||||||
|
2083 Hedemora 20 15281
|
||||||
|
2084 Avesta 20 22417
|
||||||
|
2085 Ludvika 20 26634
|
||||||
|
2101 Ockelbo 21 5715
|
||||||
|
2104 Hofors 21 9281
|
||||||
|
2121 Ovanåker 21 11341
|
||||||
|
2132 Nordanstig 21 9262
|
||||||
|
2161 Ljusdal 21 18445
|
||||||
|
2180 Gävle 21 103838
|
||||||
|
2181 Sandviken 21 38360
|
||||||
|
2182 Söderhamn 21 24545
|
||||||
|
2183 Bollnäs 21 26243
|
||||||
|
2184 Hudiksvall 21 37528
|
||||||
|
2260 Ånge 22 9044
|
||||||
|
2262 Timrå 22 17521
|
||||||
|
2280 Härnösand 22 24515
|
||||||
|
2281 Sundsvall 22 99048
|
||||||
|
2282 Kramfors 22 17491
|
||||||
|
2283 Sollefteå 22 18396
|
||||||
|
2284 Örnsköldsvik 22 55443
|
||||||
|
2303 Ragunda 23 5146
|
||||||
|
2305 Bräcke 23 6035
|
||||||
|
2309 Krokom 23 15680
|
||||||
|
2313 Strömsund 23 11023
|
||||||
|
2321 Åre 23 12693
|
||||||
|
2326 Berg 23 7108
|
||||||
|
2361 Härjedalen 23 10175
|
||||||
|
2380 Östersund 23 64979
|
||||||
|
2401 Nordmaling 24 6942
|
||||||
|
2403 Bjurholm 24 2359
|
||||||
|
2404 Vindeln 24 5417
|
||||||
|
2409 Robertsfors 24 6690
|
||||||
|
2417 Norsjö 24 3968
|
||||||
|
2418 Malå 24 2962
|
||||||
|
2421 Storuman 24 5577
|
||||||
|
2422 Sorsele 24 2357
|
||||||
|
2425 Dorotea 24 2294
|
||||||
|
2460 Vännäs 24 9132
|
||||||
|
2462 Vilhelmina 24 6229
|
||||||
|
2463 Åsele 24 2694
|
||||||
|
2480 Umeå 24 134249
|
||||||
|
2481 Lycksele 24 12118
|
||||||
|
2482 Skellefteå 24 78150
|
||||||
|
2505 Arvidsjaur 25 6089
|
||||||
|
2506 Arjeplog 25 2599
|
||||||
|
2510 Jokkmokk 25 4701
|
||||||
|
2513 Överkalix 25 3201
|
||||||
|
2514 Kalix 25 15391
|
||||||
|
2518 Övertorneå 25 4057
|
||||||
|
2521 Pajala 25 5857
|
||||||
|
2523 Gällivare 25 17233
|
||||||
|
2560 Älvsbyn 25 7774
|
||||||
|
2580 Luleå 25 79645
|
||||||
|
2581 Piteå 25 42447
|
||||||
|
2582 Boden 25 28049
|
||||||
|
2583 Haparanda 25 9151
|
||||||
|
2584 Kiruna 25 22426
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
{ "format": "{code}", "rows": "postal-code.tsv", "key": "code", "parent": "locality" }
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
{ "format": "{name}", "rows": "region.tsv", "key": "code", "name": "name", "weight": "population" }
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
code name population timezone
|
||||||
|
01 Stockholms län 2473307 Europe/Stockholm
|
||||||
|
03 Uppsala län 407912 Europe/Stockholm
|
||||||
|
04 Södermanlands län 301542 Europe/Stockholm
|
||||||
|
05 Östergötlands län 472446 Europe/Stockholm
|
||||||
|
06 Jönköpings län 370009 Europe/Stockholm
|
||||||
|
07 Kronobergs län 203351 Europe/Stockholm
|
||||||
|
08 Kalmar län 246352 Europe/Stockholm
|
||||||
|
09 Gotlands län 60971 Europe/Stockholm
|
||||||
|
10 Blekinge län 157223 Europe/Stockholm
|
||||||
|
12 Skåne län 1428626 Europe/Stockholm
|
||||||
|
13 Hallands län 345074 Europe/Stockholm
|
||||||
|
14 Västra Götalands län 1772821 Europe/Stockholm
|
||||||
|
17 Värmlands län 283384 Europe/Stockholm
|
||||||
|
18 Örebro län 308375 Europe/Stockholm
|
||||||
|
19 Västmanlands län 281158 Europe/Stockholm
|
||||||
|
20 Dalarnas län 286546 Europe/Stockholm
|
||||||
|
21 Gävleborgs län 284558 Europe/Stockholm
|
||||||
|
22 Västernorrlands län 241458 Europe/Stockholm
|
||||||
|
23 Jämtlands län 132839 Europe/Stockholm
|
||||||
|
24 Västerbottens län 281138 Europe/Stockholm
|
||||||
|
25 Norrbottens län 248620 Europe/Stockholm
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
{ "format": "{name}", "rows": "street.tsv", "parent": "locality", "weight": "segments" }
|
||||||
+14765
File diff suppressed because it is too large
Load Diff
+4
-18
@@ -1,21 +1,7 @@
|
|||||||
{
|
{
|
||||||
"format": "{street} {street-number}\n{postal-code} {locality}",
|
"format": "{street} {street-number}\n{postal-code} {locality}",
|
||||||
"street": [
|
"street": "{/geo.SE.address.street}",
|
||||||
{
|
"street-number": "{/geo.SE.address.number}",
|
||||||
"format": "{first}{last}",
|
"postal-code": "{/geo.SE.address.postal-code}",
|
||||||
"first": ["Ängs", "Bergs", "Björk", "Drottning", "Eke", "Furu", "Hamn", "Köpmans", "Kungs", "Linné", "Norra", "Nybro", "Oden", "Öster", "Park", "Skogs", "Skol", "Slotts", "Söder", "Stations", "Stor", "Strand", "Trädgårds", "Vasa", "Väster"],
|
"locality": "{/geo.SE.address.locality}"
|
||||||
"last": ["gatan", "vägen", "stigen", "gränd", "backen", "torget", "allén"]
|
|
||||||
},
|
|
||||||
["Avenyn", "Birger Jarlsgatan", "Promenaden", "Staby", "Sveavägen", "Vintjärn"]
|
|
||||||
],
|
|
||||||
"street-number": [
|
|
||||||
"{int(1,9)}",
|
|
||||||
"{int(10,99)}",
|
|
||||||
{ "format": "{int(100,999)}", "weight": 0.2 },
|
|
||||||
{ "format": "{int(1,9)}{upper(1)}", "weight": 0.2 },
|
|
||||||
{ "format": "{int(10,99)}{upper(1)}", "weight": 0.1 },
|
|
||||||
{ "format": "{int(100,999)}{upper(1)}", "weight": 0.05 }
|
|
||||||
],
|
|
||||||
"postal-code": "{int(100,999)} {int(10,99)}",
|
|
||||||
"locality": ["Alingsås", "Alvesta", "Ängelholm", "Arboga", "Arvika", "Avesta", "Boden", "Bollnäs", "Borås", "Borlänge", "Enköping", "Eskilstuna", "Eslöv", "Fagersta", "Falkenberg", "Falköping", "Falun", "Finspång", "Gällivare", "Gävle", "Göteborg", "Halmstad", "Haparanda", "Härnösand", "Hässleholm", "Helsingborg", "Huddinge", "Hudiksvall", "Jönköping", "Kalmar", "Karlshamn", "Karlskoga", "Karlskrona", "Karlstad", "Katrineholm", "Kiruna", "Köping", "Kramfors", "Kristianstad", "Kristinehamn", "Landskrona", "Lidingö", "Lidköping", "Lindesberg", "Linköping", "Ljungby", "Ludvika", "Luleå", "Lund", "Lycksele", "Malmö", "Mariestad", "Mjölby", "Mölndal", "Mora", "Motala", "Nacka", "Nässjö", "Norrköping", "Norrtälje", "Nyköping", "Nynäshamn", "Örebro", "Örnsköldsvik", "Oskarshamn", "Östersund", "Piteå", "Rabbalshede", "Ronneby", "Säffle", "Sandviken", "Sävsjö", "Sigtuna", "Skara", "Skellefteå", "Skövde", "Söderhamn", "Södertälje", "Sollentuna", "Solna", "Sölvesborg", "Stockholm", "Strängnäs", "Sundbyberg", "Sundsvall", "Täby", "Tierp", "Tranås", "Trelleborg", "Trollhättan", "Uddevalla", "Ulricehamn", "Umeå", "Upplands Väsby", "Uppsala", "Vänersborg", "Varberg", "Värnamo", "Västerås", "Västervik", "Växjö", "Vetlanda", "Vimmerby", "Visby", "Ystad"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user