From da8b4d10794998ebf0d1a3c6deeaecaf6a4d6efb Mon Sep 17 00:00:00 2001 From: JANGHYUNn Date: Thu, 31 Aug 2023 18:22:07 +0900 Subject: [PATCH] =?UTF-8?q?mapbox=20=EC=96=B8=EC=96=B4=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD,=20=EA=B4=80=EC=A0=9C=20=EA=B8=B0=EC=B2=B4=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=ED=8C=9D=EC=97=85=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=A4=91,=20=EA=B4=80=EC=A0=9C=20=EA=B8=B0=EC=B2=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=9E=91=EC=97=85,=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 10 + package.json | 2 + src/components/map/MapControl.js | 4 +- src/components/map/mapbox/MapBoxMap.js | 114 ++- src/components/map/mapbox/dron/DronMarker.js | 657 ++++++++++++++++++ .../map/mapbox/feature/FeatureAirZone.js | 207 +++--- test/pav-warning.js | 12 +- 7 files changed, 857 insertions(+), 149 deletions(-) create mode 100644 src/components/map/mapbox/dron/DronMarker.js diff --git a/package-lock.json b/package-lock.json index d0fff620..894a75c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1973,6 +1973,11 @@ "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==" }, + "@mapbox/mapbox-gl-language": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-language/-/mapbox-gl-language-1.0.1.tgz", + "integrity": "sha512-gL58ojl7gaWLfbSISoB2QJEfTK3j+NKvPH9og0r+c3bd5BNqhY19Eb4OPfDc5+dGmjW03LhtZBc4n2b7Xn8kjA==" + }, "@mapbox/mapbox-gl-supported": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", @@ -24895,6 +24900,11 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, + "threebox-plugin": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/threebox-plugin/-/threebox-plugin-2.2.7.tgz", + "integrity": "sha512-H87Nm4w1PfisHPHzavTGXlwIoJpx2+QU57GooQYIhF51lsg+U5A0KGf3Jrv/HWsLCGOwV2BTnv7UTLfpO1EccQ==" + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", diff --git a/package.json b/package.json index 95f2a30e..f15f57f9 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@fullcalendar/timegrid": "5.7.2", "@fullcalendar/timeline": "5.7.2", "@hookform/resolvers": "1.3.4", + "@mapbox/mapbox-gl-language": "1.0.1", "@stomp/stompjs": "^6.1.0", "@turf/buffer": "^6.5.0", "@turf/turf": "6.5.0", @@ -110,6 +111,7 @@ "sweetalert2": "10.14.0", "sweetalert2-react-content": "3.0.1", "swiper": "6.0.4", + "threebox-plugin": "2.2.7", "typesafe-actions": "^5.1.0", "uppy": "1.21.2", "webpack": "4.43.0", diff --git a/src/components/map/MapControl.js b/src/components/map/MapControl.js index 993c7138..e550c41e 100644 --- a/src/components/map/MapControl.js +++ b/src/components/map/MapControl.js @@ -5,8 +5,8 @@ import MapBoxMap from './mapbox/MapBoxMap'; export const MapControl = props => { return ( - // <>{props.mapType === 'google' ? : } + <>{props.mapType === 'google' ? : } // - + // ); }; diff --git a/src/components/map/mapbox/MapBoxMap.js b/src/components/map/mapbox/MapBoxMap.js index 1100437f..07e81586 100644 --- a/src/components/map/mapbox/MapBoxMap.js +++ b/src/components/map/mapbox/MapBoxMap.js @@ -1,7 +1,7 @@ import { MAPBOX_TOKEN } from '../../../configs/constants'; import { useEffect, useState, useRef } from 'react'; import { useSelector, useDispatch } from 'react-redux'; -// import { DronMarker } from './dron/DronMarker'; +import { DronMarker } from './dron/DronMarker'; // import { DronHistory } from './dron/DronHistory'; // import NaverMapControl from './NaverMapControl'; // import { NaverMapSearch } from './search/NaverMapSearch'; @@ -13,7 +13,8 @@ import { FeatureAirZone } from './feature/FeatureAirZone'; // import SensorZone from './sensor/SensorZone'; import 'mapbox-gl/dist/mapbox-gl.css'; import mapboxgl from 'mapbox-gl'; - +import threebox from 'threebox-plugin'; +import MapboxLanguage from '@mapbox/mapbox-gl-language'; // mapboxgl.accessToken = MAPBOX_TOKEN; export default function MapBoxMap() { // const mapContainer = useRef(null); @@ -234,7 +235,7 @@ export default function MapBoxMap() { }; useEffect(() => { - NaverMapInit(); + mapBoxMapInit(); }, []); useEffect(() => { @@ -261,7 +262,7 @@ export default function MapBoxMap() { } }, [mapObject]); let popup; - const NaverMapInit = () => { + const mapBoxMapInit = () => { mapboxgl.accessToken = MAPBOX_TOKEN; // const mapOptions = { // center: new naver.maps.LatLng(37.520357, 126.610166), @@ -280,12 +281,30 @@ export default function MapBoxMap() { container: 'map', // container ID style: 'mapbox://styles/mapbox/streets-v12', // style URL center: [127.85101412107547, 37.520357], // starting position [lng, lat] - zoom: 9 // starting zoom + zoom: 8.5, // starting zoom + antialias: true + // pitch: 64.9, + // bearing: 172.5 }); + const language = new MapboxLanguage(); + map.addControl(language); + + // const tb = (window.tb = new threebox.Threebox( + // map, + // map.getCanvas().getContext('webgl'), + // { + // defaultLights: true, + // enableSelectingObjects: true, + // enableDraggingObjects: true, + // enableRotatingObjects: true, + // enableTooltips: true + // } + // )); + map.on('load', () => { const layers = map.getStyle().layers; - map.setLayoutProperty('country-label', 'text-field', ['get', `name_kr`]); + const labelLayerId = layers.find( layer => layer.type === 'symbol' && layer.layout['text-field'] ).id; @@ -390,31 +409,62 @@ export default function MapBoxMap() { setMapObject(map); setMapLoaded(true); }); - map.on('mouseover', 'maine', e => { - const feature = e.features[0]; - const properties = feature.properties; - - // Remove the existing popup if it exists - if (popup) { - popup.remove(); - } - - // Create a new popup with the feature's properties - popup = new mapboxgl.Popup() - .setLngLat(e.lngLat) - .setHTML( - Object.entries(properties) - .map(([key, value]) => `${key}: ${value}`) - .join('
') - ) - .addTo(map); - }); + // map.on('style.load', () => { + // map.addLayer({ + // id: 'custom-threebox-model', + // type: 'custom', + // renderingMode: '3d', + // onAdd: function () { + // // Creative Commons License attribution: Metlife Building model by https://sketchfab.com/NanoRay + // // https://sketchfab.com/3d-models/metlife-building-32d3a4a1810a4d64abb9547bb661f7f3 + // const scale = -20; + // const options = { + // obj: './src/components/map/mapbox/diorama/scene.gltf', + // // obj: 'https://docs.mapbox.com/mapbox-gl-js/assets/metlife-building.gltf', + // type: 'gltf', + // units: 'meters', + // scale: 1, + // anchor: 'center', + // rotation: { x: 90, y: 0, z: 0 } + // }; + + // tb.loadObj(options, model => { + // model.setCoords([127.85101412107547, 37.520357, 1000]); + // model.setRotation({ x: 0, y: 0, z: 241 }); + // tb.add(model); + // }); + // }, + + // render: function () { + // tb.update(); + // } + // }); + // }); + // map.on('mouseover', 'maine', e => { + // const feature = e.features[0]; + // const properties = feature.properties; + + // // Remove the existing popup if it exists + // if (popup) { + // popup.remove(); + // } - map.on('mouseout', 'maine', () => { - if (popup) { - popup.remove(); - } - }); + // // Create a new popup with the feature's properties + // popup = new mapboxgl.Popup() + // .setLngLat(e.lngLat) + // .setHTML( + // Object.entries(properties) + // .map(([key, value]) => `${key}: ${value}`) + // .join('
') + // ) + // .addTo(map); + // }); + + // map.on('mouseout', 'maine', () => { + // if (popup) { + // popup.remove(); + // } + // }); // airPort?.map(air => polyArea(air, map)); }; @@ -588,7 +638,7 @@ export default function MapBoxMap() { > {isMapLoaded && mapObject ? ( <> - {/* */} + {/* @@ -610,7 +660,7 @@ export default function MapBoxMap() { {/* */} - + {/* */} {/* */} diff --git a/src/components/map/mapbox/dron/DronMarker.js b/src/components/map/mapbox/dron/DronMarker.js new file mode 100644 index 00000000..bbcd23dc --- /dev/null +++ b/src/components/map/mapbox/dron/DronMarker.js @@ -0,0 +1,657 @@ +import $ from 'jquery'; +import { useEffect, useState } from 'react'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; +import '../../../../assets/css/custom.css'; +import FlightIcon from '../../../../assets/images/airplan_org.svg'; +import FlightDetailIcon from '../../../../assets/images/airplan_pp.svg'; +import DronIcon from '../../../../assets/images/drone-marker-icon.png'; +import DronDetailIcon from '../../../../assets/images/drone-marker-icon-pulple.png'; +import DronUamIcon from '../../../../assets/images/uam_icon.png'; +import DronUamDetailIcon from '../../../../assets/images/uam_icon_purple.png'; +import { + controlGpDtlAction, + controlGpFlightPlanAction, + controlGpCountAction +} from '../../../../modules/control/gp'; +import { + objectClickAction, + objectUnClickAction +} from '../../../../modules/control/map/actions/controlMapActions'; +import { JOIN_LIST } from '../../../../modules/basis/group/actions/basisGroupAction'; + +export const DronMarker = props => { + const dispatch = useDispatch(); + + const { controlGpList, controlGroupAuthInfo } = useSelector( + state => state.controlGpState + ); + const { objectId, isClickObject } = useSelector( + state => state.controlMapReducer, + shallowEqual + ); + // const { controlGroupAuthInfo } = useSelector( + // state => state.controlGroupAuthState + // ); + const { controlGpArcrftWarnList } = useSelector( + state => state.controlGpLogState + ); + + const { user } = useSelector(state => state.authState); + const { joinList } = useSelector(state => state.groupState); + + const [arrMarkers, setArrMarkers] = useState([]); + const [arrInfos, setArrInfos] = useState([]); + + const [count, setCount] = useState({ + drone: [], + flight: [] + }); + + let mapboxgl = props.mapboxgl; + let map = props.map; + let CustomOverlay; + let infoWindow; + // const infowindowOpen = data => { + // const content = ` + //
+ //
+ // ${data?.id} + //
+ //
+ //
+ // ${ + // data?.speed + // }${data?.speedType} | ${data?.elev}${data?.elevType} | ${ + // data?.heading + // } + // ${(data?.coord._lat).toFixed( + // 6 + // )} | ${(data?.coord._lng).toFixed(6)} + //
+ //
+ //
+ // + // `; + + // infoWindow = new naver.maps.InfoWindow({ + // class: 'tooltip-dblock', + // content: content, + // maxWidth: 160, + // backgroundColor: '#283046', //박스안쪽영역 컬러 + // // borderColor: '#333', //테두리컬러 + // // borderWidth: 3, //테두리 굵기 + // // anchorSize: new naver.maps.Size(30, -10), + // anchorSkew: false, + // anchorColor: '#283046', + + // pixelOffset: new naver.maps.Point(20, -20) + // }); + + // infoWindow.open(props.map, data.coord); + // }; + + // useLayoutEffect(() => { + // dispatch(controlGroupAuthAction.request()); + // }, [controlGpList]); + + useEffect(() => { + if (count.drone.length > 0 || count.flight.length > 0) { + dispatch( + controlGpCountAction.request({ + count + }) + ); + } else { + const count = { + drone: [], + flight: [] + }; + dispatch( + controlGpCountAction.request({ + count + }) + ); + } + }, [count]); + + useEffect(() => { + dispatch( + JOIN_LIST.request({ + cstmrSno: user?.cstmrSno + }) + ); + }, []); + + useEffect(() => { + if (arrMarkers.length != 0) { + markerInfo(arrMarkers); + } + }, [arrMarkers]); + + useEffect(() => { + markerInit(); + }, [controlGpList]); + + // useEffect(() => { + // let imageUrl; + // arrMarkers.map(clickMarker => { + // if (objectId === clickMarker.controlId && isClickObject) { + // // console.log(clickMarker.gps.objectId, '><><'); + // imageUrl = + // // clickMarker.type === 'DRONE' ? DronUamDetailIcon : FlightDetailIcon; + // clickMarker.type === 'DRONE' + // ? clickMarker.gps.objectId.includes('UAM') + // ? DronUamDetailIcon + // : DronDetailIcon + // : FlightDetailIcon; + // clickMarker.setIcon({ + // content: ``, + // origin: new naver.maps.Point(0, 0), + // anchor: new naver.maps.Point(15, 15) + // }); + // } else { + // // imageUrl = clickMarker.type === 'DRONE' ? DronUamIcon : FlightIcon; + // imageUrl = + // clickMarker.type === 'DRONE' + // ? clickMarker.gps.objectId.includes('UAM') + // ? DronUamIcon + // : DronIcon + // : FlightIcon; + // clickMarker.setIcon({ + // content: ``, + // origin: new naver.maps.Point(0, 0), + // anchor: new naver.maps.Point(15, 15) + // }); + // } + // }); + // }, [objectId, isClickObject]); + + // useEffect(() => { + // arrMarkers.map(clickMarker => { + // if (objectId === clickMarker.controlId) { + // dispatch(controlGpDtlAction.request(objectId)); + // props.map.setCenter(clickMarker.getPosition()); + // props.map.setZoom(13, true); + // } + // }); + // }, [objectId]); + + //마커를 그린다. + const addMarkers = (position, id, controlId, gps) => { + const gpsCnt = { + gps: gps, + type: '' + }; + + const markerOption = {}; + + if (id.substring(0, 2) === 'PA') { + const pal = controlGroupAuthInfo?.find( + prev => prev.idntfNum === gps.objectId + ); + if (pal || !pal || id.includes('NAMWON')) { + if (id.includes('UAM')) { + markerOption.url = DronUamIcon; + } else { + markerOption.url = DronIcon; + } + + markerOption.type = 'DRONE'; + gpsCnt.type = 'drone'; + } else { + if (user?.authId === 'SUPER' || user?.authId === 'ADMIN') { + markerOption.url = FlightIcon; + markerOption.type = 'FLIGHT'; + gpsCnt.type = 'flight'; + } else { + const terminal = joinList?.find(prev => prev.trmnlId === gps.trmnlId); + if ( + terminal?.groupAuthCd === 'MASTER' || + terminal?.groupAuthCd === 'LEADER' + ) { + markerOption.url = FlightIcon; + markerOption.type = 'FLIGHT'; + gpsCnt.type = 'flight'; + } + } + } + } else { + if (user?.authId === 'SUPER' || user?.authId === 'ADMIN') { + markerOption.url = FlightIcon; + markerOption.type = 'FLIGHT'; + gpsCnt.type = 'flight'; + } else { + const terminal = joinList?.find(prev => prev.trmnlId === gps.trmnlId); + if ( + terminal?.groupAuthCd === 'MASTER' || + terminal?.groupAuthCd === 'LEADER' + ) { + markerOption.url = FlightIcon; + markerOption.type = 'FLIGHT'; + gpsCnt.type = 'flight'; + } + } + } + + if (gpsCnt.type === 'drone') { + setCount(prev => ({ + ...prev, + drone: [...prev.drone, gpsCnt] + })); + } else if (gpsCnt.type === 'flight') { + setCount(prev => ({ + ...prev, + flight: [...prev.flight, gpsCnt] + })); + } + + // if (id.substring(0, 2) === 'PA') { + // const pal = controlGroupAuthInfo?.find( + // prev => prev.idntfNum === gps.objectId + // ); + // markerOption.url = pal ? DronIcon : FlightIcon; + // markerOption.type = pal ? 'DRONE' : 'FLIGHT'; + // } else { + // markerOption.url = FlightIcon; + // markerOption.type = 'FLIGHT'; + // } + + // markerOption.origin = new naver.maps.Point(0, 0); + // markerOption.anchor = new naver.maps.Point(15, 15); + + // const marker = new naver.maps.Marker({ + // position: position, + // title: id, + // id: id, + // controlId: controlId, + // type: markerOption.type, + // icon: { + // content: ``, + // origin: markerOption.origin, + // anchor: markerOption.anchor + // }, + // gps: gps + // }); + // marker.setMap(props.map); + const el = document.createElement('img'); + el.id = id; + el.src = markerOption.url; + el.style.transform = `rotate(${gps.heading}deg)`; + + const marker = new props.mapboxgl.Marker({ + element: el, + rotation: gps.heading + }) + .setLngLat(position) + .addTo(props.map); + marker.id = id; + marker.title = id; + marker.controlId = controlId; + marker.type = markerOption.type; + marker.gps = gps; + marker.position = position; + marker.icon = { + content: ``, + origin: markerOption.origin, + anchor: markerOption.anchor + }; + + dispatch(controlGpFlightPlanAction.request(marker.id)); //예상경로 + // dispatch(controlGpHisAction.request({ id: marker.controlId })); //진행경로; + + /** drone 상세보기 */ + marker.getElement().addEventListener('click', function (e) { + handlerDronClick(marker.controlId, marker.id); + }); + + if (markerOption.url) { + setArrMarkers(m => [...m, marker]); + } + }; + + const handlerDronClick = (controlId, idntfNum) => { + // get detail, history, flight-plan + dispatch(objectClickAction(controlId)); + // dispatch(controlGpDtlAction.request(controlId)); + // dispatch(controlGpFlightPlanAction.request(idntfNum)); + }; + + //마커를 삭제 한다. + const removeMarkers = marker => { + // marker.setMap(null); + marker.remove(); + }; + + const removeInfos = info => { + // info.setMap(null); + info.remove(); + }; + + //마커에 위치를 이동한다. + const moveMarkers = (marker, position, gps) => { + const getIcon = marker.icon; + + marker.setLngLat(position); + const warnList = controlGpArcrftWarnList?.filter( + i => i.cntrlId === gps.controlId + ); + + if (warnList?.length > 0) { + if (warnList[0].controlWarnCd) { + // marker.setIcon({ + // content: ``, + // anchor: getIcon.anchor + // }); + } else { + // console.log(marker); + // marker.setIcon({ + // content: ``, + // anchor: getIcon.anchor + // }); + marker.setRotation(gps.heading); + } + return; + } + marker.setRotation(gps.heading); + // marker.setIcon({ + // content: ``, + // anchor: getIcon.anchor + // }); + }; + + const moveInfos = (info, position, item, idx) => { + if (info) { + info.setLngLat([position.lng, position.lat]); + // info.setPosition(position, info); + // // info._element.html(`
${info?._id} + // // ${item?.speed}${item?.speedType} | ${item?.elev}${item?.elevType} | ${item?.heading}
`); + // info._element.html(` + //
+ // ${info?._id} + //
+ //
+ //
+ // ${item?.elev}M + // ${item?.speed}km + // ${ + // typeof item?.lat === 'number' && typeof item?.lng === 'number' + // ? ` + // + // ${(item?.lat).toFixed(6)} ${(item?.lng).toFixed(6)} + // ` + // : '' + // } + //
+ //
+ // `); + } + }; + + //데이터가 없는 마커를 모두 삭제 한다. + const allRemoveMarkers = () => { + let isUnClick = false; + if (arrMarkers && controlGpList) { + arrMarkers.map(marker => { + const isExists = controlGpList.find( + item => item.objectId === marker.id + ); + + if (!isExists) { + removeMarkers(marker); + const arrData = arrMarkers.filter(item => item.id != marker.id); + + const drone = count.drone.filter(d => d.gps.objectId != marker.id); + const flight = count.flight.filter(d => d.gps.objectId != marker.id); + + setCount({ + drone: drone, + flight: flight + }); + + removeArrMarkers(arrData); + if (marker.controlId === objectId) { + dispatch(objectUnClickAction()); + } + } + }); + + arrInfos.map(info => { + const isExists = controlGpList.find(item => item.objectId === info._id); + if (!isExists) { + removeInfos(info); + const arrData = arrInfos.filter(item => item.id != info._id); + removeArrInfos(arrData); + } + }); + } + return isUnClick; + }; + + //마커를 셋팅 한다. + const markerInit = () => { + if (controlGpList) { + allRemoveMarkers(); + controlGpList.map((item, idx) => { + // let position = new naver.maps.LatLng(item.lat, item.lng); + let position = new props.mapboxgl.LngLat(item.lng, item.lat); + + if (arrMarkers.length > 0) { + const isExists = arrMarkers.find(ele => ele.id === item.objectId); + const isInfos = arrInfos.find(info => info._id === item.objectId); + if (isExists) { + moveMarkers(isExists, position, item); + moveInfos(isInfos, position, item, idx); + } else { + addMarkers(position, item.objectId, item.controlId, item); + } + } else { + addMarkers(position, item.objectId, item.controlId, item); + } + }); + } + }; + + //운항정보 창 셋팅 + const infoInit = (marker, gps, idx) => { + CustomOverlay = function (options) { + this._element = $(` +
+
+ ${marker?.id} +
+
+
+ ${gps?.elev}M + ${gps?.speed}km + ${ + typeof gps?.lat === 'number' && typeof gps?.lng === 'number' + ? ` + + ${(gps?.lat).toFixed(6)} ${(gps?.lng).toFixed(6)} + ` + : '' + } +
+
+
+ `); + + // this._element = $(` + //
+ //
+ // ${marker?.id} + //
+ //
+ //
+ // 속도: ${gps?.speed}${gps?.speedType} + //
+ //
+ // 고도: ${gps?.elev}${gps?.elevType} + //
+ //
+ // 헤딩방향: ${gps?.heading} + //
+ //
+ //
+ // + // `) + + this.setPosition(options.position, idx); + this.setMap(options.map || null); + this.setId(options.id); + this.setIdx(idx); + this.setControlId(options.controlId); + }; + + CustomOverlay.prototype = new naver.maps.OverlayView(); + CustomOverlay.prototype.constructor = CustomOverlay; + + //메소드 재정의 + //필수 + CustomOverlay.prototype.onAdd = function () { + let overlayLayer = this.getPanes().overlayLayer; + + this._element.appendTo(overlayLayer); + }; + + CustomOverlay.prototype.draw = function (idx) { + if (!this.getMap()) { + return; + } + + let projection = this.getProjection(), + position = this.getPosition(), + pixelPosition = projection.fromCoordToOffset(position); + + // let cnt = 0; + // let index = idx?._idx; + // cnt = index * 98; + this._element.css('left', pixelPosition.x); + this._element.css('top', pixelPosition.y); + // this._element.css('top', pixelPosition.y + -cnt) + }; + + CustomOverlay.prototype.onRemove = function () { + let overlayLayer = this.getPanes().overlayLayer; + + this._element.remove(); + this._element.off(); + }; + + //속성 + CustomOverlay.prototype.setPosition = function (position, idx) { + this._position = position; + this.draw(idx); + }; + + CustomOverlay.prototype.getPosition = function () { + return this._position; + }; + + CustomOverlay.prototype.setId = function (id) { + this._id = id; + }; + + CustomOverlay.prototype.getId = function () { + return this._id; + }; + + CustomOverlay.prototype.setIdx = function (idx) { + this._idx = idx; + }; + + CustomOverlay.prototype.getIdx = function () { + return this._idx; + }; + + CustomOverlay.prototype.setControlId = function (controlId) { + this._controlId = controlId; + }; + + CustomOverlay.prototype.getControlId = function () { + return this._controlId; + }; + }; + + const removeArrMarkers = arrData => { + setArrMarkers(arrData); + }; + + const removeArrInfos = arrData => { + setArrInfos(arrData); + }; + + const markerInfo = arrMarkers => { + arrMarkers.forEach((marker, idx) => { + if (arrInfos.filter(i => i._controlId === marker.controlId).length > 0) { + return; + } + + const info = new props.mapboxgl.Popup({ + closeButton: false, + closeOnClick: false + }) + .setLngLat([marker.position.lng, marker.position.lat]) + .setHTML( + ` +
+
+ ${marker?.id} +
+
+
+ ${marker.gps?.elev}M + ${marker.gps?.speed}km + ${ + typeof marker.gps?.lat === 'number' && + typeof marker.gps?.lng === 'number' + ? ` + + ${(marker.gps?.lat).toFixed( + 6 + )} ${(marker.gps?.lng).toFixed(6)} + ` + : '' + } +
+
+
+ ` + ) + .addTo(props.map); + // infoInit(marker, controlGpList[idx], idx); + info._id = marker.id; + info.gps = marker.gps; + // if (controlGpList.length != 0) { + // const info = new CustomOverlay({ + // position: new naver.maps.LatLng( + // controlGpList[idx]?.lat, + // controlGpList[idx]?.lng + // ), + // // map: map, + // id: marker.id, + // idx: idx, + // controlId: marker.controlId + // }); + // info.setMap(map); + + setArrInfos(m => [...m, info]); + // } + }); + }; + + return null; +}; diff --git a/src/components/map/mapbox/feature/FeatureAirZone.js b/src/components/map/mapbox/feature/FeatureAirZone.js index ad09a1a0..f4343892 100644 --- a/src/components/map/mapbox/feature/FeatureAirZone.js +++ b/src/components/map/mapbox/feature/FeatureAirZone.js @@ -5,6 +5,7 @@ import geoJson from '../../../../components/map/geojson/airArea.json'; import gimPo from '../../../../components/map/geojson/airportAirArea.json'; import '../../../../assets/css/custom.css'; +// 격자 공역 Source const airPort = [ { title: '김포공항', @@ -34,34 +35,13 @@ const airPort = [ export const FeatureAirZone = props => { const mapControl = useSelector(state => state.controlMapReducer); + let popup; let infoWindow; useEffect(() => { featureAirZoneInit(); - // featureAirEvent(); }, [mapControl]); - // useEffect(() => { - // const addLayers = () => { - // props.poly.forEach((layer, idx) => { - // // console.log(layer); - // props.map.addSource(`line${idx}`, { - // type: 'geojson', - // data: { - // layer - // } - // }); - // props.map.addLayer(layer); - // }); - // }; - - // addLayers(); - - // // return () => { - // // props.map.remove(); - // // }; - // }, [props.poly]); - const infowindowOpen = data => { const content = '
' + @@ -72,48 +52,99 @@ export const FeatureAirZone = props => { '
' + '
' + '
' + - // '설명' + '' + data.description + '' + '
' + - // '
' + - // '좌표정보' + - // ''+data.coord+'' + - // '
' + '
' + - // '' + ''; - infoWindow = new props.naver.maps.InfoWindow({ - class: 'tooltip-test', - content: content, - maxWidth: 200, - backgroundColor: '#283046', //박스안쪽영역 컬러 - // borderColor: '#333', //테두리컬러 - // borderWidth: 3, //테두리 굵기 - anchorSize: new props.naver.maps.Size(30, -10), - anchorSkew: false, - anchorColor: '#283046', - - pixelOffset: new props.naver.maps.Point(20, -20) + if (popup) { + popup.remove(); + } + + // Create a popup element + popup = new props.mapboxgl.Popup({ + offset: [20, -20], + closeButton: false, + closeOnClick: false, + closeOnMove: true + }) + .setLngLat(data.coord) + .setHTML(content) + .addTo(props.map); + }; + + const featureAirEvent = markers => { + markers.forEach(marker => { + marker.getElement().addEventListener('mouseover', e => { + const data = {}; + data.coord = marker.getLngLat(); + data.title = marker.properties.name; + data.description = marker.properties.description; + + infowindowOpen(data); + }); + marker.getElement().addEventListener('mouseout', () => { + if (popup) { + popup.remove(); + } + }); + }); + + props.map.on('mouseover', 'maine', e => { + props.map.getCanvas().style.cursor = 'pointer'; + const feature = e.features[0]; + const data = feature.properties; + data.coord = e.lngLat; + data.title = feature.properties.name; + + infowindowOpen(data); + }); + + props.map.on('mouseout', 'maine', () => { + props.map.getCanvas().style.cursor = ''; + if (popup) { + popup.remove(); + } }); - infoWindow.open(props.map, data.coord); + props.map.on('click', e => { + // 클릭한 지점의 피처들을 얻어옵니다. + const features = props.map.queryRenderedFeatures(e.point, { + layers: ['add-3d-buildings'] // 빌딩 레이어의 ID를 지정합니다. + }); + + // 빌딩 피처가 있는 경우 + if (features.length > 0) { + // 첫 번째 빌딩 피처의 높이 값을 얻어옵니다. + const height = features[0].properties.height; + + // 팝업을 생성하고 지도에 추가합니다. + new props.mapboxgl.Popup() + .setLngLat(e.lngLat) + .setHTML(`Building height: ${height}m`) + .addTo(props.map); + } + }); }; + // 공역 생성 함수 const featureAirZoneInit = () => { let arrGeoJson = []; - // props.map.data.removeGeoJson(props.geoJson); - // let geoJson = originGeoJson; + const useGeoJson = { ...geoJson, features: [...geoJson.features, ...gimPo.features] }; // props.map.data.removeGeoJson(useGeoJson); - // props.map.removeLayer('maine'); + if (props.map.getLayer('maine')) { + props.map.removeLayer('maine'); + props.map.removeSource('maine'); + } + // 공역 색상 및 공역 표출 정보에 따른 노출 useGeoJson.features.map(item => { if (item.properties.type === '0001' && mapControl.area0001) { arrGeoJson.push({ @@ -149,6 +180,7 @@ export const FeatureAirZone = props => { }); useGeoJson.features = arrGeoJson; + // 공역 생성 start props.map.addSource('maine', { type: 'geojson', data: { @@ -158,81 +190,33 @@ export const FeatureAirZone = props => { props.map.addLayer({ id: 'maine', type: 'fill', - source: 'maine', // reference the data source + source: 'maine', layout: {}, paint: { 'fill-color': ['get', 'color'], 'fill-opacity': 0.5 } }); - - airPort.map(air => polyArea(air)); - - // useGeoJson.features = arrGeoJson; - // props.map.data.addGeoJson(useGeoJson); - - // props.map.data.setStyle(feature => { - // var color; - - // //0001 비행금지구역 #FF3648 - // //0002 비행제한구역 #FFA1AA - // //0003 관제권(공항) #FFA800 - // //0004 비행장(군사) #A16B00 - // //0005 이착륙장(RC비행장) #AB40FF - // //0006 초경량비행장치 #009cad - - // // 공역 색상 변경 - // const type = feature.getProperty('type'); - // if (type === '0001') { - // color = '#FF3648'; - // } else if (type === '0002') { - // color = '#FFA1AA'; - // } else if (type === '0003') { - // color = '#FFA800'; - // } else if (type === '0004') { - // color = '#A16B00'; - // } else if (type === '0005') { - // color = '#AB40FF'; - // } else if (type === '0006') { - // color = '#009cad'; - // } - - // return { - // fillColor: color, - // strokeColor: color, - // strokeWeight: 0.7, - // icon: null - // }; - // }); - }; - - const featureAirEvent = () => { - props.map.data.addListener('click', function (e) { - // e.feature.setProperty('isColorful', true); - }); - - props.map.data.addListener('mouseover', function (e) { - const data = {}; - data.coord = e.coord; - data.title = e.feature.property_name; - data.description = e.feature.property_description; - props.map.data.overrideStyle(e.feature, { - strokeWeight: 3 - // icon: HOME_PATH +'/img/example/pin_spot.png' + // 공역 생성 end + + // 비행장 마커 생성 + const markers = useGeoJson.features + .filter(i => i.geometry.type === 'Point') + .map(i => { + const marker = new props.mapboxgl.Marker() + .setLngLat(i.geometry.coordinates) + .addTo(props.map); + marker.properties = i.properties; + return marker; }); - infowindowOpen(data); - }); - - props.map.data.addListener('mouseout', function (e) { - props.map.data.revertStyle(); - - if (infoWindow) { - infoWindow.close(); - } - }); + // 격자 공역 생성 + airPort.map(air => polyArea(air)); + // 마우스 오버 이벤트 생성 + featureAirEvent(markers); }; + // 격자 공역 셋팅 함수 const polyArea = air => { const polyArr = []; const radius = air.buffer; @@ -374,7 +358,10 @@ export const FeatureAirZone = props => { }; polyArr.push(EW); - + if (props.map.getLayer(air.title)) { + props.map.removeLayer(air.title); + props.map.removeSource(air.title); + } props.map.addSource(air.title, { type: 'geojson', data: { diff --git a/test/pav-warning.js b/test/pav-warning.js index 2d8b8dcf..2e5f2edb 100644 --- a/test/pav-warning.js +++ b/test/pav-warning.js @@ -2,18 +2,20 @@ const { getConnection, writeData } = require('./pav-client'); const { getCoordsFormBetweenCoord, dumyData } = require('./pav-utils'); // const host = "192.168.0.24" -const host = 'localhost'; +// const host = 'localhost'; // const host = '192.168.0.34'; +// const port = 8082; +const host = '211.253.38.218'; const port = 8082; // 기본정보 -const prefix = 'PA'; -const terminalId = 'SANDBOX-001'; +const prefix = ''; +const terminalId = ''; const pathSampleCoord = [ [37.5215, 126.605], - [37.5215, 126.61], //비정상, 날릴 것 - // [37.5215, 126.6082], 계획서 + // [37.5215, 126.61], //비정상, 날릴 것 + [37.5215, 126.6082], //계획서 [37.521, 126.6082], [37.521, 126.6057], [37.5205, 126.6057],