Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-image-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-model-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-query-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-tile-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-vector-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-vectortile-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
2 changes: 1 addition & 1 deletion configure/src/metaconfigs/layer-velocity-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@
"new": true,
"field": "legend",
"name": "Legend From URL",
"description": "A URL to a .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"description": "A URL to a static image or .csv with the following header: 'color,strokecolor,shape,value'. If the path is relative, it will be relative to the mission's directory. This legend is overridden if a legend is also configured below.",
"type": "text",
"width": 12
}
Expand Down
5 changes: 5 additions & 0 deletions src/essence/Basics/Formulae_/Formulae_.js
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,11 @@ var Formulae_ = {
},
csvToJSON: function (csv) {
if (csv == null) return {}

// Ensure csv is a string
if (typeof csv !== 'string') {
return {}
}

var lines = csv.split('\n')
var result = []
Expand Down
90 changes: 89 additions & 1 deletion src/essence/Tools/Legend/LegendTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,56 @@ function refreshLegends() {
layersTool.populateCogScale(L_.layers.data[l].name)
}

// Check if there's a legend URL that points to an image
const legendURL = L_.layers.data[l]?.legend
if (legendURL && typeof legendURL === 'string') {
let isImageUrl = false

// First check for file extensions
const fileExtension = legendURL.toLowerCase().split('.').pop().split('?')[0] // Remove query params
const imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'tiff', 'tif', 'bmp', 'ico', 'avif']

if (imageExtensions.includes(fileExtension)) {
isImageUrl = true
} else if (['csv'].includes(fileExtension)) {
isImageUrl = false
} else {
// If no file extension and not a csv, check for image MIME types in URL parameters (e.g., WMS GetLegendGraphic)
try {
const url = new URL(legendURL)
const formatParam = url.searchParams.get('FORMAT') || url.searchParams.get('format')

if (formatParam) {
const imageMimeTypes = [
'image/png', 'image/jpeg', 'image/jpg', 'image/gif',
'image/svg+xml', 'image/webp', 'image/tiff',
'image/bmp', 'image/ico', 'image/avif'
]

const decodedFormat = decodeURIComponent(formatParam).toLowerCase()
if (imageMimeTypes.includes(decodedFormat)) {
isImageUrl = true
}
}
} catch (e) {
// URL parsing failed, treat as non-image
console.warn('Failed to parse legend URL:', legendURL)
}
}

if (isImageUrl) {
// Handle image legend directly
drawLegends(
LegendTool.tools,
legendURL, // Pass the URL string directly
l,
L_.layers.data[l].display_name,
L_.layers.opacity[l]
)
continue; // Skip the CSV processing below
}
}

if (L_.layers.data[l]?._legend != undefined) {
drawLegends(
LegendTool.tools,
Expand Down Expand Up @@ -216,6 +266,44 @@ function drawLegends(tools, _legend, layerUUID, display_name, opacity) {

let lastContinues = []
let lastShape = ''

// Check if _legend is an image URL (string)
if (typeof _legend === 'string') {
// Render image directly
const imageContainer = c
.append('div')
.attr('class', 'legend-image-container')
.style('display', 'flex')
.style('justify-content', 'center')
.style('margin', '8px')
.style('padding', '8px')

imageContainer
.append('img')
.attr('src', _legend.startsWith('http') ? _legend : L_.missionPath + _legend)
.attr('alt', `Legend for ${display_name}`)
.style('max-width', '100%')
.style('max-height', '220px')
.style('height', 'auto')
.style('background-color', 'white')
.style('border', '1px solid var(--color-i)')
.style('border-radius', '3px')
.style('opacity', opacity)
.on('error', function() {
// Handle image load error
d3.select(this.parentNode)
.append('div')
.style('color', '#ff6b6b')
.style('padding', '8px')
.style('text-align', 'center')
.style('font-size', '12px')
.text('Failed to load legend.')
d3.select(this).remove()
})

return // Exit early since we've rendered the image
}

for (let d in _legend) {
var shape = _legend[d].shapeImage && _legend[d].shapeImage.trim()
? _legend[d].shapeImage : _legend[d].shapeIcon && _legend[d].shapeIcon.trim()
Expand All @@ -234,7 +322,7 @@ function drawLegends(tools, _legend, layerUUID, display_name, opacity) {
})
lastShape = shape
} else {

// finalize discreet and continuous
if (lastContinues.length > 0) {
pushScale(lastContinues)
Expand Down