@allmaps/stdlib
Version:
Allmaps Standard Library
213 lines (212 loc) • 6.52 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { parse } from 'svg-parser';
// Assert
export function isSvgCircle(input) {
return input.type === 'circle';
}
export function isSvgLine(input) {
return input.type === 'line';
}
export function isSvgPolyLine(input) {
return input.type === 'polyline';
}
export function isSvgRect(input) {
return input.type === 'rect';
}
export function isSvgPolygon(input) {
return input.type === 'polygon';
}
// Read from string
export function* stringToSvgGeometriesGenerator(svg) {
function* helper(node) {
if ('children' in node) {
for (const childNode of node.children) {
if (typeof childNode !== 'string') {
yield* helper(childNode);
}
}
}
yield node;
}
const parsedSvg = parse(svg);
for (const node of helper(parsedSvg)) {
if ('tagName' in node) {
if (node.tagName !== 'svg' && node.tagName !== 'g') {
const geometry = getNodeSvgGeometry(node);
if (geometry) {
yield geometry;
}
}
}
}
}
function getNodeSvgGeometry(node) {
const tag = node?.tagName?.toLowerCase();
if (tag === 'circle') {
return {
type: 'circle',
coordinates: [
getNodeNumberProperty(node, 'cx'),
getNodeNumberProperty(node, 'cy')
]
};
}
else if (tag === 'line') {
return {
type: 'line',
coordinates: [
[getNodeNumberProperty(node, 'x1'), getNodeNumberProperty(node, 'y1')],
[getNodeNumberProperty(node, 'x2'), getNodeNumberProperty(node, 'y2')]
]
};
}
else if (tag === 'polyline') {
return {
type: 'polyline',
coordinates: getNodePoints(node)
};
}
else if (tag === 'polygon') {
return {
type: 'polygon',
coordinates: getNodePoints(node)
};
}
else if (tag === 'rect') {
return {
type: 'rect',
coordinates: [
[getNodeNumberProperty(node, 'x'), getNodeNumberProperty(node, 'y')],
[
getNodeNumberProperty(node, 'x') +
getNodeNumberProperty(node, 'width'),
getNodeNumberProperty(node, 'y')
],
[
getNodeNumberProperty(node, 'x') +
getNodeNumberProperty(node, 'width'),
getNodeNumberProperty(node, 'y') +
getNodeNumberProperty(node, 'height')
],
[
getNodeNumberProperty(node, 'x'),
getNodeNumberProperty(node, 'y') +
getNodeNumberProperty(node, 'height')
],
[getNodeNumberProperty(node, 'x'), getNodeNumberProperty(node, 'y')]
]
};
}
else {
throw new Error(`Unsupported SVG element: ${tag}`);
}
}
function getNodeNumberProperty(node, prop) {
const value = node?.properties?.[prop];
return Number(value) || 0;
}
function getNodePoints(node) {
const points = node?.properties?.points;
if (points) {
return String(points)
.trim()
.split(/\s+/)
.map((coordStr) => {
const coord = coordStr.split(',').map((numberStr) => Number(numberStr));
return [coord[0], coord[1]];
});
}
return [];
}
function pointsToString(coordinates) {
return coordinates.map((coordinate) => coordinate.join(',')).join(' ');
}
// Convert to String
export function svgGeometriesToSvgString(geometries) {
return `<svg xmlns="http://www.w3.org/2000/svg">
${geometries.map(svgGeometryToString).join('\n')}
</svg>`;
}
export function svgGeometryToString(geometry) {
if (geometry.type === 'circle') {
return elementToString('circle', {
...geometry.attributes,
cx: geometry.coordinates[0],
cy: geometry.coordinates[1]
});
}
else if (geometry.type === 'line') {
return elementToString('line', {
...geometry.attributes,
x1: geometry.coordinates[0][0],
y1: geometry.coordinates[0][1],
x2: geometry.coordinates[1][0],
y2: geometry.coordinates[1][1]
});
}
else if (geometry.type === 'polyline') {
return elementToString('polyline', {
...geometry.attributes,
points: pointsToString(geometry.coordinates)
});
}
else if (geometry.type === 'polygon') {
return elementToString('polygon', {
...geometry.attributes,
points: pointsToString(geometry.coordinates)
});
}
else if (geometry.type === 'rect') {
return elementToString('rect', {
...geometry.attributes,
x: geometry.coordinates[0][0],
y: geometry.coordinates[0][1],
width: geometry.coordinates[1][0] - geometry.coordinates[0][0],
height: geometry.coordinates[2][1] - geometry.coordinates[0][1]
});
}
else {
throw new Error('Unknown SVG element');
}
}
function elementToString(tag, attributes) {
const attributeStrings = Object.entries(attributes).map(([key, value]) => `${key}="${value}"`);
return `<${tag} ${attributeStrings.join(' ')} />`;
}
export function mapToResourceMaskSvgPolygon(map) {
return {
type: 'polygon',
attributes: {
'data-image-id': encodeURIComponent(map.resource.id)
},
coordinates: map.resourceMask
};
}
/**
* Convert a SVG Geometry to Geometry
*
* Note: Multi-geometries are not supported
*
* @param svgGeometry - SVG Geometry
* @returns Geometry
*/
export function svgGeometryToGeometry(svgGeometry) {
if (isSvgCircle(svgGeometry)) {
return svgGeometry.coordinates;
}
else if (isSvgLine(svgGeometry)) {
return svgGeometry.coordinates;
}
else if (isSvgPolyLine(svgGeometry)) {
return svgGeometry.coordinates;
}
else if (isSvgRect(svgGeometry)) {
return [svgGeometry.coordinates];
}
else if (isSvgPolygon(svgGeometry)) {
return [svgGeometry.coordinates];
}
else {
throw new Error(`Unsupported SVG geometry`);
}
}