From e3c422222b5741c20af5ce5541beb9a425a9033a Mon Sep 17 00:00:00 2001 From: Binabh Date: Wed, 24 Jun 2026 20:34:28 +0545 Subject: [PATCH] feat: add external graphic handling and icon symbolizer creation for point symbolizers --- .../styleeditor/WMSJsonLegendIcon.jsx | 34 ++++++++++- .../__tests__/WMSJsonLegendIcon-test.jsx | 61 ++++++++++++++++++- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/web/client/components/styleeditor/WMSJsonLegendIcon.jsx b/web/client/components/styleeditor/WMSJsonLegendIcon.jsx index 8bfef20f56..28d1bdb225 100644 --- a/web/client/components/styleeditor/WMSJsonLegendIcon.jsx +++ b/web/client/components/styleeditor/WMSJsonLegendIcon.jsx @@ -226,6 +226,35 @@ function createSymbolizerForPoint(pointSymbolizer) { return symbolizer; } +export function getExternalGraphicUrl(graphic) { + if (!graphic) return null; + const externalGraphic = graphic['external-graphic']; + if (typeof externalGraphic === 'string') { + return externalGraphic; + } + return graphic['external-graphic-url'] + || externalGraphic?.url + || externalGraphic?.href + || null; +} + +export function getPointExternalGraphicUrl(pointSymbolizer) { + return pointSymbolizer?.graphics + ?.map(getExternalGraphicUrl) + .find(url => !!url); +} + +export function createIconSymbolizerForPoint(pointSymbolizer) { + return { + ...pointSymbolizer, + image: pointSymbolizer?.image + || getPointExternalGraphicUrl(pointSymbolizer) + || pointSymbolizer?.url, + rotate: pointSymbolizer?.rotate || pointSymbolizer?.rotation || 0, + opacity: pointSymbolizer?.opacity || 1 + }; +} + function WMSJsonLegendIcon({ rule }) { @@ -273,10 +302,11 @@ function WMSJsonLegendIcon({ // Handle Point symbolizers (individual icons, not stacked) pointSymbolizers.forEach((pointSym) => { let graphicSymbolyzer = pointSym?.graphics?.find(gr => Object.keys(gr).includes('mark')); - const graphicType = graphicSymbolyzer ? 'Mark' : 'Icon'; + const externalGraphicUrl = getPointExternalGraphicUrl(pointSym); + const graphicType = graphicSymbolyzer && !externalGraphicUrl ? 'Mark' : 'Icon'; const processedSymbolizer = graphicType === 'Mark' ? createSymbolizerForPoint(pointSym) - : pointSym; + : createIconSymbolizerForPoint(pointSym); if (graphicType === 'Mark' && graphicSymbolyzer) { processedSymbolizer.wellKnownName = graphicSymbolyzer.mark.charAt(0).toUpperCase() + graphicSymbolyzer.mark.slice(1); } diff --git a/web/client/components/styleeditor/__tests__/WMSJsonLegendIcon-test.jsx b/web/client/components/styleeditor/__tests__/WMSJsonLegendIcon-test.jsx index cca91a49b8..fdc72ec4d2 100644 --- a/web/client/components/styleeditor/__tests__/WMSJsonLegendIcon-test.jsx +++ b/web/client/components/styleeditor/__tests__/WMSJsonLegendIcon-test.jsx @@ -9,9 +9,40 @@ import React from 'react'; import ReactDOM from 'react-dom'; import expect from 'expect'; -import WMSJsonLegendIcon from '../WMSJsonLegendIcon'; +import WMSJsonLegendIcon, { + createIconSymbolizerForPoint, + getExternalGraphicUrl, + getPointExternalGraphicUrl +} from '../WMSJsonLegendIcon'; describe('WMSJsonLegendIcon', () => { + const imageSrc = 'styles/tree.png'; + const pointUrl = 'styles/point-url.png'; + + const createExternalGraphicLegend = () => ({ + Legend: [{ + layerName: 'tasmania_cities', + title: 'Tasmania cities', + rules: [{ + title: 'City Point', + symbolizers: [{ + Point: { + title: 'title', + 'abstract': 'abstract', + url: pointUrl, + size: '6', + opacity: '1.0', + rotation: '0.0', + graphics: [{ + 'external-graphic-url': imageSrc, + 'external-graphic-type': 'image/png' + }] + } + }] + }] + }] + }); + beforeEach((done) => { document.body.innerHTML = '
'; setTimeout(done); @@ -23,6 +54,34 @@ describe('WMSJsonLegendIcon', () => { setTimeout(done); }); + it('should parse external graphic url from graphic object', () => { + expect(getExternalGraphicUrl({ + 'external-graphic-url': imageSrc, + 'external-graphic-type': 'image/png' + })).toBe(imageSrc); + }); + it('should parse point external graphic url from GeoServer legend json', () => { + const legend = createExternalGraphicLegend(); + const pointSymbolizer = legend.Legend[0].rules[0].symbolizers[0].Point; + expect(getPointExternalGraphicUrl(pointSymbolizer)).toBe(imageSrc); + }); + it('should create icon symbolizer from external graphic url', () => { + const legend = createExternalGraphicLegend(); + const pointSymbolizer = legend.Legend[0].rules[0].symbolizers[0].Point; + expect(createIconSymbolizerForPoint(pointSymbolizer)).toInclude({ + image: imageSrc, + rotate: '0.0', + opacity: '1.0' + }); + }); + it('should create icon symbolizer from point url when external graphic is not available', () => { + expect(createIconSymbolizerForPoint({ + url: imageSrc, + graphics: [] + })).toInclude({ + image: imageSrc + }); + }); it('should render polygon icon ', () => { const symbolizers = [{"Polygon": { "fill": "#4DFF4D",