Merge branch 'iipbs/common/main' into iipbs/fo/dev

This commit is contained in:
2025-08-26 13:31:21 +09:00
3 changed files with 129 additions and 4 deletions
@@ -19,6 +19,14 @@ const customStyles = {
} as CSSProperties,
};
const fncNumberFormet = (value : string | number) => {
return String(value).includes('.')?
String(parseFloat(value.toString().replace(/,/g, ''))).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
type BarChartProps = {
chartConfig: ChartConfig | undefined;
chartData: any[] | undefined;
@@ -51,7 +59,7 @@ const CustomTooltip = ({
<div key={index} style={{ marginTop: "10px", display: "flex", alignItems: "center", gap: "8px" }}>
{/* 색상 박스 추가 (범례 색상과 동일) */}
<div style={{ width: "10px", height: "10px", backgroundColor: item.color, borderRadius: "50%" }}></div>
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value}`}</span>
<span className="text-[11px] font-bold">{` ${item.name} : ${fncNumberFormet(item.value)}`}</span>
</div>
))}
</div>
@@ -688,6 +688,114 @@ GisOlLayer.prototype = {
});
return source;
<<<<<<< HEAD
=======
},
/**
* 여러 개의 검색 결과를 GeoJSON으로 표시
* @param {string} lyrTyNm 레이어 타입명
* @param {Array|string} featureIds 피처 ID 배열 또는 단일 ID
*/
showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds) {
const layer = this.ensureSearchLayer();
const prev = layer.getSource();
if (prev) prev.clear(true);
const layerCode = GisApp.layerCode;
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
layer.set("gobjTy", matched.gobjTy);
const source = this.getMultiFeatureSourceFromGeoserver(lyrTyNm, featureIds);
const onLoadEnd = async () => {
const feats = source.getFeatures();
if (feats && feats.length > 0) {
const select = GisApp.Module.select;
await select.resetSelectedFeature();
for (const feature of feats) select.selectMultiFeature(feature, true);
const geometries = feats.map(f => f.getGeometry());
if (geometries.length > 0) {
const extent = this.combineExtents(geometries.map(g => g?.getExtent()));
let map = GisApp.Module.core.map;
map.getView().fit(extent, {
duration: 300,
padding: [50, 50, 50, 50],
maxZoom: 16
});
}
} else {
console.warn('[search-layer] GeoJSON에 피처가 없습니다.');
}
source.un('featuresloadend', onLoadEnd);
source.un('featuresloaderror', onLoadError);
};
const onLoadError = (e) => {
console.error('[search-layer] GeoJSON 로드 실패:', e);
source.un('featuresloadend', onLoadEnd);
source.un('featuresloaderror', onLoadError);
};
source.once('featuresloadend', onLoadEnd);
source.once('featuresloaderror', onLoadError);
layer.setSource(source);
source.refresh();
},
/**
* 피쳐 ID로 지오서버에서 검색해서 여러개 피처의 벡터소스를 리턴
* @param {} lyrTyNm
* @param {*} featureId
* @returns
*/
getMultiFeatureSourceFromGeoserver: function (lyrTyNm, featureId) {
const typeName = this.storageNm + lyrTyNm;
const srsName = GisApp.TO_PROJ;
const baseUrl = GisApp.GEOSERVER_URL;
const ids = Array.isArray(featureId) ? featureId : [featureId];
const source = new VectorSource({
format: new GeoJSON(),
url: () => {
const params = new URLSearchParams({
SERVICE: 'WFS',
VERSION: '1.1.0',
REQUEST: 'GetFeature',
TYPENAME: typeName,
SRSNAME: srsName,
OUTPUTFORMAT: 'application/json',
FEATUREID: ids.join(','),
});
return `${baseUrl}?${params.toString()}`;
},
strategy: all,
});
return source;
},
/**
* 여러 extent 배열을 받아 결합된 extent를 반환
* @param {Array<Array<number>>} extents [[minX,minY,maxX,maxY], ...]
* @returns {Array<number>|null}
*/
combineExtents: function (extents) {
if (!extents || extents.length === 0) return null;
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (const e of extents) {
if (!e || e.length < 4) continue;
minX = Math.min(minX, e[0]);
minY = Math.min(minY, e[1]);
maxX = Math.max(maxX, e[2]);
maxY = Math.max(maxY, e[3]);
}
if (!isFinite(minX) || !isFinite(minY) || !isFinite(maxX) || !isFinite(maxY)) return null;
return [minX, minY, maxX, maxY];
>>>>>>> iipbs/common/main
}
};
@@ -30,6 +30,7 @@ type UiTableProps = {
boldArray?: number[];
resetSelection?: boolean;
index?: number;
headButton?: React.ReactNode | undefined;
};
type UiHeadProps = {
@@ -53,6 +54,7 @@ export function UiTable({
boldArray = [],
resetSelection = false,
index = -1,
headButton = undefined,
}: UiTableProps) {
const [cur, setCur] = useState<number>(1);
const [idx, setIdx] = useState<number>(index);
@@ -100,9 +102,16 @@ export function UiTable({
return (
<>
{headName ? (
<div className="card-header">
<h3 className="text-2xl font-medium text-label">{headName}</h3>
</div>
headButton ? (
<div className="card-header flex items-center justify-between">
<h3 className="text-2xl font-medium text-label">{headName}</h3>
<>{headButton}</>
</div>
) : (
<div className="card-header">
<h3 className="text-2xl font-medium text-label">{headName}</h3>
</div>
)
) : (
<></>
)}