preview map editor 60%

This commit is contained in:
taDuc
2026-04-13 21:47:28 +07:00
parent 3023fa947c
commit 458de8dadc
16 changed files with 1664 additions and 1149 deletions

View File

@@ -11,6 +11,7 @@ const EMPTY_PREVIEW: GeoJSON.FeatureCollection = {
features: [],
};
// Khởi tạo engine vẽ circle bằng thao tác kéo chuột từ tâm ra biên.
export function initCircle(
map: maplibregl.Map,
getMode: ModeGetter,
@@ -21,12 +22,14 @@ export function initCircle(
let isDragging = false;
let dragPanDisabledByCircle = false;
// Xóa dữ liệu preview circle trên map.
const clearPreview = () => {
(map.getSource("draw-circle-preview") as maplibregl.GeoJSONSource | undefined)?.setData(
EMPTY_PREVIEW
);
};
// Bật lại drag pan nếu trước đó bị tắt khi đang kéo vẽ circle.
const releaseDragPan = () => {
if (!dragPanDisabledByCircle) return;
dragPanDisabledByCircle = false;
@@ -35,6 +38,7 @@ export function initCircle(
}
};
// Reset toàn bộ trạng thái vẽ circle tạm thời.
const resetDrawingState = () => {
center = null;
radiusMeters = 0;
@@ -43,6 +47,7 @@ export function initCircle(
releaseDragPan();
};
// Cập nhật polygon preview theo tâm và bán kính hiện tại.
const updatePreview = () => {
if (!center || radiusMeters < MIN_RADIUS_METERS) {
clearPreview();
@@ -65,6 +70,7 @@ export function initCircle(
});
};
// Bắt đầu phiên vẽ circle khi nhấn chuột trái.
const onMouseDown = (e: maplibregl.MapMouseEvent) => {
if (getMode() !== "add-circle") return;
if ((e.originalEvent as MouseEvent | undefined)?.button !== 0) return;
@@ -82,6 +88,7 @@ export function initCircle(
}
};
// Cập nhật bán kính theo vị trí chuột trong lúc kéo.
const onMouseMove = (e: maplibregl.MapMouseEvent) => {
const canvas = map.getCanvas();
if (getMode() !== "add-circle") {
@@ -101,6 +108,7 @@ export function initCircle(
updatePreview();
};
// Hoàn tất circle và trả geometry cho callback.
const finishCircle = () => {
if (!isDragging || !center) {
resetDrawingState();
@@ -120,12 +128,14 @@ export function initCircle(
resetDrawingState();
};
// Kết thúc thao tác kéo bằng mouseup chuột trái.
const onMouseUp = (e: maplibregl.MapMouseEvent) => {
if (getMode() !== "add-circle") return;
if ((e.originalEvent as MouseEvent | undefined)?.button !== 0) return;
finishCircle();
};
// Hủy phiên vẽ circle khi nhấn Escape.
const onKeyDown = (e: KeyboardEvent) => {
if (getMode() !== "add-circle") return;
if (e.key !== "Escape") return;
@@ -150,6 +160,7 @@ export function initCircle(
};
}
// Tạo vòng polygon xấp xỉ hình tròn từ tâm, bán kính và số phân đoạn.
function buildCircleRing(
center: [number, number],
radiusMeters: number,
@@ -163,6 +174,7 @@ function buildCircleRing(
return ring;
}
// Tính khoảng cách hai điểm theo công thức Haversine (đơn vị mét).
function distanceMeters(a: [number, number], b: [number, number]): number {
const lat1 = toRad(a[1]);
const lat2 = toRad(b[1]);
@@ -178,6 +190,7 @@ function distanceMeters(a: [number, number], b: [number, number]): number {
return EARTH_RADIUS_METERS * c; // Khoảng cách cung tròn: d = R * c.
}
// Tính tọa độ điểm đích từ tâm, khoảng cách và góc phương vị.
function destinationPoint(
center: [number, number],
distance: number,
@@ -205,22 +218,26 @@ function destinationPoint(
return [normalizeLng(toDeg(lng2)), toDeg(lat2)];
}
// Chuẩn hóa kinh độ về miền [-180, 180].
function normalizeLng(lng: number): number {
let normalized = ((lng + 540) % 360) - 180; // Wrap về khoảng [-180, 180).
if (normalized === -180) normalized = 180;
return normalized;
}
// Kẹp giá trị trong đoạn [min, max].
function clamp(value: number, min: number, max: number): number {
if (value < min) return min;
if (value > max) return max;
return value;
}
// Đổi đơn vị góc từ độ sang radian.
function toRad(value: number): number {
return (value * Math.PI) / 180; // Đổi độ sang radian.
}
// Đổi đơn vị góc từ radian sang độ.
function toDeg(value: number): number {
return (value * 180) / Math.PI; // Đổi radian sang độ.
}