Files
frontend/packages/common/business_components/src/ui/map/lib/gis.layer.js
T
yschoi 9cf281db7e
iipbs_wr / build (push) Waiting to run
gis 원복
2025-07-01 19:32:25 +09:00

573 lines
16 KiB
JavaScript

import Feature from 'ol/Feature.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import Point from 'ol/geom/Point.js';
import VectorLayer from 'ol/layer/Vector';
import VectorImageLayer from 'ol/layer/VectorImage.js';
import { all, bbox } from 'ol/loadingstrategy';
import { transform } from 'ol/proj';
import Cluster from 'ol/source/Cluster.js';
import VectorSource from 'ol/source/Vector.js';
import VectorTile from 'ol/source/VectorTile.js';
import CircleStyle from 'ol/style/Circle.js';
import Fill from 'ol/style/Fill.js';
import Icon from 'ol/style/Icon.js';
import Stroke from 'ol/style/Stroke.js';
import Style from 'ol/style/Style.js';
import Text from 'ol/style/Text.js';
import { createXYZ } from 'ol/tilegrid';
import GisApp from './gis.core.js';
import GisOlStyle from './gis.style.js';
import Tile from 'ol/layer/Tile.js';
import TileWMS from 'ol/source/TileWMS.js';
import gisCore from './gis.core.js';
/**
* GisApp.layer
* @requires gis.core.js
* @requires gis.style.js
*/
const GisOlLayer = function (options) {
this.initialize(options);
};
GisOlLayer.prototype = {
layers: [],
cesiumLayers: [],
defaults: {},
storageNm: 'hasudo:',
defaultLayerOptions: {
name: '',
zIndex: 2,
opacity: 1,
flterSql: null,
minZoom: '15',
},
initialize: function (options) {
Object.assign(this.defaults, options);
this.gisStyle = new GisOlStyle();
GisApp.Module.style = this.gisStyle;
},
gisStyle: null,
/**
* [사용]빈 레이어 생성
*/
makeLayer: function (options) {
let layerOptions = {
...this.defaultLayerOptions,
...options,
minZoom: options?.minViewSize ? options?.minViewSize : options?.minZoom,
zIndex: options?.drawOrdr ? options?.drawOrdr : 1,
};
let returnLayer = new VectorImageLayer(layerOptions);
this.layers.push(returnLayer);
return returnLayer;
},
/**
* gisStyle 반환
*/
getGisStyle: function () {
return this.gisStyle;
},
/**
* [사용]WFS 벡터 소스 반환
*/
getVectorSourceGeoserver: function (options) {
let url = GisApp.GEOSERVER_URL;
let toProj = GisApp.TO_PROJ;
let lyrTyNm = options.lyrTyNm;
let typename = this.storageNm + lyrTyNm;
if(options.lyrTyNm == "om_dt_untrt_swg_rain" //중점우수토실
|| options.lyrTyNm == "om_wqlt_station" //관측소
|| options.lyrTyNm == "tb_wr_bdst_ruse_fclt" //재이용시설
|| options.lyrTyNm == "tb_wr_rnwtr_utztn_fclt" //빗물이용시설
|| options.lyrTyNm == "tb_wr_rswtr_fclt" //중수도시설
|| options.lyrTyNm == "tb_wr_htdfl_fclt" //온배수시설
|| options.lyrTyNm == "tb_wr_tmpr_dmd_co" //잠재수요처
)
{
url = GisApp.SN_GEOSERVER_URL;
typename = lyrTyNm;
}
const params = {};
params['TYPENAME'] = typename;
params['SERVICE'] = 'WFS';
params['VERSION'] = '1.1.0';
params['REQUEST'] = 'GetFeature';
params['SRS'] = toProj;
params['OUTPUTFORMAT'] = 'application/json';
if (options?.flterSql && options?.flterSql?.trim() !== 'null') {
params['CQL_FILTER'] = options?.flterSql;
}
const strParams = new URLSearchParams(params).toString();
let source = new VectorSource({
format: new GeoJSON(),
url: function (extent) {
return url + '?' + strParams;
},
strategy: all,
});
return source;
},
/**
* [사용]WFS 벡터 소스 반환
*/
getVectorTileSource: function (options) {
let url = GisApp.GEOSERVER_URL;
let toProj = GisApp.TO_PROJ;
let params = {};
params['TYPENAME'] = this.storageNm + options.typeName;
params['SERVICE'] = 'WFS';
params['VERSION'] = '1.1.0';
params['REQUEST'] = 'GetFeature';
params['SRS'] = toProj;
params['OUTPUTFORMAT'] = 'application/json';
let strParams = new URLSearchParams(params).toString();
let source = new VectorTile({
format: new GeoJSON(),
tileUrlFunction: function (tileCoord, pixelRatio, projection) {
let filterStr = '&CQL_FILTER=bbox(GEOM,' + source.getTileGrid().getTileCoordExtent(tileCoord).join(',') + ",'" + projection.getCode() + "')";
if (options.flterSql) {
filterStr += ' AND ' + options.flterSql;
}
let paramWithUrl = url + '?' + strParams + filterStr;
return paramWithUrl;
},
tileGrid: createXYZ,
});
return source;
},
/**
* [사용]WFS 레이어 생성
*/
getWfsLayer: function (option) {
option = option || {};
//레이어 생성
let returnLayer = this.makeLayer(option);
//레이어 소스 생성
let source = this.getVectorSourceGeoserver(option);
returnLayer.setSource(source);
//스타일 추가
returnLayer.setStyle(this.gisStyle.getLayerStyleFunction(returnLayer, option));
returnLayer.set('id', option.id + '');
return returnLayer;
},
/**
* [사용]WMS 레이어 생성
*/
getWmsLayer: function (options) {
let layerOptions = Object.assign({}, this.defaultLayerOptions, options);
let url = GisApp.GEOSERVER_WMS_URL;
let toProj = GisApp.TO_PROJ;
const lyrTyNm = options.lyrTyNm;
let params = {};
params['LAYERS'] = this.storageNm + lyrTyNm;
params['SERVICE'] = 'WMS';
params['VERSION'] = '1.1.1';
params['REQUEST'] = 'GetMap';
params['SRS'] = toProj;
params['FORMAT'] = 'image/png';
params['TILED'] = true;
const wmsLayer = new Tile({
...layerOptions,
id: layerOptions?.id,
source: new TileWMS({
url,
params,
serverType: 'geoserver',
}),
});
wmsLayer.setZIndex(options.draw0rdr);
wmsLayer.setMinZoom(options.minViewSize);
this.layers.push(wmsLayer);
return wmsLayer;
},
/**
* [사용]WFS 레이어 생성
*/
getWfsEmptyLayer: function (option) {
option = option || {};
//레이어 생성
let returnLayer = this.makeLayer(option);
let vectorSource = new VectorSource({
strategy: all,
});
returnLayer.setSource(vectorSource);
//스타일 추가
returnLayer.setStyle(this.gisStyle.getLayerStyle(returnLayer, option));
returnLayer.set('id', option.id + '');
let map = GisApp.Module.core.map;
map.addLayer(returnLayer);
return returnLayer;
},
/**
* 피처 리스트로 레이어 생성
*/
getLayerByFeature: function (feature, layerOptions, layerStyle) {
if (!layerOptions || !layerOptions.id) {
return null;
}
let vectorSource = new VectorSource({
strategy: all,
});
if (feature) {
if (Object.prototype.toString.call(feature).slice(8, -1).indexOf('Object') > -1) {
vectorSource.addFeature(feature);
} else {
vectorSource.addFeatures(feature);
}
}
GisApp.Module.core.deleteLayer(layerOptions.id);
layerOptions.source = vectorSource;
let layer = new VectorImageLayer(layerOptions);
layer.setStyle(layerStyle);
layer.set('id', layerOptions.id + '');
return layer;
},
/**
* 피처 하이라이트
*/
highLightFeature: function (hlFeature, layerOptions, styleOptions, customStyleFunction) {
const color = styleOptions.color || 'rgba(135, 116, 243, 0.4)';
const width = styleOptions.width || 10;
const featureKey = styleOptions.featureKey || '';
const fitExtent = layerOptions.fitExtent || 'true';
const styleFunction = function (feature, resolution) {
const featureStyle = feature.getStyle();
if (featureStyle) return null;
const style = new Style({
image: new CircleStyle({
fill: new Fill({
color: color,
}),
radius: width,
}),
stroke: new Stroke({
color: color,
width: width,
}),
});
const getFeatureText = function (feature) {
if (featureKey) {
let text = feature.get(featureKey);
if (text) return text;
}
return '';
};
const text = new Text({
stroke: new Stroke({
color: 'white',
width: 2,
}),
fill: new Fill({
color: 'black',
}),
font: '12px Verdana',
text: getFeatureText(feature),
});
style.setText(text);
return style;
};
let actualStyleFunction = customStyleFunction || styleFunction;
GisApp.Module.core.deleteLayer(layerOptions.id);
let layer = this.getLayerByFeature(hlFeature, layerOptions, actualStyleFunction);
if (hlFeature && hlFeature.length > 0) {
let map = GisApp.Module.core.map;
map.addLayer(layer);
if (fitExtent === 'true') {
GisApp.Module.core.fitExtent(layer.getSource().getExtent());
}
}
return layer;
},
/**
* 마커 레이어 맵에 추가
*/
addMarkerLayer: function (lat, lon, fromProj, layerOptions) {
let p = transform([Number(lon), Number(lat)], fromProj, GisApp.TO_PROJ);
let feature = new Feature({
geometry: new Point(p),
});
let layer = this.getMarkerLayer(layerOptions);
layer.getSource().addFeature(feature);
let map = GisApp.Module.core.map;
map.addLayer(layer);
GisApp.Module.core.fitExtent(feature.getGeometry().getExtent());
return layer;
},
/**
* 마커 레이어 생성
*/
getMarkerLayer: function (layerOptions) {
let imgSrc = GisApp.CONTEXT_PATH + 'images/marker/marker-icon.png';
let style = new Style({
image: new Icon({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: imgSrc,
}),
});
let layer = this.getLayerByFeature(null, layerOptions, style);
return layer;
},
/**
* 피처 생성
*/
getFeatureCoord: function (coord) {
let feature = new Feature({
geometry: new Point(coord),
});
return feature;
},
/**
* 피처 생성(lat,lon)
*/
getFeatureLonLat: function (lat, lon) {
let p = transform([Number(lon), Number(lat)], GisApp.FROM_PROJ, GisApp.TO_PROJ);
let feature = new Feature({
geometry: new Point(p),
});
return feature;
},
addPolygonLayer: function (features, option) {
const map = GisApp.Module.core.map;
option = Object.assign(
{
gobjTy: 'lineLabelPoligon',
minZoom: option?.minZoom || '10',
lblAtrbNm: option?.lblAtrbNm || 'fclty_nm',
lnWdth: option?.lnWdth || '2',
lnColor: option?.lnColor || 'rgba(0, 0, 0, 1)',
polgnColor: option?.polgnColor || 'rgba(255, 0, 0, 0.2)',
clickPsblYn: option?.clickPsblYn || 'N',
fondNm: '18px "맑은 고딕",Malgun Gothic',
},
option
);
features[0].set('overLayId', option?.overlayId || null);
features[0].set('html', option?.html || null);
//레이어 생성
let vectorLayer = this.makeLayer(option);
//레이어 소스 생성
const vectorSource = new VectorSource({});
vectorSource.addFeatures(features);
vectorLayer.setSource(vectorSource);
//스타일 추가
vectorLayer.setStyle(this.gisStyle.getLayerStyleFunction(vectorLayer, option));
vectorLayer.set('id', option?.id + '');
GisApp.Module.core.deleteLayer(option?.id);
map.addLayer(vectorLayer);
},
addLayerByLyrTyNm: function (id, lyrTyNm, addQurryFlter) {
const map = GisApp.Module.core.map;
const layerCode = GisApp.layerCode;
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
matched['id'] = id;
let layer = GisApp.Module.core.getLayerById(matched?.id);
if (layer) {
layer.setVisible(true);
} else {
const queryFlter = layerCode?.flterSql || '' + ' ' + (addQurryFlter || '');
matched['flterSql'] = queryFlter;
layer = this.getWfsLayer(matched);
GisApp.Module.core.deleteLayer(id);
map.addLayer(layer);
}
},
/**
* 클러스터 레이어 맵에 추가
*/
addClusterLayer: function (features, { layerId, color }) {
const map = GisApp.Module.core.map;
const vectorSource = new VectorSource({});
vectorSource.addFeatures(features);
const clusterLayerId = layerId + 'Cluster';
const vectorLayerId = layerId + 'Vector';
// 클러스터 레이어 생성
const clusterLayer = new VectorLayer({
id: clusterLayerId,
source: new Cluster({
distance: 100, // 클러스터링 거리
source: vectorSource,
geometryFunction: function (feature) {
let geometry = feature.getGeometry();
let type = geometry.getType();
let coordinates;
if (type === 'Point') {
coordinates = geometry.getCoordinates();
} else if (type === 'LineString' || type === 'MultiLineString') {
let totalX = 0;
let totalY = 0;
let totalPoints = 0;
geometry.getCoordinates().forEach(line => {
line.forEach(point => {
totalX += point[0];
totalY += point[1];
totalPoints++;
});
});
coordinates = [totalX / totalPoints, totalY / totalPoints];
}
return new Point(coordinates);
},
}),
style: feature => {
const clusterSize = feature.get('features').length;
return new Style({
image: new CircleStyle({
radius: 10,
fill: new Fill({ color }),
}),
stroke: new Stroke({
color,
width: 10,
}),
text: new Text({
text: clusterSize.toString(),
fill: new Fill({
color: 'white',
}),
stroke: new Stroke({
color: 'black',
width: 4,
}),
}),
});
},
});
const vectorLayer = new VectorLayer({
id: vectorLayerId,
zIndex: 1,
source: vectorSource,
style: new Style({
image: new CircleStyle({
radius: 10,
fill: new Fill({ color }),
}),
stroke: new Stroke({
color,
width: 10,
}),
}),
});
GisApp.Module.core.deleteLayer(clusterLayerId);
GisApp.Module.core.deleteLayer(vectorLayerId);
map.addLayer(clusterLayer);
map.addLayer(vectorLayer);
function handleZoomEnd() {
const findClusterLayer = map
?.getLayers()
?.getArray()
.find(function (layer) {
return layer.get('id') === clusterLayerId;
});
const findVectorLayer = map
?.getLayers()
?.getArray()
.find(function (layer) {
return layer.get('id') === vectorLayerId;
});
if (!findClusterLayer) {
GisApp.Module.core.deleteLayer(vectorLayerId);
}
if (!findVectorLayer) {
GisApp.Module.core.deleteLayer(vectorLayerId);
}
const zoomLevel = map.getView().getZoom();
if (zoomLevel >= 18) {
vectorLayer.setVisible(true);
clusterLayer.setVisible(false);
} else {
clusterLayer.setVisible(true);
vectorLayer.setVisible(false);
}
if (!findClusterLayer && !findVectorLayer) {
map.getView().un('change:resolution', handleZoomEnd);
}
}
// 줌 이벤트 핸들러 등록
map.getView().un('change:resolution', handleZoomEnd);
map.getView().on('change:resolution', handleZoomEnd);
handleZoomEnd();
},
setLayersToCesium: function (ol3dInstance) {
let me = this;
//const { Cesium } = window;
const cesiumScene = ol3dInstance.getCesiumScene(); // Cesium Scene 가져오기
me.cesiumLayers.forEach(cesiumLayer => {
cesiumScene.imageryLayers.remove(cesiumLayer);
});
const layers = GisApp.Module.core.map
.getAllLayers()
.filter(layer => layer.get('type') !== 'base')
.sort((a, b) => {
const orderA = a.getProperties().drawOrdr || 0;
const orderB = b.getProperties().drawOrdr || 0;
return orderA - orderB; // 낮은 값이 먼저 오도록 정렬
});
layers.forEach(layer => {
const properties = layer.getProperties();
let cesiumLayer = null;
if (properties?.lyrTyNm) {
const visible = layer.getVisible();
const lyrTyNm = properties.lyrTyNm;
// OpenLayers에서 레이어의 가시성 확인
const { Cesium } = window;
const wmsUrl = GisApp.GEOSERVER_WMS_URL;
const wmsProvider = new Cesium.WebMapServiceImageryProvider({
url: wmsUrl,
layers: GisApp.Module.layer.storageNm + lyrTyNm, // WMS 레이어 이름
parameters: {
service: 'WMS',
version: '1.1.1',
request: 'GetMap',
format: 'image/png',
transparent: true,
styles: lyrTyNm,
},
});
cesiumLayer = cesiumScene.imageryLayers.addImageryProvider(wmsProvider);
cesiumLayer['properties'] = properties;
me.cesiumLayers.push(cesiumLayer);
if (properties?.visible) {
const zoomLevel = getOpenLayersZoom(cesiumScene);
cesiumLayer.show = zoomLevel >= Number(properties?.minZoom);
} else {
cesiumLayer.show = false;
}
}
});
if (me.cesiumLayers.length > 0) {
cesiumScene._moveEndListener = () => {
const zoomLevel = getOpenLayersZoom(cesiumScene); // 대략적인 줌 계산
me.cesiumLayers.forEach(cesiumLayer => {
const properties = cesiumLayer.properties;
if (properties?.visible) {
console.log('zoomLevel >= minZoom==', properties?.lyrTyNm, zoomLevel >= properties?.minZoom);
cesiumLayer.show = zoomLevel >= Number(properties?.minZoom);
}
});
};
cesiumScene.camera.moveEnd.addEventListener(cesiumScene._moveEndListener);
}
},
};
function getOpenLayersZoom(cesiumScene) {
const cameraHeight = cesiumScene.camera.positionCartographic.height;
const maxResolution = 156543.03392804097; // OpenLayers 기본 해상도 (Google Mercator 기준)
const tileSize = 256; // 기본 타일 크기
return Math.ceil(Math.log2(maxResolution / (cameraHeight / tileSize)));
}
export default GisOlLayer;