Skip to content

Commit

Permalink
improved(display): The display quality of dashed lines has been impro…
Browse files Browse the repository at this point in the history
…ved.

added(display): It is now possible to combine Images/Icons with dashed line patterns by using [strokeDashimage](https://heremaps.github.io/xyz-maps/docs/interfaces/core.linestyle.html#strokedashimage).
added(display): Image/Icon styles now support the use of Icon[Atlases](https://heremaps.github.io/xyz-maps/docs/interfaces/core.imagestyle.html#atlas).

improved 2 pass alpha rendering and improved tile stenciling.
refactored line rendering.
redone icon image resource loading and handling.

Signed-off-by: Tim Deubler <[email protected]>
  • Loading branch information
TerminalTim committed Nov 2, 2023
1 parent 84d99b8 commit bdbdf13
Show file tree
Hide file tree
Showing 26 changed files with 539 additions and 352 deletions.
10 changes: 8 additions & 2 deletions packages/core/src/styles/ImageStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ export interface ImageStyle {
*/
src: string | StyleValueFunction<string> | StyleZoomRange<string>;

/**
* If specified, the Image provided by {@link src} is considered as an IconAtlas/TextureAtlas.
* The clipping region for the image must be defined by x, y, width and height.
*/
atlas?: {x: number, y: number, width: number, height: number};

/**
* Defines the opacity of the style.
* The value must be between 0.0 (fully transparent) and 1.0 (fully opaque).
Expand Down Expand Up @@ -165,7 +171,7 @@ export interface ImageStyle {
repeat?: number | StyleValueFunction<number> | StyleZoomRange<number>;

/**
* Sets the anchor point for styles of type "Circle" used with Line or Polygon geometry.
* Sets the anchor point for styles of type "Image" used with Line or Polygon geometry.
*
* Possible values for Line geometry are "Coordinate" and "Line".
* - "Coordinate": the respective style is displayed at each coordinate of the polyline.
Expand All @@ -175,7 +181,7 @@ export interface ImageStyle {
* - "Center": the center of the bounding box of the polygon.
* - "Centroid": the geometric centroid of the polygon geometry.
*
* @defaultValue For Polygon geometry the default is "Center". For Line geometry the default is "Line""Coordinate".
* @defaultValue For Polygon geometry the default is "Center". For Line geometry the default is "Line".
*/
anchor?: 'Line' | 'Coordinate' | 'Centroid'

Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/styles/LineStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export interface LineStyle {
*/
strokeDasharray?: number[] | StyleValueFunction<number[]> | StyleZoomRange<number[]> | 'none';

/**
* Specifies the URL of the image to be rendered at the positions of the dashes.
* If strokeDashimage is defined, only the first dash and gap definition of the {@link strokeDasharry} pattern is used.
* The dashimage will be colored with the color defined in {@link stroke}.
*/
strokeDashimage?: string

/**
* Define the starting position of a segment of the entire line in %.
* A Segment allows to display and style parts of the entire line individually.
Expand Down
6 changes: 6 additions & 0 deletions packages/display/src/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ import {
import {FlightAnimator} from './animation/FlightAnimator';
import Copyright from './ui/copyright/Copyright';
import Logo from './ui/Logo';
import {fillMap} from './displays/styleTools';
import {toRGB} from './displays/webgl/color';


const project = webMercator;
// const alt2z = webMercator.alt2z;
Expand Down Expand Up @@ -112,6 +115,9 @@ export class Map {
return instances.slice();
}

static fillZoomMap = fillMap;
static toRGB = toRGB;

id: number;

private readonly _el: HTMLElement;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2022 HERE Europe B.V.
* Copyright (C) 2019-2023 HERE Europe B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,7 +42,7 @@ function onLoad() {

img._cbs = null;

if (size > MAX_IMAGE_SIZE) {
if (!img.skipAutoScale && size > MAX_IMAGE_SIZE) {
// rescale image to fit max allowed source size
const scale = MAX_IMAGE_SIZE / size;
img = img._r[img.src] = createScaledImage(img, scale);
Expand All @@ -59,19 +59,42 @@ function onLoad() {
}
}


declare global {
interface HTMLImageElement {
ready: boolean
_cbs: { [key: string]: [(...args) => void, any[]] }
_r: ImgDataMap
}
export const loadImage = (url: string, skipAutoScale?): Promise<HTMLCanvasElement | HTMLImageElement> => new Promise((resolve, reject) => {
let img = new Image();
img.crossOrigin = 'Anonymous';
// img.skipAutoScale = skipAutoScale;
img.addEventListener('load', () => {
let image: HTMLCanvasElement | HTMLImageElement = img;
const size = Math.max(img.width, img.height);
if (!skipAutoScale && size > MAX_IMAGE_SIZE) {
// rescale image to fit max allowed source size
const scale = MAX_IMAGE_SIZE / size;
image = createScaledImage(img, scale);
} else {
// workaround for chrome issue (bug!?) in case of base64 encoded svg image
// is send to texture with incorrect size.
_ctx.drawImage(img, 0, 0);
}
resolve(image);
});
img.addEventListener('error', (err) => reject(err));
img.src = url;
});


interface Image extends HTMLImageElement{
ready: boolean;
_cbs: { [key: string]: [(...args) => void, any[]] };
_r: ImgDataMap;
skipAutoScale: boolean;
}

type ImgDataMap = { [url: string]: HTMLImageElement };

class ImageResourceHandler {
private imgData: ImgDataMap = {};
type ImgDataMap = { [url: string]: Image };

export class ImageLoader {
imgData: ImgDataMap = {};
private promises: any = {};

constructor() {
}
Expand All @@ -84,35 +107,31 @@ class ImageResourceHandler {
return this.imgData[url]?.ready;
};

get(url: string, cb?: (img: HTMLImageElement, ...args) => void, cbID?: string, args?) {
get(url: string, cb?: (img: HTMLImageElement, ...args) => void, cbID?: string, args?, skipAutoScale?: boolean) {
let resources = this.imgData;
if (resources[url] == UNDEF) {
let img = resources[url] = new Image();
let img = resources[url] = new Image() as Image;
img.ready = false;
img._cbs = {};
if (cb) {
img._cbs[cbID] = [cb, args];
}
img._r = resources;

img.crossOrigin = 'Anonymous';
img.skipAutoScale = skipAutoScale;
img.onload = onLoad;
// if (img.decode) {
// img.decode().then(() => onLoad(img)).catch((e) => {});
// }
img.src = url;
} else if (cb) {
// image is still getting loaded..
// image is still getting loaded...
if (!resources[url].ready) {
resources[url]._cbs[cbID] = [cb, args];
} else {
cb(resources[url], args);
}
}

return resources[url];
};
}


export default ImageResourceHandler;
4 changes: 2 additions & 2 deletions packages/display/src/displays/canvas/drawPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/

import {getValue} from '../styleTools';
import ImgResourceHandler from '../ImageResourceHandler';
import {ImageLoader} from '../ImageLoader';

const imgResources = new ImgResourceHandler();
const imgResources = new ImageLoader();
const PI2 = 2 * Math.PI;
const TO_RAD = Math.PI / 180;
const MAX_CANVAS_SIZE = 512;
Expand Down
15 changes: 10 additions & 5 deletions packages/display/src/displays/styleTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ const parseSizeValue = (size: string | number, float: boolean = false): [number,
}
}
if (!float) {
value = <number>value ^ 0; // no "float pixels"
value = Math.round(value as number || 0); // no "float pixels"
// value = <number>value ^ 0; // no "float pixels"
}
return [<number>value, unit];
};
Expand Down Expand Up @@ -496,9 +497,13 @@ const searchLerp = (map, search: number, parseSize: boolean = true) => {
}
return rawVal;
};
const fillMap = (map, searchMap, parseSizeValue: boolean) => {
export const fillMap = (searchMap, parseSizeValue: boolean, map = {}) => {
let fixedZoomMap = {};
for (let zoom in searchMap) {
fixedZoomMap[Math.round(zoom)] = searchMap[zoom];
}
for (let zoom = 1; zoom <= 20; zoom++) {
map[zoom] = searchLerp(searchMap, zoom, parseSizeValue);
map[zoom] = searchLerp(fixedZoomMap, zoom, parseSizeValue);
}
return map;
};
Expand All @@ -512,7 +517,7 @@ export const parseColorMap = (map: { [zoom: string]: RGBA }) => {
};

export const createZoomRangeFunction = (map: StyleZoomRange<RGBA>, /* isFeatureContext?:boolean,*/ parseSizeValue?: boolean) => {
map = fillMap({}, map, parseSizeValue);
map = fillMap(map, parseSizeValue);
// return new Function('f,zoom', `return (${JSON.stringify(map)})[zoom];`);
const range = (feature, zoom: number) => {
return map[zoom ?? feature];
Expand Down Expand Up @@ -544,7 +549,7 @@ const parseStyleGroup = (styleGroup: Style[]) => {
const parseSizeValue = !allowedFloatProperties[name];

style[name] = createZoomRangeFunction(value, parseSizeValue);
// let map = fillMap({}, value, parseSizeValue);
// let map = fillMap(value, parseSizeValue, {});
// style[name] = createZoomRangeFunction(map);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/display/src/displays/webgl/Atlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Atlas {
return this.c.get(key);
};

init() {
private init() {
let {texture, gl, maxSize, d} = this;

if (!this.texture) {
Expand Down
4 changes: 2 additions & 2 deletions packages/display/src/displays/webgl/DashAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class DashAtlas {
this.gl = gl;
}

create(dashArray: DashArray) {
private create(dashArray: DashArray) {
let size =
dashArray.reduce((a, b) => a + b) *
// double size for odd dasharray size to get repeating pattern
Expand Down Expand Up @@ -62,7 +62,7 @@ class DashAtlas {
});
}

get(dashArray: DashArray): SharedTexture {
get(dashArray: DashArray, dashImage?): SharedTexture {
const id = String(dashArray);
let dashData = this.data[id];

Expand Down
58 changes: 29 additions & 29 deletions packages/display/src/displays/webgl/Display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,27 @@ const stencilQuad = (quadkey: string, subQuadkey: string) => {


export type TileBufferData = {
z: number;
tiled: true;
b: GeometryBuffer;
data: {
tile: ScreenTile;
preview?: [string, number, number, number, number, number, number, number, number];
previewTile?: GLTile;
stencils?;
};
z: number;
tiled: true;
b: GeometryBuffer;
data: {
tile: ScreenTile;
preview?: [string, number, number, number, number, number, number, number, number];
previewTile?: GLTile;
stencils?;
};
};

type CustomBufferData = {
z: number;
tiled: boolean;
b: {
zLayer?: number;
zIndex?: number;
pass?: number;
flat: boolean;
};
data: CustomLayer;
z: number;
tiled: boolean;
b: {
zLayer?: number;
zIndex?: number;
pass?: number;
flat: boolean;
};
data: CustomLayer;
};

type BufferData = CustomBufferData | TileBufferData;
Expand Down Expand Up @@ -138,7 +138,7 @@ class WebGlDisplay extends BasicDisplay {

this.rayCaster = new Raycaster(render.screenMat, render.invScreenMat);

this.factory = new FeatureFactory(render.gl, render.icons, this.collision, this.dpr);
this.factory = new FeatureFactory(render.gl, this.collision, this.dpr);
}

private refreshTile(quadkey: string, layerId: string) {
Expand Down Expand Up @@ -206,15 +206,15 @@ class WebGlDisplay extends BasicDisplay {

// from unprojected screen pixels to projected screen pixels
project(x: number, y: number, z: number = 0, sx = this.sx, sy = this.sy): [number, number, number] {
// x -= screenOffsetX;
// y -= screenOffsetY;
// const p = [x, y, 0];
// const s = this.s;
// const p = [x * s, y * s, 0];
// x -= screenOffsetX;
// y -= screenOffsetY;
// const p = [x, y, 0];
// const s = this.s;
// const p = [x * s, y * s, 0];
const p = [x - sx, y - sy, -z];
return transformMat4(p, p, this.render.screenMat);
// transformMat4(p, p, this.render.vPMats);
// return fromClipSpace(p, this.w, this.h);
// transformMat4(p, p, this.render.vPMats);
// return fromClipSpace(p, this.w, this.h);
}

setSize(w: number, h: number) {
Expand All @@ -224,14 +224,14 @@ class WebGlDisplay extends BasicDisplay {
}

setTransform(scale: number, rotZ: number, rotX: number) {
// if (this.s != scale || this.rz != rotZ || this.rx != rotX)
// {
// if (this.s != scale || this.rz != rotZ || this.rx != rotX)
// {
const PI2 = 2 * Math.PI;
rotZ = (rotZ + PI2) % PI2;
this.s = scale;
this.rz = rotZ;
this.rx = rotX;
// }
// }
}

setView(
Expand Down
Loading

0 comments on commit bdbdf13

Please sign in to comment.