關閉      標題:dem 20 m RTree gdr 轉資料庫 可作 dem 地形高查詢
內容:
把 *.gdr / *.grd 轉成 csv,再合併成 taiwan_gdr.db (EPSG:3826)
// 並建立快速最近點查詢用的 RTree 索引
<?php
// build_taiwan_gdr_db.php
// 需求:把 *.gdr / *.grd 轉成 csv,再合併成 taiwan_gdr.db (EPSG:3826)
// 並建立快速最近點查詢用的 RTree 索引

ini_set('memory_limit', '-1');
set_time_limit(0);

function my_pathinfo($path)
{
    $dirname = '';
    $basename = '';
    $extension = '';
    $filename = '';

    $pos = strrpos($path, '/');
    if ($pos !== false) {
        $dirname = substr($path, 0, strrpos($path, '/'));
        $basename = substr($path, strrpos($path, '/') + 1);
    } else {
        $basename = $path;
    }

    $ext = strrchr($path, '.');
    if ($ext !== false) {
        $extension = substr($ext, 1);
    }

    $filename = $basename;
    $pos = strrpos($basename, '.');
    if ($pos !== false) {
        $filename = substr($basename, 0, $pos);
    }

    return array(
        'dirname'   => $dirname,
        'basename'  => $basename,
        'extension' => $extension,
        'filename'  => $filename
    );
}

function mainname($fname)
{
    $pathinfo = my_pathinfo($fname);
    return $pathinfo['filename'];
}

function rrmdirFilesByPattern($pattern)
{
    foreach (glob($pattern) as $f) {
        if (is_file($f)) {
            @unlink($f);
        }
    }
}

function runCmd($cmd)
{
    echo "\n[CMD] {$cmd}\n";
    exec($cmd . " 2>&1", $output, $ret);
    if (!empty($output)) {
        echo implode("\n", $output) . "\n";
    }
    if ($ret !== 0) {
        throw new Exception("指令執行失敗,exit code={$ret}\n{$cmd}");
    }
}

function grdToCsv($srcFile, $csvFile, $sourceName)
{
    $in = fopen($srcFile, 'r');
    if (!$in) {
        throw new Exception("無法開啟檔案: {$srcFile}");
    }

    $out = fopen($csvFile, 'w');
    if (!$out) {
        fclose($in);
        throw new Exception("無法寫入 CSV: {$csvFile}");
    }

    // 加欄位,保留來源檔名
    fputcsv($out, array('X', 'Y', 'Z', 'SOURCE_FILE'));

    $rowCount = 0;
    while (($line = fgets($in)) !== false) {
        $line = trim(str_replace(array("\r", "\n"), '', $line));
        if ($line === '') {
            continue;
        }

        // 允許空白 / tab / 多個空白
        $parts = preg_split('/\s+/', $line);

        if (count($parts) < 3) {
            continue;
        }

        $x = trim($parts[0]);
        $y = trim($parts[1]);
        $z = trim($parts[2]);

        if (!is_numeric($x) || !is_numeric($y) || !is_numeric($z)) {
            continue;
        }

        fputcsv($out, array($x, $y, $z, $sourceName));
        $rowCount++;
    }

    fclose($in);
    fclose($out);

    return $rowCount;
}

function buildCombinedCsv($csvFiles, $outputCsv)
{
    $out = fopen($outputCsv, 'w');
    if (!$out) {
        throw new Exception("無法建立 {$outputCsv}");
    }

    fputcsv($out, array('X', 'Y', 'Z', 'SOURCE_FILE'));

    $totalRows = 0;
    foreach ($csvFiles as $csv) {
        $in = fopen($csv, 'r');
        if (!$in) {
            continue;
        }

        $isFirst = true;
        while (($row = fgetcsv($in)) !== false) {
            if ($isFirst) {
                $isFirst = false; // 跳過 header
                continue;
            }
            if (count($row) < 4) {
                continue;
            }
            fputcsv($out, $row);
            $totalRows++;
        }
        fclose($in);
    }

    fclose($out);
    return $totalRows;
}

// =========================================================
// 主流程
// =========================================================

try {
    $dbFile          = 'taiwan_gdr.db';
    $allCsv          = 'all_points.csv';
    $vrtFile         = 'all_points.vrt';
    $tableName       = 'points';

    // 清理舊檔
    @unlink($dbFile);
    @unlink($allCsv);
    @unlink($vrtFile);
    rrmdirFilesByPattern('*.csv');

    // 同時支援 .gdr / .grd
    $files = array_merge(glob('*.gdr'), glob('*.grd'));
    $files = array_values(array_unique($files));

    if (count($files) === 0) {
        throw new Exception("目前目錄找不到 *.gdr 或 *.grd");
    }

    echo "找到 " . count($files) . " 個來源檔\n";

    $csvFiles = array();
    $grandTotal = 0;
    $maxK = count($files);

    foreach ($files as $k => $srcFile) {
        $mn = mainname($srcFile);
        $csvFile = $mn . '.csv';
        if(is_file($csvFile)) {
            continue;
        }
        //echo $csvFile;
        //exit();
        //echo "WTF";
        $rows = grdToCsv($srcFile, $csvFile, basename($srcFile));
        $csvFiles[] = $csvFile;
        $grandTotal += $rows;

        echo sprintf("[%d/%d] %s -> %s, rows=%d\n", $k + 1, $maxK, $srcFile, $csvFile, $rows);
    }

    echo "全部點數: {$grandTotal}\n";

    // 合併成單一 CSV
    $mergedRows = buildCombinedCsv($csvFiles, $allCsv);
    echo "合併完成: {$allCsv}, rows={$mergedRows}\n";

    // 建立 VRT,讓 ogr2ogr 可把 X/Y/Z 變成 3D Point
    $csvLayerName = mainname($allCsv); // all_points

    $vrt = <<<EOF
    <OGRVRTDataSource>
      <OGRVRTLayer name="{$tableName}">
        <SrcDataSource>{$allCsv}</SrcDataSource>
        <SrcLayer>{$csvLayerName}</SrcLayer>
        <GeometryType>wkbPoint25D</GeometryType>
        <LayerSRS>EPSG:3826</LayerSRS>
        <GeometryField encoding="PointFromColumns" x="X" y="Y" z="Z"/>
      </OGRVRTLayer>
    </OGRVRTDataSource>
    EOF;

    file_put_contents($vrtFile, $vrt);

    // 匯入 SQLite
    // 保留 X/Y/Z/SOURCE_FILE 欄位 + Geometry
    $cmd = 'ogr2ogr -f SQLite '
         . escapeshellarg($dbFile) . ' '
         . escapeshellarg($vrtFile)
         . ' -nln ' . escapeshellarg($tableName)
         . ' -a_srs EPSG:3826'
         . ' -lco SPATIAL_INDEX=NO';

    runCmd($cmd);

    // 建索引:一般索引 + RTree
    $pdo = new PDO('sqlite:' . $dbFile);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 一般索引
    $pdo->exec("CREATE INDEX IF NOT EXISTS idx_points_x ON {$tableName}(X)");
    $pdo->exec("CREATE INDEX IF NOT EXISTS idx_points_y ON {$tableName}(Y)");
    $pdo->exec("CREATE INDEX IF NOT EXISTS idx_points_xy ON {$tableName}(X, Y)");
    $pdo->exec("CREATE INDEX IF NOT EXISTS idx_points_source_file ON {$tableName}(SOURCE_FILE)");

    // RTree for nearest search
    $pdo->exec("DROP TABLE IF EXISTS points_rtree");
    $pdo->exec("
        CREATE VIRTUAL TABLE points_rtree USING rtree(
            id,
            minX, maxX,
            minY, maxY
        )
    ");

    $pdo->exec("
        INSERT INTO points_rtree(id, minX, maxX, minY, maxY)
        SELECT
            OGC_FID,
            CAST(X AS REAL), CAST(X AS REAL),
            CAST(Y AS REAL), CAST(Y AS REAL)
        FROM {$tableName}
    ");

    // 統計資訊
    $pdo->exec("ANALYZE");

    $count = $pdo->query("SELECT COUNT(*) FROM {$tableName}")->fetchColumn();
    echo "DB 建立完成: {$dbFile}\n";
    echo "總筆數: {$count}\n";
    echo "座標系統: EPSG:3826\n";
    echo "資料表: {$tableName}\n";
    echo "已建立 RTree: points_rtree\n";

} catch (Exception $e) {
    echo "\n[ERROR] " . $e->getMessage() . "\n";
    exit(1);
}




			$POSTS_STRING = "x,y"; // x6 y7
            $POSTS = getGET_POST($POSTS_STRING,"POST");
            $x = (float) $POSTS['x'];
            $y = (float) $POSTS['y'];
			
            $pdoGDR = new PDO('sqlite:dem/taoyuan/taiwan_gdr.db');
            $pdoGDR->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
			
            //$result = findNearestPoint($pdoGDR, 250000.0, 2650000.0);
			$result = findNearestPoint($pdoGDR, $x, $y, 1, 5000, 16);
			$OUTPUT=[];
            $OUTPUT['status'] = 'OK';
            $OUTPUT['data'] = $result;
            // 計算高度
            $OUTPUT['x'] = $x;
            $OUTPUT['y'] = $y;
            if(is_array($result)){
                $OUTPUT['z'] = getGridInterpolatedZ($result,$x,$y); // 內插高度
            }
            echo json_encode($OUTPUT);
            
            exit();