@@ -1442,8 +1461,8 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
);
})}
- {/* 마커 렌더링 */}
- {markers.map((marker) => {
+ {/* 마커 렌더링 (지역 필터 적용) */}
+ {filterVehiclesByRegion(markers, selectedRegion).map((marker) => {
// 마커의 소스에 해당하는 데이터 소스 찾기
const sourceDataSource = dataSources?.find((ds) => ds.name === marker.source) || dataSources?.[0];
const markerType = sourceDataSource?.markerType || "circle";
@@ -1771,7 +1790,12 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
{/* 하단 정보 */}
{(markers.length > 0 || polygons.length > 0) && (
- {markers.length > 0 && `마커 ${markers.length}개`}
+ {markers.length > 0 && (
+ <>
+ 마커 {filterVehiclesByRegion(markers, selectedRegion).length}개
+ {selectedRegion !== "all" && ` (전체 ${markers.length}개)`}
+ >
+ )}
{markers.length > 0 && polygons.length > 0 && " · "}
{polygons.length > 0 && `영역 ${polygons.length}개`}
diff --git a/frontend/lib/constants/regionBounds.ts b/frontend/lib/constants/regionBounds.ts
new file mode 100644
index 00000000..2b0f15ba
--- /dev/null
+++ b/frontend/lib/constants/regionBounds.ts
@@ -0,0 +1,238 @@
+/**
+ * 전국 시/도별 좌표 범위 (경계 좌표)
+ * 차량 위치 필터링에 사용
+ */
+
+export interface RegionBounds {
+ south: number; // 최남단 위도
+ north: number; // 최북단 위도
+ west: number; // 최서단 경도
+ east: number; // 최동단 경도
+}
+
+export interface RegionOption {
+ value: string;
+ label: string;
+ bounds?: RegionBounds;
+}
+
+// 전국 시/도별 좌표 범위
+export const regionBounds: Record
= {
+ // 서울특별시
+ seoul: {
+ south: 37.413,
+ north: 37.715,
+ west: 126.734,
+ east: 127.183,
+ },
+ // 부산광역시
+ busan: {
+ south: 34.879,
+ north: 35.389,
+ west: 128.758,
+ east: 129.314,
+ },
+ // 대구광역시
+ daegu: {
+ south: 35.601,
+ north: 36.059,
+ west: 128.349,
+ east: 128.761,
+ },
+ // 인천광역시
+ incheon: {
+ south: 37.166,
+ north: 37.592,
+ west: 126.349,
+ east: 126.775,
+ },
+ // 광주광역시
+ gwangju: {
+ south: 35.053,
+ north: 35.267,
+ west: 126.652,
+ east: 127.013,
+ },
+ // 대전광역시
+ daejeon: {
+ south: 36.197,
+ north: 36.488,
+ west: 127.246,
+ east: 127.538,
+ },
+ // 울산광역시
+ ulsan: {
+ south: 35.360,
+ north: 35.710,
+ west: 128.958,
+ east: 129.464,
+ },
+ // 세종특별자치시
+ sejong: {
+ south: 36.432,
+ north: 36.687,
+ west: 127.044,
+ east: 127.364,
+ },
+ // 경기도
+ gyeonggi: {
+ south: 36.893,
+ north: 38.284,
+ west: 126.387,
+ east: 127.839,
+ },
+ // 강원도 (강원특별자치도)
+ gangwon: {
+ south: 37.017,
+ north: 38.613,
+ west: 127.085,
+ east: 129.359,
+ },
+ // 충청북도
+ chungbuk: {
+ south: 36.012,
+ north: 37.261,
+ west: 127.282,
+ east: 128.657,
+ },
+ // 충청남도
+ chungnam: {
+ south: 35.972,
+ north: 37.029,
+ west: 125.927,
+ east: 127.380,
+ },
+ // 전라북도 (전북특별자치도)
+ jeonbuk: {
+ south: 35.287,
+ north: 36.133,
+ west: 126.392,
+ east: 127.923,
+ },
+ // 전라남도
+ jeonnam: {
+ south: 33.959,
+ north: 35.507,
+ west: 125.979,
+ east: 127.921,
+ },
+ // 경상북도
+ gyeongbuk: {
+ south: 35.571,
+ north: 37.144,
+ west: 128.113,
+ east: 130.922,
+ },
+ // 경상남도
+ gyeongnam: {
+ south: 34.599,
+ north: 35.906,
+ west: 127.555,
+ east: 129.224,
+ },
+ // 제주특별자치도
+ jeju: {
+ south: 33.106,
+ north: 33.959,
+ west: 126.117,
+ east: 126.978,
+ },
+};
+
+// 지역 선택 옵션 (드롭다운용)
+export const regionOptions: RegionOption[] = [
+ { value: "all", label: "전체" },
+ { value: "seoul", label: "서울특별시", bounds: regionBounds.seoul },
+ { value: "busan", label: "부산광역시", bounds: regionBounds.busan },
+ { value: "daegu", label: "대구광역시", bounds: regionBounds.daegu },
+ { value: "incheon", label: "인천광역시", bounds: regionBounds.incheon },
+ { value: "gwangju", label: "광주광역시", bounds: regionBounds.gwangju },
+ { value: "daejeon", label: "대전광역시", bounds: regionBounds.daejeon },
+ { value: "ulsan", label: "울산광역시", bounds: regionBounds.ulsan },
+ { value: "sejong", label: "세종특별자치시", bounds: regionBounds.sejong },
+ { value: "gyeonggi", label: "경기도", bounds: regionBounds.gyeonggi },
+ { value: "gangwon", label: "강원특별자치도", bounds: regionBounds.gangwon },
+ { value: "chungbuk", label: "충청북도", bounds: regionBounds.chungbuk },
+ { value: "chungnam", label: "충청남도", bounds: regionBounds.chungnam },
+ { value: "jeonbuk", label: "전북특별자치도", bounds: regionBounds.jeonbuk },
+ { value: "jeonnam", label: "전라남도", bounds: regionBounds.jeonnam },
+ { value: "gyeongbuk", label: "경상북도", bounds: regionBounds.gyeongbuk },
+ { value: "gyeongnam", label: "경상남도", bounds: regionBounds.gyeongnam },
+ { value: "jeju", label: "제주특별자치도", bounds: regionBounds.jeju },
+];
+
+/**
+ * 좌표가 특정 지역 범위 내에 있는지 확인
+ */
+export function isInRegion(
+ latitude: number,
+ longitude: number,
+ region: string
+): boolean {
+ if (region === "all") return true;
+
+ const bounds = regionBounds[region];
+ if (!bounds) return false;
+
+ return (
+ latitude >= bounds.south &&
+ latitude <= bounds.north &&
+ longitude >= bounds.west &&
+ longitude <= bounds.east
+ );
+}
+
+/**
+ * 좌표로 지역 찾기 (해당하는 첫 번째 지역 반환)
+ */
+export function findRegionByCoords(
+ latitude: number,
+ longitude: number
+): string | null {
+ for (const [region, bounds] of Object.entries(regionBounds)) {
+ if (
+ latitude >= bounds.south &&
+ latitude <= bounds.north &&
+ longitude >= bounds.west &&
+ longitude <= bounds.east
+ ) {
+ return region;
+ }
+ }
+ return null;
+}
+
+/**
+ * 차량 목록을 지역별로 필터링
+ */
+export function filterVehiclesByRegion<
+ T extends { latitude?: number; longitude?: number; lat?: number; lng?: number }
+>(vehicles: T[], region: string): T[] {
+ if (region === "all") return vehicles;
+
+ const bounds = regionBounds[region];
+ if (!bounds) return vehicles;
+
+ return vehicles.filter((v) => {
+ const lat = v.latitude ?? v.lat;
+ const lng = v.longitude ?? v.lng;
+
+ if (lat === undefined || lng === undefined) return false;
+
+ return (
+ lat >= bounds.south &&
+ lat <= bounds.north &&
+ lng >= bounds.west &&
+ lng <= bounds.east
+ );
+ });
+}
+
+/**
+ * 지역명(한글) 가져오기
+ */
+export function getRegionLabel(regionValue: string): string {
+ const option = regionOptions.find((opt) => opt.value === regionValue);
+ return option?.label ?? regionValue;
+}
+