From 88048f873db0d27a3eda413d95e0a6d42b9e34fa Mon Sep 17 00:00:00 2001 From: Tom <58078313+reed-tom@users.noreply.github.com> Date: Fri, 12 May 2023 13:28:33 -0400 Subject: [PATCH 01/11] Bundle/various bugfixes enhancements updates (#212) * added fallback layers under esri community basemaps * excluded esri community layers from print request * fixed "more menu" theme and tool display and enabled floating button when sidebar is open * removed stats canada from add data tool as url no longer valid * various bugfixes, enhancements, and updates merged in --- package-lock.json | 71 +-- package.json | 15 +- src/config.json | 37 +- src/header/Search.jsx | 44 +- src/helpers/FloatingImageSlider.jsx | 64 +-- src/helpers/InfoRow.jsx | 15 +- src/helpers/OLHelpers.js | 145 +++++- src/helpers/Popup.jsx | 83 +-- src/helpers/SC.css | 4 +- src/helpers/URLWindow.jsx | 274 +++++----- src/helpers/drawingHelpers.js | 34 +- src/helpers/helpers.js | 69 ++- src/helpers/images/email-icon.png | Bin 0 -> 410 bytes src/helpers/images/information.png | Bin 0 -> 778 bytes src/helpers/images/notebook.png | Bin 0 -> 649 bytes src/layerInfo/App.js | 3 +- src/layerInfo/config.json | 3 +- src/legend/App.js | 3 +- src/legend/config.json | 1 - src/map/Attachments.jsx | 39 ++ src/map/BMap.jsx | 75 +-- src/map/BasemapSwitcher.jsx | 66 ++- src/map/BasicBasemapSwitcher.jsx | 24 +- src/map/FooterTools.jsx | 8 +- src/map/Identify.css | 147 +++--- src/map/Identify.jsx | 64 ++- src/map/Navigation.css | 25 + src/map/Navigation.jsx | 292 ++++++----- src/map/OLOverrides.css | 179 +++---- src/map/PropertyReportClick.jsx | 1 - src/map/SCMap.jsx | 24 +- src/map/basemapSwitcherConfig.json | 85 ++- src/sidebar/MenuButton.css | 274 +++++----- src/sidebar/MenuButton.jsx | 20 +- src/sidebar/Sidebar.jsx | 7 +- src/sidebar/components/mymaps/ButtonBar.jsx | 198 +++---- src/sidebar/components/mymaps/ColorBar.jsx | 279 +++++----- src/sidebar/components/mymaps/MyMaps.css | 486 +++++++++--------- src/sidebar/components/mymaps/MyMaps.jsx | 10 +- .../components/mymaps/images/email-icon.png | Bin 0 -> 410 bytes .../components/mymaps/images/loading20.gif | Bin 0 -> 5239 bytes .../five11livefeeds/Five11LiveFeeds.jsx | 2 - .../localrealestate/LocalRealEstate.jsx | 4 +- .../LocalRealEstateRecents.jsx | 134 ++--- .../themeComponents/ThemePopupContent.css | 3 +- .../components/toc/common/TOCHelpers.jsx | 34 +- .../toc/toc-folder-view/LayerItem.css | 138 ++--- .../toc/toc-folder-view/LayerItem.jsx | 4 + .../toc/toc-list-view/LayerItem.jsx | 4 + .../components/tools/addlayer/config.json | 13 +- src/sidebar/components/tools/print/Print.css | 100 ++-- src/sidebar/components/tools/print/Print.jsx | 18 +- .../tools/print/printRequest/printRequest.js | 48 +- .../components/tools/settings/Settings.jsx | 9 - src/sidebar/images/loading.gif | Bin 0 -> 14917 bytes 55 files changed, 2053 insertions(+), 1626 deletions(-) create mode 100644 src/helpers/images/email-icon.png create mode 100644 src/helpers/images/information.png create mode 100644 src/helpers/images/notebook.png create mode 100644 src/map/Attachments.jsx create mode 100644 src/sidebar/components/mymaps/images/email-icon.png create mode 100644 src/sidebar/components/mymaps/images/loading20.gif create mode 100644 src/sidebar/images/loading.gif diff --git a/package-lock.json b/package-lock.json index 41d58f84..1fc6f2ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,22 +17,22 @@ "ol": "6.12.0", "ol-contextmenu": "^3.3.2", "ol-mapbox-style": "^8.2.1", - "proj4": "^2.7.0", + "proj4": "^2.7.5", "rc-menu": "^9.8.4", "rc-slider": "^8.7.1", - "re-resizable": "^6.9.0", - "react": "^17.0.1", + "re-resizable": "^6.9.1", + "react": "^17.0.2", "react-autocomplete": "^1.8.1", "react-collapsible": "^2.10.0", "react-color": "^2.19.3", - "react-copy-to-clipboard": "^5.0.3", + "react-copy-to-clipboard": "^5.0.4", "react-datepicker": "^2.16.0", - "react-device-detect": "^1.15.0", - "react-dom": "^16.14.0", + "react-device-detect": "^1.17.0", + "react-dom": "^17.0.2", "react-ga4": "^2.0.0", "react-github-btn": "^1.4.0", "react-highlight-words": "^0.16.0", - "react-icons": "^4.7.1", + "react-icons": "^4.8.0", "react-masonry-css": "^1.0.16", "react-responsive-modal": "^3.6.0", "react-router-dom": "^4.3.1", @@ -41,7 +41,6 @@ "react-sidebar": "^3.0.2", "react-slick": "^0.25.2", "react-sortable-hoc": "^2.0.0", - "react-sortable-hoc-ie-optimization": "^0.8.5", "react-switch": "^5.0.1", "react-table": "^7.8.0", "react-table-sticky": "^1.1.3", @@ -15645,17 +15644,16 @@ } }, "node_modules/react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.2" }, "peerDependencies": { - "react": "^16.14.0" + "react": "17.0.2" } }, "node_modules/react-error-overlay": { @@ -16015,20 +16013,6 @@ "react-dom": "^16.3.0 || ^17.0.0" } }, - "node_modules/react-sortable-hoc-ie-optimization": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/react-sortable-hoc-ie-optimization/-/react-sortable-hoc-ie-optimization-0.8.5.tgz", - "integrity": "sha512-bG9oWCIqZYOgxGIUPxI7U+MEYFYirFdMZzwH0HsIkSw4o9ctn/hfR4MSvW3LxgW7t2rdCjxvsBrf7QwxfNSmdA==", - "dependencies": { - "babel-runtime": "^6.11.6", - "invariant": "^2.2.1", - "prop-types": "^15.5.7" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16.0.0", - "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" - } - }, "node_modules/react-switch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-5.0.1.tgz", @@ -16670,9 +16654,9 @@ } }, "node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -30552,14 +30536,13 @@ } }, "react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.2" } }, "react-error-overlay": { @@ -30838,16 +30821,6 @@ "prop-types": "^15.5.7" } }, - "react-sortable-hoc-ie-optimization": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/react-sortable-hoc-ie-optimization/-/react-sortable-hoc-ie-optimization-0.8.5.tgz", - "integrity": "sha512-bG9oWCIqZYOgxGIUPxI7U+MEYFYirFdMZzwH0HsIkSw4o9ctn/hfR4MSvW3LxgW7t2rdCjxvsBrf7QwxfNSmdA==", - "requires": { - "babel-runtime": "^6.11.6", - "invariant": "^2.2.1", - "prop-types": "^15.5.7" - } - }, "react-switch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-5.0.1.tgz", @@ -31284,9 +31257,9 @@ } }, "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" diff --git a/package.json b/package.json index 367163ea..f181081e 100644 --- a/package.json +++ b/package.json @@ -13,22 +13,22 @@ "ol": "6.12.0", "ol-contextmenu": "^3.3.2", "ol-mapbox-style": "^8.2.1", - "proj4": "^2.7.0", + "proj4": "^2.7.5", "rc-menu": "^9.8.4", "rc-slider": "^8.7.1", - "re-resizable": "^6.9.0", - "react": "^17.0.1", + "re-resizable": "^6.9.1", + "react": "^17.0.2", "react-autocomplete": "^1.8.1", "react-collapsible": "^2.10.0", "react-color": "^2.19.3", - "react-copy-to-clipboard": "^5.0.3", + "react-copy-to-clipboard": "^5.0.4", "react-datepicker": "^2.16.0", - "react-device-detect": "^1.15.0", - "react-dom": "^16.14.0", + "react-device-detect": "^1.17.0", + "react-dom": "^17.0.2", "react-ga4": "^2.0.0", "react-github-btn": "^1.4.0", "react-highlight-words": "^0.16.0", - "react-icons": "^4.7.1", + "react-icons": "^4.8.0", "react-masonry-css": "^1.0.16", "react-responsive-modal": "^3.6.0", "react-router-dom": "^4.3.1", @@ -37,7 +37,6 @@ "react-sidebar": "^3.0.2", "react-slick": "^0.25.2", "react-sortable-hoc": "^2.0.0", - "react-sortable-hoc-ie-optimization": "^0.8.5", "react-switch": "^5.0.1", "react-table": "^7.8.0", "react-table-sticky": "^1.1.3", diff --git a/src/config.json b/src/config.json index 535b05fc..1e1316ab 100644 --- a/src/config.json +++ b/src/config.json @@ -13,12 +13,12 @@ "htmlIdentify": false, "leftClickIdentify": false, "excludeIdentifyTitleName": false, + "allowIdentifyExport": false, "showFeedbackMessageOnStartup": false, "showWhatsNewOnStartup": false, "showWhatsNewPopupOnStartup": false, "showTermsOnStartup": true, "googleAnalyticsID": "G-GC5RRXFML6", - "appStatsUrl": "https://opengis.simcoe.ca/api_v2/public/stats/write/", "apiUrl": "https://opengis.simcoe.ca/api_v2/", "apiUrlDev": "http://localhost:8085/", @@ -39,40 +39,6 @@ "pushMapNotificationIDs": [1, 5, 6], "showHelpButtonInsteadOfFeedback": false, "onCoordinateZoomID": false, - "centerCoords": [-8878504.68, 5543492.45], - "defaultZoom": 10, - "maxZoom": 20, - "controls": { - "rotate": true, - "fullScreen": true, - "zoomInOut": true, - "currentLocation": true, - "zoomExtent": true, - "scale": true, - "scaleLine": true, - "basemap": true - }, - "mapTheme": "SIMCOE_COUNTY", - "showFloatingMenuHeader": false, - "showLoadingScreens": false, - "storageKeys": { - "SearchHistory": "searchHistory", - "Draw": "myMaps", - "URLDontShowAgain": "sc_dontshowagain" - }, - "onlyStandardCursor": true, - "restrictOriginForUrlWindow": false, - "rightClickMenuVisibility": { - "sc-floating-menu-basic-mode": true, - "sc-floating-menu-property-click": true, - "sc-floating-menu-add-mymaps": true, - "sc-floating-menu-save-map-extent": true, - "sc-floating-menu-report-problem": true, - "sc-floating-menu-identify": true, - "sc-floating-menu-google-maps": false, - "sc-floating-menu-more": true - }, - "centerCoords": [-8878504.68, 5543492.45], "defaultZoom": 10, "maxZoom": 20, @@ -98,7 +64,6 @@ }, "onlyStandardCursor": true, "restrictOriginForUrlWindow": false, - "rightClickMenuVisibility": { "sc-floating-menu-basic-mode": true, "sc-floating-menu-property-click": true, diff --git a/src/header/Search.jsx b/src/header/Search.jsx index 14c50fe0..f3bbafe6 100644 --- a/src/header/Search.jsx +++ b/src/header/Search.jsx @@ -15,7 +15,7 @@ import Select from "react-select"; // URLS const googleDirectionsURL = (lat, long) => `https://www.google.com/maps?saddr=Current+Location&daddr=${lat},${long}`; -const searchURL = (apiUrl, searchText, type, muni, limit) => `${apiUrl}public/search/?q=${searchText}&type=${type}&muni=${muni}&limit=${limit}`; +const searchURL = (apiUrl, searchText, type, muni, limit) => `${apiUrl}public/search?q=${searchText}&type=${type}&muni=${muni}&limit=${limit}`; const searchInfoURL = (apiUrl, locationID) => `${apiUrl}public/search/${locationID}`; const searchTypesURL = (apiUrl) => `${apiUrl}public/search/types`; @@ -78,7 +78,7 @@ class Search extends Component { window.emitter.addListener("tocLoaded", () => this.onInitialSearch()); // LISTEN FOR EXTERNAL COMPONENT SEARCH - window.emitter.addListener("searchItem", (searchType, searchText, hidden = false) => this.onSearch(searchType, searchText, hidden)); + window.emitter.addListener("searchItem", (searchType, searchText, hidden = false, timeout = undefined) => this.onSearch(searchType, searchText, hidden, timeout)); this.state = { value: "", @@ -109,7 +109,13 @@ class Search extends Component { componentDidMount() { helpers.waitForLoad(["map", "settings"], Date.now(), 30, () => { - if (window.config.municipality !== undefined) this.setState({ municipality: window.config.municipality }); + let muni = window.config.municipality; + if (!muni) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + muni = urlParams.get("MUNI"); + } + if (muni) this.setState({ municipality: muni }); this.apiUrl = window.config.apiUrl; this.storageKey = window.config.storageKeys.SearchHistory; if (window.config.search) { @@ -169,7 +175,7 @@ class Search extends Component { this.onSearch(search_type, search); }; - onSearch = (search_type = undefined, search = undefined, hidden = false) => { + onSearch = (search_type = undefined, search = undefined, hidden = false, timeout = undefined) => { if (!search && search === null) return; if (!search_type && search_type === null) { search_type = "All"; @@ -181,7 +187,7 @@ class Search extends Component { }); helpers.getJSON(encodeURI(searchURL(this.apiUrl, search, search_type, this.state.municipality, 1)), (responseJson) => { if (responseJson[0] !== undefined && responseJson[0].location_id !== null && responseJson[0].location_id !== undefined) { - helpers.getJSON(searchInfoURL(this.apiUrl, responseJson[0].location_id), (result) => this.jsonCallback(result, hidden)); + helpers.getJSON(searchInfoURL(this.apiUrl, responseJson[0].location_id), (result) => this.jsonCallback(result, hidden, timeout)); } }); }; @@ -278,7 +284,7 @@ class Search extends Component { } } - jsonCallback(result, hidden = false) { + jsonCallback(result, hidden = false, timeout = undefined) { if (!hidden) { const savedResult = Object.assign({}, result); delete savedResult["geojson"]; @@ -336,6 +342,18 @@ class Search extends Component { searchGeoLayer.setZIndex(300); searchIconLayer.setZIndex(300); + } else if (hidden && timeout) { + // SET SOURCE + fullFeature.setProperties({ + label: result.alias ? result.alias : result.name, + name: result.name, + is_open_data: result.is_open_data !== undefined && result.is_open_data !== null ? result.is_open_data : true, + }); + searchGeoLayer.getSource().addFeature(fullFeature); + searchGeoLayer.setZIndex(300); + setTimeout(() => { + searchGeoLayer.getSource().clear(); + }, timeout); } const zoomFactor = window.config.featureHighlitStyles && window.config.featureHighlitStyles["zoomFactor"] >= 0 ? window.config.featureHighlitStyles["zoomFactor"] : 1; @@ -359,8 +377,6 @@ class Search extends Component { window.map.getView().setZoom(window.map.getView().getZoom() - zoomFactor); } - //fullFeature.setStyle(myMapsHelpers.getDefaultDrawStyle([255, 0, 0, 0.8], false, 2, fullFeature.getGeometry().getType())); - //fullFeature.setStyle(defaultStyle); if (result.geojson.indexOf("Point") !== -1) { const pointStyle = new Style({ image: new CircleStyle({ @@ -389,15 +405,15 @@ class Search extends Component { fullFeature.setStyle(pointStyle); } else { - let defaultStyle = drawingHelpers.getDefaultDrawStyle( + let defaultStyle = drawingHelpers.getDefaultDrawStyle({ + drawColor: window.config.featureHighlitStyles && window.config.featureHighlitStyles["stroke"] !== null && window.config.featureHighlitStyles["stroke"] !== undefined ? window.config.featureHighlitStyles["stroke"] : [255, 0, 0, 0.8], - false, - window.config.featureHighlitStyles && window.config.featureHighlitStyles["strokeWidth"] ? window.config.featureHighlitStyles["strokeWidth"] : 2, - - fullFeature.getGeometry().getType() - ); + isText: false, + strokeWidth: window.config.featureHighlitStyles && window.config.featureHighlitStyles["strokeWidth"] ? window.config.featureHighlitStyles["strokeWidth"] : 2, + pointType: fullFeature.getGeometry().getType(), + }); defaultStyle.setFill( new Fill({ color: diff --git a/src/helpers/FloatingImageSlider.jsx b/src/helpers/FloatingImageSlider.jsx index 3e8f147f..3fac9e36 100644 --- a/src/helpers/FloatingImageSlider.jsx +++ b/src/helpers/FloatingImageSlider.jsx @@ -79,41 +79,43 @@ class FloatingImageSlider extends Component { let nextIndex = this.state.featureIndex + 1; if (nextIndex > this.state.features.length - 1) nextIndex = 0; const currentFeature = this.state.features[nextIndex]; + const geom = currentFeature.getGeometry(); + if (geom) + this.setState( + { + featureIndex: nextIndex, + currentFeature, + }, + () => { + var feature = new Feature({ + geometry: fromExtent(buffer(geom.getExtent(), 100)), + }); + if (zoom) helpers.zoomToFeature(feature, false); - this.setState( - { - featureIndex: nextIndex, - currentFeature, - }, - () => { - var feature = new Feature({ - geometry: fromExtent(buffer(this.state.currentFeature.getGeometry().getExtent(), 100)), - }); - if (zoom) helpers.zoomToFeature(feature, false); - - this.onFeatureChange(currentFeature); - } - ); + this.onFeatureChange(currentFeature); + } + ); }; - onPreviousFeature = (zoom) => { + onPreviousFeature = (zoom = false) => { let previousIndex = this.state.featureIndex - 1; if (previousIndex < 0) previousIndex = this.state.features.length - 1; const currentFeature = this.state.features[previousIndex]; - - this.setState( - { - featureIndex: previousIndex, - currentFeature, - }, - () => { - var feature = new Feature({ - geometry: fromExtent(buffer(this.state.currentFeature.getGeometry().getExtent(), 100)), - }); - helpers.zoomToFeature(feature, false); - this.onFeatureChange(currentFeature); - } - ); + const geom = currentFeature.getGeometry(); + if (geom) + this.setState( + { + featureIndex: previousIndex, + currentFeature, + }, + () => { + var feature = new Feature({ + geometry: fromExtent(buffer(geom.getExtent(), 100)), + }); + helpers.zoomToFeature(feature, false); + this.onFeatureChange(currentFeature); + } + ); }; onResize = (e, direction, ref, d) => { @@ -218,8 +220,8 @@ class FloatingImageSlider extends Component { src={this.state.currentFeature.get(this.state.callerObj.imageUrlField)} alt="property" onError={(e) => { - console.log("auto next"); - this.onNextFeature(false); + // console.log("auto next"); + // this.onNextFeature(false); e.target.onerror = null; e.target.src = images["noPhoto.png"]; }} diff --git a/src/helpers/InfoRow.jsx b/src/helpers/InfoRow.jsx index e9326c9a..693f7119 100644 --- a/src/helpers/InfoRow.jsx +++ b/src/helpers/InfoRow.jsx @@ -1,13 +1,18 @@ import React from "react"; import moment from "moment"; import * as helpers from "../helpers/helpers"; +import Attachments from "../map/Attachments"; export const InfoRow = (props) => { // CONVERT URL'S TO LINKS + let label = props.label; let value = props.value; var formats = [moment.ISO_8601, "YYYY-MM-DDZ"]; const infoRowStyle = window.config.infoRowStyle !== undefined ? window.config.infoRowStyle.toLowerCase() : "Default"; - if (props.value != null && props.value.toString().substring(0, 4).toUpperCase() === "HTTP") { + if (props.value != null && props.value.toString().substring(0, 4).toUpperCase() === "HTTP" && label === "Attachment Url") { + label = "Attachments"; + value = ; + } else if (props.value != null && props.value.toString().substring(0, 4).toUpperCase() === "HTTP") { value = ( Click To Open @@ -19,13 +24,19 @@ export const InfoRow = (props) => { Click To Open ); + } else if (props.value != null && props.value.toString().substring(1, 3).toUpperCase() === ":\\") { + value = ( + + {props.value} + + ); } else if (props.value != null && props.value.length >= 8 && moment(props.value, formats, true).isValid()) { value = moment(props.value).format("YYYY-MM-DD"); } return (
-
{props.label}
+
{label}
{value} {props.children} diff --git a/src/helpers/OLHelpers.js b/src/helpers/OLHelpers.js index 4f4dcd31..0eca814c 100644 --- a/src/helpers/OLHelpers.js +++ b/src/helpers/OLHelpers.js @@ -157,6 +157,8 @@ export class LayerHelpers { : layer.getSource().getFeatureInfoUrl(coordinate, viewResolution, "EPSG:3857", { INFO_FORMAT: "application/json", }); + let attachmentUrl = layer.get("attachmentUrl"); + const hasAttachments = layer.get("hasAttachments"); const params = {}; const secureKey = layer.get("secureKey"); if (secureKey !== undefined) { @@ -171,6 +173,7 @@ export class LayerHelpers { const tolerance = 20 - zoom; url = url .replace("#GEOMETRY#", coordinate) + .replace("#GEOMETRYTYPE#", "esriGeometryPoint") .replace("#TOLERANCE#", tolerance >= 10 ? tolerance : 10) .replace("#EXTENT#", extent.join(",")) .replace("#RESOLUTION#", arcgisResolution); @@ -178,6 +181,15 @@ export class LayerHelpers { if (url) { await helpers.getJSONWaitWithParams(url, params, (result) => { let features = isArcGISLayer ? LayerHelpers.parseESRIIdentify(result) : new GeoJSON().readFeatures(result); + if (hasAttachments) { + features = features.map((feature) => { + const keys = feature.getKeys(); + const objectId = feature.get(keys.filter((item) => item.indexOf("OBJECTID") !== -1)[0]); + feature.values_["attachmentUrl"] = attachmentUrl.replace("#OBJECTID#", objectId); + return feature; + }); + } + if (callback === undefined) { return features.length > 0 ? features[0] : undefined; } else { @@ -194,6 +206,9 @@ export class LayerHelpers { : layer.getSource().getFeatureInfoUrl(coordinate, viewResolution, "EPSG:3857", { INFO_FORMAT: "application/json", }); + let attachmentUrl = layer.get("attachmentUrl"); + const hasAttachments = layer.get("hasAttachments"); + const params = {}; const secureKey = layer.get("secureKey"); if (secureKey !== undefined) { @@ -208,6 +223,7 @@ export class LayerHelpers { const tolerance = 20 - zoom; url = url .replace("#GEOMETRY#", coordinate) + .replace("#GEOMETRYTYPE#", "esriGeometryPoint") .replace("#TOLERANCE#", tolerance >= 10 ? tolerance : 10) .replace("#EXTENT#", extent.join(",")) .replace("#RESOLUTION#", arcgisResolution); @@ -215,6 +231,14 @@ export class LayerHelpers { if (url) { helpers.getJSONWithParams(url, params, (result) => { let features = isArcGISLayer ? LayerHelpers.parseESRIIdentify(result) : new GeoJSON().readFeatures(result); + if (hasAttachments) { + features = features.map((feature) => { + const keys = feature.getKeys(); + const objectId = feature.get(keys.filter((item) => item.indexOf("OBJECTID") !== -1)[0]); + feature.values_["attachmentUrl"] = attachmentUrl.replace("#OBJECTID#", objectId); + return feature; + }); + } callback(features.length > 0 ? features[0] : undefined); }); } @@ -228,7 +252,7 @@ export class LayerHelpers { delete item.geometryType; let keys = Object.keys(item.attributes); keys.forEach((key) => { - if (item.attributes[key] === "Null" || item.attributes[key] === "") delete item.attributes[key]; + if (item.attributes[key] === "Null" || item.attributes[key] === "") item.attributes[key] = ""; //delete item.attributes[key]; }); let tempFeature = new EsriJSON().readFeature(item); @@ -279,7 +303,7 @@ export class LayerHelpers { //headers["token"] = "GIS"; params["headers"] = headers; } - helpers.httpGetTextWithParams(url, params, (responseText) => { + const parseResponseText = (responseText, token = undefined) => { if (responseText === null) { callback([]); return; @@ -321,7 +345,7 @@ export class LayerHelpers { case "rest": response = JSON.parse(responseText); if (response.layers !== undefined) { - this.getESRILegend(`${root_url}/legend?f=json`, (legends) => { + this.getESRILegend(token ? `${root_url}/legend?f=json&token=${token}` : `${root_url}/legend?f=json`, (legends) => { response.layers.forEach((item) => { if (item !== undefined) { item["layer_name"] = item.name; @@ -342,7 +366,7 @@ export class LayerHelpers { })[0]; if (item.drawingInfo !== undefined && item.drawingInfo.renderer !== undefined && item.drawingInfo.renderer.symbol !== undefined && item.legend === undefined) item["style"] = `data:${item.drawingInfo.renderer.symbol.contentType};base64,${item.drawingInfo.renderer.symbol.imageData}`; - item["url"] = `${root_url}/${item.id}`; + item["url"] = token ? `${root_url}/${item.id}?token=${token}` : `${root_url}/${item.id}`; layers.push(item); } }); @@ -377,8 +401,105 @@ export class LayerHelpers { console.warn("Unexpected error: " + error.message); callback(layers); } + }; + if (options.token) { + url = `${url}&token=${options.token}`; + helpers.httpGetTextWithParams(url, params, (responseText) => { + parseResponseText(responseText, options.token); + }); + } else { + helpers.httpGetTextWithParams(url, params, (responseText) => { + parseResponseText(responseText); + }); + } + } + + static createArcGISRestLayersFromService(capabilities, options, callback) { + const { returnLayers } = options; + let layerArray = []; + capabilities.forEach((layer) => { + if (!returnLayers || returnLayers.includes(layer.id)) { + const hasAttachments = layer.hasAttachments; + const layerOptions = { + sourceType: OL_DATA_TYPES.ImageArcGISRest, + source: "rest", + projection: layer.sourceSpatialReference && layer.sourceSpatialReference.latestWkid ? `${layer.sourceSpatialReference.latestWkid}` : "3857", + layerName: layer.name, + url: layer.url, + tiled: false, + extent: layer.extent, + name: layer.name, + }; + if (layer.grouped) { + layerOptions["layers"] = layer.layers; + layerOptions.sourceType = OL_DATA_TYPES.LayerGroup; + } + LayerHelpers.getLayer(layerOptions, (newLayer) => { + const identifyUrl = (options) => + `${options.url}/identify?geometry=${options.point}&geometryType=${options.geometryType}&layers=visible%3A${options.layerId}&sr=3857&datumTransformations=3857&tolerance=${options.tolerance}&mapExtent=${options.extent}&imageDisplay=${options.resolution}&maxAllowableOffset=10&returnGeometry=true&returnFieldName=false&f=json`; + const getAttachmentUrl = (options) => `${options.url}/${options.layerId}/queryAttachments?objectIds=${options.objectId}&returnUrl=true&f=json`; + const getRecordCountUrl = (options) => `${options.url}/${options.layerId}/query?where=0%3D0&returnCountOnly=true&f=json`; + const getQueryUrl = (options) => `${options.url}/${options.layerId}/query?where=#WHERE#&outFields=*&outSR=3857&returnCountOnly=false&f=geojson`; + const rootInfoUrl = layer.url; + let attachmentUrl = getAttachmentUrl({ + url: layer.rootUrl, + layerId: layer.id, + objectId: "#OBJECTID#", + }); + let wfsUrl = identifyUrl({ + url: layer.rootUrl, + point: "#GEOMETRY#", + layerId: layer.id, + tolerance: "#TOLERANCE#", + extent: "#EXTENT#", + resolution: "#RESOLUTION#", + geometryType: "#GEOMETRYTYPE#", + }); + let queryUrl = getQueryUrl({ + url: layer.rootUrl, + layerId: layer.id, + }); + let recordCountUrl = getRecordCountUrl({ + url: layer.rootUrl, + layerId: layer.id, + }); + var url = new URL(rootInfoUrl); + const urlParams = new URLSearchParams(url.searchParams); + const url_token = urlParams.get("token"); + if (url_token) wfsUrl = `${wfsUrl}&token=${url_token}`; + if (url_token && hasAttachments) attachmentUrl = `${attachmentUrl}&token=${url_token}`; + if (url_token) recordCountUrl = `${recordCountUrl}&token=${url_token}`; + if (url_token) queryUrl = `${queryUrl}&token=${url_token}`; + + newLayer.setVisible(layer.defaultVisibility); + newLayer.setOpacity(layer.options ? layer.options.opacity || 1 : 1); + newLayer.setProperties({ + layerId: layer.id, + name: layer.name, + displayName: layer.name, + tocDisplayName: layer.name, + wfsUrl: wfsUrl, + rootInfoUrl: rootInfoUrl, + clickable: true, + disableParcelClick: false, + queryable: layer.queryable !== undefined ? layer.queryable : false, + opaque: layer.opaque !== undefined ? layer.opaque : false, + minScale: layer.minScale, + maxScale: layer.maxScale, + attachmentUrl: hasAttachments ? attachmentUrl : null, + hasAttachments: hasAttachments, + recordCountUrl: recordCountUrl, + featureQueryUrl: queryUrl, + }); + newLayer["layerConfig"] = layer; + newLayer["key"] = helpers.getUID(); + layerArray.push(newLayer); + }); + } }); + callback(layerArray); } + static getESRILegend(url, callback) { helpers.httpGetText(url, (responseText) => { var response = JSON.parse(responseText); @@ -518,6 +639,7 @@ export class LayerHelpers { let projection = options.projection !== undefined ? options.projection : "EPSG:3857"; let layerName = options.layerName; let url = options.url; + let params = options.params; let tiled = options.tiled !== undefined ? options.tiled : false; let file = options.file; let extent = options.extent !== undefined ? options.extent : []; @@ -825,7 +947,6 @@ export class LayerHelpers { declutter: true, tilePixelRatio: 8, background: background, - source: new VectorTileSource({ name: name, format: new MVT(), @@ -907,17 +1028,27 @@ export class LayerHelpers { ); break; case OL_DATA_TYPES.ImageArcGISRest: - let urlArray = url.split("/"); + let token = undefined; + let urlParam = url.split("?"); + let urlArray = urlParam[0].split("/"); + urlParam.shift(); let url_layer = urlArray[urlArray.length - 1]; urlArray.pop(); url = urlArray.join("/"); + if (urlParam[0]) { + let tokenArray = urlParam[0].split("="); + token = tokenArray[1]; + } const sourceParams = { url: url, - params: { LAYERS: `SHOW:${url_layer}` }, + params: params || { LAYERS: `SHOW:${url_layer}` }, ratio: 1, projection: projection, crossOrigin: "anonymous", }; + if (token) { + sourceParams.params["TOKEN"] = token; + } if (extent !== undefined) sourceParams["extent"] = [extent.xmin, extent.ymin, extent.xmax, extent.ymax]; callback( new ImageLayer({ diff --git a/src/helpers/Popup.jsx b/src/helpers/Popup.jsx index 3b650343..d0c5a0ba 100644 --- a/src/helpers/Popup.jsx +++ b/src/helpers/Popup.jsx @@ -127,7 +127,7 @@ export default class Popup extends Overlay { // x.classList.remove("sc-hidden"); const containers = document.getElementsByClassName("ol-overlay-container ol-selectable"); Array.prototype.forEach.call(containers, (el) => { - if (el.childNodes[0].id === "sc-window-popup") el.classList.remove("sc-hidden"); + if (el && el.childNodes && el.childNodes[0].id === "sc-window-popup") el.classList.remove("sc-hidden"); }); // SET TITLE @@ -153,46 +153,47 @@ export default class Popup extends Overlay { // console.info('start dragging'); // }); - header.addEventListener("mousedown", (evt) => { - // IGNORE CLOSE BUTTON OR MOBILE - var isCloser = evt.target.classList.contains("ol-popup-closer"); - if (isClosing || isCloser || helpers.isMobile()) { - isMoving = false; - isClosing = false; - return; - } - - // THIS FOLLOWING IS TO SUPPORT MAKE POPUP MOVABLE. NOT PERFECT YET. - var that = this; - function move(evt) { - var coord = window.map.getEventCoordinate(evt); - var pixel = window.map.getPixelFromCoordinate(coord); - var width = getWidth(window.map.getView().getProjection().getExtent()) / window.map.getView().getResolution(); - var pixelX = ((pixel[0] % width) + width) % width; - var pixelY = pixel[1]; - var popupElem = document.getElementsByClassName("ol-popup")[0]; - var popupHeight = popupElem.offsetHeight; - var popupWidth = popupElem.offsetWidth; - - // THIS SNAPS TO CENTER OF POPUP X - var calc = pixelX - popupWidth / 4; - var pixel2 = [calc, pixelY + popupHeight]; - var point = window.map.getCoordinateFromPixel(pixel2); - - if (isMoving) { - that.setPosition(point); - } else { - isMoving = true; + if (header) + header.addEventListener("mousedown", (evt) => { + // IGNORE CLOSE BUTTON OR MOBILE + var isCloser = evt.target.classList.contains("ol-popup-closer"); + if (isClosing || isCloser || helpers.isMobile()) { + isMoving = false; + isClosing = false; + return; } - } - function end(evt) { - window.removeEventListener("mousemove", move); - window.removeEventListener("mouseup", end); - isMoving = false; - } - window.addEventListener("mousemove", move); - window.addEventListener("mouseup", end); - }); + + // THIS FOLLOWING IS TO SUPPORT MAKE POPUP MOVABLE. NOT PERFECT YET. + var that = this; + function move(evt) { + var coord = window.map.getEventCoordinate(evt); + var pixel = window.map.getPixelFromCoordinate(coord); + var width = getWidth(window.map.getView().getProjection().getExtent()) / window.map.getView().getResolution(); + var pixelX = ((pixel[0] % width) + width) % width; + var pixelY = pixel[1]; + var popupElem = document.getElementsByClassName("ol-popup")[0]; + var popupHeight = popupElem.offsetHeight; + var popupWidth = popupElem.offsetWidth; + + // THIS SNAPS TO CENTER OF POPUP X + var calc = pixelX - popupWidth / 4; + var pixel2 = [calc, pixelY + popupHeight]; + var point = window.map.getCoordinateFromPixel(pixel2); + + if (isMoving) { + that.setPosition(point); + } else { + isMoving = true; + } + } + function end(evt) { + window.removeEventListener("mousemove", move); + window.removeEventListener("mouseup", end); + isMoving = false; + } + window.addEventListener("mousemove", move); + window.addEventListener("mouseup", end); + }); if (callback !== undefined) { closeCallback = callback; @@ -247,7 +248,7 @@ export default class Popup extends Overlay { window.activeClick = null; const containers = document.getElementsByClassName("ol-overlay-container ol-selectable"); Array.prototype.forEach.call(containers, (el) => { - if (el.childNodes[0].id === "sc-window-popup") el.classList.add("sc-hidden"); + if (el && el.childNodes && el.childNodes[0] && el.childNodes[0] && el.childNodes[0].id === "sc-window-popup") el.classList.add("sc-hidden"); }); isMoving = false; diff --git a/src/helpers/SC.css b/src/helpers/SC.css index f8ce2f1b..b09eada9 100644 --- a/src/helpers/SC.css +++ b/src/helpers/SC.css @@ -486,7 +486,9 @@ input[type="number"]::-webkit-outer-spin-button { font-size: 20px; padding-top: 12px; } - +.sc-attachment-icon { + color: #626262; +} @media print { .no-print, .no-print * { diff --git a/src/helpers/URLWindow.jsx b/src/helpers/URLWindow.jsx index 02399bf8..6b5df079 100644 --- a/src/helpers/URLWindow.jsx +++ b/src/helpers/URLWindow.jsx @@ -3,140 +3,140 @@ import "./UrlWindow.css"; import * as helpers from "./helpers"; class URLWindow extends Component { - constructor(props) { - super(props); - - this.state = { - hide: false, - }; - } - - onCloseClick = (value) => { - this.setState({ hide: true }); - }; - - onPopoutClick = () => { - window.open(this.props.url, "_blank"); - }; - - componentWillUnmount() { - this.sidebarEmitter.remove(); - document.removeEventListener("keydown", this.escFunction, false); - } - - componentDidMount() { - helpers.waitForLoad("settings", Date.now(), 30, () => { - this.storageKey = window.config.storageKeys.URLDontShowAgain; - - try { - if (this.props.honorDontShow) { - const saved = this.getStorage(); - if (saved !== null && saved !== undefined) { - if (saved.find((item) => (item.url !== undefined ? item.url.toLowerCase() === this.props.url.toLowerCase() : false))) this.setState({ hide: true }); - } - } - } catch (e) { - console.log(e); - } - - document.addEventListener("keydown", this.escFunction, false); - }); - // LISTEN FOR SIDEPANEL CHANGES - this.sidebarEmitter = window.emitter.addListener("sidebarChanged", (isSidebarOpen) => this.sidebarChanged(isSidebarOpen)); - } - - isDontShow = () => { - try { - if (this.props.honorDontShow) { - const saved = this.getStorage(); - if (saved !== null && saved !== undefined) { - return saved.find((item) => (item.url !== undefined ? item.url.toLowerCase() === this.props.url.toLowerCase() : false)); - } else { - return false; - } - } else { - return false; - } - } catch (e) { - return false; - } - - if (this.props.honorDontShow) { - const saved = this.getStorage(); - if (saved.includes(this.props.url)) return true; - else return false; - } - }; - - sidebarChanged = (isSidebarOpen) => { - this.forceUpdate(); - }; - - escFunction = (event) => { - if (event.keyCode === 27) { - this.setState({ hide: true }); - } - }; - - onDontShowThisAgain = () => { - this.saveToStorage(); - }; - - saveToStorage = () => { - let item = { url: this.props.url, dateAdded: new Date().toLocaleString() }; - helpers.appendToStorage(this.storageKey, item); - this.setState({ hide: true }); - }; - - // GET STORAGE - getStorage() { - const storage = localStorage.getItem(this.storageKey); - if (storage === null) return []; - - const data = JSON.parse(storage); - return data; - } - - render() { - // DONT RENDER IF WERE NOT SHOWING, OTHERWISE ALL DATA/IMAGES FROM URL LINK WILL DOWNLOAD - if (this.isDontShow()) return
; - - let className = ""; - if (this.state.hide) className = "sc-hidden"; - else if (this.props.mode === "full") className = "full"; - else if (!window.sidebarOpen) className = "full"; - - let hideScrollClassName = ""; - if (this.props.hideScroll) hideScrollClassName = "sc-url-window-content-no-scroll"; - return ( -
-
-
Information
-
- -
-
- -
-
-
-