/** * 원(Circle) 리사이즈 커스텀 인터랙션 * * 원은 64점 Polygon으로 저장되어 있으므로, 테두리 드래그 시 * 중심에서 드래그 좌표까지의 거리를 새 반지름으로 계산하고 * fromCircle()로 Polygon을 재생성. * * 감지 방식: 개별 꼭짓점이 아닌, 중심~포인터 거리와 반지름 비교 (테두리 전체 감지) */ import PointerInteraction from 'ol/interaction/Pointer'; import { fromCircle } from 'ol/geom/Polygon'; import OlCircle from 'ol/geom/Circle'; const PIXEL_TOLERANCE = 16; const MIN_RADIUS = 100; // 최소 반지름 (미터) export default class CircleResizeInteraction extends PointerInteraction { constructor(options) { super({ handleDownEvent: (evt) => CircleResizeInteraction.prototype._handleDown.call(this, evt), handleDragEvent: (evt) => CircleResizeInteraction.prototype._handleDrag.call(this, evt), handleUpEvent: (evt) => CircleResizeInteraction.prototype._handleUp.call(this, evt), }); this.feature_ = options.feature; this.center_ = options.center; // EPSG:3857 [x, y] this.onResize_ = options.onResize || null; this.dragging_ = false; } /** 중심~포인터 픽셀 거리와 표시 반지름 비교 */ _isNearEdge(map, pixel) { const centerPixel = map.getPixelFromCoordinate(this.center_); const coords = this.feature_.getGeometry().getCoordinates()[0]; const edgePixel = map.getPixelFromCoordinate(coords[0]); const radiusPixels = Math.hypot( edgePixel[0] - centerPixel[0], edgePixel[1] - centerPixel[1], ); const distFromCenter = Math.hypot( pixel[0] - centerPixel[0], pixel[1] - centerPixel[1], ); return Math.abs(distFromCenter - radiusPixels) < PIXEL_TOLERANCE; } _handleDown(evt) { if (this._isNearEdge(evt.map, evt.pixel)) { this.dragging_ = true; return true; } return false; } _handleDrag(evt) { if (!this.dragging_) return; const coord = evt.coordinate; const dx = coord[0] - this.center_[0]; const dy = coord[1] - this.center_[1]; const newRadius = Math.max(Math.sqrt(dx * dx + dy * dy), MIN_RADIUS); const circleGeom = new OlCircle(this.center_, newRadius); const polyGeom = fromCircle(circleGeom, 64); this.feature_.setGeometry(polyGeom); } _handleUp() { if (this.dragging_) { this.dragging_ = false; if (this.onResize_) this.onResize_(this.feature_); return true; } return false; } /** 외부에서 center 업데이트 (Translate 후) */ setCenter(center) { this.center_ = center; } /** * 호버 감지: 픽셀이 리사이즈 핸들(테두리) 위인지 확인 * @returns {{ cursor: string }} | null */ isOverHandle(map, pixel) { if (this._isNearEdge(map, pixel)) { return { cursor: 'nesw-resize' }; } return null; } }