@orbat-mapper/tactical-map-sheet 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -298,6 +298,14 @@ Each layer has a stable `id`, ordered `{ graphic }[]`, and optional shared portr
298
298
  color, line/dash/cap/join, and label size/clamp. A graphic is a control-measure `ControlMeasure`, a
299
299
  `MapSheetPointSymbol`, or a `MapSheetCustomSymbol`.
300
300
 
301
+ Control-measure label sizes and Text sizes authored in metres retain their existing Web Mercator
302
+ construction-unit meaning. The renderer converts the sheet's true-ground scale at each graphic's
303
+ control-point bounding-box midpoint latitude for both label layout and final SVG/PDF text sizing.
304
+ For example, a `labelSize` of 280 at 60° latitude prints at 14 CSS pixels when the sheet represents
305
+ 10 true-ground metres per CSS pixel. Ordinary ground labels use the layer's clamp band; ground Text
306
+ graphics instead scale freely and hide above their `maxSizePixels` threshold. Screen sizes remain
307
+ fixed CSS reference pixels. Authored graphics are never rewritten by export.
308
+
301
309
  Point symbols carry `id`, `sidc`, position, rotation in radians, pixel- or metre-based size, optional
302
310
  amplifiers/modifiers, and renderer style/options. `MapSheetPointSymbolCapability.render(symbol)`
303
311
  must return a `ControlledVectorSymbol`: stable resource ID, controlled-subset SVG, complete width and
@@ -1,8 +1,8 @@
1
1
  import GeographicLibMgrs from "geographiclib-mgrs";
2
2
  import PolarStereographic from "geographiclib-mgrs/src/polar-stereographic.mjs";
3
3
  import opentype from "opentype.js";
4
- import { DEFAULT_LABEL_HEIGHT_CSS_PIXELS, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_PORTRAYAL, DEFAULT_STROKE_DASH_CSS_PIXELS, DEFAULT_STROKE_WIDTH_CSS_PIXELS, getFillPatternDefinition, renderControlMeasure, resolveSymbolColor } from "@orbat-mapper/control-measures";
5
- import { effectivePointSymbolPixelSize, resolvePointSymbolOffset, scalePointSymbolLayout } from "@orbat-mapper/tactical-draw";
4
+ import { DEFAULT_LABEL_HEIGHT_CSS_PIXELS, DEFAULT_LABEL_SIZE_CLAMP_CSS_PIXELS, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_PORTRAYAL, DEFAULT_STROKE_DASH_CSS_PIXELS, DEFAULT_STROKE_WIDTH_CSS_PIXELS, getFillPatternDefinition, renderControlMeasure, resolveConstructionMetersPerCssPixel, resolveSymbolColor } from "@orbat-mapper/control-measures";
5
+ import { effectivePointSymbolPixelSize, resolvePointSymbolOffset, resolveTextSizePixels, scalePointSymbolLayout } from "@orbat-mapper/tactical-draw";
6
6
  import { SaxesParser } from "saxes";
7
7
  //#region src/projection/geographic-lib.ts
8
8
  const backend = GeographicLibMgrs.UTMUPS;
@@ -1453,20 +1453,18 @@ function pathFromPositions(positions, closed, properties, portrayal, context) {
1453
1453
  ...fillPatternId ? { fillPatternId } : {}
1454
1454
  };
1455
1455
  }
1456
- function resolveTextSizeCssPixels(properties, portrayal, groundMetersPerCssPixel) {
1457
- if (properties.textSizePixels !== void 0) return properties.textSizePixels;
1458
- if (properties.textSizeMeters !== void 0) {
1459
- const requested = properties.textSizeMeters / groundMetersPerCssPixel;
1460
- if (properties.textMaxSizePixels !== void 0 && requested > properties.textMaxSizePixels) return;
1461
- const min = Math.min(portrayal.labelSizeClampCssPixels.min, portrayal.labelSizeClampCssPixels.max);
1462
- const max = Math.max(portrayal.labelSizeClampCssPixels.min, portrayal.labelSizeClampCssPixels.max);
1463
- return Math.min(max, Math.max(min, requested));
1464
- }
1465
- return portrayal.labelHeightCssPixels;
1456
+ function resolveTextSizeCssPixels(properties, portrayal, constructionMetersPerCssPixel) {
1457
+ if (properties.textSizePixels === void 0 && properties.textSizeMeters === void 0) return portrayal.labelHeightCssPixels;
1458
+ const { min, max } = portrayal.labelSizeClampCssPixels;
1459
+ return resolveTextSizePixels({
1460
+ ...properties,
1461
+ textSizeMinPixels: Math.min(min, max),
1462
+ textSizeMaxPixels: Math.max(min, max)
1463
+ }, constructionMetersPerCssPixel) ?? void 0;
1466
1464
  }
1467
- function textFromFeature(feature, portrayal, context) {
1465
+ function textFromFeature(feature, portrayal, context, constructionMetersPerCssPixel) {
1468
1466
  if (feature.geometry.type !== "Point" || feature.properties.text === void 0) return;
1469
- const fontSizeCssPixels = resolveTextSizeCssPixels(feature.properties, portrayal, context.groundMetersPerCssPixel);
1467
+ const fontSizeCssPixels = resolveTextSizeCssPixels(feature.properties, portrayal, constructionMetersPerCssPixel);
1470
1468
  if (fontSizeCssPixels === void 0) return;
1471
1469
  requirePositiveFinite(fontSizeCssPixels, "Text size");
1472
1470
  const style = feature.properties.textStyle ?? "regular";
@@ -1498,8 +1496,8 @@ function textFromFeature(feature, portrayal, context) {
1498
1496
  } : {}
1499
1497
  };
1500
1498
  }
1501
- function elementsFromGeometry(feature, portrayal, context) {
1502
- const text = textFromFeature(feature, portrayal, context);
1499
+ function elementsFromGeometry(feature, portrayal, context, constructionMetersPerCssPixel) {
1500
+ const text = textFromFeature(feature, portrayal, context, constructionMetersPerCssPixel);
1503
1501
  if (text) return [text];
1504
1502
  if (feature.geometry.type === "Point" && feature.properties.text !== void 0) return [];
1505
1503
  const path = (positions, closed = false) => pathFromPositions(positions, closed, feature.properties, portrayal, context);
@@ -1693,13 +1691,14 @@ function renderGraphic(layerId, authored, portrayal, context) {
1693
1691
  layerId,
1694
1692
  graphicId: graphic.id
1695
1693
  }] };
1694
+ const constructionMetersPerCssPixel = resolveConstructionMetersPerCssPixel(graphic.controlPoints, context.groundMetersPerCssPixel);
1696
1695
  const elements = rendered.features.flatMap((feature, index) => {
1697
1696
  try {
1698
1697
  const geometry = featureOutOfRange[index] ? clipGeometryToBounds(feature.geometry, frameBounds) : feature.geometry;
1699
1698
  return geometry ? elementsFromGeometry({
1700
1699
  ...feature,
1701
1700
  geometry
1702
- }, portrayal, context) : [];
1701
+ }, portrayal, context, constructionMetersPerCssPixel) : [];
1703
1702
  } catch (error) {
1704
1703
  throw new Error(`Cannot render Graphic "${graphic.id}": ${String(error)}`, { cause: error });
1705
1704
  }
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as geographicToMgrs, c as normalizeMgrsReference, d as snapGeographicToMgrs, f as snapProjectedToMgrs, i as resolveMapSheetRasterPlan, l as parseMgrsReference, o as mgrsToGeographic, r as mapSheetRasterPointToGeographic, s as mgrsToProjected, t as createTacticalMapSheet, u as projectedToMgrs } from "./create-tactical-map-sheet-Dm9IiFHP.mjs";
1
+ import { a as geographicToMgrs, c as normalizeMgrsReference, d as snapGeographicToMgrs, f as snapProjectedToMgrs, i as resolveMapSheetRasterPlan, l as parseMgrsReference, o as mgrsToGeographic, r as mapSheetRasterPointToGeographic, s as mgrsToProjected, t as createTacticalMapSheet, u as projectedToMgrs } from "./create-tactical-map-sheet-DOsMPUJp.mjs";
2
2
  //#region src/paper.ts
3
3
  const A0 = Object.freeze({
4
4
  widthMm: 841,
package/dist/pdf.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as openSansMiddleBaselineOffset, g as openSansCentralBaselineOffset, h as POINTS_PER_MILLIMETRE, m as MILLIMETRES_PER_CSS_PIXEL, n as createTacticalMapSheetDocument, p as formatDecimal } from "./create-tactical-map-sheet-Dm9IiFHP.mjs";
1
+ import { _ as openSansMiddleBaselineOffset, g as openSansCentralBaselineOffset, h as POINTS_PER_MILLIMETRE, m as MILLIMETRES_PER_CSS_PIXEL, n as createTacticalMapSheetDocument, p as formatDecimal } from "./create-tactical-map-sheet-DOsMPUJp.mjs";
2
2
  import { jsPDF } from "jspdf";
3
3
  //#region src/pdf-transparency.ts
4
4
  const FORM_DICTIONARY_MARKER = new TextEncoder().encode("/Subtype /Form\n/BBox");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbat-mapper/tactical-map-sheet",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Scale-accurate projected vector tactical map-sheet renderer.",
5
5
  "license": "MIT",
6
6
  "author": "Orbat Mapper",
@@ -30,8 +30,8 @@
30
30
  "jspdf": "4.2.1",
31
31
  "opentype.js": "2.0.0",
32
32
  "saxes": "6.0.0",
33
- "@orbat-mapper/control-measures": "^0.25.0",
34
- "@orbat-mapper/tactical-draw": "^0.8.1"
33
+ "@orbat-mapper/control-measures": "^0.26.0",
34
+ "@orbat-mapper/tactical-draw": "^0.8.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@napi-rs/canvas": "1.0.7",
@@ -45,7 +45,7 @@
45
45
  "typescript": "~5.9.3",
46
46
  "vite": "^8.0.16",
47
47
  "vitest": "^4.1.8",
48
- "@orbat-mapper/point-symbols": "0.2.1"
48
+ "@orbat-mapper/point-symbols": "0.2.2"
49
49
  },
50
50
  "inlinedDependencies": {
51
51
  "@types/geojson": "7946.0.16"