573 lines
16 KiB
JavaScript
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;
|