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:

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:

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

https://hrubos.tech/blogy/content/images/20260819124803-Sni%CC%81mka%20obrazovky%202026-08-19%20o%2012.46.45.png

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: liptovska osada

https://hrubos.tech/blogy/content/images/20260818234731-Sni%CC%81mka%20obrazovky%202026-08-18%20o%2023.44.48.png

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;
}

vysek radius


Author: AarNoma

The first Slovak cyborg 1 system

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:”