gis 오버레이 수정

This commit is contained in:
leedj312
2025-09-09 10:10:19 +09:00
parent f9e8748906
commit f50de19137
@@ -10,8 +10,8 @@ export default function createOverlayManager(map, vectorLayer, overlayFormatter,
const getCoordinateKey = buildCoordinateKeyFactory(coordPrecision);
let currentFormatter = overlayFormatter;
const overlayMap = new Map(); // coordKey -> Overlay
const overlayElementMap = new Map(); // coordKey -> HTMLElement
const overlayMap = new Map(); // feature -> Overlay
const overlayElementMap = new Map(); // feature -> HTMLElement
const vectorSource = vectorLayer?.getSource?.();
const SYNC_MIN_INTERVAL = 120;
@@ -25,22 +25,22 @@ export default function createOverlayManager(map, vectorLayer, overlayFormatter,
const clearAllOverlaysAsync = (batch = 150) => {
const keys = Array.from(overlayMap.keys());
if (!keys.length) return;
const features = Array.from(overlayMap.keys());
if (!features.length) return;
// 재귀로 사용하는 이유 한번에 다지운는ㄴ것이아닌 150개 이상 더있을경우를 대비해서 다시 호출하기 위해(지도가 계속 멈춤)
const step = () => {
let iNum = 0;
const end = Math.min(iNum + batch, keys.length);
const end = Math.min(iNum + batch, features.length);
for (; iNum < end; iNum++) {
const k = keys[iNum];
const ov = overlayMap.get(k);
const feat = features[iNum];
const ov = overlayMap.get(feat);
try { if (ov) map.removeOverlay(ov); } catch {}
overlayMap.delete(k);
const el = overlayElementMap.get(k);
if (el) { try { el.remove?.(); } catch {} overlayElementMap.delete(k); }
overlayMap.delete(feat);
const el = overlayElementMap.get(feat);
if (el) { try { el.remove?.(); } catch {} overlayElementMap.delete(feat); }
}
if (iNum < keys.length) {
if (iNum < features.length) {
(typeof requestAnimationFrame === 'function' ? requestAnimationFrame : setTimeout)(step, 0);
}
};
@@ -75,9 +75,8 @@ export default function createOverlayManager(map, vectorLayer, overlayFormatter,
const ensureOverlay = (feature, properties) => {
const coord = getFeatureLabelCoordinate(feature);
if (!Array.isArray(coord)) return null;
const key = getCoordinateKey(coord);
let el = overlayElementMap.get(key);
let el = overlayElementMap.get(feature);
if (!el) {
if (!currentFormatter || typeof currentFormatter !== 'function') return null;
@@ -86,19 +85,19 @@ export default function createOverlayManager(map, vectorLayer, overlayFormatter,
// 1) 동기 HTMLElement
if (maybeEl && typeof maybeEl === 'object' && (maybeEl.nodeType === 1 || maybeEl.tagName)) {
el = maybeEl;
overlayElementMap.set(key, el);
overlayElementMap.set(feature, el);
}
// 2) 비동기 Promise<HTMLDivElement|null>
else if (maybeEl && typeof maybeEl.then === 'function') {
maybeEl.then((resolvedEl) => {
if (!resolvedEl || !(resolvedEl.nodeType === 1 || resolvedEl.tagName)) return;
// 이미 다른 사이클에서 만들어졌으면 덮어쓰지 않음
if (!overlayElementMap.has(key)) overlayElementMap.set(key, resolvedEl);
if (!overlayElementMap.has(feature)) overlayElementMap.set(feature, resolvedEl);
let ov = overlayMap.get(key);
let ov = overlayMap.get(feature);
if (!ov) {
ov = new Overlay({ element: resolvedEl, positioning: 'bottom-center', offset: [0, -18], stopEvent: true });
overlayMap.set(key, ov);
overlayMap.set(feature, ov);
try { map.addOverlay(ov); } catch {}
} else {
try { ov.setElement(resolvedEl); } catch {}
@@ -115,10 +114,10 @@ export default function createOverlayManager(map, vectorLayer, overlayFormatter,
}
// 여기까지 왔으면 el은 HTMLElement
let ov = overlayMap.get(key);
let ov = overlayMap.get(feature);
if (!ov) {
ov = new Overlay({ element: el, positioning: 'bottom-center', offset: [0, -18], stopEvent: true });
overlayMap.set(key, ov);
overlayMap.set(feature, ov);
map.addOverlay(ov);
}
ov.setPosition(coord);
@@ -132,52 +131,35 @@ export default function createOverlayManager(map, vectorLayer, overlayFormatter,
const size = map.getSize?.();
const viewExtent = view && size ? view.calculateExtent(size) : null;
// gather features in viewport only and group by coordinate
const groups = new Map(); // coordKey -> feature[]
const addFeature = (f) => {
const c = getFeatureLabelCoordinate(f);
if (!Array.isArray(c)) return;
const k = getCoordinateKey(c);
const arr = groups.get(k);
if (arr) arr.push(f); else groups.set(k, [f]);
};
const features = [];
const pushFeature = (f) => features.push(f);
if (vectorSource?.forEachFeatureInExtent && viewExtent) {
vectorSource.forEachFeatureInExtent(viewExtent, addFeature);
vectorSource.forEachFeatureInExtent(viewExtent, pushFeature);
} else {
vectorSource?.forEachFeature(addFeature);
vectorSource?.forEachFeature(pushFeature);
}
// cap overlays per viewport with simple uniform sampling
const keys = Array.from(groups.keys());
let selectedKeys;
if (!isFinite(maxOverlaysPerViewport) || keys.length <= maxOverlaysPerViewport) {
selectedKeys = keys;
} else {
const step = Math.ceil(keys.length / maxOverlaysPerViewport);
selectedKeys = [];
for (let i = 0; i < keys.length && selectedKeys.length < maxOverlaysPerViewport; i += step) {
selectedKeys.push(keys[i]);
let selected = features;
if (isFinite(maxOverlaysPerViewport) && features.length > maxOverlaysPerViewport) {
const step = Math.ceil(features.length / maxOverlaysPerViewport);
selected = [];
for (let i = 0; i < features.length && selected.length < maxOverlaysPerViewport; i += step) {
selected.push(features[i]);
}
}
// ensure selected overlays
for (const k of selectedKeys) {
const featuresAtKey = groups.get(k);
const rep = featuresAtKey[0];
const props = rep.getProperties?.() ?? {};
if (featuresAtKey.length > 1) props.__groupCount = featuresAtKey.length;
ensureOverlay(rep, props);
for (const f of selected) {
const props = f.getProperties?.() ?? {};
ensureOverlay(f, props);
}
// remove non-selected overlays
const keep = new Set(selectedKeys);
for (const [k, ov] of overlayMap.entries()) {
if (!keep.has(k)) {
const keep = new Set(selected);
for (const [feat, ov] of overlayMap.entries()) {
if (!keep.has(feat)) {
try { map.removeOverlay(ov); } catch {}
overlayMap.delete(k);
const el = overlayElementMap.get(k);
if (el) { try { el.remove?.(); } catch {} overlayElementMap.delete(k); }
overlayMap.delete(feat);
const el = overlayElementMap.get(feat);
if (el) { try { el.remove?.(); } catch {} overlayElementMap.delete(feat); }
}
}
};