91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
|
|
/**
|
||
|
|
* 원(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;
|
||
|
|
}
|
||
|
|
}
|