@masterportal/masterportalapi 2.63.0 → 2.64.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,27 @@
3
3
 
4
4
  The [Semantic Versioning](https://semver.org/spec/v2.0.0.html) is used.
5
5
 
6
+ ## 2.64.0 - 2026-09-23
7
+
8
+ ### Added
9
+ - Readme: Information and link to issues in opencode added.
10
+ - Draw: Added functions to draw and modify features: write text and draw square. Integrated wfsImgPath to apply configured styles.
11
+ - Export: Added functions to export drawn features in kml, geojson, gpx and csv formats.
12
+
13
+ ### Changed
14
+ - The following packages have been updated:
15
+ - dependencies:
16
+ - ol: 10.9.0 to 10.10.0
17
+ - ol-mapbox-style: 13.4.1 to 13.5.0
18
+ - olcs: 2.23.0 to 2.23.1
19
+ - peerDependencies:
20
+ - @cesium/engine: 26.0.0 to 26.3.0
21
+
22
+ ### Fixed
23
+ - AttributeMapper: prevented mapping of empty strings for GFI.
24
+
25
+ ---
26
+
6
27
  ## 2.63.0 - 2026-07-17
7
28
 
8
29
  ### Changed
@@ -11,6 +32,7 @@
11
32
  - core-js: 3.33.1 to 3.49.0
12
33
  - dayjs: 1.11.10 to 1.11.21
13
34
  - proj4: 2.10.0 to 2.20.9
35
+ - ol-mapbox-style: 13.3.0 to 13.4.1
14
36
  - devDependencies:
15
37
  - @parcel/config-default: 2.15.4 to 2.16.4
16
38
  - @parcel/transformer-sass: 2.15.4 to 2.16.4
package/README.md CHANGED
@@ -25,3 +25,11 @@ Both methods may be required for useful user feedback. For example, a WMS may an
25
25
  |``npm run generate-jsdoc``|Generates the project documentation within the folder ``./docs``.|
26
26
  |``npm test``|Runs all tests. Prints code coverage to console.|
27
27
  |``npm run test:watch``|Runs all tests in watch mode. Good practive to let this run during development in a separate terminal. |
28
+
29
+ ## Community
30
+
31
+ The Bitbucket issue tracker is being removed on August 20, 2026. All issues have been migrated to openCode. You can access them via the link listed below, where you can also create new issues.
32
+
33
+ * [Issue tracker](https://gitlab.opencode.de/lgvhh/urban-data-solutions/masterportalapi/-/work_items)
34
+
35
+ **Please do not report security vulnerabilities via the public issue tracker.** Instead, report them privately by email as described in our [Security Policy](./SECURITY.md).
@@ -8,15 +8,15 @@ pipelines:
8
8
  tags:
9
9
  'v2.*.*':
10
10
  - step:
11
- name: deploy on openCode
11
+ name: deploy on new openCode
12
12
  script:
13
- - echo 'deploy to opencode:'$BITBUCKET_TAG
13
+ - echo 'deploy to new opencode:'$BITBUCKET_TAG
14
14
  - git fetch --all --tags
15
15
  - git checkout tags/$BITBUCKET_TAG
16
- - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git $BITBUCKET_TAG
16
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/lgvhh/urban-data-solutions/masterportalapi.git $BITBUCKET_TAG
17
17
  - git clone --single-branch --branch master https://geowerkstatt@bitbucket.org/geowerkstatt-hamburg/masterportalapi.git
18
18
  - cd masterportalapi
19
- - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git -f
19
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/lgvhh/urban-data-solutions/masterportalapi.git -f
20
20
  custom: # Pipelines that are triggered manually
21
21
  npm audit:
22
22
  - step:
@@ -26,18 +26,18 @@ pipelines:
26
26
  script:
27
27
  - npm install
28
28
  - npm audit --json
29
- deploy last tag on openCode:
29
+ deploy last tag on new openCode:
30
30
  - variables:
31
31
  - name: BITBUCKET_TAG
32
32
  description: tag to publish docs for
33
33
  - step:
34
- name: deploy last tag on openCode
34
+ name: deploy last tag on new openCode
35
35
  script:
36
36
  - apt-get update && apt-get install -y curl wput jq
37
37
  - git fetch --all --tags
38
- - echo 'deply to opencode:'$BITBUCKET_TAG
38
+ - echo 'deploy to new opencode:'$BITBUCKET_TAG
39
39
  - git checkout tags/$BITBUCKET_TAG
40
- - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git $BITBUCKET_TAG
40
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/lgvhh/urban-data-solutions/masterportalapi.git $BITBUCKET_TAG
41
41
  - git clone --single-branch --branch master https://geowerkstatt@bitbucket.org/geowerkstatt-hamburg/masterportalapi.git
42
42
  - cd masterportalapi
43
- - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/geowerkstatt-hamburg/masterportalapi.git -f
43
+ - git push https://$OpenCode_User:$OpenCode_Password@gitlab.opencode.de/lgvhh/urban-data-solutions/masterportalapi.git -f
@@ -58,7 +58,7 @@
58
58
  {
59
59
  "id": "4001",
60
60
  "name": "Gelaende",
61
- "url": "https://daten-hamburg.de/gdi3d/datasource-data/Gelaende",
61
+ "url": "https://www.daten-hamburg.de/gdi3d/datasource-data/Gelaende",
62
62
  "typ": "Terrain3D",
63
63
  "cesiumTerrainProviderOptions": {
64
64
  "requestVertexNormals": true
@@ -67,7 +67,7 @@
67
67
  {
68
68
  "id": "4002",
69
69
  "name": "Gebäude LoD2",
70
- "url": "https://daten-hamburg.de/gdi3d/datasource-data/LoD2",
70
+ "url": "https://www.daten-hamburg.de/gdi3d/datasource-data/LoD2",
71
71
  "typ": "TileSet3D",
72
72
  "styleId": "3DTileSetStyle",
73
73
  "cesium3DTilesetOptions": {
@@ -80,7 +80,7 @@
80
80
  "typ": "Entities3D",
81
81
  "entities": [
82
82
  {
83
- "url": "https://daten-hamburg.de/gdi3d/datasource-data/Simple_Building.glb",
83
+ "url": "https://www.daten-hamburg.de/gdi3d/datasource-data/Simple_Building.glb",
84
84
  "attributes": {
85
85
  "name": "einfaches Haus in Planten und Blomen"
86
86
  },
@@ -95,7 +95,7 @@
95
95
  "show": true
96
96
  },
97
97
  {
98
- "url": "https://daten-hamburg.de/gdi3d/datasource-data/Simple_Building.glb",
98
+ "url": "https://www.daten-hamburg.de/gdi3d/datasource-data/Simple_Building.glb",
99
99
  "attributes": {
100
100
  "name": "einfaches Haus im See"
101
101
  },
package/jest.setup.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import "regenerator-runtime/runtime";
2
+ import {TextEncoder, TextDecoder} from "util";
3
+
4
+ global.TextEncoder = TextEncoder;
5
+ global.TextDecoder = TextDecoder;
2
6
 
3
7
  class Worker {
4
8
  constructor (stringUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masterportal/masterportalapi",
3
- "version": "2.63.0",
3
+ "version": "2.64.0",
4
4
  "description": "Basic functions of the Masterportal as api",
5
5
  "homepage": "https://bitbucket.org/geowerkstatt-hamburg/masterportalapi#readme",
6
6
  "bugs": {
@@ -35,9 +35,9 @@
35
35
  "dependencies": {
36
36
  "core-js": "^3.49.0",
37
37
  "dayjs": "^1.11.21",
38
- "ol": "10.9.0",
39
- "ol-mapbox-style": "13.3.0",
40
- "olcs": "^2.23.0",
38
+ "ol": "^10.10.0",
39
+ "ol-mapbox-style": "^13.5.0",
40
+ "olcs": "^2.23.1",
41
41
  "proj4": "^2.20.9"
42
42
  },
43
43
  "devDependencies": {
@@ -68,7 +68,7 @@
68
68
  "util": "^0.12.5"
69
69
  },
70
70
  "peerDependencies": {
71
- "@cesium/engine": "^26.0.0"
71
+ "@cesium/engine": "^26.3.0"
72
72
  },
73
73
  "engines": {
74
74
  "node": "^22.19.0 || ^24.11.1",
package/publiccode.yml CHANGED
@@ -81,8 +81,8 @@ maintenance:
81
81
  name: Masterportal
82
82
  platforms:
83
83
  - web
84
- releaseDate: '2026-07-17'
84
+ releaseDate: '2026-09-23'
85
85
  roadmap: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi/src/master/'
86
86
  softwareType: api
87
- softwareVersion: 2.63.0
87
+ softwareVersion: 2.64.0
88
88
  url: 'https://bitbucket.org/geowerkstatt-hamburg/masterportalapi.git'
package/src/index.js CHANGED
@@ -16,6 +16,8 @@ import {search, setGazetteerUrl} from "./searchAddress";
16
16
  import setBackgroundImage from "./lib/setBackgroundImage";
17
17
  import ping from "./lib/ping";
18
18
  import ogcApiProcesses from "./api/ogcApiProcesses";
19
+ import * as exportItem from "./shared/exportItem";
20
+ import * as kmlExport from "./shared/kmlExport";
19
21
 
20
22
  export {
21
23
  createMapView,
@@ -36,5 +38,7 @@ export {
36
38
  search,
37
39
  crs,
38
40
  webgl,
39
- ogcApiProcesses
41
+ ogcApiProcesses,
42
+ exportItem,
43
+ kmlExport
40
44
  };
@@ -266,7 +266,7 @@ function mapAttributes (properties, mappingObject, isNested = true) {
266
266
  value = prepareValueFromObject(key, newKey, properties);
267
267
  newKey = newKey.name;
268
268
  }
269
- if (value && value !== "undefined") {
269
+ if (value !== undefined && value !== null && value !== "" && value !== "undefined") {
270
270
  mappedProperties[newKey] = value;
271
271
  }
272
272
  });
@@ -1,4 +1,6 @@
1
- import {createBox} from "ol/interaction/Draw.js";
1
+ import {createBox, createRegularPolygon} from "ol/interaction/Draw.js";
2
+ import {Circle as CircleStyle, Icon, Style, Fill} from "ol/style.js";
3
+ import Text from "ol/style/Text.js";
2
4
  import {Draw} from "ol/interaction.js";
3
5
 
4
6
  import createStyle from "../../vectorStyle/createStyle";
@@ -32,12 +34,29 @@ const earthRadius = 6378137,
32
34
  },
33
35
  polygon: {
34
36
  type: "Polygon"
37
+ },
38
+ writeText: {
39
+ type: "Point"
40
+ },
41
+ square: {
42
+ type: "Circle",
43
+ options: {
44
+ geometryFunction: createRegularPolygon(4)
45
+ }
35
46
  }
36
47
  };
37
48
  let styleObject = null,
38
49
  styleObjectOuterCircle = null,
39
50
  zIndex = 0;
40
51
 
52
+ /**
53
+ * Returns the current style rule from styleObject.
54
+ * @returns {Object|null} The current style rule or null.
55
+ */
56
+ function getCurrentStyleRule () {
57
+ return styleObject?.rules?.[0]?.style || null;
58
+ }
59
+
41
60
  /**
42
61
  * Converts a value from one unit to another.
43
62
  * @param {Number} value The value to convert.
@@ -66,12 +85,9 @@ function convertUnits (value, sourceUnit, targetUnit) {
66
85
  */
67
86
  function transformMapUnitRadius (radius, mapProjection) {
68
87
  const unit = mapProjection.getUnits();
69
- let mapUnitRadius;
88
+ let mapUnitRadius = radius;
70
89
 
71
- if (unit === "m") {
72
- mapUnitRadius = radius;
73
- }
74
- else if (unit === "degrees") {
90
+ if (unit === "degrees") {
75
91
  mapUnitRadius = radius * roh / earthRadius;
76
92
  }
77
93
 
@@ -86,6 +102,7 @@ function transformMapUnitRadius (radius, mapProjection) {
86
102
  * @param {Object} [options={}] The options for drawing features.
87
103
  * @param {Number} [options.innerRadius] The radius for the inner circle.
88
104
  * @param {Number} [options.outerRadius] The radius for the outer circle.
105
+ * @param {String} [options.unit] The unit for the radii (e.g. "m", "km").
89
106
  * @returns {void}
90
107
  */
91
108
  function drawOuterCircle (feature, mapProjection, source, options = {}) {
@@ -110,10 +127,11 @@ function drawOuterCircle (feature, mapProjection, source, options = {}) {
110
127
 
111
128
  /**
112
129
  * Create a feature for the (inner) circle of draw type: circle or double circle.
113
- * @param {ol/feature} feature The ol feature.vb
130
+ * @param {ol/feature} feature The ol feature.
114
131
  * @param {Object} mapProjection The map projection.
115
132
  * @param {Object} [options={}] The options for drawing features.
116
133
  * @param {Number} [options.innerRadius] The radius for the (inner) circle.
134
+ * @param {String} [options.unit] The unit for the radius (e.g. "m", "km").
117
135
  * @returns {void}
118
136
  */
119
137
  function drawInnerCircle (feature, mapProjection, options = {}) {
@@ -133,6 +151,7 @@ function drawInnerCircle (feature, mapProjection, options = {}) {
133
151
  * @param {Number} [options.innerRadius] The radius for the inner circle.
134
152
  * @param {Boolean} [options.interactive] Circle is drawn interactively or not.
135
153
  * @param {Number} [options.outerRadius] The radius for the outer circle.
154
+ * @param {String} [options.unit] The unit for the radii (e.g. "m", "km").
136
155
  * @returns {void}
137
156
  */
138
157
  function drawCircle (drawInteraction, drawType, mapProjection, source, options = {}) {
@@ -157,7 +176,9 @@ function drawCircle (drawInteraction, drawType, mapProjection, source, options =
157
176
  */
158
177
  function setStyleObject (currentLayout, outerCircle = false) {
159
178
  const fillOpacity = 1 - (currentLayout.fillTransparency / 100),
179
+ strokeOpacity = fillOpacity,
160
180
  fillColorRgba = [...currentLayout.fillColor, fillOpacity],
181
+ strokeColorRgba = [...currentLayout.strokeColor, strokeOpacity],
161
182
  style = {
162
183
  rules: [
163
184
  {
@@ -165,13 +186,16 @@ function setStyleObject (currentLayout, outerCircle = false) {
165
186
  circleFillColor: fillColorRgba,
166
187
  circleStrokeColor: currentLayout.strokeColor,
167
188
  circleStrokeWidth: currentLayout.strokeWidth,
168
- lineStrokeColor: currentLayout.strokeColor,
189
+ lineStrokeColor: strokeColorRgba,
169
190
  lineStrokeWidth: currentLayout.strokeWidth,
170
191
  polygonFillColor: fillColorRgba,
171
192
  polygonStrokeColor: currentLayout.strokeColor,
172
193
  polygonStrokeDash: currentLayout.polygonDash,
173
194
  polygonStrokeDashOffset: currentLayout.polygonStrokeDashOffset,
174
- polygonStrokeWidth: currentLayout.strokeWidth
195
+ polygonStrokeWidth: currentLayout.strokeWidth,
196
+ text: currentLayout.text || "Text",
197
+ textFont: currentLayout.font || "Arial",
198
+ textFontSize: currentLayout.fontSize || 10
175
199
  }
176
200
  }
177
201
  ]
@@ -188,11 +212,12 @@ function setStyleObject (currentLayout, outerCircle = false) {
188
212
  /**
189
213
  * Sets the style to overlay and feature.
190
214
  * @param {ol/interaction/Draw} drawInteraction The draw interaction.
215
+ * @param {String} wfsImgPath Path to the image folder from Config.
191
216
  * @returns {void}
192
217
  */
193
- function setStyleToDrawInteraction (drawInteraction) {
218
+ function setStyleToDrawInteraction (drawInteraction, wfsImgPath) {
194
219
  drawInteraction.on("drawstart", event => {
195
- const style = createStyle.createStyle(styleObject, event.feature, false);
220
+ const style = createStyle.createStyle(styleObject, event.feature, false, wfsImgPath);
196
221
 
197
222
  style.setZIndex(zIndex++);
198
223
  drawInteraction.getOverlay().setStyle(style);
@@ -200,16 +225,139 @@ function setStyleToDrawInteraction (drawInteraction) {
200
225
  });
201
226
  }
202
227
 
228
+ /**
229
+ * Sets the style to the text.
230
+ * @param {ol/interaction/Draw} drawInteraction The draw interaction.
231
+ * @returns {void}
232
+ */
233
+ function setStyleToDrawText (drawInteraction) {
234
+ drawInteraction.on("drawstart", event => {
235
+ const style = getCurrentStyleRule();
236
+
237
+ event.feature.set("drawType", "writeText");
238
+
239
+ if (!style?.text) {
240
+ console.error("Style object for text is not properly initialized.");
241
+ return;
242
+ }
243
+
244
+ const textStyle = new Text({
245
+ text: style.text ?? "",
246
+ font: `${style.textFontSize}px ${style.textFont}`,
247
+ fill: new Fill({color: style.polygonFillColor || "black"}),
248
+ textAlign: style.textAlign || "left",
249
+ textBaseline: "bottom"
250
+ }),
251
+ olStyle = new Style({text: textStyle});
252
+
253
+ olStyle.setZIndex(zIndex++);
254
+ event.feature.set("textContent", style.text);
255
+ event.feature.set("textFontSize", style.textFontSize);
256
+ event.feature.set("textFont", style.textFont);
257
+ event.feature.set("textFillColor", style.polygonFillColor);
258
+ event.feature.setStyle(olStyle);
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Returns the current text style from the styleObject for use as sketch/preview.
264
+ * @returns {ol/style/Style|null} The text style or null.
265
+ */
266
+ function getTextStyle () {
267
+ const style = getCurrentStyleRule();
268
+
269
+ if (!style) {
270
+ return null;
271
+ }
272
+
273
+ if (!style.text || style.text === "Text") {
274
+ return null;
275
+ }
276
+
277
+ return new Style({
278
+ text: new Text({
279
+ text: style.text,
280
+ font: `${style.textFontSize}px ${style.textFont}`,
281
+ fill: new Fill({color: style.polygonFillColor || "black"}),
282
+ textAlign: style.textAlign || "left",
283
+ textBaseline: "bottom"
284
+ })
285
+ });
286
+ }
287
+
288
+ /**
289
+ * Returns a style for simple point features without stroke.
290
+ * Uses the current circleFillColor from the styleObject.
291
+ * @returns {ol/style/Style} The point style.
292
+ */
293
+ function getSimplePointStyle () {
294
+ const rawColor = getCurrentStyleRule()?.circleFillColor || [0, 0, 0, 1],
295
+ fillColor = [rawColor[0], rawColor[1], rawColor[2], 1];
296
+
297
+ return new Style({
298
+ image: new CircleStyle({
299
+ radius: 8,
300
+ fill: new Fill({color: fillColor})
301
+ })
302
+ });
303
+ }
304
+
203
305
  /**
204
306
  * Create the draw interaction.
205
307
  * @param {String} drawType The current draw type.
206
308
  * @param {ol/source} source The vector layer source for draw features.
309
+ * @param {Object[]} [iconList=[]] The list of symbols for the draw type symbols.
310
+ * @param {String} [wfsImgPath=""] Path to the image folder, used as prefix when symbol value is not an absolute path.
207
311
  * @returns {ol/interaction/Draw|null} The draw interaction.
208
312
  */
209
- function createDrawInteraction (drawType, source) {
313
+ function createDrawInteraction (drawType, source, iconList, wfsImgPath = "") {
210
314
  let drawInteraction = null;
315
+ const symbol = iconList?.length > 0 ? iconList.find(val => val.id === drawType) : null;
316
+
317
+ if (symbol) {
318
+ if (symbol.type === "image") {
319
+ const iconSrc = symbol.value.startsWith("/") || symbol.value.startsWith("http") || symbol.value.startsWith(".")
320
+ ? symbol.value
321
+ : `${wfsImgPath}${symbol.value}`,
322
+ svgStyle = new Style({
323
+ image: new Icon({
324
+ src: iconSrc,
325
+ scale: symbol.scale || 1,
326
+ anchor: [0.5, 1]
327
+ })
328
+ });
211
329
 
212
- if (typeof mappingDrawTypes[drawType] === "object") {
330
+ drawInteraction = new Draw({
331
+ source: source,
332
+ type: "Point",
333
+ style: svgStyle
334
+ });
335
+ }
336
+ else if (symbol.type === "simple_point") {
337
+ drawInteraction = new Draw({
338
+ source: source,
339
+ type: "Point",
340
+ style: getSimplePointStyle
341
+ });
342
+ drawInteraction.on("drawstart", event => {
343
+ const style = getSimplePointStyle();
344
+
345
+ style.setZIndex(zIndex++);
346
+ event.feature.setStyle(style);
347
+ });
348
+ }
349
+ else {
350
+ drawInteraction = new Draw({
351
+ source: source,
352
+ type: "Point"
353
+ });
354
+ if (styleObject !== null) {
355
+ setStyleToDrawInteraction(drawInteraction, wfsImgPath);
356
+ }
357
+ drawInteraction.getOverlay().setStyle([]);
358
+ }
359
+ }
360
+ else if (typeof mappingDrawTypes[drawType] === "object") {
213
361
  const options = mappingDrawTypes[drawType].options || {};
214
362
 
215
363
  drawInteraction = new Draw({
@@ -219,8 +367,16 @@ function createDrawInteraction (drawType, source) {
219
367
  type: mappingDrawTypes[drawType].type
220
368
  });
221
369
 
222
- if (styleObject !== null && drawType !== "doubleCircle") {
223
- setStyleToDrawInteraction(drawInteraction);
370
+ if (styleObject !== null && drawType !== "doubleCircle" && drawType !== "writeText") {
371
+ setStyleToDrawInteraction(drawInteraction, wfsImgPath);
372
+ }
373
+
374
+ if (drawType === "writeText") {
375
+ setStyleToDrawText(drawInteraction);
376
+ drawInteraction.getOverlay().setStyle(getTextStyle() ?? []);
377
+ }
378
+ else {
379
+ drawInteraction.getOverlay().setStyle([]);
224
380
  }
225
381
  }
226
382
 
@@ -235,6 +391,7 @@ export default {
235
391
  drawCircle,
236
392
  drawInnerCircle,
237
393
  drawOuterCircle,
394
+ getTextStyle,
238
395
  setStyleObject,
239
396
  transformMapUnitRadius
240
397
  };
@@ -3,11 +3,24 @@ import {Modify} from "ol/interaction.js";
3
3
  /**
4
4
  * Create the modify interaction.
5
5
  * @param {ol/source} source The vector layer source containing the features to be modified.
6
+ * @param {Object} [options={}] Optional object to register watchers for modification of features.
7
+ * @param {Function} [options.onModifyEnd] Callback function triggered when a modify operation ends.
8
+ * @param {Function} [options.onModifyStart] Callback function triggered when a modify operation starts.
6
9
  * @returns {ol/interaction/Modify} The modify interaction.
7
10
  */
8
- function createModifyInteraction (source) {
11
+ function createModifyInteraction (source, options = {}) {
9
12
  const modifyInteraction = new Modify({
10
- source: source
13
+ source: source
14
+ }),
15
+ watcherMap = {
16
+ modifyend: options.onModifyEnd,
17
+ modifystart: options.onModifyStart
18
+ };
19
+
20
+ Object.entries(watcherMap).forEach(([eventName, callback]) => {
21
+ if (typeof callback === "function") {
22
+ modifyInteraction.on(eventName, callback);
23
+ }
11
24
  });
12
25
 
13
26
  return modifyInteraction;