Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(StyleOptions): move StyleOptions from Style to its own file #2439

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions src/Core/Feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as THREE from 'three';
import Extent from 'Core/Geographic/Extent';
import Coordinates from 'Core/Geographic/Coordinates';
import CRS from 'Core/Geographic/Crs';
import Style from 'Core/Style';
import StyleOptions from 'Core/StyleOptions';

function defaultExtent(crs) {
return new Extent(crs, Infinity, -Infinity, Infinity, -Infinity);
Expand Down Expand Up @@ -251,7 +251,7 @@ class Feature {
}
this._pos = 0;
this._pushValues = (this.size === 3 ? push3DValues : push2DValues).bind(this);
this.style = Style.setFromProperties;
this.style = StyleOptions.setFromProperties;
}
/**
* Instance a new {@link FeatureGeometry} and push in {@link Feature}.
Expand Down
246 changes: 0 additions & 246 deletions src/Core/Style.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { FEATURE_TYPES } from 'Core/Feature';
import Cache from 'Core/Scheduler/Cache';
import Fetcher from 'Provider/Fetcher';
import * as mapbox from '@mapbox/mapbox-gl-style-spec';
import { Color } from 'three';
import { deltaE } from 'Renderer/Color';
import Coordinates from 'Core/Geographic/Coordinates';
Expand All @@ -13,8 +11,6 @@ const cacheStyle = new Cache();
const matrix = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix();
const canvas = document.createElement('canvas');

const inv255 = 1 / 255;

function baseAltitudeDefault(properties, ctx) {
return ctx?.coordinates?.z || 0;
}
Expand Down Expand Up @@ -47,37 +43,6 @@ export function readExpression(property, ctx) {
return property;
}

function rgba2rgb(orig) {
if (!orig) {
return {};
} else if (orig.stops || orig.expression) {
return { color: orig };
} else if (typeof orig == 'string') {
const result = orig.match(/(?:((hsl|rgb)a? *\(([\d.%]+(?:deg|g?rad|turn)?)[ ,]*([\d.%]+)[ ,]*([\d.%]+)[ ,/]*([\d.%]*)\))|(#((?:[\d\w]{3}){1,2})([\d\w]{1,2})?))/i);
if (result === null) {
return { color: orig, opacity: 1.0 };
} else if (result[7]) {
let opacity = 1.0;
if (result[9]) {
opacity = parseInt(result[9].length == 1 ? `${result[9]}${result[9]}` : result[9], 16) * inv255;
}
return { color: `#${result[8]}`, opacity };
} else if (result[1]) {
return { color: `${result[2]}(${result[3]},${result[4]},${result[5]})`, opacity: (result[6] ? Number(result[6]) : 1.0) };
}
}
}

function readVectorProperty(property, options) {
if (property != undefined) {
if (mapbox.expression.isExpression(property)) {
return mapbox.expression.createExpression(property, options).value;
} else {
return property;
}
}
}

async function loadImage(source) {
let promise = cacheStyle.get(source, 'null');
if (!promise) {
Expand Down Expand Up @@ -700,217 +665,6 @@ class Style {
this.context = ctx;
}

/**
* set Style from (geojson-like) properties.
* @param {Object} properties (geojson-like) properties.
* @param {FeatureContext} featCtx the context of the feature
*
* @returns {StyleOptions} containing all properties for itowns.Style
*/
static setFromProperties(properties, featCtx) {
const type = featCtx.type;
const style = {};
if (type === FEATURE_TYPES.POINT) {
const point = {
...(properties.fill !== undefined && { color: properties.fill }),
...(properties['fill-opacity'] !== undefined && { opacity: properties['fill-opacity'] }),
...(properties.stroke !== undefined && { line: properties.stroke }),
...(properties.radius !== undefined && { radius: properties.radius }),
};
if (Object.keys(point).length) {
style.point = point;
}
const text = {
...(properties['label-color'] !== undefined && { color: properties['label-color'] }),
...(properties['label-opacity'] !== undefined && { opacity: properties['label-opacity'] }),
...(properties['label-size'] !== undefined && { size: properties['label-size'] }),
};
if (Object.keys(point).length) {
style.text = text;
}
const icon = {
...(properties.icon !== undefined && { source: properties.icon }),
...(properties['icon-scale'] !== undefined && { size: properties['icon-scale'] }),
...(properties['icon-opacity'] !== undefined && { opacity: properties['icon-opacity'] }),
...(properties['icon-color'] !== undefined && { color: properties['icon-color'] }),
};
if (Object.keys(icon).length) {
style.icon = icon;
}
} else {
const stroke = {
...(properties.stroke !== undefined && { color: properties.stroke }),
...(properties['stroke-width'] !== undefined && { width: properties['stroke-width'] }),
...(properties['stroke-opacity'] !== undefined && { opacity: properties['stroke-opacity'] }),
};
if (Object.keys(stroke).length) {
style.stroke = stroke;
}
if (type !== FEATURE_TYPES.LINE) {
const fill = {
...(properties.fill !== undefined && { color: properties.fill }),
...(properties['fill-opacity'] !== undefined && { opacity: properties['fill-opacity'] }),
};
if (Object.keys(fill).length) {
style.fill = fill;
}
}
}
return style;
}

/**
* set Style from vector tile layer properties.
* @param {Object} layer vector tile layer.
* @param {Object} sprites vector tile layer.
* @param {Number} [order=0]
* @param {Boolean} [symbolToCircle=false]
*
* @returns {StyleOptions} containing all properties for itowns.Style
*/
static setFromVectorTileLayer(layer, sprites, order = 0, symbolToCircle = false) {
const style = {
fill: {},
stroke: {},
point: {},
text: {},
icon: {},
};

layer.layout = layer.layout || {};
layer.paint = layer.paint || {};

style.order = order;

if (layer.type === 'fill') {
const { color, opacity } = rgba2rgb(readVectorProperty(layer.paint['fill-color'] || layer.paint['fill-pattern'], { type: 'color' }));
style.fill.color = color;
style.fill.opacity = readVectorProperty(layer.paint['fill-opacity']) || opacity;
if (layer.paint['fill-pattern']) {
try {
style.fill.pattern = {
id: layer.paint['fill-pattern'],
source: sprites.source,
cropValues: sprites[layer.paint['fill-pattern']],
};
} catch (err) {
err.message = `VTlayer '${layer.id}': argument sprites must not be null when using layer.paint['fill-pattern']`;
throw err;
}
}

if (layer.paint['fill-outline-color']) {
const { color, opacity } = rgba2rgb(readVectorProperty(layer.paint['fill-outline-color'], { type: 'color' }));
style.stroke.color = color;
style.stroke.opacity = opacity;
style.stroke.width = 1.0;
style.stroke.dasharray = [];
}
} else if (layer.type === 'line') {
const prepare = readVectorProperty(layer.paint['line-color'], { type: 'color' });
const { color, opacity } = rgba2rgb(prepare);
style.stroke.dasharray = readVectorProperty(layer.paint['line-dasharray']);
style.stroke.color = color;
style.stroke.lineCap = layer.layout['line-cap'];
style.stroke.width = readVectorProperty(layer.paint['line-width']);
style.stroke.opacity = readVectorProperty(layer.paint['line-opacity']) || opacity;
} else if (layer.type === 'circle' || symbolToCircle) {
const { color, opacity } = rgba2rgb(readVectorProperty(layer.paint['circle-color'], { type: 'color' }));
style.point.color = color;
style.point.opacity = opacity;
style.point.radius = readVectorProperty(layer.paint['circle-radius']);
} else if (layer.type === 'symbol') {
// overlapping order
style.text.zOrder = readVectorProperty(layer.layout['symbol-z-order']);
if (style.text.zOrder == 'auto') {
style.text.zOrder = readVectorProperty(layer.layout['symbol-sort-key']) || 'Y';
} else if (style.text.zOrder == 'viewport-y') {
style.text.zOrder = 'Y';
} else if (style.text.zOrder == 'source') {
style.text.zOrder = 0;
}

// position
style.text.anchor = readVectorProperty(layer.layout['text-anchor']);
style.text.offset = readVectorProperty(layer.layout['text-offset']);
style.text.padding = readVectorProperty(layer.layout['text-padding']);
style.text.size = readVectorProperty(layer.layout['text-size']);
style.text.placement = readVectorProperty(layer.layout['symbol-placement']);
style.text.rotation = readVectorProperty(layer.layout['text-rotation-alignment']);

// content
style.text.field = readVectorProperty(layer.layout['text-field']);
style.text.wrap = readVectorProperty(layer.layout['text-max-width']);
style.text.spacing = readVectorProperty(layer.layout['text-letter-spacing']);
style.text.transform = readVectorProperty(layer.layout['text-transform']);
style.text.justify = readVectorProperty(layer.layout['text-justify']);

// appearance
const { color, opacity } = rgba2rgb(readVectorProperty(layer.paint['text-color'], { type: 'color' }));
style.text.color = color;
style.text.opacity = readVectorProperty(layer.paint['text-opacity']) || (opacity !== undefined && opacity);

style.text.font = readVectorProperty(layer.layout['text-font']);
const haloColor = readVectorProperty(layer.paint['text-halo-color'], { type: 'color' });
if (haloColor) {
style.text.haloColor = haloColor.color || haloColor;
style.text.haloWidth = readVectorProperty(layer.paint['text-halo-width']);
style.text.haloBlur = readVectorProperty(layer.paint['text-halo-blur']);
}

// additional icon
const iconImg = readVectorProperty(layer.layout['icon-image']);
if (iconImg) {
try {
style.icon.id = iconImg;
if (iconImg.stops) {
const iconCropValue = {
...(iconImg.base !== undefined && { base: iconImg.base }),
stops: iconImg.stops.map((stop) => {
let cropValues = sprites[stop[1]];
if (stop[1].includes('{')) {
cropValues = function _(p) {
const id = stop[1].replace(/\{(.+?)\}/g, (a, b) => (p[b] || '')).trim();
cropValues = sprites[id];
return sprites[id];
};
}
return [stop[0], cropValues];
}),
};
style.icon.cropValues = iconCropValue;
} else {
style.icon.cropValues = sprites[iconImg];
if (iconImg[0].includes('{')) {
style.icon.cropValues = function _(p) {
const id = iconImg.replace(/\{(.+?)\}/g, (a, b) => (p[b] || '')).trim();
style.icon.cropValues = sprites[id];
return sprites[id];
};
}
}
style.icon.source = sprites.source;
style.icon.size = readVectorProperty(layer.layout['icon-size']) || 1;
const { color, opacity } = rgba2rgb(readVectorProperty(layer.paint['icon-color'], { type: 'color' }));
style.icon.color = color;
style.icon.opacity = readVectorProperty(layer.paint['icon-opacity']) || (opacity !== undefined && opacity);
} catch (err) {
err.message = `VTlayer '${layer.id}': argument sprites must not be null when using layer.layout['icon-image']`;
throw err;
}
}
}
// VectorTileSet: by default minZoom = 0 and maxZoom = 24
// https://docs.mapbox.com/style-spec/reference/layers/#maxzoom and #minzoom
// Should be move to layer properties, when (if) one mapBox layer will be considered as several itowns layers.
// issue https://github.com/iTowns/itowns/issues/2153 (last point)
style.zoom = {
min: layer.minzoom || 0,
max: layer.maxzoom || 24,
};
return style;
}

/**
* Applies the style.fill to a polygon of the texture canvas.
* @param {CanvasRenderingContext2D} txtrCtx The Context 2D of the texture canvas.
Expand Down
Loading
Loading