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;