Skip to content

Commit

Permalink
Handle and test scaledenom at each level
Browse files Browse the repository at this point in the history
  • Loading branch information
ger-benjamin committed May 18, 2020
1 parent ac7274a commit 852b226
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 20 deletions.
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ module.exports = {
"Boolean",
"boolean",
"Undefined",
"undefined"
],
"id-match": "error",
"max-len": [
Expand Down
47 changes: 47 additions & 0 deletions data/mapfiles/point_scale.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
LAYER
NAME "point_scale"
TYPE POINT
DATA "../shapes/points.shp"
EXTENT -180 -90 180 90
METADATA
"wms_title" "point_scale"
END
MAXSCALEDENOM 160000
CLASS
NAME "Max scale from layer (priority)"
STYLE
MAXSCALEDENOM 7000
SYMBOL "circle"
COLOR 0 0 0
SIZE 24
END
END
CLASS
NAME "Scales from class"
MAXSCALEDENOM 320000
MINSCALEDENOM 160000
STYLE
SYMBOL "circle"
COLOR 0 0 0
SIZE 12
END
END
CLASS
NAME "Max scale from layer"
MINSCALEDENOM 320000
STYLE
SYMBOL "circle"
COLOR 0 0 0
SIZE 6
END
END
CLASS
NAME "Scale 0 and max scale from layer (priority)"
MAXSCALEDENOM 0
MINSCALEDENOM 0
STYLE
SYMBOL "circle"
SIZE 20
END
END
END
1 change: 1 addition & 0 deletions data/mapfiles/point_simple_point_label.map
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ LAYER
METADATA
"wms_title" "point_simple_point_label"
END
MINSCALEDENOM 5000
CLASS
NAME "Test label"
LABEL
Expand Down
55 changes: 55 additions & 0 deletions data/styles/point_scale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Style } from 'geostyler-style';

const pointStyle: Style = {
name: 'point_scale',
rules: [{
name: 'Max scale from layer (priority)',
scaleDenominator: {
max: 160000,
},
symbolizers: [{
kind: 'Mark',
wellKnownName: 'Circle',
color: '#000000',
radius: 12,
}],
}, {
name: 'Scales from class',
scaleDenominator: {
max: 320000,
min: 160000,
},
symbolizers: [{
kind: 'Mark',
wellKnownName: 'Circle',
color: '#000000',
radius: 6,
}],
}, {
name: 'Max scale from layer',
scaleDenominator: {
max: 160000,
min: 320000,
},
symbolizers: [{
kind: 'Mark',
wellKnownName: 'Circle',
color: '#000000',
radius: 3,
}],
}, {
name: 'Scale 0 and max scale from layer (priority)',
scaleDenominator: {
max: 160000,
min: 0,
},
symbolizers: [{
kind: 'Mark',
wellKnownName: 'Circle',
visibility: false,
radius: 10,
}],
}],
};

export default pointStyle;
3 changes: 3 additions & 0 deletions data/styles/point_simple_point_label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const pointStyle: Style = {
rules: [
{
name: 'Test label',
scaleDenominator: {
max: 5000,
},
symbolizers: [{
kind: 'Text',
label: 'Nisosa',
Expand Down
15 changes: 12 additions & 3 deletions src/MapfileStyleParser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import point_simple_point from '../data/styles/point_simple_point';
import point_simple_point_label from '../data/styles/point_simple_point_label';

import point_simple_point_many_classes_filters from '../data/styles/point_simple_point_many_classes_filters';
import point_scale from '../data/styles/point_scale';
import point_st_sample_point_style_tags from '../data/styles/point_st_sample_point_style_tags';
import point_st_sample_point_style_tags_single_filter_list from '../data/styles/point_st_sample_point_style_tags_single_filter_list';
import point_st_sample_point_style_tags_single_filter_regex from '../data/styles/point_st_sample_point_style_tags_single_filter_regex';
Expand Down Expand Up @@ -65,12 +66,20 @@ describe('MapfileStyleParser implements StyleParser', () => {
expect(geoStylerStyle).toEqual(raster_simple_raster);
});

it('can read a simple MapFile Label', async () => {
it('can read a Point MapFile with scales', async () => {
expect.assertions(2);
const mapfile = fs.readFileSync( './data/mapfiles/point_simple_point_label.map', 'utf8');
const mapfile = fs.readFileSync( './data/mapfiles/point_scale.map', 'utf8');
const geoStylerStyle = await styleParser.readStyle(mapfile);
expect(geoStylerStyle).toBeDefined();
expect(geoStylerStyle).toEqual(point_simple_point_label);
expect(geoStylerStyle).toEqual(point_scale);
});

it('can read a PointSymbolizer with style tags', async () => {
expect.assertions(2);
const mapfile = fs.readFileSync( './data/mapfiles/point_st_sample_point_style_tags.map', 'utf8');
const geoStylerStyle = await styleParser.readStyle(mapfile);
expect(geoStylerStyle).toBeDefined();
expect(geoStylerStyle).toEqual(point_st_sample_point_style_tags);
});

// TODO: fixme there are no Square, Triangle and other WellKlnownName equivalents in Mapfiles
Expand Down
103 changes: 87 additions & 16 deletions src/MapfileStyleParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,23 +264,72 @@ export class MapfileStyleParser implements StyleParser {
}

/**
* Get the GeoStyler-Style ScaleDenominator from an Mapfile CLASS.
*
* @param {object} mapfileClass The Mapfile Class
* @return {ScaleDenominator} The GeoStyler-Style ScaleDenominator
* Get the GeoStyler-Style ScaleDenominator from an Mapfile element (layer, class or style).
* Returns null if there is no defined ScaleDenominator in the element.
* @param {object} mapfileElement The Mapfile layer, class or style.
* @return {ScaleDenominator} The GeoStyler-Style ScaleDenominator or null.
*/
getScaleDenominatorFromClass(mapfileClass: any): ScaleDenominator | undefined {
getScaleDenominator(mapfileElement: any): ScaleDenominator | null {
const scaleDenominator = {} as ScaleDenominator;

scaleDenominator.min = parseFloat(mapfileClass.minscaledenom);
const maxScale = parseFloat(mapfileElement.maxscaledenom);
if (!isNaN(maxScale)) {
scaleDenominator.max = maxScale;
}

scaleDenominator.max = parseFloat(mapfileClass.maxscaledenom);
const minScale = parseFloat(mapfileElement.minscaledenom);
if (!isNaN(minScale)) {
scaleDenominator.min = minScale;
}

if (scaleDenominator.min || scaleDenominator.max) {
return (scaleDenominator.max !== undefined || scaleDenominator.min !== undefined) ? scaleDenominator : null;
}

/**
* Update the given scaleDenominator with the scaleDenominator from the given mapfileElement;
*
* @param {object} mapfileElement The Mapfile layer, class or style.
* @param {ScaleDenominator} scaleDenominator The scaleDenominator to try to override.
* @return {scaleDenominator} A ScaleDenominator value or null.
*/
updateScaleDenominator(mapfileElement: any,
scaleDenominator: ScaleDenominator | null): ScaleDenominator | null {
const elementScaleDenominator = this.getScaleDenominator(mapfileElement);

// No child scale - can't update, return the base scaleDenominator.
if (!elementScaleDenominator) {
return scaleDenominator;
} else {
return;
}

// No parent scale - return the child scaleDenominator.
if (!scaleDenominator) {
return elementScaleDenominator;
}

const mergedScale = {} as ScaleDenominator;
// Take max scale only if it's defined and take parent max scale only if it's bigger
// (more restrictive) than the children one.
if (scaleDenominator.max === undefined && elementScaleDenominator.max !== undefined) {
mergedScale.max = elementScaleDenominator.max;
} else if (scaleDenominator.max !== undefined && elementScaleDenominator.max === undefined) {
mergedScale.max = scaleDenominator.max;
} else if (scaleDenominator.max !== undefined && elementScaleDenominator.max !== undefined) {
mergedScale.max = (scaleDenominator.max || 0) > (elementScaleDenominator.max || 0) ?
scaleDenominator.max : elementScaleDenominator.max;
}

// Take mix scale only if it's defined and take parent min scale only if it's lesser
// (more restrictive) than the children one.
if (scaleDenominator.min === undefined && elementScaleDenominator.min !== undefined) {
mergedScale.min = elementScaleDenominator.min;
} else if (scaleDenominator.min !== undefined && elementScaleDenominator.min === undefined) {
mergedScale.min = scaleDenominator.min;
} else if (scaleDenominator.min !== undefined && elementScaleDenominator.min !== undefined) {
mergedScale.min = (scaleDenominator.min || 0) < (elementScaleDenominator.min || 0) ?
scaleDenominator.min : elementScaleDenominator.min;
}

return mergedScale;
}

/**
Expand Down Expand Up @@ -589,7 +638,7 @@ export class MapfileStyleParser implements StyleParser {
getSymbolizersFromClass(
mapfileClass: any,
mapfileLayerType: string,
mapfileLayerLabelItem: string
mapfileLayerLabelItem: string,
): Symbolizer[] {
const symbolizers = [] as Symbolizer[];
// Mapfile STYLE
Expand Down Expand Up @@ -619,10 +668,14 @@ export class MapfileStyleParser implements StyleParser {
}
const baseSymbolizer: any = this.getBaseSymbolizerFromStyle(styleParameters);
symbolizers.push(Object.assign(symbolizer, baseSymbolizer));

this.checkWarnDropRule('MINSCALEDENOM', 'STYLE', styleParameters.minscaledenom);
this.checkWarnDropRule('MAXSCALEDENOM', 'STYLE', styleParameters.maxscaledenom);
});
}

// Mapfile LABEL
// FIXME it can have multiple label in a class.
if (mapfileClass.label) {
mapfileLayerLabelItem = mapfileClass.text ? mapfileClass.text : mapfileLayerLabelItem;
mapfileClass.label.text = mapfileClass.label.text ? mapfileClass.label.text : mapfileLayerLabelItem;
Expand All @@ -631,6 +684,9 @@ export class MapfileStyleParser implements StyleParser {
this.getBaseSymbolizerFromStyle(mapfileClass.label)
);
symbolizers.push(symbolizer);

this.checkWarnDropRule('MINSCALEDENOM', 'LABEL', mapfileClass.label.minscaledenom);
this.checkWarnDropRule('MAXSCALEDENOM', 'LABEL', mapfileClass.label.maxscaledenom);
}

return symbolizers;
Expand All @@ -651,29 +707,32 @@ export class MapfileStyleParser implements StyleParser {
const mapfileLayerType: string = mapfileLayer.type;
const mapfileLayerClassItem: string = mapfileLayer.classitem;
const mapfileLayerLabelItem: string = mapfileLayer.labelitem;
const layerScaleDenominator = this.getScaleDenominator(mapfileLayer);

mapfileLayer.class.forEach((mapfileClass: any) => {
const name = mapfileLayer.group ? `${mapfileLayer.group}.${mapfileClass.name}` : mapfileClass.name;
const filter = this.getFilterFromMapfileClass(mapfileClass, mapfileLayerClassItem);
const scaleDenominator = this.getScaleDenominatorFromClass(mapfileClass);
const classScaleDenominator = this.updateScaleDenominator(mapfileClass, layerScaleDenominator);
const symbolizers = this.getSymbolizersFromClass(
mapfileClass,
mapfileLayerType,
mapfileLayerLabelItem
mapfileLayerLabelItem,
);
const name = mapfileLayer.group ? `${mapfileLayer.group}.${mapfileClass.name}` : mapfileClass.name;

const rule = { name } as Rule;
if (filter) {
rule.filter = filter;
}
if (scaleDenominator) {
rule.scaleDenominator = scaleDenominator;
if (classScaleDenominator) {
rule.scaleDenominator = classScaleDenominator;
}
if (symbolizers) {
rule.symbolizers = symbolizers;
}
rules.push(rule);
});
this.checkWarnDropRule('LABELMINSCALEDENOM', 'LAYER', mapfileLayer.Labelminscaledenom);
this.checkWarnDropRule('LABELMAXSCALEDENOM', 'LAYER', mapfileLayer.Labelmaxscaledenom);
});

return rules;
Expand Down Expand Up @@ -770,6 +829,18 @@ export class MapfileStyleParser implements StyleParser {
}
return nestedExpressions;
}

/**
* Generic error message for not supported rules.
* @param {string} notSupported The not supported avoided element. Printed in the warning message.
* @param {string} mapfileParentElement The mapfile parent element name. Printed in the warning message.
* @param {any} mapfileElement the value to test if it existing.
*/
private checkWarnDropRule(notSupported: string, mapfileParentElement: string, mapfileElement: any): void {
if (mapfileElement !== undefined) {
console.warn(`SLD doesn't support ${notSupported} operator in ${mapfileParentElement}. This rule is dropped.`);
}
}
}

export default MapfileStyleParser;

0 comments on commit 852b226

Please sign in to comment.