<!DOCTYPE html>
<html lang="th">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ระบบแปลงค่าพิกัด Geospatial Coordinate Converter</title>
    
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">

    <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.9.0/proj4.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js"></script>
    
    <style>
        .card-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        
        .coordinate-section {
            background-color: #f8f9fa;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
        }
        
        .result-section {
            background-color: #f5f3ff;
            border-radius: 10px;
            padding: 20px;
            margin-top: 20px;
            border: 1px solid #e9e4ff;
        }
        
        .error-section {
            background-color: #fff5f5;
            border: 1px solid #fed7d7;
            color: #c53030;
            border-radius: 10px;
            padding: 15px;
            margin-top: 20px;
        }
        
        .csv-preview {
            max-height: 300px;
            overflow-y: auto;
            background-color: white;
            border: 1px solid #dee2e6;
            border-radius: 5px;
            padding: 15px;
        }
        
        .footer {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            text-align: center;
            padding: 20px 0;
            margin-top: 50px;
        }

        .btn-success {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
            border: none !important;
            transition: 0.3s;
        }

        .btn-success:hover {
            filter: brightness(1.1);
            transform: translateY(-1px);
        }

        .btn-outline-success {
            color: #764ba2 !important;
            border-color: #764ba2 !important;
        }

        .btn-outline-success:hover {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
            color: white !important;
        }

        .table-striped > tbody > tr:nth-of-type(odd) > * {
            background-color: rgba(102, 126, 234, 0.05) !important;
            box-shadow: none !important;
        }

        .table thead th {
            color: #000000 !important;
            border-bottom: 2px solid #e9e4ff;
            font-weight: 700;
        }

        .table-danger {
            --bs-table-bg: #fff5f5 !important;
            color: #c53030 !important;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <!-- Header -->
        <header class="row">
            <div class="col-12">
                <div class="card mt-3">
                    <div class="card-header text-center">
                        <h1 class="mb-0"><i class="bi bi-geo-alt"></i> ระบบแปลงค่าพิกัด Geospatial</h1>
                        <p class="mb-0">แปลงค่าพิกัดระหว่าง WGS84/UTM (47N/48N) และ DOL Indian 1975 (24047/24048)</p>
                    </div>
                </div>
            </div>
        </header>

        <div class="row mt-4">
            <!-- Single Point Conversion Section -->
            <div class="col-lg-6">
                <div class="card h-100">
                    <div class="card-header">
                        <h4><i class="bi bi-bullseye"></i> แปลงพิกัดจุดเดียว</h4>
                    </div>
                    <div class="card-body">
    <div class="row">
        <div class="col-md-6 border-end"> <h5 class="text-primary mb-3"><i class="bi bi-box-arrow-in-right"></i> ข้อมูลต้นทาง</h5>
            
            <div class="mb-3">
                <label class="form-label">ระบบพิกัดต้นทาง</label>
                <select class="form-select" id="sourceCRS">
                    <option value="EPSG:4326" selected>EPSG:4326 (WGS84 Lat/Lon)</option>
                    <option value="EPSG:32647">EPSG:32647 (UTM Zone 47N / WGS84)</option>
                    <option value="EPSG:32648">EPSG:32648 (UTM Zone 48N / WGS84)</option>
                    <option value="EPSG:4240">EPSG:4240 (Lat/Lon / Indian 1975 - DOL)</option>
                    <option value="EPSG:24047">EPSG:24047 (UTM Zone 47N / Indian 1975 - DOL)</option>
                    <option value="EPSG:24048">EPSG:24048 (UTM Zone 48N / Indian 1975 - DOL)</option>
                    <option value="EPSG:3857">EPSG:3857 (Web Mercator)</option>
                </select>
            </div>
            <div class="mb-3" id="singleLatLonFormatDiv" style="display: none;">
    <label class="form-label">รูปแบบพิกัด (Lat/Lon)</label>
    <select class="form-select" id="singleLatLonFormat">
        <option value="DD" selected>ทศนิยม (Decimal Degrees)</option>
        <option value="DMS">องศา ลิปดา ฟิลิปดา (DMS)</option>
    </select>
</div>

            <div class="mb-3">
    <label class="form-label" id="labelInputX">X / Longitude</label>
    
    <input type="number" class="form-control" id="inputX" placeholder="" step="any">
    
    <div class="input-group" id="inputX_dms_group" style="display:none;">
        <input type="number" class="form-control" id="inputX_d" placeholder="">
        <span class="input-group-text bg-light">°</span>
        <input type="number" class="form-control" id="inputX_m" placeholder="">
        <span class="input-group-text bg-light">'</span>
        <input type="number" class="form-control" id="inputX_s" placeholder="">
        <span class="input-group-text bg-light">"</span>
    </div>
</div>

<div class="mb-3">
    <label class="form-label" id="labelInputY">Y / Latitude</label>
    
    <input type="number" class="form-control" id="inputY" placeholder="" step="any">
    
    <div class="input-group" id="inputY_dms_group" style="display:none;">
        <input type="number" class="form-control" id="inputY_d" placeholder="">
        <span class="input-group-text bg-light">°</span>
        <input type="number" class="form-control" id="inputY_m" placeholder="">
        <span class="input-group-text bg-light">'</span>
        <input type="number" class="form-control" id="inputY_s" placeholder="">
        <span class="input-group-text bg-light">"</span>
    </div>
</div>

            <button class="btn btn-primary w-100 mt-3" id="convertBtn">
                <i class="bi bi-arrow-repeat"></i> แปลงพิกัด
            </button>

            <div id="errorMessage" class="alert alert-danger mt-3" style="display: none;">
                <i class="bi bi-exclamation-triangle"></i> <span id="errorText"></span>
            </div>
        </div>

        <div class="col-md-6">
            <h5 class="text-success mb-3"><i class="bi bi-box-arrow-right"></i> ข้อมูลปลายทาง</h5>

            <div class="mb-3">
                <label class="form-label">ระบบพิกัดปลายทาง</label>
                <select class="form-select" id="targetCRS">
                    <option value="EPSG:32647" selected>EPSG:32647 (UTM Zone 47N / WGS84)</option>
                    <option value="EPSG:32648">EPSG:32648 (UTM Zone 48N / WGS84)</option>
                    <option value="EPSG:4240">EPSG:4240 (Lat/Lon / Indian 1975 - DOL)</option>
                    <option value="EPSG:24047">EPSG:24047 (UTM Zone 47N / Indian 1975 - DOL)</option>
                    <option value="EPSG:24048">EPSG:24048 (UTM Zone 48N / Indian 1975 - DOL)</option>
                    <option value="EPSG:3857">EPSG:3857 (Web Mercator)</option>
                    <option value="EPSG:4326">EPSG:4326 (WGS84 Lat/Lon)</option>
                </select>
            </div>

            <div id="singleResult" style="display: none; background-color: #f1f8ff; padding: 15px; border-radius: 10px; border: 1px solid #d0e3ff;">
                
                <div class="mb-3">
                    <label class="form-label fw-bold" id="labelOutputX">X / Longitude / Easting</label>
                    <input type="text" class="form-control bg-white" id="outputX" readonly>
                </div>
                <div class="mb-3">
                    <label class="form-label fw-bold" id="labelOutputY">Y / Latitude / Northing</label>
                    <input type="text" class="form-control bg-white" id="outputY" readonly>
                </div>

                <hr>

                <div class="row">
                    <div class="col-6">
                        <label class="form-label small">Scale Factor (k)</label>
                        <div class="input-group input-group-sm">
                            <input type="number" class="form-control" id="scaleFactor" step="0.00000001">
                            <button class="btn btn-outline-secondary" type="button" onclick="recalcFromFactors()">แก้</button>
                        </div>
                    </div>
                    <div class="col-6">
                        <label class="form-label small">Convergence (&#947;)</label>
                        <input type="text" class="form-control form-control-sm" id="gridConvergence" readonly>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</div>
</div>


            <div class="col-lg-6">
                <div class="card h-100">
                    <div class="card-header">
                        <h4><i class="bi bi-file-earmark-spreadsheet"></i> แปลงไฟล์ CSV</h4>
                    </div>
                    <div class="card-body">
                        <div class="coordinate-section">
                            <h5>อัปโหลดไฟล์ CSV</h5>
                            <div class="row mb-3">
                                <div class="col-md-6">
                                    <label class="form-label">ระบบพิกัดต้นทาง</label>
                                    <select class="form-select" id="csvSourceCRS">
                                        <option value="EPSG:4326" selected>EPSG:4326 (WGS84 Lat/Lon)</option>
                                        <option value="EPSG:32647">EPSG:32647 (UTM Zone 47N / WGS84)</option>
                                        <option value="EPSG:32648">EPSG:32648 (UTM Zone 48N / WGS84)</option>
                                        <option value="EPSG:4240">EPSG:4240 (Lat/Lon / Indian 1975 - DOL)</option>
                                        <option value="EPSG:24047">EPSG:24047 (UTM Zone 47N / Indian 1975 - DOL)</option>
                                        <option value="EPSG:24048">EPSG:24048 (UTM Zone 48N / Indian 1975 - DOL)</option>
                                        <option value="EPSG:3857">EPSG:3857 (Web Mercator)</option>
                                    </select>
                                </div>
                                <div class="col-md-6">
                                    <label class="form-label">ระบบพิกัดปลายทาง</label>
                                    <select class="form-select" id="csvTargetCRS">
                                        <option value="EPSG:32647" selected>EPSG:32647 (UTM Zone 47N / WGS84)</option>
                                        <option value="EPSG:32648">EPSG:32648 (UTM Zone 48N / WGS84)</option>
                                        <option value="EPSG:4240">EPSG:4240 (Lat/Lon / Indian 1975 - DOL)</option>
                                        <option value="EPSG:24047">EPSG:24047 (UTM Zone 47N / Indian 1975 - DOL)</option>
                                        <option value="EPSG:24048">EPSG:24048 (UTM Zone 48N / Indian 1975 - DOL)</option>
                                        <option value="EPSG:3857">EPSG:3857 (Web Mercator)</option>
                                        <option value="EPSG:4326">EPSG:4326 (WGS84 Lat/Lon)</option>
                                    </select>
                                </div>
                            </div>
                            
                            <div class="mb-3">
                                <label class="form-label">รูปแบบ Column</label>
                                <select class="form-select" id="csvFormat">
                                    <option value="Point_X_Y" selected>Point, X, Y </option>
                                    <option value="Point_Y_X">Point, Y, X </option>
                                    <option value="Point_E_N">Point, E, N </option>
                                    <option value="Point_N_E">Point, N, E </option>
                                    <option value="Point_Lat_Lon">Point, Lat, Lon </option>
                                    <option value="Point_Lon_Lat">Point, Lon, Lat </option>
                                </select>
                            </div>
                            <div class="mb-3" id="csvLatLonFormatDiv" style="display: none;">
    <label class="form-label">รูปแบบข้อมูล Lat/Lon ในไฟล์</label>
    <select class="form-select" id="csvLatLonFormat">
        <option value="DD" selected>ทศนิยม (Decimal Degrees)</option>
        <option value="DMS">องศา ลิปดา ฟิลิปดา (DMS) เช่น 100° 20' 30"</option>
    </select>
</div>

                            <div class="mb-3">
                                <label for="csvFile" class="form-label">เลือกไฟล์ CSV</label>
                                <input class="form-control" type="file" id="csvFile" accept=".csv">
                            </div>
                            <div class="mb-3">
                                <label class="form-label">กำหนดค่า Scale Factor (Optional)</label>
                                <input type="number" class="form-control" id="csvManualScale" step="0.00000001" placeholder="ปล่อยว่างเพื่อคำนวณอัตโนมัติ">
                                <div class="form-text text-muted">
                                    <i class="bi bi-info-circle"></i> หากใส่ค่านี้ ระบบจะปรับแก้พิกัดผลลัพธ์เป็น Ground Distance (Grid / k) ให้ทุกจุด
                                </div>
                            </div>

                            <button class="btn btn-success w-100" id="processCSVBtn" disabled>
                                <i class="bi bi-gear"></i> ประมวลผลไฟล์ CSV
                            </button>
                        </div>

                        <div id="csvResults" style="display: none;">
                            <div class="result-section">
                                <div class="d-flex justify-content-between align-items-center mb-3">
                                    <h5 style="color: #000000;"><i class="bi bi-table text-primary"></i> ผลลัพธ์ CSV</h5>
                                    <button class="btn btn-outline-success" id="downloadBtn">
                                        <i class="bi bi-download"></i> ดาวน์โหลด CSV
                                    </button>
                                </div>
                                <div id="csvPreview" class="csv-preview"></div>
                                <div class="mt-2">
                                    <small class="text-muted">
                                        <span id="recordCount"></span> รายการ | แสดงเพียง 10 รายการแรก
                                    </small>
                                </div>
                            </div>
                        </div>

                        <div id="csvError" class="error-section" style="display: none;">
                            <i class="bi bi-exclamation-triangle text-danger"></i>
                            <span id="csvErrorText"></span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="row mt-4">
            <div class="col-12">
                <div class="card">
                    <div class="card-header">
                        <h4><i class="bi bi-info-circle"></i> ข้อมูลระบบพิกัด</h4>
                    </div>
                    <div class="card-body">
                        <div class="row g-3">
                            <div class="col-md-4 col-lg-3">
                                <div class="card border-primary">
                                    <div class="card-body">
                                        <h6 class="card-title text-primary">EPSG:4326</h6>
                                        <p class="card-text">WGS84 Latitude/Longitude<br>
                                        หน่วย: องศา (degrees)<br>
                                        ใช้งาน: GPS, แผนที่ออนไลน์</p>
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-4 col-lg-3">
                                <div class="card border-success">
                                    <div class="card-body">
                                        <h6 class="card-title text-success">EPSG:32647</h6>
                                        <p class="card-text">UTM Zone 47N (WGS84)<br>
                                        หน่วย: เมตร (meters)<br>
                                        ใช้งาน: ประเทศไทยฝั่งตะวันตก</p>
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-4 col-lg-3">
                                <div class="card border-warning">
                                    <div class="card-body">
                                        <h6 class="card-title text-warning">EPSG:32648</h6>
                                        <p class="card-text">UTM Zone 48N (WGS84)<br>
                                        หน่วย: เมตร (meters)<br>
                                        ใช้งาน: ประเทศไทยฝั่งตะวันออก</p>
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-6 col-lg-3">
                                <div class="card border-info">
                                    <div class="card-body">
                                        <h6 class="card-title text-info">EPSG:24047</h6>
                                        <p class="card-text">UTM Zone 47N (Indian 1975 - DOL)<br>
                                        หน่วย: เมตร (meters)<br>
                                        ใช้งาน: งานรังวัด/แผนที่เก่า (ฝั่งตะวันตก)</p>
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-6 col-lg-3">
                                <div class="card border-info">
                                    <div class="card-body">
                                        <h6 class="card-title text-info">EPSG:24048</h6>
                                        <p class="card-text">UTM Zone 48N (Indian 1975 - DOL)<br>
                                        หน่วย: เมตร (meters)<br>
                                        ใช้งาน: งานรังวัด/แผนที่เก่า (ฝั่งตะวันออก)</p>
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-6 col-lg-3">
                                <div class="card border-secondary">
                                    <div class="card-body">
                                        <h6 class="card-title text-secondary">EPSG:3857</h6>
                                        <p class="card-text">Web Mercator<br>
                                        หน่วย: เมตร (meters)<br>
                                        ใช้งาน: แผนที่ออนไลน์ (XYZ tiles)</p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <footer class="footer">
        <p>&copy; 2024 Geospatial Coordinate Converter | Powered by Proj4.js & Bootstrap</p>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

    <script>
        proj4.defs("EPSG:4326", "+proj=longlat +datum=WGS84 +no_defs");
        proj4.defs("EPSG:32647", "+proj=utm +zone=47 +datum=WGS84 +units=m +no_defs");
        proj4.defs("EPSG:32648", "+proj=utm +zone=48 +datum=WGS84 +units=m +no_defs");
        proj4.defs("EPSG:4240", "+proj=longlat +ellps=evrst30 +towgs84=204.4798,837.8940,294.7765,0,0,0,0 +no_defs +type=crs");
        proj4.defs("EPSG:24047", "+proj=utm +zone=47 +a=6377276.345 +b=6356075.41314024 +towgs84=204.4798,837.8940,294.7765,0,0,0,0 +units=m +no_defs +type=crs");
        proj4.defs("EPSG:24048", "+proj=utm +zone=48 +ellps=evrst30 +towgs84=204.4798,837.8940,294.7765,0,0,0,0 +units=m +no_defs +type=crs");
        proj4.defs("EPSG:3857", "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +type=crs");

        let csvData = [];
        let convertedData = [];
        let csvParsed = null;

function parseDmsStringToDD(dmsStr) {
    if (!dmsStr) return NaN;
    const s = String(dmsStr).trim().toUpperCase();
    
    let sign = (s.includes('S') || s.includes('W') || s.startsWith('-')) ? -1 : 1;
    
    const parts = s.match(/\d+(\.\d+)?/g);
    if (!parts || parts.length === 0) return NaN;
    
    let deg = parseFloat(parts[0]) || 0;
    let min = parts.length > 1 ? parseFloat(parts[1]) : 0;
    let sec = parts.length > 2 ? parseFloat(parts[2]) : 0;
    
    return sign * (deg + (min / 60) + (sec / 3600));
}

        function stripBom(text) {
            if (!text) return text;
            return text.charAt(0) === '\ufeff' ? text.slice(1) : text;
        }
        function normalizeHeaderName(name) {
            return String(name || '')
                .replace(/^\ufeff/, '')
                .trim()
                .toLowerCase()
                .replace(/["']/g, '')
                .replace(/[\s\-_]+/g, '');
        }
        function parseCsvFile(csvText, fileName) {
            const results = Papa.parse(csvText, {
                skipEmptyLines: true,   
                guessHeader: false      
            });

            if (results.errors.length > 0) {
                console.warn("CSV Parse Warnings:", results.errors);
            }

            const rows = results.data; 
            
            if (!rows || rows.length === 0) {
                throw new Error('ไฟล์ว่าง หรือไม่พบข้อมูล CSV');
            }

            const delimiter = results.meta.delimiter; 
            
            const maxColumns = rows.reduce((m, r) => Math.max(m, r.length), 0);
            const firstRow = rows[0] || [];

            const knownHeaderNames = [
                'point', 'pt', 'name', 'id',
                'x', 'y', 'e', 'n',
                'lon', 'lat', 'long', 'longitude', 'latitude',
                'easting', 'northing'
            ].map(normalizeHeaderName);

            const firstRowLooksLikeHeader = firstRow
                .map(normalizeHeaderName)
                .some(h => knownHeaderNames.includes(h));

            let headers = [];
            let dataRows = [];
            let hasHeader = false;

            if (firstRowLooksLikeHeader) {
                hasHeader = true;
                headers = firstRow.map(h => stripBom(String(h || '')).trim());
                dataRows = rows.slice(1);
            } else {
                hasHeader = false;
                headers = Array.from({ length: maxColumns }, (_, i) => `col${i + 1}`);
                dataRows = rows;
            }

            while (headers.length < maxColumns) {
                headers.push(`extra_${headers.length + 1}`);
            }

            return {
                fileName: fileName || 'uploaded.csv',
                delimiter,
                headers,
                rows: dataRows,
                hasHeader,
                maxColumns
            };
        }

        function escapeCsvField(value, delimiter) {
            const str = value === null || value === undefined ? '' : String(value);
            const mustQuote = str.includes('"') || str.includes('\n') || str.includes('\r') || str.includes(delimiter);
            if (!mustQuote) return str;
            return `"${str.replace(/"/g, '""')}"`;
        }

        function escapeHtml(value) {
            return String(value ?? '')
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }

        function parseNumberFlexible(value, inputDelimiter) {
            let s = String(value ?? '').trim();
            if (!s) return NaN;
            if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
                s = s.slice(1, -1).trim();
            }
            s = s.replace(/\s+/g, '');

            const hasDot = s.includes('.');
            const hasComma = s.includes(',');

            if (hasDot && hasComma) {
                const lastDot = s.lastIndexOf('.');
                const lastComma = s.lastIndexOf(',');
                if (lastComma > lastDot) {
                    s = s.replace(/\./g, '').replace(/,/g, '.');
                } else {
                    s = s.replace(/,/g, '');
                }
            } else if (hasComma && !hasDot) {
                if (inputDelimiter && inputDelimiter !== ',') {
                    s = s.replace(/,/g, '.');
                } else {
                    s = s.replace(/,/g, '');
                }
            }

            const num = Number(s);
            return Number.isFinite(num) ? num : NaN;
        }

        function isLikelyLonLatSwap4326(x, y) {
            if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
            const ax = Math.abs(x);
            const ay = Math.abs(y);
            return ax <= 90 && ay > 90 && ay <= 180;
        }

        function validateSourceCoordinates(sourceCRS, x, y) {
            if (!Number.isFinite(x) || !Number.isFinite(y)) {
                return { ok: false, message: 'ค่าพิกัดไม่เป็นตัวเลข' };
            }

            if (sourceCRS === 'EPSG:4326' || sourceCRS === 'EPSG:4240') {
                const lon = x;
                const lat = y;
                if (Math.abs(lon) > 180) return { ok: false, message: 'Longitude เกินช่วง (-180..180)' };
                if (Math.abs(lat) > 90) return { ok: false, message: 'Latitude เกินช่วง (-90..90)' };
                return { ok: true };
            }

            if (sourceCRS === 'EPSG:3857') {
                const limit = 20037508.3427892;
                if (Math.abs(x) > limit || Math.abs(y) > limit) {
                    return { ok: false, message: 'พิกัด Web Mercator เกินช่วงที่เป็นไปได้' };
                }
                return { ok: true };
            }

            if (sourceCRS.startsWith('EPSG:326') || sourceCRS.startsWith('EPSG:240')) {
                const eastingOk = x >= 0 && x <= 1000000;
                const northingOk = y >= 0 && y <= 10000000;
                if (!eastingOk || !northingOk) {
                    return { ok: false, message: 'ค่า Easting/Northing อยู่นอกช่วงที่เป็นไปได้' };
                }
                return { ok: true };
            }

            return { ok: true };
        }

        function getOutputFieldNamesByTargetCrs(targetCRS) {
            if (targetCRS === 'EPSG:4326' || targetCRS === 'EPSG:4240') return { outX: 'lon_out', outY: 'lat_out' };
            if (targetCRS.startsWith('EPSG:326') || targetCRS.startsWith('EPSG:240')) return { outX: 'e_out', outY: 'n_out' };
            return { outX: 'x_out', outY: 'y_out' };
        }

        function buildUniqueOutputHeaders(baseHeaders, extraHeaders) {
            const headers = [...baseHeaders];
            const mapping = {};
            extraHeaders.forEach(h => {
                let name = h;
                let n = 1;
                while (headers.includes(name)) {
                    n++;
                    name = `${h}_${n}`;
                }
                headers.push(name);
                mapping[h] = name;
            });
            return { headers, mapping };
        }

        function findColumnIndexByCandidates(headers, candidates) {
            const normalizedHeaders = headers.map(normalizeHeaderName);
            for (const cand of candidates) {
                const idx = normalizedHeaders.indexOf(normalizeHeaderName(cand));
                if (idx !== -1) return idx;
            }
            return -1;
        }

        
        function degToRad(deg) { return deg * (Math.PI / 180); }

        function radToDmsStr(rad) {
            let d = rad * (180 / Math.PI);
            const sign = d < 0 ? "-" : "";
            d = Math.abs(d);
            const deg = Math.floor(d);
            const minFloat = (d - deg) * 60;
            const min = Math.floor(minFloat);
            const sec = ((minFloat - min) * 60).toFixed(4);
            return `${sign}${deg}° ${min}' ${sec}"`;
        }

        function calculateGridFactors(lat, lon, zoneNumber, datum) {
            let a, f_inv;
            if (datum.includes('Indian') || datum.includes('240')) {
                a = 6377276.345; f_inv = 300.8017; 
            } else {
                a = 6378137.0;   f_inv = 298.257223563; 
            }
            
            const f = 1 / f_inv;
            const e2 = 2*f - f*f;
            const k0 = 0.9996;
            const cmLon = (zoneNumber * 6) - 183; 
            
            const phi = degToRad(lat);
            const lambda = degToRad(lon);
            const lambda0 = degToRad(cmLon);
            const dLambda = lambda - lambda0;
            const t = Math.tan(phi);
            const nu = a / Math.sqrt(1 - e2 * Math.sin(phi) * Math.sin(phi));
            const gammaRad = dLambda * Math.sin(phi); 
            const x_approx = dLambda * Math.cos(phi) * nu; 
            const k = k0 * (1 + (x_approx * x_approx) / (2 * k0 * k0 * a * a));
            return { k: k.toFixed(8), gammaStr: radToDmsStr(gammaRad) };
        }

        function recalcFromFactors() {
            const currentX = parseFloat(document.getElementById('outputX').value);
            const currentY = parseFloat(document.getElementById('outputY').value);
            const userK = parseFloat(document.getElementById('scaleFactor').value);
            
            if (isNaN(currentX) || isNaN(currentY) || isNaN(userK)) {
                alert("กรุณาแปลงพิกัดก่อน หรือใส่ค่าสเกลให้ถูกต้อง");
                return;
            }
            const falseE = 500000;
            const falseN = 0; 
            const groundE = ((currentX - falseE) / userK) + falseE;
            const groundN = ((currentY - falseN) / userK) + falseN;
            document.getElementById('outputX').value = groundE.toFixed(4);
            document.getElementById('outputY').value = groundN.toFixed(4);
        }
        function showError(message) {
            document.getElementById('errorText').textContent = message;
            document.getElementById('errorMessage').style.display = 'block';
            document.getElementById('singleResult').style.display = 'none';
        }
        function showCSVError(message) {
            document.getElementById('csvErrorText').textContent = message;
            document.getElementById('csvError').style.display = 'block';
            document.getElementById('csvResults').style.display = 'none';
        }
        
function ddToDmsStr(dd) {
    const d = Math.floor(Math.abs(dd));
    const minFloat = (Math.abs(dd) - d) * 60;
    const m = Math.floor(minFloat);
    const s = ((minFloat - m) * 60).toFixed(4);
    const sign = dd < 0 ? "-" : "";
    return `${sign}${d}° ${m}' ${s}"`;
}

function getDmsInputAsDD(idPrefix) {
        const dStr = document.getElementById(idPrefix + '_d').value;
        const mStr = document.getElementById(idPrefix + '_m').value;
        const sStr = document.getElementById(idPrefix + '_s').value;

        if (dStr === '' && mStr === '' && sStr === '') {
            return NaN;
        }

        const d = parseFloat(dStr) || 0;
        const m = parseFloat(mStr) || 0;
        const s = parseFloat(sStr) || 0;
        
        return d + (m / 60) + (s / 3600);
    }

function convertSinglePoint() {
    const sourceCRS = document.getElementById('sourceCRS').value;
    const targetCRS = document.getElementById('targetCRS').value;
    const isLatLonSource = (sourceCRS === 'EPSG:4326' || sourceCRS === 'EPSG:4240');
    const singleLatLonFormat = document.getElementById('singleLatLonFormat').value;

    let inputX, inputY;

    if (isLatLonSource) {
        if (singleLatLonFormat === 'DMS') {
            inputX = getDmsInputAsDD('inputX');
            inputY = getDmsInputAsDD('inputY');
        } else {
            inputX = parseFloat(document.getElementById('inputX').value);
            inputY = parseFloat(document.getElementById('inputY').value);
        }
    } else {
        inputX = parseFloat(document.getElementById('inputX').value);
        inputY = parseFloat(document.getElementById('inputY').value);
    }

    document.getElementById('singleResult').style.display = 'none';
    document.getElementById('errorMessage').style.display = 'none';

    if (isNaN(inputX) || isNaN(inputY)) {
        document.getElementById('errorText').innerText = 'กรุณากรอกค่าพิกัดให้ครบถ้วน';
        document.getElementById('errorMessage').style.display = 'block';
        return;
    }
    if (sourceCRS === targetCRS) {
            document.getElementById('errorText').innerText = 'ระบบพิกัดต้นทางและปลายทางต้องแตกต่างกัน';
            document.getElementById('errorMessage').style.display = 'block';
            return;
        }
    try {
        const result = proj4(sourceCRS, targetCRS, [inputX, inputY]);
        const isUtmTarget = targetCRS.startsWith('EPSG:326') || targetCRS.startsWith('EPSG:240');
        const isLatLonTarget = (targetCRS === 'EPSG:4326' || targetCRS === 'EPSG:4240');

        if (isLatLonTarget) {
            document.getElementById('outputX').value = ddToDmsStr(result[0]);
            document.getElementById('outputY').value = ddToDmsStr(result[1]);
        } else {
            document.getElementById('outputX').value = result[0].toFixed(3);
            document.getElementById('outputY').value = result[1].toFixed(3);
        }

        if (isUtmTarget) {
            const wgs84Pt = proj4(targetCRS, 'EPSG:4326', [result[0], result[1]]);
            let zone = 47;
            if (targetCRS.includes('48')) zone = 48;
            const factors = calculateGridFactors(wgs84Pt[1], wgs84Pt[0], zone, targetCRS);

            document.getElementById('scaleFactor').value = factors.k;
            document.getElementById('gridConvergence').value = factors.gammaStr;
        } else {
            document.getElementById('scaleFactor').value = "";
            document.getElementById('gridConvergence').value = "-";
        }

        document.getElementById('singleResult').style.display = 'block';

    } catch (error) {
            console.error(error);
            
            let msg = error.message;
            if (msg.includes('finite numbers')) {
                msg = 'ค่าพิกัดต้องเป็นตัวเลขที่ถูกต้อง';
            } else if (msg.includes('latitude')) {
                msg = 'ค่า Latitude อยู่นอกขอบเขต';
            }
            
            document.getElementById('errorText').innerText = 'เกิดข้อผิดพลาด: ' + msg;
            document.getElementById('errorMessage').style.display = 'block';
        }
}
        function handleFileUpload(event) {
            const file = event.target.files[0];
            const btn = document.getElementById('processCSVBtn');
            const errorSection = document.getElementById('csvError');
            const resultsSection = document.getElementById('csvResults');
            btn.disabled = true;
            errorSection.style.display = 'none';
            resultsSection.style.display = 'none';
            csvParsed = null;

            if (!file) return;

            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const text = e.target.result;
                    csvParsed = parseCsvFile(text, file.name); 
                    btn.disabled = false; 
                } catch (error) {
                    document.getElementById('csvErrorText').textContent = error.message;
                    errorSection.style.display = 'block';
                }
            };
            reader.onerror = function() {
                document.getElementById('csvErrorText').textContent = 'ไม่สามารถอ่านไฟล์ได้';
                errorSection.style.display = 'block';
            };
            reader.readAsText(file);
        }

        function processCSV() {
            const sourceCRS = document.getElementById('csvSourceCRS').value;
            const targetCRS = document.getElementById('csvTargetCRS').value;
            const format = document.getElementById('csvFormat').value;
            const btn = document.getElementById('processCSVBtn');
            const manualScaleElement = document.getElementById('csvManualScale');
            const manualScale = manualScaleElement ? parseFloat(manualScaleElement.value) : NaN;

            document.getElementById('csvError').style.display = 'none';
            document.getElementById('csvResults').style.display = 'none';

            if (sourceCRS === targetCRS) {
                showCSVError('ระบบพิกัดต้นทางและปลายทางต้องแตกต่างกัน');
                return;
            }

            if (!csvParsed || !Array.isArray(csvParsed.rows) || csvParsed.rows.length === 0) {
                showCSVError('กรุณาอัปโหลดไฟล์ CSV ก่อน');
                return;
            }

            try {
                if (btn) {
                    btn.disabled = true;
                    btn.dataset.originalText = btn.innerHTML;
                    btn.innerHTML = '<i class="bi bi-hourglass-split"></i> กำลังประมวลผล...';
                }

                const headers = csvParsed.headers;
                const rows = csvParsed.rows;
                const delimiter = csvParsed.delimiter;
                const fmt = format; 
                let pointIndex = 0; 
                let xIndex = 1;     
                let yIndex = 2;     

                if (fmt === 'Point_Y_X' || fmt === 'Point_N_E' || fmt === 'Point_Lat_Lon') {
                    xIndex = 2; 
                    yIndex = 1;
                }

                if (csvParsed.maxColumns === 2) {
                    pointIndex = -1;
                    xIndex = 0;
                    yIndex = 1;
                    if (fmt === 'Point_Y_X' || fmt === 'Point_N_E' || fmt === 'Point_Lat_Lon') {
                        xIndex = 1; yIndex = 0;
                    }
                }

                if (xIndex < 0 || yIndex < 0) {
                    throw new Error(`ไม่พบคอลัมน์พิกัดในไฟล์ (ตรวจสอบชื่อ Header)`);
                }

                const xColName = csvParsed.hasHeader ? headers[xIndex] : `Col ${xIndex+1}`;
                const yColName = csvParsed.hasHeader ? headers[yIndex] : `Col ${yIndex+1}`;
                const { outX, outY } = getOutputFieldNamesByTargetCrs(targetCRS);
                const extraOutHeaders = [outX, outY, 'scale_k', 'convergence', 'error'];
                const { headers: outputHeaders, mapping } = buildUniqueOutputHeaders(headers, extraOutHeaders);
                convertedData = [];
                let successCount = 0;
                let errorCount = 0;
                let swapCount = 0;
                let errorLogList = []; 

                const isUtmTarget = targetCRS.startsWith('EPSG:326') || targetCRS.startsWith('EPSG:240');

                const csvLatLonFormat = document.getElementById('csvLatLonFormat') ? document.getElementById('csvLatLonFormat').value : 'DD';
                const isLatLonSource = (sourceCRS === 'EPSG:4326' || sourceCRS === 'EPSG:4240');

                rows.forEach((r, idx) => {
                    const rowIndex = idx + (csvParsed.hasHeader ? 2 : 1); 
                    const rowObj = {};
                    for (let c = 0; c < outputHeaders.length; c++) rowObj[outputHeaders[c]] = '';
                    
                    headers.forEach((h, i) => { rowObj[h] = (r[i] ?? '').toString().trim(); });

                    const pointValue = pointIndex >= 0 ? (r[pointIndex] ?? '').toString().trim() : '';
                    const pointDisplay = pointValue || `P${idx + 1}`;

                    const rawX = r[xIndex];
                    const rawY = r[yIndex];
                    let x, y;

                    if (isLatLonSource && csvLatLonFormat === 'DMS') {
                        x = parseDmsStringToDD(rawX);
                        y = parseDmsStringToDD(rawY);
                    } else {
                        x = parseNumberFlexible(rawX, delimiter);
                        y = parseNumberFlexible(rawY, delimiter);
                    }

                    let swapped = false;
                    if ((sourceCRS === 'EPSG:4326' || sourceCRS === 'EPSG:4240') && isLikelyLonLatSwap4326(x, y)) {
                        const tmp = x; x = y; y = tmp;
                        swapped = true;
                        swapCount++;
                    }

                    let outXVal = '';
                    let outYVal = '';
                    let err = '';
                    let factors = { k: '-', gammaStr: '-' }; 
                    if (!Number.isFinite(x)) {
                        err = `บรรทัด ${rowIndex}: ค่า "${rawX}" ไม่ถูกต้อง (คอลัมน์: ${xColName})`;
                    } else if (!Number.isFinite(y)) {
                        err = `บรรทัด ${rowIndex}: ค่า "${rawY}" ไม่ถูกต้อง (คอลัมน์: ${yColName})`;
                    } else {
                        const v = validateSourceCoordinates(sourceCRS, x, y);
                        if (!v.ok) {
                            err = `บรรทัด ${rowIndex}: ${v.message}`;
                        } else {
                            try {
                                let result = proj4(sourceCRS, targetCRS, [x, y]);
                                
                                if (isUtmTarget) {
                                    try {
                                        const wgs84Pt = proj4(targetCRS, 'EPSG:4326', [result[0], result[1]]);
                                        let zone = 47;
                                        if (targetCRS.includes('48')) zone = 48;
                                        factors = calculateGridFactors(wgs84Pt[1], wgs84Pt[0], zone, targetCRS);
                                    } catch (e) { console.log(e); }
                                }

                                if (isUtmTarget && !isNaN(manualScale) && manualScale > 0) {
                                    const falseE = 500000;
                                    const falseN = 0; 
                                    result[0] = ((result[0] - falseE) / manualScale) + falseE;
                                    result[1] = ((result[1] - falseN) / manualScale) + falseN;
                                    factors.k = manualScale.toFixed(8) + " (Manual)";
                                }

                                const decimals = targetCRS === 'EPSG:4326' ? 8 : 3;
                                outXVal = Number(result[0]).toFixed(decimals);
                                outYVal = Number(result[1]).toFixed(decimals);
                                successCount++;
                            } catch (e) {
                                err = `บรรทัด ${rowIndex}: คำนวณผิดพลาด (${e.message})`;
                            }
                        }
                    }

                    if (err) {
                        errorCount++;
                        if (errorLogList.length < 5) errorLogList.push(err); 
                    }

                    rowObj[mapping[outX]] = outXVal;
                    rowObj[mapping[outY]] = outYVal;
                    rowObj[mapping['scale_k']] = factors.k;
                    rowObj[mapping['convergence']] = factors.gammaStr;
                    rowObj[mapping['error']] = err ? (swapped ? `${err} (auto swap)` : err) : (swapped ? 'auto-swapped' : '');

                    convertedData.push({
                        point: pointDisplay,
                        inX: rawX ?? '',
                        inY: rawY ?? '',
                        outX: outXVal,
                        outY: outYVal,
                        scale: factors.k,       
                        conv: factors.gammaStr, 
                        error: rowObj[mapping['error']],
                        outputHeaders,
                        outputRow: rowObj,
                        delimiter
                    });
                });

                displayCSVResults();
                let summaryText = `ทั้งหมด ${convertedData.length} | สำเร็จ ${successCount} | ผิดพลาด ${errorCount}`;
                if (swapCount > 0) summaryText += ` | สลับ lon/lat ${swapCount}`;
                document.getElementById('recordCount').textContent = summaryText;
                const resultDiv = document.getElementById('csvResults');
                const oldAlert = document.getElementById('errorSummaryAlert');
                if (oldAlert) oldAlert.remove();
                if (errorCount > 0) {
                    const alertDiv = document.createElement('div');
                    alertDiv.id = 'errorSummaryAlert';
                    alertDiv.className = 'error-section mt-3';
                    let logHtml = errorLogList.map(e => `<li>${e}</li>`).join('');
                    if (errorCount > 5) logHtml += `<li>...และอีก ${errorCount - 5} รายการ</li>`;
                    
                    alertDiv.innerHTML = `
                        <h5><i class="bi bi-exclamation-circle"></i> พบข้อผิดพลาด ${errorCount} รายการ</h5>
                        <ul class="mb-0 small">${logHtml}</ul>
                    `;
                    resultDiv.insertBefore(alertDiv, resultDiv.firstChild);
                }

            } catch (error) {
                showCSVError('เกิดข้อผิดพลาดในการประมวลผล: ' + error.message);
            } finally {
                if (btn) {
                    btn.disabled = false;
                    if (btn.dataset.originalText) btn.innerHTML = btn.dataset.originalText;
                }
            }
        }

        function displayCSVResults() {
            const preview = document.getElementById('csvPreview');
            if (!convertedData || convertedData.length === 0) {
                preview.innerHTML = '';
                return;
            }

            const targetCRS = document.getElementById('csvTargetCRS').value;
            const format = document.getElementById('csvFormat').value.toLowerCase();
            const outLabels = getOutputFieldNamesByTargetCrs(targetCRS);
            let label1 = `${outLabels.outX.replace('_out','').toUpperCase()} (out)`; 
            let label2 = `${outLabels.outY.replace('_out','').toUpperCase()} (out)`; 
            const isSwap = (format === 'point_y_x' || format === 'point_n_e' || format === 'point_lat_lon');
            
            if (isSwap) {
                const tmp = label1; label1 = label2; label2 = tmp;
            }

            let html = '<table class="table table-sm table-striped">';
            html += '<thead><tr>';
            html += '<th>POINT</th>';
            html += `<th>${label1}</th><th>${label2}</th>`; 
            html += `<th>Scale (k)</th><th>Convergence (&gamma;)</th>`;
            html += '</tr></thead><tbody>';

            const displayData = convertedData.slice(0, 10);
            displayData.forEach(row => {
                const errClass = row.error ? 'table-danger' : '';
                let val1 = row.outX;
                let val2 = row.outY;

                if (isSwap) {
                    const tmpVal = val1; val1 = val2; val2 = tmpVal;
                }

                html += `<tr class="${errClass}">` +
                    `<td>${escapeHtml(row.point)}</td>` +
                    `<td>${escapeHtml(val1)}</td>` +
                    `<td>${escapeHtml(val2)}</td>` +
                    `<td>${escapeHtml(row.scale)}</td>` +
                    `<td>${escapeHtml(row.conv)}</td>` +
                    `</tr>`;
            });

            html += '</tbody></table>';
            preview.innerHTML = html;
            document.getElementById('csvResults').style.display = 'block';
        }
        function downloadCSV() {
            if (convertedData.length === 0) {
                alert('ไม่มีข้อมูลให้ดาวน์โหลด');
                return;
            }
            const sourceCRS = document.getElementById('csvSourceCRS').value;
            const targetCRS = document.getElementById('csvTargetCRS').value;
            const format = document.getElementById('csvFormat').value.toLowerCase();
            const delimiter = (convertedData[0] && convertedData[0].delimiter) ? convertedData[0].delimiter : ',';
            const isSwap = (format === 'point_y_x' || format === 'point_n_e' || format === 'point_lat_lon');
            const outLabels = getOutputFieldNamesByTargetCrs(targetCRS);
            const labelMap = {
                'lon_out': 'Lon',
                'lat_out': 'Lat',
                'e_out': 'E',
                'n_out': 'N',
                'x_out': 'X',
                'y_out': 'Y'
            };

            let header1 = labelMap[outLabels.outX] || 'X';
            let header2 = labelMap[outLabels.outY] || 'Y';
            if (isSwap) {
                const tmp = header1; header1 = header2; header2 = tmp;
            }
            const headers = ['Point', header1, header2, 'Scale Factor (k)', 'Convergence'];
            let csvContent = headers.map(h => escapeCsvField(h, delimiter)).join(delimiter) + '\r\n';

            convertedData.forEach(row => {
                let val1 = row.outX;
                let val2 = row.outY;
                if (isSwap) {
                    const tmp = val1; val1 = val2; val2 = tmp;
                }
                const line = [
                    escapeCsvField(row.point, delimiter),
                    escapeCsvField(val1, delimiter),
                    escapeCsvField(val2, delimiter),
                    escapeCsvField(row.scale, delimiter), 
                    escapeCsvField(row.conv, delimiter)
                ].join(delimiter);

                csvContent += line + '\r\n';
            });
            const csvWithBom = '\ufeff' + csvContent;
            const blob = new Blob([csvWithBom], { type: 'text/csv;charset=utf-8;' });
            const link = document.createElement('a');
            
            if (link.download !== undefined) {
                const url = URL.createObjectURL(blob);
                link.setAttribute('href', url);
                
                const baseName = (csvParsed && csvParsed.fileName) ? csvParsed.fileName.replace(/\.csv$/i, '') : 'converted_coordinates';
                const filename = `${baseName}_${sourceCRS.replace(':', '')}_to_${targetCRS.replace(':', '')}_${new Date().getTime()}.csv`;
                link.setAttribute('download', filename);
                link.style.visibility = 'hidden';
                
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                URL.revokeObjectURL(url);
            }
        }

    function updateLabels() {
    const sourceCRS = document.getElementById('sourceCRS').value;
    const targetCRS = document.getElementById('targetCRS').value;
    const isLatLonSource = (sourceCRS === 'EPSG:4326' || sourceCRS === 'EPSG:4240');
    const singleLatLonFormat = document.getElementById('singleLatLonFormat').value;

    if (isLatLonSource) {
        document.getElementById('singleLatLonFormatDiv').style.display = 'block';
        
        if (singleLatLonFormat === 'DMS') {
            document.getElementById('inputX').style.display = 'none';
            document.getElementById('inputY').style.display = 'none';
            document.getElementById('inputX_dms_group').style.display = 'flex';
            document.getElementById('inputY_dms_group').style.display = 'flex';
            if(document.getElementById('labelInputX')) document.getElementById('labelInputX').innerText = 'Longitude (DMS)';
            if(document.getElementById('labelInputY')) document.getElementById('labelInputY').innerText = 'Latitude (DMS)';
        } else {
            document.getElementById('inputX').style.display = 'block';
            document.getElementById('inputY').style.display = 'block';
            document.getElementById('inputX_dms_group').style.display = 'none';
            document.getElementById('inputY_dms_group').style.display = 'none';
            if(document.getElementById('labelInputX')) document.getElementById('labelInputX').innerText = 'Longitude (DD)';
            if(document.getElementById('labelInputY')) document.getElementById('labelInputY').innerText = 'Latitude (DD)';
        }
    } else {
        document.getElementById('singleLatLonFormatDiv').style.display = 'none';
        document.getElementById('inputX').style.display = 'block';
        document.getElementById('inputY').style.display = 'block';
        document.getElementById('inputX_dms_group').style.display = 'none';
        document.getElementById('inputY_dms_group').style.display = 'none';

        const labelMap = {
            'EPSG:3857': { x: 'X (Web Mercator)', y: 'Y (Web Mercator)' },
            'default': { x: 'Easting (m)', y: 'Northing (m)' }
        };
        const txt = labelMap[sourceCRS] || labelMap['default'];
        if(document.getElementById('labelInputX')) document.getElementById('labelInputX').innerText = txt.x;
        if(document.getElementById('labelInputY')) document.getElementById('labelInputY').innerText = txt.y;
    }

    const getTargetLabel = (crs) => {
        if (crs === 'EPSG:4326' || crs === 'EPSG:4240') return { x: 'Longitude (DMS)', y: 'Latitude (DMS)' };
        if (crs === 'EPSG:3857') return { x: 'X (Web Mercator)', y: 'Y (Web Mercator)' };
        return { x: 'Easting (m)', y: 'Northing (m)' };
    };
    const tgtTxt = getTargetLabel(targetCRS);
    if(document.getElementById('labelOutputX')) document.getElementById('labelOutputX').innerText = tgtTxt.x;
    if(document.getElementById('labelOutputY')) document.getElementById('labelOutputY').innerText = tgtTxt.y;
}
        document.addEventListener('DOMContentLoaded', function() {
            updateLabels();
            document.getElementById('sourceCRS').addEventListener('change', updateLabels);
            document.getElementById('targetCRS').addEventListener('change', updateLabels);
            const singleFormatElement = document.getElementById('singleLatLonFormat');
            if (singleFormatElement) {
                singleFormatElement.addEventListener('change', updateLabels);
            }

            const csvSourceCRS = document.getElementById('csvSourceCRS');
            if (csvSourceCRS) {
                csvSourceCRS.addEventListener('change', function() {
                    const val = this.value;
                    const formatDiv = document.getElementById('csvLatLonFormatDiv');
                    if (formatDiv) {
                        if (val === 'EPSG:4326' || val === 'EPSG:4240') {
                            formatDiv.style.display = 'block';
                        } else {
                            formatDiv.style.display = 'none';
                        }
                    }
                });
                csvSourceCRS.dispatchEvent(new Event('change'));
            }
            document.getElementById('convertBtn').addEventListener('click', convertSinglePoint);
            document.getElementById('csvFile').addEventListener('change', handleFileUpload);
            document.getElementById('processCSVBtn').addEventListener('click', processCSV);
            document.getElementById('downloadBtn').addEventListener('click', downloadCSV);
            document.getElementById('inputX').addEventListener('keypress', function(e) {
                if (e.key === 'Enter') {
                    convertSinglePoint();
                }
            });

            document.getElementById('inputY').addEventListener('keypress', function(e) {
                if (e.key === 'Enter') {
                    convertSinglePoint();
                }
            });
            const dmsInputs = ['inputX_d', 'inputX_m', 'inputX_s', 'inputY_d', 'inputY_m', 'inputY_s'];
            dmsInputs.forEach(id => {
                const el = document.getElementById(id);
                if (el) {
                    el.addEventListener('keypress', function(e) {
                        if (e.key === 'Enter') {
                            convertSinglePoint();
                        }
                    });
                }
            });
        });
    </script>
</body>
</html>