Edit2: Okay, na tretí deň sme už aha na 35%:

https://hrubos.tech/blogy/content/images/20260818234532-Snímka obrazovky 2026-08-18 o 23.43.56.png
Takto chýba napríklad Korytnická ulica, ak je za administratívnu hranicu považovaný výsek 3km od stredu dedinky:

Pred tým bol použitý smart skript v php na výsek z XML do rádiusu, ale hoci to bolo rýchle, nebolo to okay:
<?php
ini_set('memory_limit', '999M');
// ============================================================
// VYTVOR STREETS PRE VSETKY MESTA / OBCE
// ============================================================
//
// POIs:
// data/POIs.json
//
// OSM tiles:
// osm/
// lat_47.70_lon_18.25.osm
// lat_47.75_lon_17.70.osm
// ...
//
// Vystup:
// streets/
// lon_18.275863_lat_47.741687.json
// lon_18.373341_lat_47.747258.json
// ...
//
// ============================================================
// ------------------------------------------------------------
// KONFIGURACIA
// ------------------------------------------------------------
$poisFile = __DIR__ . '/POIs.json';
$osmDir = __DIR__ . '/tiles_xml';
$streetsDir = __DIR__ . '/streets';
// Polomer okolo centra mesta/obce v metroch.
//
// Napr.:
// 3000 = 3 km
// 5000 = 5 km
// 10000 = 10 km
//
// Pre male obce odporucam 3000-5000.
// ------------------------------------------------------------
$radiusMeters = 5000;
// Typy POI, pre ktore chceme vytvarat streets.
// ------------------------------------------------------------
$allowedPlaces = [
'city',
'town',
'village',
'hamlet'
];
// Highway typy, ktore chceme zachovat.
// ------------------------------------------------------------
$allowedHighways = [
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'secondary_link',
'tertiary',
'tertiary_link',
'unclassified',
'residential',
'living_street',
'service',
'road',
'track'
];
// ------------------------------------------------------------
// KONTROLA
// ------------------------------------------------------------
if (!file_exists($poisFile)) {
die("CHYBA: POIs.json neexistuje: $poisFile\n");
}
if (!is_dir($osmDir)) {
die("CHYBA: OSM priecinok neexistuje: $osmDir\n");
}
if (!is_dir($streetsDir)) {
mkdir($streetsDir, 0777, true);
}
// ------------------------------------------------------------
// NACITANIE POIS
// ------------------------------------------------------------
echo "Nacitam POIs.json...\n";
$json = file_get_contents($poisFile);
$pois = json_decode($json, true);
if (!is_array($pois)) {
die("CHYBA: POIs.json nie je platny JSON.\n");
}
echo "Pocet POI: " . count($pois) . "\n\n";
// ------------------------------------------------------------
// STATISTIKY
// ------------------------------------------------------------
$totalCities = 0;
$totalCreated = 0;
$totalSkipped = 0;
$totalWays = 0;
// ============================================================
// HLAVNY CYKLUS
// ============================================================
foreach ($pois as $key => $poi) {
// --------------------------------------------------------
// KONTROLA DAT
// --------------------------------------------------------
if (!isset($poi['lat'], $poi['lon'], $poi['tile'])) {
$totalSkipped++;
continue;
}
$place = $poi['place'] ?? '';
if (!in_array($place, $allowedPlaces, true)) {
continue;
}
$name = $poi['name'] ?? $key;
$lat = (float)$poi['lat'];
$lon = (float)$poi['lon'];
$tile = $poi['tile'];
$totalCities++;
echo "====================================================\n";
echo "Mesto/obec: $name\n";
echo "Lat: $lat\n";
echo "Lon: $lon\n";
echo "Tile: $tile\n";
// --------------------------------------------------------
// OSM SUBOR
// --------------------------------------------------------
$osmFile = $osmDir . '/' . basename($tile);
if (!file_exists($osmFile)) {
echo " CHYBA: OSM subor neexistuje\n";
$totalSkipped++;
continue;
}
// --------------------------------------------------------
// VYSTUPNY SUBOR
// --------------------------------------------------------
$outputName =
'lon_' .
number_format($lon, 6, '.', '') .
'_lat_' .
number_format($lat, 6, '.', '') .
'.json';
$outputFile = $streetsDir . '/' . $outputName;
// --------------------------------------------------------
// NACITAME OSM
// --------------------------------------------------------
$result = spracujOSMPreMesto(
$osmFile,
$lat,
$lon,
$radiusMeters,
$allowedHighways
);
$streetCount = count($result['streets']);
$nodeCount = count($result['nodes']);
echo " Node: $nodeCount\n";
echo " Streets: $streetCount\n";
if ($streetCount === 0) {
echo " WARNING: nenasli sa ziadne ulice\n";
$totalSkipped++;
continue;
}
// --------------------------------------------------------
// METADATA
// --------------------------------------------------------
$output = [
'name' => $name,
'lat' => $lat,
'lon' => $lon,
'place' => $place,
'tile' => $tile,
'radius' => $radiusMeters,
'nodes' => $result['nodes'],
'streets' => $result['streets']
];
// --------------------------------------------------------
// ULOZENIE
// --------------------------------------------------------
$encoded = json_encode(
$output,
JSON_PRETTY_PRINT |
JSON_UNESCAPED_UNICODE |
JSON_UNESCAPED_SLASHES
);
if ($encoded === false) {
echo " CHYBA: json_encode zlyhal\n";
$totalSkipped++;
continue;
}
file_put_contents(
$outputFile,
$encoded
);
echo " Vytvorene: $outputName\n";
$totalCreated++;
$totalWays += $streetCount;
}
// ============================================================
// KONIEC
// ============================================================
echo "\n";
echo "====================================================\n";
echo "HOTOVO\n";
echo "====================================================\n";
echo "POI mesta/obce: $totalCities\n";
echo "Vytvorene JSON: $totalCreated\n";
echo "Preskocene: $totalSkipped\n";
echo "Street ways: $totalWays\n";
echo "====================================================\n";
// ============================================================
// FUNKCIA
// SPRACUJE JEDEN OSM SUBOR
// ============================================================
function spracujOSMPreMesto(
string $osmFile,
float $cityLat,
float $cityLon,
float $radiusMeters,
array $allowedHighways
): array {
// --------------------------------------------------------
// OSMReader
// --------------------------------------------------------
$reader = new XMLReader();
if (!$reader->open($osmFile)) {
return [
'nodes' => [],
'streets' => []
];
}
// --------------------------------------------------------
// Najprv nacitame NODE
//
// Potrebujeme ich neskor pri highway way.
// --------------------------------------------------------
$nodes = [];
while ($reader->read()) {
if (
$reader->nodeType === XMLReader::ELEMENT &&
$reader->name === 'node'
) {
$id = $reader->getAttribute('id');
$lat = $reader->getAttribute('lat');
$lon = $reader->getAttribute('lon');
if (
$id === null ||
$lat === null ||
$lon === null
) {
continue;
}
$lat = (float)$lat;
$lon = (float)$lon;
// ------------------------------------------------
// Ulozime iba node, ktory je v okoli mesta.
// ------------------------------------------------
$distance = vzdialenostMetre(
$cityLat,
$cityLon,
$lat,
$lon
);
if ($distance <= $radiusMeters) {
$nodes[$id] = [
'lat' => $lat,
'lon' => $lon
];
}
}
}
$reader->close();
// --------------------------------------------------------
// DRUHE PRECITANIE XML
//
// Teraz hladame highway ways.
// --------------------------------------------------------
$reader = new XMLReader();
if (!$reader->open($osmFile)) {
return [
'nodes' => $nodes,
'streets' => []
];
}
$streets = [];
while ($reader->read()) {
if (
$reader->nodeType !== XMLReader::ELEMENT ||
$reader->name !== 'way'
) {
continue;
}
// ----------------------------------------------------
// Docasne data way
// ----------------------------------------------------
$wayId = $reader->getAttribute('id');
$wayNodes = [];
$name = null;
$highway = null;
$oneway = false;
$maxspeed = null;
// ----------------------------------------------------
// Precitaj obsah way
// ----------------------------------------------------
while ($reader->read()) {
// Koniec way
if (
$reader->nodeType === XMLReader::END_ELEMENT &&
$reader->name === 'way'
) {
break;
}
// ------------------------------------------------
// NODE REFERENCE
// ------------------------------------------------
if (
$reader->nodeType === XMLReader::ELEMENT &&
$reader->name === 'nd'
) {
$ref = $reader->getAttribute('ref');
if ($ref !== null) {
$wayNodes[] = $ref;
}
continue;
}
// ------------------------------------------------
// TAG
// ------------------------------------------------
if (
$reader->nodeType === XMLReader::ELEMENT &&
$reader->name === 'tag'
) {
$k = $reader->getAttribute('k');
$v = $reader->getAttribute('v');
if ($k === 'highway') {
$highway = $v;
}
elseif ($k === 'name') {
$name = $v;
}
elseif ($k === 'oneway') {
if (
$v === 'yes' ||
$v === '1' ||
$v === 'true'
) {
$oneway = true;
}
}
elseif ($k === 'maxspeed') {
$maxspeed = $v;
}
}
}
// ----------------------------------------------------
// Nie je highway
// ----------------------------------------------------
if ($highway === null) {
continue;
}
// ----------------------------------------------------
// Nechceny typ highway
// ----------------------------------------------------
if (!in_array($highway, $allowedHighways, true)) {
continue;
}
// ----------------------------------------------------
// Way musi mat aspon 2 body
// ----------------------------------------------------
if (count($wayNodes) < 2) {
continue;
}
// ----------------------------------------------------
// Zistime, ci way zasahuje do oblasti mesta.
//
// Staci, aby aspon jeden jeho node bol v radius.
// ----------------------------------------------------
$validNodes = [];
$hasNodeInside = false;
foreach ($wayNodes as $nodeId) {
if (isset($nodes[$nodeId])) {
$hasNodeInside = true;
$validNodes[] = $nodeId;
}
}
if (!$hasNodeInside) {
continue;
}
// ----------------------------------------------------
// Ak je ulica bez mena, dame null.
// ----------------------------------------------------
if ($name === '') {
$name = null;
}
// ----------------------------------------------------
// STREET OBJECT
// ----------------------------------------------------
$street = [
'id' => $wayId,
'name' => $name,
'highway' => $highway,
'oneway' => $oneway,
'nodes' => $validNodes
];
if ($maxspeed !== null) {
$street['maxspeed'] = $maxspeed;
}
$streets[] = $street;
}
$reader->close();
// --------------------------------------------------------
// Vysledok
// --------------------------------------------------------
return [
'nodes' => $nodes,
'streets' => $streets
];
}
// ============================================================
// HAVERSINE
// Vzdialenost dvoch GPS bodov v metroch
// ============================================================
function vzdialenostMetre(
float $lat1,
float $lon1,
float $lat2,
float $lon2
): float {
$earthRadius = 6371000;
$lat1Rad = deg2rad($lat1);
$lat2Rad = deg2rad($lat2);
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a =
sin($dLat / 2) *
sin($dLat / 2)
+
cos($lat1Rad) *
cos($lat2Rad) *
sin($dLon / 2) *
sin($dLon / 2);
$c = 2 * atan2(
sqrt($a),
sqrt(1 - $a)
);
return $earthRadius * $c;
}


Comments “Dóbre, zatiaľ 19 hodín ráta kompík takúto vec: Mali sme mestá vyseknuté 3km od stredu, ale stalo sa, že chýbalo pol mesta, tak vysekávame admin hranice miest práve:”