@geogirafe/lib-geoportal 1.1.0-dev.2456762594 → 1.1.0-dev.2463680603

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.
Files changed (85) hide show
  1. package/api/apigeogirafeapp.d.ts +3 -2
  2. package/api/apigeogirafeapp.js +14 -2
  3. package/buildtools/lib-inline.js +13 -1
  4. package/buildtools/tools.js +10 -7
  5. package/components/about/component.d.ts +2 -2
  6. package/components/alignnorthbutton-mobile/component.d.ts +2 -2
  7. package/components/auth/component.d.ts +2 -2
  8. package/components/auth-mobile/component.d.ts +2 -2
  9. package/components/basemap/component.d.ts +2 -2
  10. package/components/contact/component.d.ts +2 -2
  11. package/components/context-menu/custom-context-menu/component.d.ts +2 -2
  12. package/components/context-menu/default-context-menu/component.d.ts +2 -2
  13. package/components/coordinate/component.d.ts +2 -2
  14. package/components/cross-section/cross-section-settings/component.d.ts +2 -2
  15. package/components/cross-section/cross-section-viewer/component.d.ts +2 -2
  16. package/components/displaymenubutton-mobile/component.d.ts +2 -2
  17. package/components/displayselectorbutton-mobile/component.d.ts +2 -2
  18. package/components/drawing/component-mobile.d.ts +2 -2
  19. package/components/drawing/component.d.ts +2 -2
  20. package/components/drawing/fixed-dimension/component.d.ts +2 -2
  21. package/components/drawing/olDrawing.d.ts +7 -0
  22. package/components/drawing/olDrawing.js +132 -25
  23. package/components/drawing/shapeConstraints.d.ts +11 -0
  24. package/components/drawing/shapeConstraints.js +64 -0
  25. package/components/drawing-container-mobile/component.d.ts +2 -2
  26. package/components/edit/component.d.ts +2 -2
  27. package/components/edit/editform/component.d.ts +2 -2
  28. package/components/extlayers/component.d.ts +2 -2
  29. package/components/geolocation-mobile/component.d.ts +2 -2
  30. package/components/getdirections/component.d.ts +2 -2
  31. package/components/help/component.d.ts +2 -2
  32. package/components/infobox/component.d.ts +2 -2
  33. package/components/infowindow/component.d.ts +2 -2
  34. package/components/layout/component.d.ts +2 -2
  35. package/components/lr-panel/component.d.ts +2 -2
  36. package/components/map/component.d.ts +2 -2
  37. package/components/menu-buttons-mobile/MenuMobile3dButton.d.ts +2 -2
  38. package/components/menu-buttons-mobile/MenuMobileDrawingButton.d.ts +2 -2
  39. package/components/menu-buttons-mobile/MenuMobileOfflineButton.d.ts +2 -2
  40. package/components/menu-mobile/component.d.ts +2 -2
  41. package/components/menubutton/component.d.ts +2 -2
  42. package/components/modals/component.d.ts +2 -2
  43. package/components/navigation/component.d.ts +2 -2
  44. package/components/news/news-button/component.d.ts +2 -2
  45. package/components/news/news-panel/component.d.ts +2 -2
  46. package/components/offline/component.d.ts +2 -2
  47. package/components/print/component.d.ts +2 -2
  48. package/components/prototypebanner/component.d.ts +2 -2
  49. package/components/querybuilder/component.d.ts +2 -2
  50. package/components/scale/component.d.ts +2 -2
  51. package/components/search/component.d.ts +2 -2
  52. package/components/search-mobile/component.d.ts +2 -2
  53. package/components/selectiongrid/component.d.ts +2 -2
  54. package/components/selectionpanel-mobile/component.d.ts +2 -2
  55. package/components/selectiontool/component.d.ts +2 -2
  56. package/components/selectionwindow/component.d.ts +2 -2
  57. package/components/share/component.d.ts +2 -2
  58. package/components/swipe-up-panel-mobile/component.d.ts +2 -2
  59. package/components/themes/component.d.ts +2 -2
  60. package/components/themes-mobile/group/component.d.ts +2 -2
  61. package/components/themes-mobile/layer/component.d.ts +2 -2
  62. package/components/themes-mobile/layer-selected/component.d.ts +2 -2
  63. package/components/themes-mobile/theme/component.d.ts +2 -2
  64. package/components/themes-mobile/themes-select/component.d.ts +2 -2
  65. package/components/timerestriction/component.d.ts +2 -2
  66. package/components/timerestriction/timepicker/component.d.ts +2 -2
  67. package/components/timerestriction/timeslider/component.d.ts +2 -2
  68. package/components/treeview/treeviewgroup/component.d.ts +2 -2
  69. package/components/treeview/treeviewitem/component.d.ts +2 -2
  70. package/components/treeview/treeviewroot/component.d.ts +2 -2
  71. package/components/treeview/treeviewtheme/component.d.ts +2 -2
  72. package/components/userpreferences/component.d.ts +2 -2
  73. package/components/videorecord/component.d.ts +2 -2
  74. package/package.json +4 -1
  75. package/templates/api.html +2 -2
  76. package/templates/public/about.json +1 -1
  77. package/templates/public/api/marker-blue.png +0 -0
  78. package/templates/public/api/marker-green.png +0 -0
  79. package/templates/public/api/parking.png +0 -0
  80. package/templates/public/config.api.json +17 -0
  81. package/templates/public/config.json +77 -0
  82. package/templates/public/config.mobile.json +9 -0
  83. package/templates/vite.config.js +3 -2
  84. package/api/api.d.ts +0 -1
  85. package/api/api.js +0 -3
@@ -1,8 +1,9 @@
1
1
  import GirafeHTMLElement from '../base/GirafeHTMLElement.js';
2
2
  import IGirafeContext from '../tools/context/icontext.js';
3
3
  export default class GeoGirafeApi extends GirafeHTMLElement {
4
- protected templateUrl: string;
5
- protected styleUrls: string[];
4
+ protected templateUrl: string | null;
5
+ protected styleUrls: string[] | null;
6
+ template: () => import("uhtml").Hole;
6
7
  private isInitialized;
7
8
  constructor();
8
9
  private get config();
@@ -1,3 +1,4 @@
1
+ import { html as uHtml } from 'uhtml';
1
2
  // SPDX-License-Identifier: Apache-2.0
2
3
  import proj4 from 'proj4';
3
4
  import GirafeHTMLElement from '../base/GirafeHTMLElement.js';
@@ -10,8 +11,16 @@ import MapCustomContextMenuComponent from '../components/context-menu/custom-con
10
11
  import SearchComponent from '../components/search/component.js';
11
12
  import SelectionWindowComponent from '../components/selectionwindow/component.js';
12
13
  export default class GeoGirafeApi extends GirafeHTMLElement {
13
- templateUrl = './template.html';
14
- styleUrls = ['../styles/common.css', './style.css'];
14
+ templateUrl = null;
15
+ styleUrls = null;
16
+ template = () => {
17
+ return uHtml `<style>
18
+ *{font-family:Arial,sans-serif}.hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad2);background-color:var(--text-color-grad2);color:var(--bkg-color)}.gg-button:disabled{color:gray;cursor:not-allowed;background-color:#d3d3d3;border:none}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{color:gray;cursor:not-allowed;background-color:#d3d3d3}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover,.gg-select:hover,.gg-input:hover,.gg-textarea:hover,.gg-icon-button:hover{background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}
19
+ </style><style>
20
+ :host{position:relative}girafe-map{width:100%;height:100%;position:relative}girafe-basemap{display:block;position:absolute;bottom:.5rem;left:.5rem}girafe-search{width:50%;height:2.25rem;display:block;position:absolute;top:.5rem;left:.5rem;box-shadow:0 1px 4px #0000004d}girafe-selection-window{z-index:100;width:400px;height:240px;position:absolute;bottom:1rem;right:1rem}
21
+ </style>
22
+ <girafe-map></girafe-map>`;
23
+ };
15
24
  isInitialized = false;
16
25
  constructor() {
17
26
  super('geogirafe-api');
@@ -277,7 +286,10 @@ export default class GeoGirafeApi extends GirafeHTMLElement {
277
286
  }
278
287
  injectConfigMetaTags() {
279
288
  const location = new URL(import.meta.url);
289
+ // The 2 following lines are a small hack to make the api interface work in both context
290
+ // (debugging in vite + production app)
280
291
  location.pathname = location.pathname.replace('/src/api', '');
292
+ location.pathname = location.pathname.replace('/node_modules/.vite/deps', '');
281
293
  const origin = `${location.origin}${location.pathname.substring(0, location.pathname.lastIndexOf('/'))}`;
282
294
  const baseConfigUrl = `${origin}/config.json`;
283
295
  const apiConfigUrl = `${origin}/config.api.json`;
@@ -4,7 +4,19 @@ import path from 'path';
4
4
  import { findFilesRecursive, inlineTemplate } from './tools.js';
5
5
 
6
6
  async function main() {
7
- const fileList = findFilesRecursive(path.resolve('src', 'components'), ['.ts', '.js']);
7
+ // Inline template for components
8
+ let fileList = findFilesRecursive(path.resolve('src', 'components'), ['.ts', '.js']);
9
+ for (const filepath of fileList) {
10
+ console.info(`Integrating inline HTML for file ${filepath}`);
11
+ const newCodeMapObject = await inlineTemplate(filepath);
12
+ const newFilePath = filepath.replace('src', path.join('dist', 'lib-src-inline'));
13
+
14
+ fs.mkdirSync(path.dirname(newFilePath), { recursive: true });
15
+ fs.writeFileSync(newFilePath, newCodeMapObject.code, 'utf-8');
16
+ }
17
+
18
+ // Inline template for the API
19
+ fileList = findFilesRecursive(path.resolve('src', 'api'), ['.ts', '.js']);
8
20
  for (const filepath of fileList) {
9
21
  console.info(`Integrating inline HTML for file ${filepath}`);
10
22
  const newCodeMapObject = await inlineTemplate(filepath);
@@ -57,9 +57,12 @@ export function deleteFiles(patterns) {
57
57
  }
58
58
  }
59
59
 
60
- export function copy(filename, sourceDir, targetDir) {
60
+ export function copy(filename, sourceDir, targetDir, targetFilename = null) {
61
+ if (targetFilename === null) {
62
+ targetFilename = filename;
63
+ }
61
64
  const source = path.join(sourceDir, filename);
62
- const target = path.join(targetDir, filename);
65
+ const target = path.join(targetDir, targetFilename);
63
66
 
64
67
  if (fs.existsSync(source)) {
65
68
  // First create destination directory
@@ -111,8 +114,8 @@ async function getHtmlCode(currentFilename, relativeHtmlPath, styleCode) {
111
114
  }
112
115
  });
113
116
  htmlCode = `
114
- templateUrl = null;
115
- styleUrls = null;
117
+ protected override templateUrl: string | null = null;
118
+ protected override styleUrls: string[] | null = null;
116
119
  template = () => { return uHtml\`${styleCode}\n${htmlCode}\`; }`;
117
120
  return htmlCode;
118
121
  } catch (error) {
@@ -167,9 +170,9 @@ function isStringCommented(line) {
167
170
  }
168
171
 
169
172
  // Regex definitions
170
- export const styleRegex = /(?:override\s+)?styleUrl *= *['"](.*)['"] *;?/g;
171
- export const stylesRegex = /(?:override\s+)?styleUrls *= *\[([\s\S]*?)\] *;?/gs;
172
- export const htmlRegex = /(?:override\s+)?templateUrl *= *['"](.*)['"] *;?/g;
173
+ export const styleRegex = /(?:(?:public|private|protected)\s+)?(?:override\s+)?styleUrl *= *['"](.*)['"] *;?/g;
174
+ export const stylesRegex = /(?:(?:public|private|protected)\s+)?(?:override\s+)?styleUrls *= *\[([\s\S]*?)\] *;?/gs;
175
+ export const htmlRegex = /(?:(?:public|private|protected)\s+)?(?:override\s+)?templateUrl *= *['"](.*)['"] *;?/g;
173
176
 
174
177
  export async function inlineTemplate(filename) {
175
178
  // Read the file
@@ -1,8 +1,8 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  import IGirafePanel from '../../tools/state/igirafepanel.js';
3
3
  declare class AboutComponent extends GirafeHTMLElement implements IGirafePanel {
4
- templateUrl: null;
5
- styleUrls: null;
4
+ protected templateUrl: string | null;
5
+ protected styleUrls: string[] | null;
6
6
  template: () => import("uhtml").Hole;
7
7
  isPanelVisible: boolean;
8
8
  panelTitle: string;
@@ -1,7 +1,7 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  export default class AlignNorthButtonMobile extends GirafeHTMLElement {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  constructor();
7
7
  protected connectedCallback(): void;
@@ -1,7 +1,7 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  export default class OauthComponent extends GirafeHTMLElement {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  userIconUrl?: string;
7
7
  constructor(name?: string);
@@ -1,7 +1,7 @@
1
1
  import OauthComponent from '../auth/component.js';
2
2
  export default class MobileOauthComponent extends OauthComponent {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  constructor();
7
7
  }
@@ -1,8 +1,8 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  import Basemap from '../../models/basemaps/basemap.js';
3
3
  declare class BasemapComponent extends GirafeHTMLElement {
4
- templateUrl: null;
5
- styleUrls: null;
4
+ protected templateUrl: string | null;
5
+ protected styleUrls: string[] | null;
6
6
  template: () => import("uhtml").Hole;
7
7
  constructor();
8
8
  changeBasemap(basemap: Basemap): void;
@@ -1,8 +1,8 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  import IGirafePanel from '../../tools/state/igirafepanel.js';
3
3
  declare class ContactComponent extends GirafeHTMLElement implements IGirafePanel {
4
- templateUrl: null;
5
- styleUrls: null;
4
+ protected templateUrl: string | null;
5
+ protected styleUrls: string[] | null;
6
6
  template: () => import("uhtml").Hole;
7
7
  isPanelVisible: boolean;
8
8
  panelTitle: string;
@@ -1,7 +1,7 @@
1
1
  import GirafeHTMLElement from '../../../base/GirafeHTMLElement.js';
2
2
  declare class MapCustomContextMenuComponent extends GirafeHTMLElement {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  private get map();
7
7
  private static contextMenuOverlay;
@@ -3,8 +3,8 @@ import { printCoordinate } from '../../../tools/geometrytools.js';
3
3
  import { MapContextMenuState } from './contextmenustate.js';
4
4
  import MapContextMenuManager from './contextmenumanager.js';
5
5
  declare class MapDefaultContextMenuComponent extends GirafeHTMLElement {
6
- templateUrl: null;
7
- styleUrls: null;
6
+ protected templateUrl: string | null;
7
+ protected styleUrls: string[] | null;
8
8
  template: () => import("uhtml").Hole;
9
9
  private get map();
10
10
  private readonly eventsCallbacks;
@@ -1,8 +1,8 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  declare class CoordinateComponent extends GirafeHTMLElement {
3
3
  #private;
4
- templateUrl: null;
5
- styleUrls: null;
4
+ protected templateUrl: string | null;
5
+ protected styleUrls: string[] | null;
6
6
  template: () => import("uhtml").Hole;
7
7
  east: string | null;
8
8
  north: string | null;
@@ -11,8 +11,8 @@ import { Draw, Modify } from 'ol/interaction.js';
11
11
  import OL3Parser from 'jsts/org/locationtech/jts/io/OL3Parser.js';
12
12
  import IGirafePanel from '../../../tools/state/igirafepanel.js';
13
13
  declare class CrossSectionSettingsComponent extends GirafeHTMLElement implements IGirafePanel {
14
- templateUrl: null;
15
- styleUrls: null;
14
+ protected templateUrl: string | null;
15
+ protected styleUrls: string[] | null;
16
16
  template: () => import("uhtml").Hole;
17
17
  crossSectionState: CrossSectionState;
18
18
  private readonly eventsCallbacks;
@@ -4,8 +4,8 @@ import { CrossSectionState } from './../crosssectionstate.js';
4
4
  import { Scatterplot } from '../scatterplot.js';
5
5
  import { PytreeManager } from '../pytreemanager.js';
6
6
  declare class CrossSectionViewComponent extends GirafeResizableElement {
7
- templateUrl: null;
8
- styleUrls: null;
7
+ protected templateUrl: string | null;
8
+ protected styleUrls: string[] | null;
9
9
  template: () => import("uhtml").Hole;
10
10
  crossSectionState: CrossSectionState;
11
11
  private readonly eventsCallbacks;
@@ -1,7 +1,7 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  export default class DisplayMenuButtonMobile extends GirafeHTMLElement {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  constructor();
7
7
  protected connectedCallback(): void;
@@ -1,7 +1,7 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  export default class DisplaySelectorButtonMobile extends GirafeHTMLElement {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  constructor();
7
7
  protected connectedCallback(): void;
@@ -1,7 +1,7 @@
1
1
  import DrawingComponent from './component.js';
2
2
  export default class DrawingComponentMobile extends DrawingComponent {
3
- templateUrl: null;
4
- styleUrls: null;
3
+ protected templateUrl: string | null;
4
+ protected styleUrls: string[] | null;
5
5
  template: () => import("uhtml").Hole;
6
6
  constructor();
7
7
  protected connectedCallback(): void;
@@ -7,8 +7,8 @@ import GirafeColorPicker from '../../tools/utils/girafecolorpicker.js';
7
7
  import LayerDrawing from '../../models/layers/layerdrawing.js';
8
8
  import IGirafePanel from '../../tools/state/igirafepanel.js';
9
9
  export default class DrawingComponent extends GirafeHTMLElement implements IGirafePanel {
10
- templateUrl: null;
11
- styleUrls: null;
10
+ protected templateUrl: string | null;
11
+ protected styleUrls: string[] | null;
12
12
  template: () => import("uhtml").Hole;
13
13
  isPanelVisible: boolean;
14
14
  panelTitle: string;
@@ -10,8 +10,8 @@ export type DimensionUnit = {
10
10
  };
11
11
  declare class FixedDimensionComponent extends GirafeHTMLElement {
12
12
  static readonly observedAttributes: string[];
13
- templateUrl: null;
14
- styleUrls: null;
13
+ protected templateUrl: string | null;
14
+ protected styleUrls: string[] | null;
15
15
  template: () => import("uhtml").Hole;
16
16
  checkedIcon: string;
17
17
  notCheckedIcon: string;
@@ -33,6 +33,7 @@ export default class OlDrawing {
33
33
  lastClosestFeature: Feature | null;
34
34
  translate: Translate | null;
35
35
  transform: Transform | null;
36
+ private readonly modifyFeatureChangeListeners;
36
37
  defaultStyle: StyleFunction | undefined;
37
38
  private readonly updateGeometryInState;
38
39
  private get state();
@@ -114,6 +115,12 @@ export default class OlDrawing {
114
115
  getStyle(dFeature: DrawingFeature, olFeature: Feature<Geometry>): Style[];
115
116
  private removeDrawInteraction;
116
117
  private removeModifyInteraction;
118
+ private getDrawingShapeType;
119
+ private isSquareOrRectangle;
120
+ private shouldAllowVertexInsertionAtEvent;
121
+ private getHoveredModifiableFeatureAtPixel;
122
+ private applyScaleRotateOnModifyStyle;
123
+ private clearModifyFeatureChangeListeners;
117
124
  private removeSnapInteraction;
118
125
  private removeEditContextMenu;
119
126
  registerInteractions(): void;
@@ -10,11 +10,12 @@ import VectorLayer from 'ol/layer/Vector.js';
10
10
  import GeoJSON from 'ol/format/GeoJSON.js';
11
11
  import { getPointResolution } from 'ol/proj.js';
12
12
  import { always, never, primaryAction } from 'ol/events/condition.js';
13
- import { ensurePolygonIsProperlyClosed, getAreaOfPolygon, getDistance, getHalfPoint, getLabelStyle, getRadiusDataForCircle } from '../../tools/utils/olutils.js';
13
+ import { ensurePolygonIsProperlyClosed, getAreaOfPolygon, getDistance, getHalfPoint, getLabelStyle, getRadiusDataForCircle, unByKeyAll } from '../../tools/utils/olutils.js';
14
14
  import { ContextMenu, EntryInteractionType } from '../map/tools/contextmenu.js';
15
15
  import { formatCoordinates } from '../../tools/geometrytools.js';
16
16
  import { v4 as uuidv4 } from 'uuid';
17
17
  import { isAlternateMouseClick, isPrimaryPointerAction } from '../../tools/state/userinteractionevent.js';
18
+ import { calculateCenterAndMinRadius, createScaledAndRotatedGeometry, getGeometryForRendering, shouldAllowVertexInsertionForShape } from './shapeConstraints.js';
18
19
  import Transform from 'ol-ext/interaction/Transform.js';
19
20
  function getLineStroke(strokeType, lineWidth) {
20
21
  switch (strokeType) {
@@ -68,6 +69,7 @@ export default class OlDrawing {
68
69
  lastClosestFeature = null;
69
70
  translate = null;
70
71
  transform = null;
72
+ modifyFeatureChangeListeners = [];
71
73
  defaultStyle;
72
74
  updateGeometryInState = (olFeature) => {
73
75
  const idx = this.drawingState.features.findIndex((f) => f.id === olFeature.getId());
@@ -127,6 +129,7 @@ export default class OlDrawing {
127
129
  }
128
130
  addModifyInteraction() {
129
131
  this.removeModifyInteraction();
132
+ const vertexStyle = new DrawingFeature(DrawingShape.Point, this.drawingState, this.config.drawing).getVertexStyle(true);
130
133
  this.modify = new Modify({
131
134
  features: this.modifiableFeatures,
132
135
  // Feature editing is triggered by: 1) primary action = click or touch, 2) alternate mouse click = remove vertex
@@ -135,34 +138,64 @@ export default class OlDrawing {
135
138
  isAlternateMouseClick(e)) &&
136
139
  this.canExecute('map.modify'),
137
140
  deleteCondition: never,
138
- insertVertexCondition: primaryAction,
139
- style: new DrawingFeature(DrawingShape.Point, this.drawingState, this.config.drawing).getVertexStyle(true),
141
+ // For square/rectangle, we disable vertex insertion only on the hovered shape.
142
+ insertVertexCondition: (e) => primaryAction(e) && this.shouldAllowVertexInsertionAtEvent(e),
143
+ style: (feature) => {
144
+ this.applyScaleRotateOnModifyStyle(feature);
145
+ return vertexStyle;
146
+ },
140
147
  snapToPointer: true,
141
148
  pixelTolerance: this.map.pixelTolerance
142
149
  });
143
150
  this.map.olMap.addInteraction(this.modify);
144
151
  this.modify.on('modifystart', (e) => {
152
+ this.clearModifyFeatureChangeListeners();
145
153
  e.features.forEach((olFeature) => {
146
- olFeature.on('change', (e) => {
147
- const geometry = e.target.getGeometry();
148
- if (geometry && geometry.getType() == 'Circle') {
149
- const circle = geometry;
150
- const newRadius = circle.getRadius();
151
- const properties = circle.getProperties();
152
- const { pointer } = properties;
153
- const oldRadiusLine = new LineString([circle.getCenter(), pointer]);
154
- oldRadiusLine.scale(newRadius / oldRadiusLine.getLength());
155
- circle.setProperties({
156
- ...properties,
157
- pointer: oldRadiusLine.getLastCoordinate()
158
- });
159
- }
160
- });
154
+ const geometry = olFeature.getGeometry();
155
+ if (!geometry) {
156
+ return;
157
+ }
158
+ const shapeType = this.drawingState.features.find((f) => f.id === olFeature.getId())?.type;
159
+ if (shapeType === DrawingShape.Square || shapeType === DrawingShape.Rectangle) {
160
+ olFeature.set('modifyGeometry', { geometry: geometry.clone() }, true);
161
+ }
162
+ else if (shapeType === DrawingShape.Disk) {
163
+ const listener = olFeature.on('change', (e) => {
164
+ const geometry = e.target.getGeometry();
165
+ if (!geometry) {
166
+ return;
167
+ }
168
+ if (geometry.getType() === 'Circle') {
169
+ const circle = geometry;
170
+ const newRadius = circle.getRadius();
171
+ const properties = circle.getProperties();
172
+ const { pointer } = properties;
173
+ const oldRadiusLine = new LineString([circle.getCenter(), pointer]);
174
+ oldRadiusLine.scale(newRadius / oldRadiusLine.getLength());
175
+ circle.setProperties({
176
+ ...properties,
177
+ pointer: oldRadiusLine.getLastCoordinate()
178
+ });
179
+ }
180
+ });
181
+ this.modifyFeatureChangeListeners.push(listener);
182
+ }
161
183
  });
162
184
  });
163
185
  // Update the modified geometries in the state
164
186
  this.modify.on('modifyend', (e) => {
165
- e.features.forEach((olFeature) => this.updateGeometryInState(olFeature)); //NOSONAR(typescript:S7728) Collection<?> is a custom Implementation with custom forEach
187
+ this.clearModifyFeatureChangeListeners();
188
+ e.features.forEach((olFeature) => {
189
+ const shapeType = this.drawingState.features.find((f) => f.id === olFeature.getId())?.type;
190
+ if (shapeType === DrawingShape.Square || shapeType === DrawingShape.Rectangle) {
191
+ const modifyGeometry = olFeature.get('modifyGeometry');
192
+ if (modifyGeometry?.geometry) {
193
+ olFeature.setGeometry(modifyGeometry.geometry);
194
+ }
195
+ olFeature.unset('modifyGeometry', true);
196
+ }
197
+ this.updateGeometryInState(olFeature); //NOSONAR(typescript:S7728) Collection<?> is a custom Implementation with custom forEach
198
+ });
166
199
  });
167
200
  }
168
201
  addSnapInteraction() {
@@ -302,9 +335,13 @@ export default class OlDrawing {
302
335
  }
303
336
  }
304
337
  prepareMenuEntriesForVertex = (_e, mapCoordinate) => {
305
- return this.hasEditableVertexAtCoordinate(mapCoordinate)
306
- ? EntryInteractionType.ENABLED
307
- : EntryInteractionType.NOT_SHOWN;
338
+ if (!this.hasEditableVertexAtCoordinate(mapCoordinate)) {
339
+ return EntryInteractionType.NOT_SHOWN;
340
+ }
341
+ if (this.isSquareOrRectangle(this.getDrawingShapeType(this.lastClosestFeature))) {
342
+ return EntryInteractionType.NOT_SHOWN;
343
+ }
344
+ return EntryInteractionType.ENABLED;
308
345
  };
309
346
  prepareMenuEntriesForShape = (_e, mapCoordinate) => {
310
347
  return this.hasEditableShapeAtCoordinate(mapCoordinate)
@@ -621,8 +658,10 @@ export default class OlDrawing {
621
658
  }
622
659
  // TODO Move as much parameters as possible into DrawingFeature
623
660
  getStyle(dFeature, olFeature) {
624
- const modifyGeometry = olFeature.get('modifyGeometry');
625
- const geometry = modifyGeometry ? modifyGeometry.geometry : olFeature.getGeometry();
661
+ const geometry = getGeometryForRendering(olFeature);
662
+ if (!geometry) {
663
+ return [];
664
+ }
626
665
  const measureFont = 'Bold ' + dFeature.measureFontSize + 'px/1 ' + dFeature.font;
627
666
  const nameFont = 'Bold ' + dFeature.nameFontSize + 'px/1 ' + dFeature.font;
628
667
  const measureColor = 'rgba(0, 0, 0, 0.4)';
@@ -782,7 +821,7 @@ export default class OlDrawing {
782
821
  const vertexStyle = dFeature.getVertexStyle();
783
822
  // Add a node style to every vertex of the geometry
784
823
  vertexStyle.setGeometry(function (f) {
785
- const geom = f?.getGeometry();
824
+ const geom = getGeometryForRendering(f);
786
825
  if (geom && geom instanceof Geometry) {
787
826
  return extractVerticesFromGeometry(geom);
788
827
  }
@@ -800,11 +839,79 @@ export default class OlDrawing {
800
839
  this.context.userInteractionManager.unregisterListener('map.select', this.toolName);
801
840
  }
802
841
  removeModifyInteraction() {
842
+ this.clearModifyFeatureChangeListeners();
803
843
  if (this.modify) {
804
844
  this.map.olMap.removeInteraction(this.modify);
805
845
  this.modify = null;
806
846
  }
807
847
  }
848
+ getDrawingShapeType(olFeature) {
849
+ if (!olFeature) {
850
+ return undefined;
851
+ }
852
+ return this.drawingState.features.find((f) => f.id === olFeature.getId())?.type;
853
+ }
854
+ isSquareOrRectangle(shapeType) {
855
+ return shapeType === DrawingShape.Square || shapeType === DrawingShape.Rectangle;
856
+ }
857
+ shouldAllowVertexInsertionAtEvent(e) {
858
+ const hoveredFeature = this.getHoveredModifiableFeatureAtPixel(e.pixel);
859
+ return shouldAllowVertexInsertionForShape(this.getDrawingShapeType(hoveredFeature));
860
+ }
861
+ getHoveredModifiableFeatureAtPixel(pixel) {
862
+ const modifiableFeatureSet = new Set(this.modifiableFeatures.getArray());
863
+ const hoveredFeature = this.map.olMap.forEachFeatureAtPixel(pixel, (feature, layer) => {
864
+ if (!(feature instanceof Feature) || layer !== this.drawingLayer || !modifiableFeatureSet.has(feature)) {
865
+ return undefined;
866
+ }
867
+ return feature;
868
+ }, {
869
+ hitTolerance: this.map.pixelTolerance
870
+ });
871
+ return hoveredFeature instanceof Feature ? hoveredFeature : null;
872
+ }
873
+ applyScaleRotateOnModifyStyle(modifyPointFeature) {
874
+ const modifyPointGeometry = modifyPointFeature.getGeometry();
875
+ if (!(modifyPointGeometry instanceof Point)) {
876
+ return;
877
+ }
878
+ const currentPoint = modifyPointGeometry.getCoordinates();
879
+ const features = modifyPointFeature.get('features');
880
+ if (!features) {
881
+ return;
882
+ }
883
+ features.forEach((olFeature) => {
884
+ const shapeType = this.getDrawingShapeType(olFeature);
885
+ if (!this.isSquareOrRectangle(shapeType)) {
886
+ return;
887
+ }
888
+ const modifyGeometry = olFeature.get('modifyGeometry');
889
+ if (!modifyGeometry?.geometry) {
890
+ return;
891
+ }
892
+ if (!modifyGeometry.point ||
893
+ !modifyGeometry.geometry0 ||
894
+ !modifyGeometry.center ||
895
+ modifyGeometry.minRadius === undefined) {
896
+ modifyGeometry.point = [...currentPoint];
897
+ modifyGeometry.geometry0 = modifyGeometry.geometry;
898
+ const geometryData = calculateCenterAndMinRadius(modifyGeometry.geometry0);
899
+ modifyGeometry.center = geometryData.center;
900
+ modifyGeometry.minRadius = geometryData.minRadius;
901
+ }
902
+ if (!modifyGeometry.geometry0 || !modifyGeometry.center || modifyGeometry.minRadius === undefined) {
903
+ return;
904
+ }
905
+ modifyGeometry.geometry = createScaledAndRotatedGeometry(modifyGeometry.geometry0, modifyGeometry.center, modifyGeometry.minRadius, modifyGeometry.point, currentPoint);
906
+ });
907
+ }
908
+ clearModifyFeatureChangeListeners() {
909
+ if (this.modifyFeatureChangeListeners.length === 0) {
910
+ return;
911
+ }
912
+ unByKeyAll(this.modifyFeatureChangeListeners);
913
+ this.modifyFeatureChangeListeners.length = 0;
914
+ }
808
915
  removeSnapInteraction() {
809
916
  if (this.snap) {
810
917
  this.map.olMap.removeInteraction(this.snap);
@@ -0,0 +1,11 @@
1
+ import { Coordinate } from 'ol/coordinate.js';
2
+ import Geometry from 'ol/geom/Geometry.js';
3
+ import Feature from 'ol/Feature.js';
4
+ import { DrawingShape } from './drawingFeature.js';
5
+ export declare function calculateCenterAndMinRadius(geometry: Geometry): {
6
+ center: Coordinate;
7
+ minRadius: number;
8
+ };
9
+ export declare function createScaledAndRotatedGeometry(geometry0: Geometry, center: Coordinate, minRadius: number, initialPoint: Coordinate, currentPoint: Coordinate): Geometry;
10
+ export declare function getGeometryForRendering(feature: Feature<Geometry>): Geometry | undefined;
11
+ export declare function shouldAllowVertexInsertionForShape(shapeType: DrawingShape | undefined): boolean;
@@ -0,0 +1,64 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { getCenter, getHeight, getWidth } from 'ol/extent.js';
3
+ import LineString from 'ol/geom/LineString.js';
4
+ import Polygon from 'ol/geom/Polygon.js';
5
+ import { DrawingShape } from './drawingFeature.js';
6
+ const EPSILON = 1e-12;
7
+ function sqDistance(a, b) {
8
+ const dx = a[0] - b[0];
9
+ const dy = a[1] - b[1];
10
+ return dx * dx + dy * dy;
11
+ }
12
+ export function calculateCenterAndMinRadius(geometry) {
13
+ let center;
14
+ let coordinates;
15
+ if (geometry instanceof Polygon) {
16
+ const ring = geometry.getCoordinates()[0];
17
+ const polygonCoords = ring.slice(0, -1);
18
+ const x = polygonCoords.reduce((sum, c) => sum + c[0], 0);
19
+ const y = polygonCoords.reduce((sum, c) => sum + c[1], 0);
20
+ center = [x / polygonCoords.length, y / polygonCoords.length];
21
+ coordinates = polygonCoords;
22
+ }
23
+ else if (geometry instanceof LineString) {
24
+ center = geometry.getCoordinateAt(0.5);
25
+ coordinates = geometry.getCoordinates();
26
+ }
27
+ else {
28
+ center = getCenter(geometry.getExtent());
29
+ }
30
+ if (coordinates && coordinates.length > 0) {
31
+ const maxSqDistance = Math.max(...coordinates.map((coordinate) => sqDistance(coordinate, center)));
32
+ return {
33
+ center,
34
+ minRadius: Math.sqrt(maxSqDistance) / 3
35
+ };
36
+ }
37
+ return {
38
+ center,
39
+ minRadius: Math.max(getWidth(geometry.getExtent()), getHeight(geometry.getExtent())) / 3
40
+ };
41
+ }
42
+ export function createScaledAndRotatedGeometry(geometry0, center, minRadius, initialPoint, currentPoint) {
43
+ const initialRadius = Math.sqrt(sqDistance(initialPoint, center));
44
+ if (initialRadius <= minRadius) {
45
+ return geometry0.clone();
46
+ }
47
+ const currentRadius = Math.sqrt(sqDistance(currentPoint, center));
48
+ if (currentRadius <= EPSILON) {
49
+ return geometry0.clone();
50
+ }
51
+ const initialAngle = Math.atan2(initialPoint[1] - center[1], initialPoint[0] - center[0]);
52
+ const currentAngle = Math.atan2(currentPoint[1] - center[1], currentPoint[0] - center[0]);
53
+ const geometry = geometry0.clone();
54
+ geometry.scale(currentRadius / initialRadius, undefined, center);
55
+ geometry.rotate(currentAngle - initialAngle, center);
56
+ return geometry;
57
+ }
58
+ export function getGeometryForRendering(feature) {
59
+ const modifyGeometry = feature.get('modifyGeometry');
60
+ return modifyGeometry?.geometry ?? feature.getGeometry() ?? undefined;
61
+ }
62
+ export function shouldAllowVertexInsertionForShape(shapeType) {
63
+ return shapeType !== DrawingShape.Square && shapeType !== DrawingShape.Rectangle;
64
+ }