@obliczeniowo/elementary
Version:
Library made in Angular version 20
330 lines (326 loc) • 14.7 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable } from '@angular/core';
import { ColorRGB, Point2D, TextTransform } from '@obliczeniowo/elementary/classes';
import { TextAlign, DrawingSvgInterface, Rect } from '@obliczeniowo/elementary/drawing';
import { DrawingPdfInterface } from '@obliczeniowo/elementary/drawing-pdf';
import * as i1 from '@obliczeniowo/elementary/files';
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/**
* Service that provide some extra functionality for mapbox library made to use maps in web applications
*/
class OblMapboxService {
file;
/**
* Variable that contain table of layers id and properties that can be override from default one
*/
toOverride = [
{
layerId: 'landcover_crop',
fillOpacity: 0,
fill: new ColorRGB(239, 233, 225),
},
{
layerId: 'landcover_grass',
fillOpacity: 0,
},
{
layerId: 'water-shadow',
fill: new ColorRGB(117, 207, 240),
},
{
layerId: 'road-shields-black',
draw: {
after: [],
before: [
(feature, map, dc) => {
const coords = feature.coords;
let table = [];
if (feature.type === 'MultiLineString') {
const concat = coords.reduce((prev, curr) => [...prev, ...curr], []);
table = concat.map((coord) => map.project(coord));
}
else if (feature.type === 'LineString') {
table = coords.map((coord) => map.project(coord));
}
else if (typeof coords[0] === 'number') {
table.push(map.project(coords));
}
let last = table[0];
const newTable = [];
newTable.push(last);
for (let i = 1; i < table.length; i++) {
const pt = table[i];
if (new Point2D(last.x, last.y)
.subtract(new Point2D(pt.x, pt.y))
.sickLength() > 100000) {
last = table[i];
newTable.push(last);
}
}
newTable.forEach((pt) => {
dc.drawRect(pt.x - 12, pt.y - 10, 24, 14, 1, '#000000', '#ffee00');
this.drawText(feature, dc, map, undefined, pt);
});
},
],
},
},
{
layerId: 'motorway-junction',
draw: {
after: [],
before: [
(feature, map, dc) => {
const coords = feature.coords;
let table = [];
if (feature.type === 'MultiLineString') {
const concat = coords.reduce((prev, curr) => [...prev, ...curr], []);
table = concat.map((coord) => map.project(coord));
}
else if (feature.type === 'LineString') {
table = coords.map((coord) => map.project(coord));
}
else if (typeof coords[0] === 'number') {
table.push(map.project(coords));
}
let last = table[0];
const newTable = [];
newTable.push(last);
for (let i = 1; i < table.length; i++) {
const pt = table[i];
if (new Point2D(last.x, last.y)
.subtract(new Point2D(pt.x, pt.y))
.sickLength() > 100000) {
last = table[i];
newTable.push(last);
}
}
newTable.forEach((pt) => {
dc.drawRect(pt.x - 12, pt.y - 10, 24, 14, 0, '#00aa00', '#007700');
this.drawText(feature, dc, map, undefined, pt);
});
},
],
},
},
{
layerId: 'road-shields-white',
draw: {
after: [],
before: [
(feature, map, dc) => {
const coords = feature.coords;
let table = [];
if (feature.type === 'MultiLineString') {
const concat = coords.reduce((prev, curr) => [...prev, ...curr], []);
table = concat.map((coord) => map.project(coord));
}
else if (feature.type === 'LineString') {
table = coords.map((coord) => map.project(coord));
}
else if (typeof coords[0] === 'number') {
table.push(map.project(coords));
}
let last = table[0];
const newTable = [];
newTable.push(last);
for (let i = 1; i < table.length; i++) {
const pt = table[i];
if (new Point2D(last.x, last.y)
.subtract(new Point2D(pt.x, pt.y))
.sickLength() > 100000) {
last = table[i];
newTable.push(last);
}
}
newTable.forEach((pt) => {
dc.drawRect(pt.x - 12, pt.y - 10, 24, 14, 0, '#ffffff', '#aa0000');
this.drawText(feature, dc, map, undefined, pt);
});
},
],
},
},
];
renderer;
constructor(file, rendererFactory) {
this.file = file;
this.renderer = rendererFactory.createRenderer(null, null);
}
drawText(feature, dc, map, override, point) {
if (feature.layer.layout['text-field']) {
const { x, y } = point || map.project(feature.coords);
const {
// 'font-field': fontField,
'text-field': textField,
// 'text-letter-spacing': textLetterSpacing,
// 'text-max-angle': textMaxAngle,
// 'text-padding': textPadding,
// 'text-rotation-alignment': textRotationAlignment,
'text-size': textSize, 'text-max-width': maxTextWidth, } = feature.layer.layout;
const { 'text-color': textColor, 'text-halo-color': textHaloColor, 'text-halo-width': textHaloWidth, } = feature.layer.paint;
const color = override?.textColor ||
(textColor &&
new ColorRGB(textColor.r * 255, textColor.g * 255, textColor.b * 255)
.getHexColorBase) ||
'#000000';
const haloColor = override?.haloColor || (textHaloColor && textHaloWidth)
? new ColorRGB(textHaloColor.r * 255, textHaloColor.g * 255, textHaloColor.b * 255).getHexColorBase
: undefined;
const coords = new Point2D(x, y);
dc.setFontSize(textSize);
dc.setTextAlign(TextAlign.CENTER);
const text = TextTransform.prepare(textField, feature.properties);
if (maxTextWidth) {
this.drawSplittedText(dc, text, coords, color, haloColor, maxTextWidth);
}
else {
if (haloColor) {
dc.drawText(text, coords, '#ffffff', 0, {
svgOnly: {},
style: {
strokeWidth: 2,
stroke: haloColor,
},
});
}
dc.drawText(text, coords, color, 0);
}
}
}
/**
* Method to export mapbox layers to SVG file
* @param map mapbox.map object
*/
exportToSvg(map, draw) {
const w3 = 'http://www.w3.org/2000/svg';
const svg = this.renderer.createElement('svg', w3);
const { width, height } = map.getCanvas();
svg.setAttribute('width', `${width}`);
svg.setAttribute('height', `${height}`);
svg.setAttribute('xmlns', w3);
const dc = new DrawingSvgInterface(svg, this.renderer);
this.draw(map, dc, draw);
this.file.saveSvgFile(svg.outerHTML.replace(/ /g, ' '), 'map');
}
exportToPdf(map, draw) {
const { width, height } = map.getCanvas();
const dc = new DrawingPdfInterface(new Rect(new Point2D(), new Point2D(width, height)), { orientation: 'landscape' });
this.draw(map, dc, draw);
dc.save('map');
}
draw(map, dc, draw) {
const { width, height } = map.getCanvas();
const features = map
.queryRenderedFeatures()
.map((feature) => ({
coords: feature.geometry.coordinates,
type: feature.geometry.type,
layer: feature.layer,
feature,
properties: feature.properties,
}))
.reverse();
dc.drawRect(0, 0, width, height, 0, '#efe9e1', '#efe9e1');
const drawPolygon = (coords, layer, properties) => {
const polygons = [];
coords.forEach((polygon) => polygons.push(polygon
.map((coords) => map.project(coords))
.map((coord) => new Point2D(coord.x, coord.y))));
const override = this.toOverride.find((item) => item.layerId === layer.id);
const { paint } = layer;
const lineColor = paint?.['line-color'];
const transformedLineColor = (lineColor &&
new ColorRGB(lineColor.r * 255, lineColor.g * 255, lineColor.b * 255)) ||
undefined;
const fillOpacity = override?.fillOpacity ||
(paint?.['fill-opacity'] && paint['fill-opacity']);
const toWhite = (color, opacity) => color +
(255 - color) * (1 - opacity) * ((lineColor && 1 - lineColor) || 1);
const fillColor = paint?.['fill-color'];
const transformedFillColor = override?.fill ||
(fillColor &&
new ColorRGB(toWhite(fillColor.r * 255, fillOpacity), toWhite(fillColor.g * 255, fillOpacity), toWhite(fillColor.b * 255, fillOpacity))) ||
undefined;
const lineWidth = paint?.['line-width'];
const lineOpacity = paint?.['line-opacity'];
if (lineWidth === undefined && transformedFillColor === undefined) {
return;
}
polygons.forEach((polygon) => dc.drawPolyline(polygon, lineWidth || 0, transformedLineColor?.getHexColorBase, {
close: false,
svgOnly: {},
style: {
fill: transformedFillColor?.getHexColorBase,
strokeOpacity: lineOpacity || 1,
},
}));
};
let currentLayer;
features.forEach((feature) => {
if (currentLayer !== feature.layer.id) {
if (currentLayer) {
dc.endGroup();
}
currentLayer = feature.layer.id;
dc.group({
name: currentLayer,
});
}
const override = this.toOverride.find((item) => item.layerId === feature.layer.id);
override?.draw?.before.forEach((cb) => cb(feature, map, dc));
if (feature.type === 'Polygon') {
drawPolygon(feature.coords, feature.layer, feature.properties);
}
else if (feature.type === 'MultiPolygon') {
feature.coords.forEach((coords) => drawPolygon(coords, feature.layer, feature.properties));
}
else if (feature.type === 'LineString') {
drawPolygon([feature.coords], feature.layer, feature.properties);
}
else if (feature.type === 'MultiLineString') {
drawPolygon(feature.coords, feature.layer, feature.properties);
}
else if (feature.layer.layout['text-field']) {
this.drawText(feature, dc, map, override);
}
override?.draw?.after.forEach((cb) => cb(feature, map, dc));
});
if (currentLayer) {
dc.endGroup();
}
if (draw) {
draw(dc);
}
}
drawSplittedText(dc, text, position, color, haloColor, maxLength) {
const strings = TextTransform.splitByLengthAndWords(text, maxLength);
const fontSize = dc.getFontSize() * 1.2;
strings.forEach((text, index) => {
if (haloColor) {
dc.drawText(text, position.add(new Point2D(0, index * fontSize)), color, 0, {
svgOnly: {},
style: {
strokeWidth: 2,
stroke: haloColor,
},
});
}
dc.drawText(text, position.add(new Point2D(0, index * fontSize)), color, 0);
});
return strings.length * fontSize;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: OblMapboxService, deps: [{ token: i1.OblFileService }, { token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: OblMapboxService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: OblMapboxService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: i1.OblFileService }, { type: i0.RendererFactory2 }] });
/**
* Generated bundle index. Do not edit.
*/
export { OblMapboxService };
//# sourceMappingURL=obliczeniowo-elementary-mapbox.mjs.map