1- import React , { useRef } from "react" ;
1+ import React , { useRef , useEffect } from "react" ;
22import {
33 Box ,
44 Modal ,
@@ -10,12 +10,18 @@ import {
1010 ModalCloseButton ,
1111 Button ,
1212} from "@chakra-ui/react" ;
13- import { MapContainer , FeatureGroup } from "react-leaflet" ;
14- import { EditControl } from "react-leaflet-draw" ;
15- import "leaflet/dist/leaflet.css" ;
16- import "leaflet-draw/dist/leaflet.draw.css" ;
17- import L from "leaflet" ;
18- import CommonTileLayer from "./CommonTileLayer" ;
13+ import Map from "ol/Map" ;
14+ import View from "ol/View" ;
15+ import TileLayer from "ol/layer/Tile" ;
16+ import OSM from "ol/source/OSM" ;
17+ import VectorLayer from "ol/layer/Vector" ;
18+ import VectorSource from "ol/source/Vector" ;
19+ import Draw from "ol/interaction/Draw" ;
20+ import { fromLonLat , toLonLat } from "ol/proj" ;
21+ import { Fill , Stroke , Style } from "ol/style" ;
22+ import { Coordinate } from "ol/coordinate" ;
23+ import Polygon from "ol/geom/Polygon" ;
24+ import "ol/ol.css" ;
1925
2026interface MapModalProps {
2127 isOpen : boolean ;
@@ -24,70 +30,177 @@ interface MapModalProps {
2430}
2531
2632const MapModal : React . FC < MapModalProps > = ( { isOpen, onClose, onSubmit } ) => {
27- const featureGroupRef = useRef < L . FeatureGroup > ( null ) ;
33+ const mapRef = useRef < HTMLDivElement > ( null ) ;
34+ const mapInstanceRef = useRef < Map | null > ( null ) ;
35+ const vectorSourceRef = useRef < VectorSource | null > ( null ) ;
36+ const drawRef = useRef < Draw | null > ( null ) ;
2837
2938 const handleDrawStop = ( ) => {
30- if ( ! featureGroupRef . current ) return ;
39+ if ( ! vectorSourceRef . current ) return ;
3140
32- const layers = featureGroupRef . current . getLayers ( ) ;
33- if ( ! layers . length ) return ;
41+ const features = vectorSourceRef . current . getFeatures ( ) ;
42+ if ( ! features . length ) return ;
3443
35- const layer = layers [ 0 ] as L . Rectangle ;
36- if ( ! layer . getBounds ) return ;
44+ const feature = features [ features . length - 1 ] ; // Get the latest drawn feature
45+ const geometry = feature . getGeometry ( ) ;
46+ if ( ! geometry ) return ;
3747
38- const bounds = layer . getBounds ( ) ;
48+ const extent = geometry . getExtent ( ) ;
3949
40- const southWestLng = bounds . getSouthWest ( ) . lng . toFixed ( 2 ) ;
41- const southWestLat = bounds . getSouthWest ( ) . lat . toFixed ( 2 ) ;
42- const northEastLng = bounds . getNorthEast ( ) . lng . toFixed ( 2 ) ;
43- const northEastLat = bounds . getNorthEast ( ) . lat . toFixed ( 2 ) ;
44-
45- const bbox = `${ southWestLng } , ${ southWestLat } , ${ northEastLng } , ${ northEastLat } ` ;
50+ // Transform from Web Mercator (EPSG:3857) to WGS84 (EPSG:4326)
51+ const bottomLeft = toLonLat ( [ extent [ 0 ] , extent [ 1 ] ] ) ;
52+ const topRight = toLonLat ( [ extent [ 2 ] , extent [ 3 ] ] ) ;
4653
54+ const bbox = `${ bottomLeft [ 0 ] . toFixed ( 6 ) } , ${ bottomLeft [ 1 ] . toFixed ( 6 ) } , ${ topRight [ 0 ] . toFixed ( 6 ) } , ${ topRight [ 1 ] . toFixed ( 6 ) } ` ;
4755 onSubmit ( bbox ) ;
4856 } ;
4957
5058 const handleClearAll = ( ) => {
51- if ( ! featureGroupRef . current ) return ;
52-
53- featureGroupRef . current . clearLayers ( ) ;
59+ if ( ! vectorSourceRef . current ) return ;
60+ vectorSourceRef . current . clear ( ) ;
5461 } ;
5562
63+ // Initialize map when modal opens
64+ useEffect ( ( ) => {
65+ if ( ! isOpen ) return ;
66+
67+ // Add a small delay to ensure the modal DOM is fully rendered
68+ const initMap = ( ) => {
69+ if ( ! mapRef . current ) {
70+ console . log ( "Map ref not available" ) ;
71+ return ;
72+ }
73+
74+ // Clean up existing map instance
75+ if ( mapInstanceRef . current ) {
76+ mapInstanceRef . current . dispose ( ) ;
77+ mapInstanceRef . current = null ;
78+ }
79+
80+ try {
81+ console . log ( "Initializing OpenLayers map..." ) ;
82+
83+ const vectorSource = new VectorSource ( ) ;
84+ vectorSourceRef . current = vectorSource ;
85+
86+ const vectorLayer = new VectorLayer ( {
87+ source : vectorSource ,
88+ style : new Style ( {
89+ fill : new Fill ( {
90+ color : "rgba(255, 255, 255, 0.2)" ,
91+ } ) ,
92+ stroke : new Stroke ( {
93+ color : "#ffcc33" ,
94+ width : 2 ,
95+ } ) ,
96+ } ) ,
97+ } ) ;
98+
99+ const map = new Map ( {
100+ target : mapRef . current ,
101+ layers : [
102+ new TileLayer ( {
103+ source : new OSM ( ) ,
104+ } ) ,
105+ vectorLayer ,
106+ ] ,
107+ view : new View ( {
108+ center : fromLonLat ( [ 0 , 0 ] ) ,
109+ zoom : 0 ,
110+ } ) ,
111+ } ) ;
112+
113+ // Use Box drawing for corner-to-corner rectangle drawing
114+ const draw = new Draw ( {
115+ source : vectorSource ,
116+ type : "Circle" ,
117+ geometryFunction : ( coordinates , geometry ) => {
118+ if (
119+ ! coordinates ||
120+ ! Array . isArray ( coordinates ) ||
121+ coordinates . length < 2
122+ )
123+ return geometry ;
124+
125+ const start = coordinates [ 0 ] as Coordinate ;
126+ const end = coordinates [ 1 ] as Coordinate ;
127+
128+ // Create rectangle from two corner points
129+ const minX = Math . min ( start [ 0 ] , end [ 0 ] ) ;
130+ const minY = Math . min ( start [ 1 ] , end [ 1 ] ) ;
131+ const maxX = Math . max ( start [ 0 ] , end [ 0 ] ) ;
132+ const maxY = Math . max ( start [ 1 ] , end [ 1 ] ) ;
133+
134+ const coords = [
135+ [
136+ [ minX , minY ] ,
137+ [ maxX , minY ] ,
138+ [ maxX , maxY ] ,
139+ [ minX , maxY ] ,
140+ [ minX , minY ] ,
141+ ] ,
142+ ] ;
143+
144+ if ( ! geometry ) {
145+ geometry = new Polygon ( coords ) ;
146+ } else {
147+ geometry . setCoordinates ( coords ) ;
148+ }
149+
150+ return geometry ;
151+ } ,
152+ } ) ;
153+
154+ draw . on ( "drawend" , ( ) => {
155+ setTimeout ( handleDrawStop , 100 ) ;
156+ } ) ;
157+
158+ map . addInteraction ( draw ) ;
159+ mapInstanceRef . current = map ;
160+ drawRef . current = draw ;
161+
162+ console . log ( "Map initialized successfully" ) ;
163+
164+ // Force map to update size after initialization
165+ setTimeout ( ( ) => {
166+ map . updateSize ( ) ;
167+ console . log ( "Map size updated" ) ;
168+ } , 100 ) ;
169+ } catch ( error ) {
170+ console . error ( "Error initializing map:" , error ) ;
171+ }
172+ } ;
173+
174+ // Delay initialization to ensure modal is fully rendered
175+ const timeoutId = setTimeout ( initMap , 300 ) ;
176+
177+ return ( ) => {
178+ clearTimeout ( timeoutId ) ;
179+ if ( mapInstanceRef . current ) {
180+ mapInstanceRef . current . dispose ( ) ;
181+ mapInstanceRef . current = null ;
182+ }
183+ } ;
184+ } , [ isOpen ] ) ;
185+
56186 return (
57187 < Modal isOpen = { isOpen } onClose = { onClose } size = "xl" >
58188 < ModalOverlay />
59189 < ModalContent >
60190 < ModalHeader > Draw Bounding Box</ ModalHeader >
61191 < ModalCloseButton />
62192 < ModalBody >
63- < Box height = "500px" >
64- < MapContainer
65- style = { { height : "100%" , width : "100%" } }
66- center = { [ 0 , 0 ] }
67- zoom = { 1 }
68- scrollWheelZoom = { true }
69- >
70- < CommonTileLayer />
71- < FeatureGroup ref = { featureGroupRef } >
72- < EditControl
73- position = "topright"
74- onEdited = { handleDrawStop }
75- onCreated = { handleDrawStop }
76- draw = { {
77- rectangle : true ,
78- circle : false ,
79- polyline : false ,
80- polygon : false ,
81- marker : false ,
82- circlemarker : false ,
83- } }
84- edit = { {
85- edit : false ,
86- remove : true ,
87- } }
88- />
89- </ FeatureGroup >
90- </ MapContainer >
193+ < Box height = "500px" width = "100%" >
194+ < div
195+ ref = { mapRef }
196+ style = { {
197+ height : "500px" ,
198+ width : "100%" ,
199+ position : "relative" ,
200+ backgroundColor : "#f5f5f5" ,
201+ border : "1px solid #ccc" ,
202+ } }
203+ />
91204 </ Box >
92205 </ ModalBody >
93206 < ModalFooter >
0 commit comments