Skip to content

Commit

Permalink
Merge pull request #1320 from dbauszus-glx/categorized_fields
Browse files Browse the repository at this point in the history
Allow for fields in categorized theme.
  • Loading branch information
RobAndrewHurst authored Jun 14, 2024
2 parents ed36b49 + 61f5f71 commit 698ab3a
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 30 deletions.
28 changes: 28 additions & 0 deletions lib/layer/styleParser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,27 @@ export default layer => {

styleObject(cat, structuredClone(layer.style.default))
})

// Check validity of categorized theme with multiple fields.
if (theme.type === 'categorized' && Array.isArray(theme.fields)) {

theme.categories.forEach(cat => {

if (!theme.fields.includes(cat.field)) {

console.warn(`Layer: ${layer.key}; Cat ${cat.label} missed valid field.`)
}

// Multiple field cat theme style must be icon.
if (!cat.style.icon) {

console.warn(`Layer: ${layer.key}; Cat ${cat.label} has invalid icon style.`)

cat.style.icon = { type: 'dot' }
}
})

}
}

/**
Expand All @@ -314,8 +335,15 @@ export default layer => {

if (cat.style.icon) {

// Do not merge default style into icon array.
if (Array.isArray(cat.style.icon)) return;

// Do not merge default style into icon with type definition.
if (cat.style.icon.type) return;

// Do not merge default style into svg [type] icons.
if (cat.style.icon.svg) return;

if (defaultStyle.icon && !Array.isArray(defaultStyle.icon)) {

cat.style.icon = {
Expand Down
64 changes: 52 additions & 12 deletions lib/layer/themes/categorized.mjs
Original file line number Diff line number Diff line change
@@ -1,28 +1,68 @@
/**
### mapp.layer.themes.categorized()
This module exports a function that applies a categorized theme to a feature based on a specified field value.
The module exports the categorized function as a mapp.layer.theme.
@module /layer/themes/categorized
*/
*/

/**
@function categorized
@description
The categorized theme method will assign a style from category matching the features properties.
Cluster features may not be styled by a categorized theme.
* @function categorized
* @param {Object} theme - The theme configuration object.
* @param {string} theme.field - The field name used for determining the category.
* @param {Array} theme.categories - An array of category objects.
* @param {Object} feature - The feature object.
* @param {Object} feature.properties - The properties of the feature.
* @param {Array} [feature.properties.features] - An array of clustered features.
* @returns {void}
*/
export default function (theme, feature) {
A theme can have a fields array to apply an icon style array for the individual property fields.
@param {Object} theme The theme configuration object.
@param {string} [theme.field] The feature property field to theme.
@param {array} [theme.fields] A fields array to style multiple feature properties.
@param {Array} theme.categories
@param {Object} feature
@param {Object} feature.properties
@param {Array} [feature.properties.features] A cluster feature will have a features array property.
*/
export default function categorized(theme, feature) {

// The categorized theme requires feature.properties.
if (!feature.properties) return;

// Cluster features can not be styled by category.
if (feature.properties.features?.length > 1) return;

let flat;

// Theme is using multiple fields.
if (Array.isArray(theme.fields)) {

// Map different theme fields
feature.style.icon = theme.fields.map(field => {

// Get the field value from feature properties
const catValue = feature.properties[field]

// Find category matching field and catValue
const cat = theme.categories.find(cat => (cat.value === encodeURIComponent(catValue) || cat.value === catValue) && cat.field === field)

if (!cat) return;

flat ||= Array.isArray(cat.style.icon)

return cat.style.icon

// Filter out empty icon entries from map response.
}).filter(icon => !!icon);

if (flat) {

feature.style.icon = feature.style.icon.flat()
}

return;
}

const catValue = feature.properties[theme.field]

const cat = theme.categories.find(cat => cat.value === encodeURIComponent(catValue) || cat.value === catValue)
Expand Down
38 changes: 20 additions & 18 deletions lib/ui/layers/legends/categorized.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default (layer) => {
let timeout;

// Switch all control
theme.legend.switch = layer.filter && mapp.utils.html`
theme.legend.switch = theme.field && layer.filter && mapp.utils.html`
<div
class="switch-all"
style="grid-column: 1/3;">
Expand Down Expand Up @@ -41,12 +41,14 @@ export default (layer) => {

theme.categories.forEach(cat => {

const field = cat.field || theme.field

// Check whether cat is in current filter.
cat.disabled = layer.filter?.current[theme.field]?.ni?.indexOf(cat.value) >= 0
cat.disabled = layer.filter?.current[field]?.ni?.indexOf(cat.value) >= 0

if (layer.featureFields && theme.distribution === 'count') {

cat.count = layer.featureFields[theme.field]?.[cat.value]
cat.count = layer.featureFields[field]?.[cat.value]

if (!cat.disabled && !cat.count) return;
}
Expand All @@ -70,31 +72,31 @@ export default (layer) => {
if (!layer.filter) return;
const filter = layer.filter.list?.find(f => f.type === 'ni' && f.field === theme.field)
const filter = layer.filter.list?.find(f => f.type === 'ni' && f.field === field)
e.target.classList.toggle('disabled')
// Add cat value to current NI (not in) field filter.
if (e.target.classList.contains('disabled')) {
// Create empty field filter object if non exists.
if (!layer.filter.current[theme.field]) {
layer.filter.current[theme.field] = {}
if (!layer.filter.current[field]) {
layer.filter.current[field] = {}
}
// Create empty NI filter array for field if non exists.
if (!layer.filter.current[theme.field].ni) {
layer.filter.current[theme.field].ni = []
if (!layer.filter.current[field].ni) {
layer.filter.current[field].ni = []
}
// Push cat value into the NI filter array.
layer.filter
.current[theme.field].ni
.current[field].ni
.push(cat.keys || cat.value)
// Flatten the filter in case of arrays filter.
layer.filter
.current[theme.field].ni = layer.filter.current[theme.field].ni.flat()
.current[field].ni = layer.filter.current[field].ni.flat()
// Remove cat value from current NI field filter.
} else {
Expand All @@ -105,25 +107,25 @@ export default (layer) => {
// Splice key out of the NI array.
layer.filter
.current[theme.field].ni
.splice(layer.filter.current[theme.field].ni.indexOf(key), 1)
.current[field].ni
.splice(layer.filter.current[field].ni.indexOf(key), 1)
})
} else {
// Splice value out of the NI array.
layer.filter
.current[theme.field].ni
.splice(layer.filter.current[theme.field].ni.indexOf(cat.value), 1)
.current[field].ni
.splice(layer.filter.current[field].ni.indexOf(cat.value), 1)
}
// Delete current field filter if NI array is empty.
if (!layer.filter.current[theme.field].ni.length) {
delete layer.filter.current[theme.field].ni
if (!Object.keys(layer.filter.current[theme.field]).length) {
delete layer.filter.current[theme.field]
if (!layer.filter.current[field].ni.length) {
delete layer.filter.current[field].ni
if (!Object.keys(layer.filter.current[field]).length) {
delete layer.filter.current[field]
}
}
}
Expand Down

0 comments on commit 698ab3a

Please sign in to comment.