diff --git a/package-lock.json b/package-lock.json
index 699fa39..0b7010b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,8 @@
"file-saver": "^2.0.5",
"i18next": "^23.7.6",
"i18next-browser-languagedetector": "^7.2.0",
+ "json-edit-react": "^1.15.4",
+ "jszip": "^3.10.1",
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-azure-maps": "^1.0.0",
@@ -11028,8 +11030,7 @@
"node_modules/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==",
- "dev": true
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
@@ -15438,6 +15439,11 @@
"node": ">= 4"
}
},
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+ },
"node_modules/immer": {
"version": "9.0.18",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz",
@@ -15519,8 +15525,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "2.0.0",
@@ -20379,6 +20384,18 @@
"node": ">=4"
}
},
+ "node_modules/json-edit-react": {
+ "version": "1.15.4",
+ "resolved": "https://registry.npmjs.org/json-edit-react/-/json-edit-react-1.15.4.tgz",
+ "integrity": "sha512-DxuIcBJ0FsuNrYzODDMc/C5TNP3sGpLR1Iq6UFtbYTKWJhOAt6BVncQdNZlwJ7lYO0HY0pjNyAUiWQCX3Sbayw==",
+ "dependencies": {
+ "object-property-assigner": "^1.3.0",
+ "object-property-extractor": "^1.0.11"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0"
+ }
+ },
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -20474,6 +20491,49 @@
"node": ">=4.0"
}
},
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/jszip/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/jszip/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/jszip/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
"node_modules/keyborg": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.2.0.tgz",
@@ -20552,6 +20612,14 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
"node_modules/lilconfig": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
@@ -21596,6 +21664,16 @@
"node": ">= 0.4"
}
},
+ "node_modules/object-property-assigner": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object-property-assigner/-/object-property-assigner-1.3.0.tgz",
+ "integrity": "sha512-19A0RsC9rP9klCKHDPL/MeERxeopV9wyMNfP+eD2uKOafzLjF+OUEN4FoP6RAlCFHmerBPJ4ohNv/WrgaNpeIA=="
+ },
+ "node_modules/object-property-extractor": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/object-property-extractor/-/object-property-extractor-1.0.11.tgz",
+ "integrity": "sha512-VnDQcyN0FTXZ0hMZS/CTb2QkIssZ9XKB8zlf5rnFh1HjFQX1P73EHawavSztBOiPDGqAPNXebv4agjhF9eACAw=="
+ },
"node_modules/object.assign": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
@@ -21953,6 +22031,11 @@
"node": ">=6"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
"node_modules/papaparse": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
@@ -23973,8 +24056,7 @@
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/proj4": {
"version": "2.8.1",
@@ -28602,8 +28684,7 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/util.promisify": {
"version": "1.0.1",
@@ -38057,8 +38138,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==",
- "dev": true
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
},
"cosmiconfig": {
"version": "7.1.0",
@@ -41358,6 +41438,11 @@
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
"dev": true
},
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+ },
"immer": {
"version": "9.0.18",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz",
@@ -41414,8 +41499,7 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "2.0.0",
@@ -45188,6 +45272,15 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
+ "json-edit-react": {
+ "version": "1.15.4",
+ "resolved": "https://registry.npmjs.org/json-edit-react/-/json-edit-react-1.15.4.tgz",
+ "integrity": "sha512-DxuIcBJ0FsuNrYzODDMc/C5TNP3sGpLR1Iq6UFtbYTKWJhOAt6BVncQdNZlwJ7lYO0HY0pjNyAUiWQCX3Sbayw==",
+ "requires": {
+ "object-property-assigner": "^1.3.0",
+ "object-property-extractor": "^1.0.11"
+ }
+ },
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -45266,6 +45359,51 @@
"object.assign": "^4.1.3"
}
},
+ "jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
"keyborg": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.2.0.tgz",
@@ -45326,6 +45464,14 @@
"type-check": "~0.4.0"
}
},
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
"lilconfig": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
@@ -46090,6 +46236,16 @@
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
},
+ "object-property-assigner": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object-property-assigner/-/object-property-assigner-1.3.0.tgz",
+ "integrity": "sha512-19A0RsC9rP9klCKHDPL/MeERxeopV9wyMNfP+eD2uKOafzLjF+OUEN4FoP6RAlCFHmerBPJ4ohNv/WrgaNpeIA=="
+ },
+ "object-property-extractor": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/object-property-extractor/-/object-property-extractor-1.0.11.tgz",
+ "integrity": "sha512-VnDQcyN0FTXZ0hMZS/CTb2QkIssZ9XKB8zlf5rnFh1HjFQX1P73EHawavSztBOiPDGqAPNXebv4agjhF9eACAw=="
+ },
"object.assign": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
@@ -46344,6 +46500,11 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
"papaparse": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
@@ -47649,8 +47810,7 @@
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"proj4": {
"version": "2.8.1",
@@ -51165,8 +51325,7 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"util.promisify": {
"version": "1.0.1",
diff --git a/package.json b/package.json
index 9f0fd49..c2f3130 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,8 @@
"file-saver": "^2.0.5",
"i18next": "^23.7.6",
"i18next-browser-languagedetector": "^7.2.0",
+ "json-edit-react": "^1.15.4",
+ "jszip": "^3.10.1",
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-azure-maps": "^1.0.0",
diff --git a/src/pages/conversion/download-imdf.js b/src/pages/conversion/download-imdf.js
index 7a3b863..ba991ab 100644
--- a/src/pages/conversion/download-imdf.js
+++ b/src/pages/conversion/download-imdf.js
@@ -1,11 +1,44 @@
-import { cx } from '@emotion/css';
-import { PrimaryButton } from '@fluentui/react';
-import { failedLogsButton, logsButton } from './style';
+import { cx } from '@emotion/css';
+import { PrimaryButton } from '@fluentui/react';
+import { logsButton } from './style';
+import React, { useState } from 'react';
+import JSZip from 'jszip';
+import { saveAs } from 'file-saver';
+
+// This component downloads the updated IMDF zip file and is triggered by users clicking "Download IMDF" button
+export const DownloadIMDF = ({ imdfPackageLocation, units, levels, footprint, building }) => {
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Fetches current zip file, updates the geojson files and downloads the updated zip
+ const handleUpdateZip = () => {
+ setIsLoading(true);
+ fetch(imdfPackageLocation)
+ .then(response => response.arrayBuffer())
+ .then(data => {
+ const zip = new JSZip();
+ return zip.loadAsync(data);
+ })
+ .then(zip => {
+ zip.file('unit.geojson', JSON.stringify(units, null, 2));
+ zip.file('level.geojson', JSON.stringify(levels, null, 2));
+ zip.file('footprint.geojson', JSON.stringify(footprint, null, 2));
+ zip.file('building.geojson', JSON.stringify(building, null, 2));
+ return zip.generateAsync({ type: 'blob' });
+ })
+ .then(updatedZip => {
+ saveAs(updatedZip, 'updated_imdf_package.zip');
+ setIsLoading(false);
+ })
+ .catch(() => {
+ setIsLoading(false);
+ });
+ };
-export const DownloadIMDF = ({ isFailed, link }) => {
return (
-
- Download IMDF
-
+
+
+ {isLoading ? 'Downloading..' : 'Download IMDF'}
+
+
);
};
diff --git a/src/pages/conversion/imdf-conversion.js b/src/pages/conversion/imdf-conversion.js
index db513ed..6367e43 100644
--- a/src/pages/conversion/imdf-conversion.js
+++ b/src/pages/conversion/imdf-conversion.js
@@ -1,4 +1,5 @@
import { cx } from '@emotion/css';
+import { useEffect, useState } from 'react';
import { MessageBar, MessageBarType } from '@fluentui/react';
import { Icon } from '@fluentui/react/lib/Icon';
import { PATHS } from 'common';
@@ -13,9 +14,34 @@ import { actionButtonsLeft, actionButtonsWrapper, messageWrapper } from './imdf-
import { ImdfDiagnostics } from './imdf-diagnostics';
import PlacesPreviewMap from './places-preview-map';
import StepButton from './step-button';
+import { processZip } from './places-preview-map/utils';
import { container, content, enabledStep, step as stepStyle, stepTitle, stepsContainer } from './style';
const ImdfConversion = () => {
+ const [units, setUnits] = useState({ features: [] });
+ const [levels, setLevels] = useState({ features: [] });
+ const [footprint, setFootprint] = useState({ features: [] });
+ const [building, setBuilding] = useState({ features: [] });
+
+ const handleUnitsChange = (editedUnits) => {
+ if(!editedUnits.type) {
+ editedUnits.type = 'FeatureCollection';
+ }
+ setUnits(editedUnits);
+ };
+
+ const handleLevelsChange = (editedLevels) => {
+ setLevels(editedLevels);
+ };
+
+ const handleFootprintChange = (editedFootprint) => {
+ setFootprint(editedFootprint);
+ };
+
+ const handleBuildingChange = (editedBuilding) => {
+ setBuilding(editedBuilding);
+ };
+
const { t } = useTranslation();
const navigate = useCustomNavigate();
@@ -28,7 +54,25 @@ const ImdfConversion = () => {
s.imdfPackageLocation,
s.diagnosticPackageLocation,
]);
+
+ useEffect(() => {
+ if (!imdfPackageLocation) return;
+
+ processZip(imdfPackageLocation).then(files => {
+ const unitFile = files.find(file => file.filename === 'unit.geojson');
+ const levelFile = files.find(file => file.filename === 'level.geojson');
+ const buildingFile = files.find(file => file.filename === 'building.geojson');
+ const footprintFile = files.find(file => file.filename === 'footprint.geojson');
+ if (unitFile && levelFile && footprintFile) {
+ setUnits(unitFile.content);
+ setLevels(levelFile.content);
+ setBuilding(buildingFile.content);
+ setFootprint(footprintFile.content);
+ }
+ });
+ }, [imdfPackageLocation]);
+
const { isRunningIMDFConversion, hasCompletedIMDFConversion, imdfConversionStatus, errorList } =
useIMDFConversionStatus();
@@ -76,7 +120,7 @@ const ImdfConversion = () => {
{imdfConversionStatus === conversionStatuses.finishedSuccessfully && (
-
+
)}
@@ -84,7 +128,7 @@ const ImdfConversion = () => {
{imdfConversionStatus === conversionStatuses.finishedSuccessfully && (
- {({ height }) => }
+ {({ height }) => }
)}
diff --git a/src/pages/conversion/places-preview-map/imdf-model-helpers/index.js b/src/pages/conversion/places-preview-map/imdf-model-helpers/index.js
index 3c3a29b..ef5b068 100644
--- a/src/pages/conversion/places-preview-map/imdf-model-helpers/index.js
+++ b/src/pages/conversion/places-preview-map/imdf-model-helpers/index.js
@@ -8,12 +8,12 @@ function drawingModeChanged(allLayers) {
}
// Displays information of a feature that is being drawn/edited, at time of click
-function currentEditData(map, drawingManager) {
+function currentEditData(map, drawingManager, setJsonData) {
map.events.add('drawingstarted', drawingManager, (f) => {
if(f?.data) {
- var shapeId = f.data.id;
- var geojsonData = drawingManager.getSource().getShapeById(shapeId).data;
- document.getElementById('infoPanel-json').value = JSON.stringify(geojsonData, null, 2);
+ var shapeId = f.data.id;
+ var geojsonData = drawingManager.getSource().getShapeById(shapeId).data;
+ setJsonData(geojsonData);
}
});
}
@@ -35,26 +35,119 @@ function groupAndSort(units, language, selectedLevel) {
return groupedFeatures;
}
-function writeToInfoPanel(geojsonData) {
- const { map, ...obj } = geojsonData;
- document.getElementById('infoPanel-json').value = JSON.stringify(obj, null, 2);
+// Changes the cursor to be a pointer when a clickable feature is hovered over
+function grabToPointer(layerName, hoverLayer, map) {
+ map.events.add('mousemove', layerName, function (e) {
+ hoverLayer.setOptions({ filter: ['==', ['get', '_azureMapsShapeId'], e.shapes[0].getProperties()['_azureMapsShapeId']] });
+ map.getCanvas().style.cursor = 'pointer';
+ });
+
+ map.events.add('mouseleave', layerName, function (e) {
+ hoverLayer.setOptions({ filter: ['==', ['get', '_azureMapsShapeId'], ''] });
+ map.getCanvas().style.cursor = 'grab';
+ });
+
+ map.events.add(['mousedown', 'mouseup'], layerName, function (e) {
+ map.getCanvas().style.cursor = 'pointer';
+ });
}
-// Changes the cursor to be a pointer when a clickable feature is hovered over
-function grabToPointer(layerName, map) {
- map.events.add('mouseover', layerName, function () {
- map.getCanvasContainer().style.cursor = 'pointer';
+// Changes cursor between grabbing and grabbing when map is clicked and dragged
+function grabAndGrabbing(map) {
+ map.events.add('mousedown', () => {
+ map.getCanvas().style.cursor = 'grabbing';
});
- map.events.add('mouseout', layerName, function () {
- map.getCanvasContainer().style.cursor = 'grab';
+ map.events.add('mouseup', () => {
+ map.getCanvas().style.cursor = 'grab';
});
}
+function updateSelectedColor(map, unitSelected) {
+ if(unitSelected) {
+ const lineLayer = map.layers.getLayerById('lineClickLayer');
+ try {
+ lineLayer.setOptions({strokeColor: 'hsla(0, 0%, 0%, 0)'});
+ } catch {
+ console.log('No line layer to change color of.')
+ }
+ const unitLayer = map.layers.getLayerById('unitClickChange');
+ try {
+ unitLayer.setOptions({fillColor: 'rgba(75, 146, 210, 0.8)'});
+ } catch {
+ console.log('No unit layer to change color of.')
+ }
+ } else {
+ const polygonLayer = map.layers.getLayerById('lineClickLayer');
+ try {
+ polygonLayer.setOptions({strokeColor: 'rgba(75, 146, 210, 0.8)'});
+ } catch {
+ console.log('No unit layer to change color of.')
+ }
+ const unitLayer = map.layers.getLayerById('unitClickChange');
+ try {
+ unitLayer.setOptions({fillColor: 'hsla(0, 0%, 0%, 0)'});
+ } catch {
+ console.log('No unit layer to change color of.')
+ }
+ }
+}
+
+function updateLevels(levels, selectedLevel, newValue) {
+ levels.features = levels.features.map(item =>
+ item.id === selectedLevel.id ? newValue : item
+ );
+
+ return levels;
+}
+
+function setFields(feature, selectedLevel) {
+ if (!feature.data.properties.name) {
+ feature.data.properties.name = {};
+ feature.data.properties.name.en = '';
+ }
+
+ if(!feature.data.properties.category) {
+ feature.data.properties.category = 'unspecified';
+ }
+
+ if(!feature.data.properties.level_id) {
+ feature.data.properties.level_id = selectedLevel.id;
+ }
+
+ if(!feature.data.properties.display_point) {
+ feature.data.properties.display_point = {};
+ feature.data.properties.display_point.type = 'Point';
+ feature.data.properties.display_point.coordinates = [];
+ }
+
+ if(!feature.data.properties.label) {
+ feature.data.properties.label = '';
+ }
+
+ if(feature.data.properties._azureMapsShapeId) {
+ let savedAzureId = feature.data.properties._azureMapsShapeId;
+ delete feature.data.properties._azureMapsShapeId;
+ feature.data.properties._azureMapsShapeId = savedAzureId;
+ }
+
+ if ('bbox' in feature.data.geometry) {
+ delete feature.data.geometry.bbox;
+ }
+}
+
+function deleteUnitPrevEdits(units, selectedLevel) {
+ units.features = units.features.filter(item => item.properties.level_id !== selectedLevel.id);
+}
+
export {
currentEditData,
groupAndSort,
drawingModeChanged,
- writeToInfoPanel,
- grabToPointer
+ grabToPointer,
+ updateLevels,
+ setFields,
+ deleteUnitPrevEdits,
+ grabAndGrabbing,
+ updateSelectedColor
};
\ No newline at end of file
diff --git a/src/pages/conversion/places-preview-map/indes.style.js b/src/pages/conversion/places-preview-map/indes.style.js
index 0f78087..6179d5d 100644
--- a/src/pages/conversion/places-preview-map/indes.style.js
+++ b/src/pages/conversion/places-preview-map/indes.style.js
@@ -18,27 +18,21 @@ export const imdfPreviewMap = css`
export const layerSelect = css `
position: relative;
- top: 65%;
+ bottom: -55%;
`;
+
export const textWrapper = css `
- flex: 1 1 4rem;
+ flex: 1 1 5rem;
position: relative;
display: inline;
- margin-top: 1%;
`;
-export const textArea = css `
- width: 95%;
- height: 95%;
- white-space: nowrap;
- resize: none;
+export const toolbar = css `
+ backgroundColor: #f0f0f0,
+ border: 2px solid #0078d4,
+ color: #0078d4,
+ cursor: not-allowed,
+ pointerEvents: none
`;
-export const saveButtonWrapper = css `
- display: flex;
- flex-direction: row;
- justify-content: flex-end;
-`;
-
-
diff --git a/src/pages/conversion/places-preview-map/index.js b/src/pages/conversion/places-preview-map/index.js
index 1807348..d90d4ec 100644
--- a/src/pages/conversion/places-preview-map/index.js
+++ b/src/pages/conversion/places-preview-map/index.js
@@ -1,29 +1,32 @@
-import { Map, layer, source, control } from 'azure-maps-control';
+import { Map, layer, source, control, HtmlMarker } from 'azure-maps-control';
import { control as draw_control, drawing } from 'azure-maps-drawing-tools';
import { getDomain, useConversionStore, useLevelsStore, useUserStore } from 'common/store';
-import { useEffect, useMemo, useState } from 'react';
-import { DefaultButton } from '@fluentui/react';
-import { logsButton } from '../style';
-import { imdfPreviewMap, imdfPreviewMapWrapper, layerSelect, textWrapper, textArea, mapTextWrapper, saveButtonWrapper } from './indes.style';
+import { useEffect, useMemo, useState, useRef } from 'react';
+import { imdfPreviewMap, imdfPreviewMapWrapper, layerSelect, textWrapper, mapTextWrapper } from './indes.style';
import LevelSelector from './level-selector';
import LayerSelector from './layer-selector';
import MapNotification from './map-notification';
import { calculateBoundingBox, getFeatureLabel, getFillStyles, getLineStyles, getTextStyle, processZip } from './utils';
-import { currentEditData, groupAndSort, drawingModeChanged, writeToInfoPanel, grabToPointer } from './imdf-model-helpers';
+import { currentEditData, groupAndSort, drawingModeChanged, updateLevels, setFields, deleteUnitPrevEdits, grabAndGrabbing, grabToPointer, updateSelectedColor } from './imdf-model-helpers';
+import { JsonEditor } from 'json-edit-react'
import 'azure-maps-drawing-tools/dist/atlas-drawing.min.css';
import 'azure-maps-control/dist/atlas.min.css';
-const PlacesPreviewMap = ({ style }) => {
+const PlacesPreviewMap = ({ style, unitsChanged, levelsChanged, footprintChanged, buildingChanged }) => {
const [geography, subscriptionKey] = useUserStore(s => [s.geography, s.subscriptionKey]);
const [imdfPackageLocation] = useConversionStore(s => [s.imdfPackageLocation]);
const [language] = useLevelsStore(s => [s.language]);
- const [units, setUnits] = useState({ features: [] });
+ const [units, setUnits] = useState({ features: [], type: 'FeatureCollection' });
const [levels, setLevels] = useState({ features: [] });
-
- // const [building, setBuilding] = useState({ features: [] }); // building will be used eventually
+ const [building, setBuilding] = useState({ features: [] });
const [footprint, setFootprint] = useState({ features: [] });
+ const [jsonData, setJsonData] = useState({});
+ const newDataRef = useRef(false);
+
+ // const [prevStates, setPrevStates] = useState([]); // Tracking previous changes for Undo feature
+
useEffect(() => {
if (!imdfPackageLocation) return;
@@ -36,13 +39,15 @@ const PlacesPreviewMap = ({ style }) => {
if (unitFile && levelFile && buildingFile && footprintFile) {
setUnits(unitFile.content);
setLevels(levelFile.content);
- // setBuilding(buildingFile.content);
+ setBuilding(buildingFile.content);
setFootprint(footprintFile.content);
}
});
}, [imdfPackageLocation]);
const [selectedLevelId, setSelectedLevelId] = useState(null);
+ const [selectedLayerId, setSelectedLayerId] = useState(null);
+ const [drawNotif, setDrawNotif] = useState(false); // Triggers drawing tip if true
const selectedLevel = useMemo(() => {
const level = levels.features.find(item => item.id === selectedLevelId) || levels.features[0] || {};
@@ -50,8 +55,6 @@ const PlacesPreviewMap = ({ style }) => {
return level;
}, [levels, selectedLevelId]);
- const [selectedLayerId, setSelectedLayerId] = useState(null);
-
useEffect(() => {
var drawingManager;
@@ -61,7 +64,7 @@ const PlacesPreviewMap = ({ style }) => {
language: 'en-US',
domain: getDomain(geography),
staticAssetsDomain: getDomain(geography),
- style: 'blank',
+ style: 'dark',
});
map.controls.add([new control.ZoomControl()], {
@@ -70,6 +73,8 @@ const PlacesPreviewMap = ({ style }) => {
map.events.add('ready', () => {
var drawingToolbar;
+ setJsonData({});
+
if(selectedLayerId === 'unitButton') {
drawingToolbar = new draw_control.DrawingToolbar({
position: 'bottom-right',
@@ -104,18 +109,17 @@ const PlacesPreviewMap = ({ style }) => {
});
footprintInteractions(footprint, drawingManager, map);
+ } else if(selectedLayerId === 'buildingButton') {
+ buildingInteractions(building, map);
} else {
- drawingToolbar = new draw_control.DrawingToolbar({
- position: 'bottom-right',
- style: 'light',
- buttons: ['edit-geometry']
- });
-
- drawingManager = new drawing.DrawingManager(map, {
- toolbar: drawingToolbar
- });
+ fullViewInteractions(units, levels, map);
+ }
- fullViewInteractions(units, levels, drawingManager, map);
+ if(selectedLayerId !== 'buildingButton') {
+ var layers = drawingManager.getLayers();
+ map.events.add('mousedown', layers.polygonLayer, handleDeletion);
+ map.events.add('touchstart', layers.polygonLayer, handleDeletion);
+ map.events.add('click', layers.polygonLayer, handleDeletion);
}
});
@@ -128,107 +132,178 @@ const PlacesPreviewMap = ({ style }) => {
features = map.layers.getRenderedShapes(e.position, 'levelClick');
else if(selectedLayerId === 'footprintButton')
features = map.layers.getRenderedShapes(e.position, 'footprintClick');
+ else if(selectedLayerId === 'buildingButton') {
+
+ }
else
features = map.layers.getRenderedShapes(e.position, ['unitClick', 'levelClick']);
- features.forEach(function (feature) {
- writeToInfoPanel(feature.data);
- });
+ if(selectedLayerId !== 'buildingButton') {
+ features.forEach(function (feature) {
+ const newData = feature.data || {};
+ setJsonData(newData);
+ });
+ }
});
- // Shows change in corresponding feature color when mouse hovers over that feature
- function featureHover(layerName, polygonHoverLayer) {
- map.events.add('mousemove', layerName, function (e) {
- polygonHoverLayer.setOptions({ filter: ['==', ['get', '_azureMapsShapeId'], e.shapes[0].getProperties()['_azureMapsShapeId']] });
- });
+ grabAndGrabbing(map);
- map.events.add('mouseleave', layerName, function (e) {
- polygonHoverLayer.setOptions({ filter: ['==', ['get', '_azureMapsShapeId'], ''] });
- });
- }
+ // Shows change in corresponding feature color when mouse hovers over OR clicks on that feature
+ let selectedFeatureID = null;
+ function featureHoverClick(layerName, hoverLayer, clickLayer, unitSelected=false) {
+ grabToPointer(layerName, hoverLayer, map);
- // Entry point when "unit.geojson" is pressed; the following code should be refactored due to redundancy
- function unitInteractions(units, drawingManager, map) {
- var unitLayer, unitLines, polygonHoverLayer, unitSymbols;
- var layersAdded = [unitLayer, unitLines, polygonHoverLayer, unitSymbols];
- const groupedFeatures = groupAndSort(units, language, selectedLevel);
- const keys = Object.keys(groupedFeatures);
-
- keys.forEach(category => {
- const features = groupedFeatures[category].features;
-
- const dataSource = new source.DataSource();
- map.sources.add(dataSource);
- dataSource.add(features);
-
- unitLayer = new layer.PolygonLayer(dataSource, 'unitClick', getFillStyles('unit', category));
- unitLines = new layer.LineLayer(dataSource, null, getLineStyles('unit', category));
- polygonHoverLayer = new layer.PolygonLayer(dataSource, null, {
- fillColor: 'rgba(150, 50, 255, 0.2)',
- filter: ['==', ['get', 'id'], '']
- });
+ map.events.add('click', layerName, function (e) {
+ const clickedFeatureID = e.shapes[0].getProperties()['_azureMapsShapeId'];
+ map.getCanvas().style.cursor = 'pointer';
- unitSymbols = new layer.SymbolLayer(dataSource, null, getTextStyle(category));
-
- map.layers.add([unitLayer, polygonHoverLayer, unitLines, unitSymbols], 'roomPolygons');
-
- grabToPointer([unitLayer, polygonHoverLayer], map);
- featureHover(unitLayer, polygonHoverLayer);
-
- // map.layers.add(new layer.SymbolLayer(dataSource, null, getTextStyle(category)), 'roomLabels');
-
- var drawingSource = drawingManager.getSource();
- drawingSource.add(features);
-
- let dmLayers = drawingManager.getLayers();
- dmLayers.polygonLayer.setOptions({ visible: false });
- dmLayers.polygonOutlineLayer.setOptions({ visible: false });
- layersAdded = [unitLayer, unitLines, polygonHoverLayer, unitSymbols];
-
- map.events.add('drawingmodechanged', drawingManager, (e) => {
- let dmLayers = drawingManager.getLayers();
- layersAdded = [unitLayer, unitLines, polygonHoverLayer, unitSymbols];
-
- if (e === 'idle') {
- dmLayers.polygonLayer.setOptions({ visible: false });
- dmLayers.polygonOutlineLayer.setOptions({ visible: false });
- map.layers.remove([unitLayer, unitLines, polygonHoverLayer]);
-
- unitLayer = new layer.PolygonLayer(drawingManager.getSource(), 'unitClick', getFillStyles('unit', category));
- unitLines = new layer.LineLayer(drawingManager.getSource(), null, getLineStyles('unit', category));
- polygonHoverLayer = new layer.PolygonLayer(drawingManager.getSource(), null, {
- fillColor: 'rgba(150, 50, 255, 0.2)',
- filter: ['==', ['get', 'id'], '']
- });
- unitSymbols = new layer.SymbolLayer(drawingManager.getSource(), null, getTextStyle(category));
-
- map.layers.add([unitLayer, polygonHoverLayer, unitLines, unitSymbols], 'roomPolygons');
-
- grabToPointer([unitLayer, polygonHoverLayer], map);
- featureHover(unitLayer, polygonHoverLayer);
- layersAdded = [unitLayer, unitLines, polygonHoverLayer, unitSymbols];
- }
- else if (e === 'edit-geometry' || e === 'erase-geometry' || e === 'draw-polygon') {
- drawingModeChanged(layersAdded);
- dmLayers.polygonLayer.setOptions({ visible: true });
- dmLayers.polygonOutlineLayer.setOptions({ visible: true });
- }
- else {
- // This will eventually be a visible pop-up
- console.log('Not a valid drawing toolbar option.');
- }
- });
-
- currentEditData(map, drawingManager);
+ if (selectedFeatureID !== clickedFeatureID) {
+ // If feature is clicked, change color of feature
+ selectedFeatureID = clickedFeatureID;
+ clickLayer.setOptions({
+ filter: ['==', ['get', '_azureMapsShapeId'], clickedFeatureID]
+ });
+
+ // Handles the change in color of the feature when clicked (for full view)
+ updateSelectedColor(map, unitSelected)
+
+ var features;
+ if(selectedLayerId === 'unitButton')
+ features = map.layers.getRenderedShapes(e.position, 'unitClick');
+ else if(selectedLayerId === 'levelButton')
+ features = map.layers.getRenderedShapes(e.position, 'levelFill');
+ else if(selectedLayerId === 'footprintButton')
+ features = map.layers.getRenderedShapes(e.position, 'footprintClick');
+ else
+ features = map.layers.getRenderedShapes(e.position, ['unitClick', 'levelClick']);
+
+ features.forEach(function (feature) {
+ const newData = feature.data || {};
+ setJsonData(newData);
+ });
+ }
+ else {
+ // If feature clicked is currently chosen OR it is another feature, change color of feature back to original
+ selectedFeatureID = null;
+ clickLayer.setOptions({
+ filter: ['==', ['get', '_azureMapsShapeId'], '']
+ });
+ setJsonData({});
+ }
});
}
+ function handleDeletion(e) {
+ if (drawingManager.getOptions().mode === 'erase-geometry') {
+ if (window.confirm('Do you want to proceed with removing this feature?')) {
+ drawingManager.getSource().remove(e.shapes[0]);
+ }
+ }
+ }
+
+ // Entry point when "unit.geojson" is pressed; the following code should be refactored due to redundancy
+ function unitInteractions(units, drawingManager, map) {
+ // Update the units state with the edited features (for updating zip)
+ unitsChanged(units);
+
+ var unitLayer, unitLines, polygonHoverLayer, polygonClickLayer, unitSymbols;
+ var layersAdded = [unitLayer, unitLines, polygonHoverLayer, polygonClickLayer, unitSymbols];
+ const groupedFeatures = groupAndSort(units, language, selectedLevel);
+ const keys = Object.keys(groupedFeatures);
+
+ keys.forEach(category => {
+ var features = groupedFeatures[category].features;
+ const dataSource = new source.DataSource();
+ map.sources.add(dataSource);
+ dataSource.add(features);
+
+ unitLayer = new layer.PolygonLayer(dataSource, 'unitClick', getFillStyles('unit', category));
+ unitLines = new layer.LineLayer(dataSource, null, getLineStyles('unit', category));
+ polygonHoverLayer = new layer.PolygonLayer(dataSource, null, {
+ fillColor: 'rgba(135, 206, 250, 0.8)',
+ filter: ['==', ['get', 'id'], ''],
+ cursor: 'pointer !important',
+ });
+
+ polygonClickLayer = new layer.PolygonLayer(dataSource, 'unitClickChange', {
+ fillColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], ''] ,
+ cursor: 'pointer !important',
+ });
+
+ unitSymbols = new layer.SymbolLayer(dataSource, null, getTextStyle(category));
+ map.layers.add([unitLayer, polygonHoverLayer, unitLines, polygonClickLayer, unitSymbols], 'roomPolygons');
+ featureHoverClick(unitLayer, polygonHoverLayer, polygonClickLayer, true);
+
+ var drawingSource = drawingManager.getSource();
+ drawingSource.add(features);
+
+ let dmLayers = drawingManager.getLayers();
+ dmLayers.polygonLayer.setOptions({ visible: false });
+ dmLayers.polygonOutlineLayer.setOptions({ visible: false });
+ layersAdded = [unitLayer, unitLines, polygonHoverLayer, polygonClickLayer, unitSymbols];
+
+ // Saving previous states for undo functionality
+ // if(prevStates.length === 0 || units.features !== prevStates[prevStates.length - 1].features) {
+ // setPrevStates(prev => [...prev, {...units}]);
+ // }
+
+ map.events.add('drawingmodechanged', drawingManager, (e) => {
+ let dmLayers = drawingManager.getLayers();
+ layersAdded = [unitLayer, unitLines, polygonHoverLayer, polygonClickLayer, unitSymbols];
+
+ if (e === 'idle') {
+ setDrawNotif(false);
+ let updatedFeatures = drawingManager.getSource().shapes;
+
+ deleteUnitPrevEdits(units, selectedLevel);
+
+ const newFeatures = updatedFeatures.reduce((acc, feature) => {
+ if (isNaN(feature.data.properties.ordinal)) {
+ setFields(feature, selectedLevel);
+ acc.push(feature.data);
+ }
+ return acc;
+ }, []);
+
+ setUnits(prevUnits => ({
+ features: [...prevUnits.features, ...newFeatures],
+ }));
+ }
+ else if (e === 'edit-geometry' || e === 'erase-geometry' || e === 'draw-polygon') {
+ drawingModeChanged(layersAdded);
+ dmLayers.polygonLayer.setOptions({ visible: true });
+ dmLayers.polygonOutlineLayer.setOptions({ visible: true });
+
+ if(e === 'draw-polygon') {
+ setDrawNotif(true);
+ map.events.add('mousedown', function (e) {
+ map.getCanvas().style.cursor = 'crosshair';
+ });
+
+ map.events.add('mouseup', function (e) {
+ map.getCanvas().style.cursor = 'crosshair';
+ });
+ }
+ if(e === 'edit-geometry') {
+ currentEditData(map, drawingManager, setJsonData);
+ }
+ }
+ else {
+ // This will eventually be a visible pop-up
+ console.log('Not a valid drawing toolbar option.');
+ }
+ });
+ });
+ }
+
// Entry point when "level.geojson" is pressed; the following code should be refactored due to redundancy
function levelInteractions(levels, drawingManager, map) {
// Retrieve information about the level currently chosen by user
+ levelsChanged(levels);
const selectedLevelDetails = levels.features.filter(item => item.id === selectedLevel.id);
- var lineLayer, lineHoverLayer;
- var layersAdded = [lineLayer, lineHoverLayer];
+ var lineLayer, lineHoverLayer, lineClickLayer, linePolygonLayer;
+ var layersAdded = [lineLayer, lineHoverLayer, lineClickLayer, linePolygonLayer];
const dataSource = new source.DataSource();
map.sources.add(dataSource);
@@ -236,15 +311,20 @@ const PlacesPreviewMap = ({ style }) => {
// Displays outline of level + change in color when cursor is hovering
lineLayer = new layer.LineLayer(dataSource, 'levelClick', getLineStyles('level', 'walkway'));
- lineHoverLayer = new layer.LineLayer(dataSource, null, {
- fillColor: 'rgba(150, 50, 255, 0.2)',
+ linePolygonLayer = new layer.PolygonLayer(dataSource, 'levelFill', getFillStyles('level', 'unspecified'));
+ lineHoverLayer = new layer.PolygonLayer(dataSource, null, {
+ fillColor: 'rgba(135, 206, 250, 0.8)',
filter: ['==', ['get', '_azureMapsShapeId'], '']
});
- map.layers.add([lineLayer, lineHoverLayer], 'walkwayPolygons');
- grabToPointer([lineLayer, lineHoverLayer], map);
- layersAdded = [lineLayer, lineHoverLayer];
- featureHover(lineLayer, lineHoverLayer);
+ lineClickLayer = new layer.PolygonLayer(dataSource, 'lineClickLayer', {
+ fillColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], '']
+ });
+
+ map.layers.add([lineLayer, linePolygonLayer, lineHoverLayer, lineClickLayer], 'walkwayPolygons');
+ layersAdded = [lineLayer, lineHoverLayer, lineClickLayer, linePolygonLayer];
+ featureHoverClick(linePolygonLayer, lineHoverLayer, lineClickLayer);
var drawingSource = drawingManager.getSource();
drawingSource.add(selectedLevelDetails);
@@ -260,32 +340,53 @@ const PlacesPreviewMap = ({ style }) => {
dmLayers.polygonOutlineLayer.setOptions({ visible: false });
var lineLayer = new layer.LineLayer(drawingManager.getSource(), 'levelClick', getLineStyles('level', 'walkway'));
- lineHoverLayer = new layer.LineLayer(drawingManager.getSource(), null, {
- fillColor: 'rgba(150, 50, 255, 0.2)',
+ linePolygonLayer = new layer.PolygonLayer(drawingManager.getSource(), 'levelFill', getFillStyles('level', 'unspecified'));
+ lineHoverLayer = new layer.PolygonLayer(drawingManager.getSource(), null, {
+ fillColor: 'rgba(135, 206, 250, 0.8)',
filter: ['==', ['get', '_azureMapsShapeId'], '']
});
+
+ lineClickLayer = new layer.PolygonLayer(drawingManager.getSource(), 'lineClickLayer', {
+ fillColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], '']
+ });
+
+ map.layers.add([lineLayer, lineHoverLayer, lineClickLayer, linePolygonLayer], 'walkwayPolygons');
+ layersAdded = [lineLayer, lineHoverLayer, lineClickLayer, linePolygonLayer];
+ featureHoverClick(linePolygonLayer, lineHoverLayer, lineClickLayer);
+
+ let updatedFeatures = drawingManager.getSource().shapes;
+ setLevels(prevLevels => updateLevels(prevLevels, selectedLevel, (updatedFeatures[0]).data));
- map.layers.add([lineLayer, lineHoverLayer], 'walkwayPolygons');
- grabToPointer([lineLayer, lineHoverLayer], map);
- layersAdded = [lineLayer, lineHoverLayer];
- featureHover(lineLayer, lineHoverLayer);
+ // Update the levels state with the edited features (for updating zip)
+ levelsChanged(levels);
}
- else if (e === 'edit-geometry' || e === 'erase-geometry' || e === 'draw-polygon') {
+ else if (e === 'edit-geometry') {
drawingModeChanged(layersAdded);
dmLayers.polygonOutlineLayer.setOptions({ visible: true });
- dmLayers.polygonLayer.setOptions({ visible: false });
+ dmLayers.polygonLayer.setOptions({ visible: true });
+
+ currentEditData(map, drawingManager, setJsonData);
+
+ document.addEventListener('keydown', function (e) {
+ // Check if the delete or backspace key is pressed
+ if (e.key === 'Delete' || e.key === 'Backspace') {
+ drawingManager.setOptions({ mode: 'idle' });
+ }
+ });
}
else {
// This will eventually be a visible pop-up
console.log('Not a valid drawing toolbar option.');
}
- });
+ });
}
// Entry point when "footprint.geojson" is pressed; the following code should be refactored due to redundancy
function footprintInteractions(footprint, drawingManager, map) {
- var footprintLayer, footprintLines, footprintHoverLayer;
- var layersAdded = [footprintLayer, footprintLines, footprintHoverLayer];
+ footprintChanged(footprint);
+ var footprintLayer, footprintLines, footprintHoverLayer, footprintClickLayer;
+ var layersAdded = [footprintLayer, footprintLines, footprintHoverLayer, footprintClickLayer];
const groupedFeatures = {};
const keys = Object.keys(groupedFeatures);
@@ -298,14 +399,18 @@ const PlacesPreviewMap = ({ style }) => {
footprintLines = new layer.LineLayer(dataSource, null, getLineStyles('footprint', 'walkway'));
footprintLayer = new layer.PolygonLayer(dataSource, 'footprintClick', getFillStyles('footprint', keys.category));
footprintHoverLayer = new layer.PolygonLayer(dataSource, null, {
- fillColor: 'rgba(150, 50, 255, 0.2)',
+ fillColor: 'rgba(135, 206, 250, 0.8)',
filter: ['==', ['get', '_azureMapsShapeId'], '']
});
- map.layers.add([footprintLayer, footprintHoverLayer, footprintLines], 'roomPolygons');
- grabToPointer([footprintLayer, footprintHoverLayer], map);
- featureHover(footprintLayer, footprintHoverLayer);
- layersAdded = [footprintLayer, footprintLines, footprintHoverLayer];
+ footprintClickLayer = new layer.PolygonLayer(dataSource, null, {
+ fillColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], '']
+ });
+
+ map.layers.add([footprintLayer, footprintHoverLayer, footprintLines, footprintClickLayer], 'roomPolygons');
+ featureHoverClick(footprintLayer, footprintHoverLayer, footprintClickLayer);
+ layersAdded = [footprintLayer, footprintLines, footprintHoverLayer, footprintClickLayer];
var drawingSource = drawingManager.getSource();
@@ -324,39 +429,113 @@ const PlacesPreviewMap = ({ style }) => {
footprintLines = new layer.LineLayer(drawingManager.getSource(), null, getLineStyles('footprint', 'walkway'));
footprintLayer = new layer.PolygonLayer(drawingManager.getSource(), 'footprintClick', getFillStyles('footprint', keys.category));
footprintHoverLayer = new layer.PolygonLayer(drawingManager.getSource(), null, {
- fillColor: 'rgba(150, 50, 255, 0.2)',
+ fillColor: 'rgba(135, 206, 250, 0.8)',
filter: ['==', ['get', '_azureMapsShapeId'], '']
});
+ footprintClickLayer = new layer.PolygonLayer(drawingManager.getSource(), null, {
+ fillColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], '']
+ });
+
+ map.layers.add([footprintLayer, footprintHoverLayer, footprintLines, footprintClickLayer], 'roomPolygons');
+ featureHoverClick(footprintLayer, footprintHoverLayer, footprintClickLayer);
+ layersAdded = [footprintLayer, footprintLines, footprintHoverLayer, footprintClickLayer];
- map.layers.add([footprintLayer, footprintHoverLayer, footprintLines], 'roomPolygons');
- grabToPointer([footprintLayer, footprintHoverLayer], map);
- featureHover(footprintLayer, footprintHoverLayer);
- layersAdded = [footprintLayer, footprintLines, footprintHoverLayer];
+ let updatedFeatures = drawingManager.getSource().shapes;
+ footprint.features[0] = updatedFeatures[0].data;
+
+ // Update the footprint state with the edited features (for updating zip)
+ footprintChanged(footprint);
}
- else if (e === 'edit-geometry' || e === 'erase-geometry' || e === 'draw-polygon') {
+ else if (e === 'edit-geometry') {
drawingModeChanged(layersAdded);
dmLayers.polygonLayer.setOptions({ visible: true });
dmLayers.polygonOutlineLayer.setOptions({ visible: true });
+
+ currentEditData(map, drawingManager, setJsonData);
}
else {
// This will eventually be a visible pop-up
console.log('Not a valid drawing toolbar option.');
}
- });
+ });
+ }
+
+ function buildingInteractions(building, map) {
+ var buildingLayer = new HtmlMarker({
+ position: building.features[0].properties.display_point.coordinates,
+ });
+ map.events.add('click', buildingLayer, highlight);
+ map.markers.add(buildingLayer);
+
+ function highlight(e) {
+ setJsonData(building.features[0]);
+ }
+
+ map.setCamera({
+ zoom: 12
+ });
}
// Entry point when "full view" is pressed; the following code may need to be changed to allow fill color of units while editing
- // Needs to be given edit properties
- function fullViewInteractions(units, levels, drawingManager, map) {
- unitInteractions(units, drawingManager, map);
- levelInteractions(levels, drawingManager, map);
+ function fullViewInteractions(units, levels, map) {
+ unitsChanged(units);
+ levelsChanged(levels);
+
+ var unitLayer, unitLines, polygonHoverLayer, polygonClickLayer, unitSymbols, lineLayer, lineHoverLayer, lineClickLayer;
+ const groupedFeatures = groupAndSort(units, language, selectedLevel);
+ const keys = Object.keys(groupedFeatures);
+
+ keys.forEach(category => {
+ var features = groupedFeatures[category].features;
+ const dataSource = new source.DataSource();
+ map.sources.add(dataSource);
+ dataSource.add(features);
+
+ unitLayer = new layer.PolygonLayer(dataSource, 'unitClick', getFillStyles('unit', category));
+ unitLines = new layer.LineLayer(dataSource, null, getLineStyles('unit', category));
+ polygonHoverLayer = new layer.PolygonLayer(dataSource, null, {
+ fillColor: 'rgba(135, 206, 250, 0.8)',
+ filter: ['==', ['get', 'id'], ''],
+ cursor: 'pointer !important',
+ });
+
+ polygonClickLayer = new layer.PolygonLayer(dataSource, 'unitClickChange', {
+ fillColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], ''] ,
+ cursor: 'pointer !important',
+ });
+
+ unitSymbols = new layer.SymbolLayer(dataSource, null, getTextStyle(category));
+ map.layers.add([unitLayer, polygonHoverLayer, unitLines, polygonClickLayer, unitSymbols], 'roomPolygons');
+ featureHoverClick(unitLayer, polygonHoverLayer, polygonClickLayer, true);
+ });
+
+ const selectedLevelDetails = levels.features.filter(item => item.id === selectedLevel.id);
+ const dataSource = new source.DataSource();
+ map.sources.add(dataSource);
+ dataSource.add(selectedLevelDetails);
+
+ // Displays outline of level + change in color when cursor is hovering
+ lineLayer = new layer.LineLayer(dataSource, 'levelClick', getLineStyles('level', 'walkway'));
+ lineHoverLayer = new layer.LineLayer(dataSource, null, {
+ strokeColor: 'rgba(135, 206, 250, 0.8)',
+ filter: ['==', ['get', '_azureMapsShapeId'], '']
+ });
+ lineClickLayer = new layer.LineLayer(dataSource, 'lineClickLayer', {
+ strokeColor: 'rgba(75, 146, 210, 0.8)',
+ filter: ['==', ['get', 'id'], '']
+ });
+
+ map.layers.add([lineLayer, lineHoverLayer, lineClickLayer], 'walkwayPolygons');
+ featureHoverClick(lineLayer, lineHoverLayer, lineClickLayer, false);
}
// Cleanup function to remove the map instance when component unmounts or reinitializes
return () => {
map.dispose();
};
- }, [units, levels, footprint, selectedLevel, selectedLayerId, subscriptionKey, geography, language]);
+ }, [ units, levels, footprint, building, selectedLevel, selectedLayerId, subscriptionKey, geography, language, imdfPackageLocation, unitsChanged, levelsChanged, footprintChanged ]);
const handleLevelChange = levelId => {
setSelectedLevelId(levelId);
@@ -366,6 +545,56 @@ const PlacesPreviewMap = ({ style }) => {
setSelectedLayerId(layerId);
};
+ const updateJsonData = (newData) => {
+ setJsonData(newData);
+ };
+
+ // Handles updates when a property is changed in the JSON editor
+ const handleUpdate = ({ newData, currentData, newValue, currentValue, name, path }) => {
+ if(newData.feature_type === 'building') {
+ // Building
+ building.features = [newData];
+ buildingChanged(building);
+ }
+ else if (newData.properties.name && isNaN(newData.properties.ordinal)) {
+ // Unit
+ let editedIndex = units.features.findIndex(unit => unit.id === currentData.id);
+ if (editedIndex !== -1) {
+ // Replace the old data with the new data for specific feature, then save to zip
+ const updatedFeatures = [...units.features];
+ updatedFeatures[editedIndex] = {
+ ...newData,
+ properties: {
+ ...newData.properties,
+ label: newData.properties.name.en,
+ },
+ };
+
+ setUnits({ features: updatedFeatures }); // To update state to trigger map refresh
+ unitsChanged(units); // To update zip
+ newDataRef.current = true;
+ }
+ else {
+ console.log('Invalid property change.');
+ }
+ }
+ else if (newData.properties.name) {
+ // Level
+ let editedIndex = levels.features.findIndex(level => level.id === currentData.id);
+ if (editedIndex !== -1) {
+ // Replace the old data with the new data for specific feature, then save to zip
+ levels.features[editedIndex] = newData;
+ levelsChanged(levels);
+ }
+ else {
+ console.log('Invalid property change.');
+ }
+ }
+
+ return true;
+ };
+
+
return (
@@ -376,7 +605,6 @@ const PlacesPreviewMap = ({ style }) => {
options={levels.features.map(level => ({ key: level.id, text: getFeatureLabel(level, language) }))}
/>
-
{
Zoom in to see labels and icons.
+ {drawNotif &&
Click to draw a point. To connect the final lines of current drawing, press 'c'.}
-
-
+
+ {
+ const allowedFields = [
+ 'properties.name.en',
+ 'properties.category'
+ ];
+ return !allowedFields.includes(path.join('.'));
+ }}
+ restrictTypeSelection={ ({ path, value }) => {
+ if (typeof value === 'string')
+ return ['string'];
+ else
+ return ['string', 'number', 'boolean', 'array', 'object'];
+ }}
+ rootFontSize={12}
+ indent={2}
+ theme="githubLight"
+ onUpdate={handleUpdate}
+ />
-
-
-
- Save Changes
-
-
-
+
);
};
diff --git a/src/pages/conversion/places-preview-map/layer-selector/index.js b/src/pages/conversion/places-preview-map/layer-selector/index.js
index 62b1592..821da13 100644
--- a/src/pages/conversion/places-preview-map/layer-selector/index.js
+++ b/src/pages/conversion/places-preview-map/layer-selector/index.js
@@ -4,10 +4,11 @@ import { buttonStyle, layerSelectorWrapper, selectedButtonStyle } from './index.
const layerButtons = [
// will need to add a "buildingButton" once basemap is loaded
- {id: 'fullViewButton', text: 'full view'},
- {id: 'footprintButton', text: 'footprint.geojson'},
- {id: 'levelButton', text: 'level.geojson'},
{id: 'unitButton', text: 'unit.geojson'},
+ {id: 'levelButton', text: 'level.geojson'},
+ {id: 'footprintButton', text: 'footprint.geojson'},
+ {id: 'buildingButton', text: 'building.geojson'},
+ {id: 'fullViewButton', text: 'full view'},
];
const LayerSelector = props => {
diff --git a/src/pages/conversion/places-preview-map/layer-selector/index.style.js b/src/pages/conversion/places-preview-map/layer-selector/index.style.js
index c66977a..29a46a3 100644
--- a/src/pages/conversion/places-preview-map/layer-selector/index.style.js
+++ b/src/pages/conversion/places-preview-map/layer-selector/index.style.js
@@ -32,6 +32,7 @@ export const buttonStyle = css`
background-repeat: no-repeat;
overflow: hidden;
+
&:hover {
color: #31acce;
}
diff --git a/src/pages/conversion/places-preview-map/utils.js b/src/pages/conversion/places-preview-map/utils.js
index 037fa07..fb06013 100644
--- a/src/pages/conversion/places-preview-map/utils.js
+++ b/src/pages/conversion/places-preview-map/utils.js
@@ -7,13 +7,10 @@ import stairs from './assets/stairs.svg';
import { categoryToFillColor, defaultFillColor, levelStyles, levelOnlyStyles, textStyles, footprintStyles, unitStyles } from './theme/floorPlanStyles';
export function getFillStyles(featureType, category) {
- if (featureType !== 'unit') {
- return {};
- }
-
var fillStyle = {
fillColor: categoryToFillColor.has(category) ? categoryToFillColor.get(category) : defaultFillColor,
fillOpacity: 1,
+ cursor: 'pointer !important',
};
return fillStyle;
@@ -41,6 +38,7 @@ export function getLineStyles(featureType, category) {
strokeWidth: levelStyles.lineWidth,
lineJoin: 'round',
lineCap: 'round',
+ fillColor: 'hsla(0, 0%, 0%, 0)',
};
}