diff --git a/package-lock.json b/package-lock.json
index 71d7dfc90..5029ca290 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1062,7 +1062,7 @@
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
- "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
"asn1": {
@@ -1077,7 +1077,7 @@
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"astral-regex": {
@@ -1098,13 +1098,13 @@
"async-foreach": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
- "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==",
+ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
"atob": {
@@ -1120,7 +1120,7 @@
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"aws4": {
@@ -1149,7 +1149,7 @@
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
- "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"requires": {
"tweetnacl": "^0.14.3"
@@ -1313,7 +1313,7 @@
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"chalk": {
@@ -1485,7 +1485,7 @@
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"confusing-browser-globals": {
@@ -1503,7 +1503,7 @@
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true
},
"content-disposition": {
@@ -1600,7 +1600,7 @@
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
- "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
@@ -1618,7 +1618,7 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"decamelize-keys": {
@@ -1702,13 +1702,13 @@
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true
},
"depd": {
@@ -1796,7 +1796,7 @@
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
- "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"dev": true,
"requires": {
"jsbn": "~0.1.0",
@@ -2556,7 +2556,7 @@
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
},
"fancy-log": {
@@ -2694,7 +2694,7 @@
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
- "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true
},
"form-data": {
@@ -2834,7 +2834,7 @@
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
- "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
"dev": true
},
"get-stream": {
@@ -2856,7 +2856,7 @@
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
- "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
@@ -2974,7 +2974,7 @@
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
@@ -3049,7 +3049,7 @@
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true
},
"hosted-git-info": {
@@ -3190,7 +3190,7 @@
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
- "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
@@ -3217,7 +3217,7 @@
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
- "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
"dev": true,
"requires": {
"ms": "^2.0.0"
@@ -3434,7 +3434,7 @@
"is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
- "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+ "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=",
"dev": true
},
"is-negative-zero": {
@@ -3473,7 +3473,7 @@
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
- "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
"dev": true
},
"is-plain-object": {
@@ -3528,7 +3528,7 @@
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
"is-weakref": {
@@ -3570,7 +3570,7 @@
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
- "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"jest-worker": {
@@ -3620,7 +3620,7 @@
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true
},
"json-parse-better-errors": {
@@ -3661,7 +3661,7 @@
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
- "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"json5": {
@@ -4477,7 +4477,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
"object-inspect": {
@@ -4928,7 +4928,7 @@
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
- "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
"promise-retry": {
@@ -4944,7 +4944,7 @@
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
- "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"dev": true
}
}
@@ -5576,7 +5576,7 @@
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
},
"setprototypeof": {
@@ -5903,7 +5903,7 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"readable-stream": {
@@ -6273,7 +6273,7 @@
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
@@ -6282,7 +6282,7 @@
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
},
"type-check": {
@@ -6422,7 +6422,7 @@
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
- "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
@@ -6433,7 +6433,7 @@
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
}
}
diff --git a/scss/_infowindow.scss b/scss/_infowindow.scss
index e3ad93119..cfee37624 100644
--- a/scss/_infowindow.scss
+++ b/scss/_infowindow.scss
@@ -311,3 +311,7 @@
transform: none;
}
}
+
+.groupfootercontainer {
+ margin-left:36px;
+}
diff --git a/src/geom.js b/src/geom.js
index a7ce27cd5..62263e29a 100644
--- a/src/geom.js
+++ b/src/geom.js
@@ -1,8 +1,10 @@
import getcenter from './geometry/getcenter';
import getarea from './geometry/getarea';
+import getlength from './geometry/getlength';
const geom = {};
geom.center = getcenter;
geom.area = getarea;
+geom.length = getlength;
export default geom;
diff --git a/src/geometry/getarea.js b/src/geometry/getarea.js
index 20ecfb181..469341b56 100644
--- a/src/geometry/getarea.js
+++ b/src/geometry/getarea.js
@@ -1,11 +1,12 @@
-import round from '../utils/round';
+import { getArea as olGetArea } from 'ol/sphere';
+import formatAreaString from '../utils/formatareastring';
-export default function getArea(geometryIn, decimals) {
- let area = geometryIn.getArea ? geometryIn.getArea() : 0;
- if (decimals) {
- area = round(area, decimals);
- } else {
- area = round(area, '2');
+export default function getArea(geometryIn, decimals, map) {
+ let area = 0;
+ const geomType = geometryIn.getType();
+ if (geomType === 'Polygon' || geomType === 'MultiPolygon') {
+ area = olGetArea(geometryIn, { projection: map.getView().getProjection() });
}
- return area;
+
+ return formatAreaString(area, { decimals: decimals || 2 });
}
diff --git a/src/geometry/getlength.js b/src/geometry/getlength.js
new file mode 100644
index 000000000..b7deed267
--- /dev/null
+++ b/src/geometry/getlength.js
@@ -0,0 +1,13 @@
+import { getLength as olGetLength } from 'ol/sphere';
+import formatLengthString from '../utils/formatlengthstring';
+
+export default function getLength(geometryIn, decimals, map) {
+ let length = 0;
+
+ const geomType = geometryIn.getType();
+ if (geomType === 'LineString' || geomType === 'LinearRing' || geomType === 'MultiLineString') {
+ length = olGetLength(geometryIn, { projection: map.getView().getProjection() });
+ }
+
+ return formatLengthString(length, { decimals: decimals || 2 });
+}
diff --git a/src/infowindow.js b/src/infowindow.js
index 9f341d4f2..bd2e8623a 100644
--- a/src/infowindow.js
+++ b/src/infowindow.js
@@ -9,9 +9,11 @@ let mainContainer;
let urvalContainer;
let listContainer;
let exportContainer;
+let groupFooterContainer;
let sublists;
let subexports;
let urvalElements;
+let footerContainers;
let expandableContents;
let exportOptions;
let activeSelectionGroup;
@@ -127,8 +129,15 @@ function render(viewerId) {
exportContainer = document.createElement('div');
exportContainer.classList.add('exportcontainer');
+ groupFooterContainer = document.createElement('div');
+ groupFooterContainer.classList.add('groupfootercontainer');
+
+ // Add some divs to populate later. It works by replacing the contents of these containers with the
+ // information for the selected selectionGroup. Each selectionGroup is rendered in memory only
+ // and is put into DOM when it should be visible
mainContainer.appendChild(urvalContainer);
mainContainer.appendChild(listContainer);
+ mainContainer.appendChild(groupFooterContainer);
mainContainer.appendChild(exportContainer);
parentElement = document.getElementById(viewerId);
@@ -161,6 +170,13 @@ function showSelectedList(selectionGroup) {
const sublistToAppend = sublists.get(selectionGroup);
listContainer.appendChild(sublistToAppend);
+ // Replace the container with this group's content
+ while (groupFooterContainer.firstChild) {
+ groupFooterContainer.removeChild(groupFooterContainer.firstChild);
+ }
+ const footerToAppend = footerContainers.get(selectionGroup);
+ groupFooterContainer.appendChild(footerToAppend);
+
while (exportContainer.firstChild) {
exportContainer.removeChild(exportContainer.firstChild);
}
@@ -442,6 +458,12 @@ function createSubexportComponent(selectionGroup) {
return subexportContainer;
}
+/**
+ * Creates everything that is needed internally before adding items to a selectionGroup.
+ * Creates some elements, but does not add them to the DOM. That is done when a selectiongroup is displayed.
+ * @param {any} selectionGroup
+ * @param {any} selectionGroupTitle
+ */
function createUrvalElement(selectionGroup, selectionGroupTitle) {
const urvalElement = document.createElement('div');
urvalElement.classList.add('urvalelement');
@@ -456,6 +478,9 @@ function createUrvalElement(selectionGroup, selectionGroupTitle) {
const sublistContainter = document.createElement('div');
sublists.set(selectionGroup, sublistContainter);
+ const footerContainer = document.createElement('div');
+ footerContainers.set(selectionGroup, footerContainer);
+
const subexportComponent = createSubexportComponent(selectionGroup);
subexports.set(selectionGroup, subexportComponent);
}
@@ -643,6 +668,16 @@ function updateUrvalElementText(selectionGroup, selectionGroupTitle, sum) {
urvalElement.childNodes[0].nodeValue = newNodeValue;
}
+/**
+ * Updates the footer text for the given selectionGroup.
+ * @param {any} selectionGroup
+ * @param {any} text Some html to display in the footer
+ */
+function updateSelectionGroupFooter(selectionGroup, text) {
+ const footerContainer = footerContainers.get(selectionGroup);
+ footerContainer.innerHTML = text;
+}
+
function init(options) {
viewer = options.viewer;
selectionManager = options.viewer.getSelectionManager();
@@ -653,6 +688,7 @@ function init(options) {
subexports = new Map();
urvalElements = new Map();
expandableContents = new Map();
+ footerContainers = new Map();
render(options.viewer.getId());
@@ -667,7 +703,8 @@ function init(options) {
showSelectedList,
scrollListElementToView,
hide: hideInfowindow,
- show: showInfowindow
+ show: showInfowindow,
+ updateSelectionGroupFooter
};
}
diff --git a/src/infowindow_expandableList.js b/src/infowindow_expandableList.js
index 2813f911e..735b6f6c1 100644
--- a/src/infowindow_expandableList.js
+++ b/src/infowindow_expandableList.js
@@ -488,6 +488,10 @@ function createListElement(item) {
const urvalContent = urvalElementContents.get(item.getSelectionGroup());
urvalContent.replaceChildren(sublist);
+ const groupFooterEl = document.createElement('div');
+ groupFooterEl.classList.add('groupfootercontainer');
+ urvalContent.appendChild(groupFooterEl);
+
const subexportToAppend = subexports.get(item.getSelectionGroup());
urvalContent.appendChild(subexportToAppend);
}
@@ -538,19 +542,36 @@ function removeListElement(item) {
}
}
-function hideUrvalElement(selectionGroup) {
+/**
+ * Helper to get the DOM element for a selection group's urval
+ * @param {any} selectionGroup
+ */
+function getUrvalElement(selectionGroup) {
const urvalCmp = urvalElements.get(selectionGroup);
- const urvalElement = document.getElementById(urvalCmp.getId());
+ return document.getElementById(urvalCmp.getId());
+}
+
+function hideUrvalElement(selectionGroup) {
+ const urvalElement = getUrvalElement(selectionGroup);
urvalElement.classList.add('hidden');
}
function updateUrvalElementText(selectionGroup, selectionGroupTitle, sum) {
- const urvalCmp = urvalElements.get(selectionGroup);
- const urvalElement = document.getElementById(urvalCmp.getId());
+ const urvalElement = getUrvalElement(selectionGroup);
const newNodeValue = `${selectionGroupTitle} (${sum})`;
urvalElement.getElementsByTagName('span')[0].innerText = newNodeValue;
}
+/**
+ * Updates the footer text for the given selectionGroup.
+ * @param {any} selectionGroup
+ * @param {any} text Some html to display in the footer
+ */
+function updateSelectionGroupFooter(selectionGroup, text) {
+ const urvalElement = getUrvalElement(selectionGroup);
+ urvalElement.getElementsByClassName('groupfootercontainer')[0].innerHTML = text;
+}
+
function init(options) {
viewer = options.viewer;
selectionManager = options.viewer.getSelectionManager();
@@ -577,7 +598,8 @@ function init(options) {
showSelectedList,
scrollListElementToView,
hide: hideInfowindow,
- show: showInfowindow
+ show: showInfowindow,
+ updateSelectionGroupFooter
};
}
diff --git a/src/selectionmanager.js b/src/selectionmanager.js
index 96d49e5f1..d2c277785 100644
--- a/src/selectionmanager.js
+++ b/src/selectionmanager.js
@@ -1,10 +1,13 @@
import Collection from 'ol/Collection';
+import { getArea, getLength } from 'ol/sphere';
import { Component } from './ui';
import featurelayer from './featurelayer';
import infowindowManagerV1 from './infowindow';
import infowindowManagerV2 from './infowindow_expandableList';
import Style from './style';
import StyleTypes from './style/styletypes';
+import formatAreaString from './utils/formatareastring';
+import formatLengthString from './utils/formatlengthstring';
const styleTypes = StyleTypes();
@@ -12,6 +15,11 @@ const Selectionmanager = function Selectionmanager(options = {}) {
const {
toggleSelectOnClick = false
} = options;
+
+ let aggregations = [];
+ if (options.infowindowOptions && options.infowindowOptions.groupAggregations) {
+ aggregations = options.infowindowOptions.groupAggregations;
+ }
let viewer;
let selectedItems;
let urval;
@@ -157,6 +165,97 @@ const Selectionmanager = function Selectionmanager(options = {}) {
infowindow.createUrvalElement(selectionGroup, selectionGroupTitle);
}
+ /**
+ * Calculates all configured aggregations for one selectionGroup
+ * @param {any} selectionGroup
+ */
+ function calculateGroupAggregations(selectionGroup) {
+ const retval = [];
+ // function pointer to a function that takes an array as argumnet
+ let aggregationFn;
+
+ aggregations.forEach(currAggregation => {
+ const {
+ useHectare = true
+ } = currAggregation;
+ let helperName;
+ if (!currAggregation.layer || currAggregation.layer === selectionGroup) {
+ let valFound = false;
+ // Suck out the attribute to aggregate.
+ const values = urval.get(selectionGroup).getFeatures().map(currFeature => {
+ let val = 0;
+ if (currAggregation.attribute.startsWith('@')) {
+ helperName = currAggregation.attribute.substring(1);
+ const geometry = currFeature.getGeometry();
+ const geomType = geometry.getType();
+ const proj = viewer.getProjection();
+ if (helperName === 'area') {
+ if (geomType === 'Polygon' || geomType === 'MultiPolygon') {
+ val = getArea(geometry, { projection: proj });
+ valFound = true;
+ }
+ } else if (helperName === 'length') {
+ if (geomType === 'LineString' || geomType === 'LinearRing' || geomType === 'MultiLineString') {
+ val = getLength(geometry, { projection: proj });
+ valFound = true;
+ }
+ } else {
+ console.error(`Unsupported geometry operation: ${helperName}`);
+ }
+ } else {
+ val = currFeature.get(currAggregation.attribute);
+ if (val !== undefined) {
+ valFound = true;
+ }
+ }
+ return val;
+ });
+
+ // Only add the aggregation if this layer has the attribute (or function)
+ if (valFound) {
+ switch (currAggregation.function) {
+ case 'sum':
+ // Define the "sum" aggregation.
+ // To be honest, we could have just performed the aggregation here
+ // but it is cool to use function pointers.
+ // javascript does not provide a sum function. Define our own.
+ aggregationFn = (arr) => arr.reduce((a, b) => (Number(a) || 0) + (Number(b) || 0), 0);
+ break;
+ default:
+ console.error(`Unsupported aggregation function: ${currAggregation.function}`);
+ return; // Return from labmda. Skips this aggregation
+ }
+
+ let result = aggregationFn(values);
+ const decimals = currAggregation.decimals !== undefined ? currAggregation.decimals : 2;
+
+ // Correct result depending on type
+ let resultstring;
+ if (helperName === 'area' && !currAggregation.unit) {
+ resultstring = formatAreaString(result, { useHectare, decimals });
+ } else if (helperName === 'length' && !currAggregation.unit) {
+ resultstring = formatLengthString(result, { decimals });
+ } else {
+ if (currAggregation.scalefactor) {
+ result *= currAggregation.scalefactor;
+ }
+ resultstring = result.toFixed(decimals);
+ if (currAggregation.unit) {
+ resultstring = `${resultstring} ${currAggregation.unit}`;
+ }
+ }
+
+ const prefix = currAggregation.label || `${currAggregation.function}(${currAggregation.attribute}):`;
+ const line = `${prefix} ${resultstring}`;
+ retval.push(line);
+ }
+ }
+ });
+
+ // Return all aggregations in one multiline string
+ return retval.join('
');
+ }
+
function onItemAdded(event) {
const item = event.element;
@@ -176,6 +275,8 @@ const Selectionmanager = function Selectionmanager(options = {}) {
const sum = urval.get(selectionGroup).getFeatures().length;
infowindow.updateUrvalElementText(selectionGroup, selectionGroupTitle, sum);
+ const aggregationstring = calculateGroupAggregations(selectionGroup);
+ infowindow.updateSelectionGroupFooter(selectionGroup, aggregationstring);
}
function onItemRemoved(event) {
@@ -193,6 +294,8 @@ const Selectionmanager = function Selectionmanager(options = {}) {
const sum = urval.get(selectionGroup).getFeatures().length;
infowindow.updateUrvalElementText(selectionGroup, selectionGroupTitle, sum);
+ const aggregationstring = calculateGroupAggregations(selectionGroup);
+ infowindow.updateSelectionGroupFooter(selectionGroup, aggregationstring);
if (urval.get(selectionGroup).getFeatures().length < 1) {
infowindow.hideUrvalElement(selectionGroup);
diff --git a/src/utils/formatareastring.js b/src/utils/formatareastring.js
new file mode 100644
index 000000000..3e1a97525
--- /dev/null
+++ b/src/utils/formatareastring.js
@@ -0,0 +1,27 @@
+/**
+ *Creates a pre-formatted string with a rounded value and unit depending on size
+ * @param {any} area The area in m2
+ * @param {any} opts
+ */
+function formatAreaString(area, opts = {}) {
+ const {
+ useHectare = true,
+ decimals
+ } = opts;
+ let result = area;
+ let unit = 'm2';
+ if (result > 10000000) {
+ result /= 1000000;
+ unit = 'km2';
+ } else if (result > 10000 && useHectare) {
+ result /= 10000;
+ unit = 'ha';
+ }
+ if (decimals !== undefined) {
+ result = result.toFixed(decimals);
+ }
+ const retstr = `${result} ${unit}`;
+ return retstr;
+}
+
+export default formatAreaString;
diff --git a/src/utils/formatlengthstring.js b/src/utils/formatlengthstring.js
new file mode 100644
index 000000000..616898f73
--- /dev/null
+++ b/src/utils/formatlengthstring.js
@@ -0,0 +1,24 @@
+/**
+ * Creates a pre-formatted string with a rounded value and unit depending on length
+ * @param {any} length Length in meters
+ * @param {any} opts
+ */
+function formatLengthString(length, opts = {}) {
+ const {
+ decimals
+ } = opts;
+ let result = length;
+ let unit = 'm';
+ if (result > 1000) {
+ result /= 1000;
+ unit = 'km';
+ }
+
+ if (decimals !== undefined) {
+ result = result.toFixed(decimals);
+ }
+ const retstr = `${result} ${unit}`;
+ return retstr;
+}
+
+export default formatLengthString;