Tích Hợp Tính Năng Định Vị Và Hiển Thị Tọa Độ Trên CesiumJS

Việc tích hợp chức năng tìm kiếm tọa độ địa lý trong ứng dụng bản đồ 3D là yêu cầu phổ biến khi làm việc với CesiumJS. Quy trình thực hiện bao gồm việc tiếp nhận dữ liệu đầu vào từ người dùng, xác thực định dạng (bao gồm cả số thập phân và độ-phút-giây), chuyển đổi sang hệ tọa độ phù hợp, tạo đối tượng đánh dấu trên bề mặt trái đất và điều khiển góc nhìn máy bay đến vị trí đích.

1. Cơ chế hiển thị điểm và di chuyển Camera

Khi nhận được cặp giá trị kinh độ và vĩ độ đã chuẩn hóa, chúng ta cần xóa các thực thể cũ trên bản đồ để tránh trùng lặp, sau đó thêm một biểu tượng mới kèm theo nhãn văn bản. Đồng thời, kích hoạt hàm flyTo để đưa mắt xem đến mục tiêu một cách mượt mà.

renderMarker(longitude, latitude) {
    // Xóa toàn bộ thực thể hiện có để dọn dẹp
    this.viewer.entities.removeAll();

    // Tạo cấu hình cho điểm đánh dấu
    const position = Cesium.Cartesian3.fromDegrees(longitude, latitude);
    
    const entityConfig = {
        position: position,
        billboard: {
            image: markerIconUrl, // Đường dẫn hoặc tham chiếu đến ảnh marker
            scale: 1.0
        },
        label: {
            text: 'Vị trí mục tiêu',
            font: '14px sans-serif',
            fillColor: Cesium.Color.WHITE,
            backgroundColor: Cesium.Color.BLUE,
            showBackground: true,
            style: Cesium.LabelStyle.FILL,
            outlineWidth: 1,
            verticalOrigin: Cesium.VerticalOrigin.CENTER,
            horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
            pixelOffset: new Cesium.Cartesian2(15, 0)
        }
    };

    // Thêm thực thể vào scene
    this.viewer.entities.add(entityConfig);

    // Điều khiển camera bay tới vị trí với độ cao nhất định
    this.viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, 300000),
        duration: 1.5
    });
}

2. Xử lý nhập liệu và chuẩn hóa tọa độ

Hệ thống cần hỗ trợ hai kiểu nhập liệu phổ biến: số thập phân (ví dụ: 120.5, 30.5) và định dạng độ/phút/giây (ví dụ: 120°11'11",30°11'11"). Hàm xử lý chính sẽ phân tách chuỗi, kiểm tra tính hợp lệ của ký tự ngăn cách và đảm bảo giá trị nằm trong phạm vi địa lý quy chuẩn trước khi chuyển đổi.

handleCoordinateSearch(rawInput) {
    if (!rawInput || typeof rawInput !== 'string') return;
    
    const parts = rawInput.split(',').map(item => item.trim());
    
    // Kiểm tra số lượng thành phần phải đúng 2
    if (parts.length !== 2 || parts.includes('')) {
        this.notifyUser('Dữ liệu không đủ 2 thành phần', 'warning');
        return;
    }

    let finalLon, finalLat;
    const [lonStr, latStr] = parts;

    try {
        // Thử phân tích dưới dạng số thập phân trước
        const lonDec = Number(lonStr);
        const latDec = Number(latStr);
        
        if (!isNaN(lonDec) && !isNaN(latDec)) {
            // Xác thực khoảng số thập phân
            if (lonDec < -180 || lonDec > 180 || latDec < -90 || latDec > 90) {
                throw new Error('Giá trị ngoài phạm vi');
            }
            finalLon = lonDec;
            finalLat = latDec;
        } else {
            // Nếu không phải số thập phân, chuyển sang xử lý DMS
            const parsedLon = this.parseDMSCoordinate(lonStr, -180, 180, true);
            const parsedLat = this.parseDMSCoordinate(latStr, -90, 90, false);
            
            if (parsedLon === null || parsedLat === null) {
                throw new Error('Định dạng DMS không hợp lệ');
            }
            finalLon = parsedLon;
            finalLat = parsedLat;
        }

        // Gọi hàm hiển thị nếu tất cả kiểm duyệt qua
        this.renderMarker(finalLon, finalLat);

    } catch (err) {
        this.notifyUser(err.message || 'Lỗi phân tích tọa độ', 'error');
    }
}

parseDMSCoordinate(str, minLimit, maxLimit, isLongitude) {
    // Regex để khớp mẫu: Số độ ° Số phút ' Số giây "
    const dmsPattern = /(-?\d+)\s*°\s*(\d+)\s*'\s*(\d+)\s*"/;
    const match = str.match(dmsPattern);

    if (!match) return null;

    const degrees = parseFloat(match[1]);
    const minutes = parseFloat(match[2]);
    const seconds = parseFloat(match[3]);

    // Kiểm tra điều kiện đặc biệt cho đường kinh tuyến 180
    if ((degrees === 180 || degrees === -180) && (minutes !== 0 || seconds !== 0)) {
        return null;
    }

    // Kiểm tra giới hạn phút và giây
    if (minutes < 0 || minutes > 60 || seconds < 0 || seconds > 60) {
        return null;
    }

    // Chuyển đổi về hệ thập phân
    let decimalValue = Math.abs(degrees) + (minutes / 60) + (seconds / 3600);
    if (degrees < 0) decimalValue *= -1;

    // Kiểm tra phạm vi tổng quát
    if (isLongitude && (decimalValue > 180 || decimalValue < -180)) return null;
    if (!isLongitude && (decimalValue > 90 || decimalValue < -90)) return null;

    return decimalValue;
}

Thẻ: CesiumJS JavaScript WebGIS Geolocation Coordinate Transformation

Đăng vào ngày 17 tháng 05 lúc 17:05