Merge branch 'colouring-cities:master' into ali

This commit is contained in:
Ali 2023-08-24 16:54:21 -04:00 committed by GitHub
commit a33e98c27b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 4970 additions and 2458 deletions

View File

@ -8,9 +8,8 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-python@v2 - uses: actions/setup-python@v2
with: with:
python-version: '3.7' python-version: "3.11"
- name: - name: Install dependencies
Install dependencies
run: | run: |
sudo apt-get install libgeos-dev sudo apt-get install libgeos-dev
python -m pip install --upgrade pip python -m pip install --upgrade pip
@ -19,7 +18,7 @@ jobs:
python -m pip install -r etl/requirements.txt python -m pip install -r etl/requirements.txt
- name: Run Flake8 - name: Run Flake8
run: | run: |
ls etl/*py | grep -v 'join_building_data' | xargs flake8 --exclude etl/__init__.py flake8 etl --ignore=E501
- name: Run tests - name: Run tests
run: | run: |
python -m pytest python -m pytest

View File

@ -1,4 +1,4 @@
# Colouring London # Colouring Cities Core Platform
[![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors) [![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors)
![Build status](https://github.com/colouring-cities/colouring-core/workflows/Node.js%20CI/badge.svg) ![Build status](https://github.com/colouring-cities/colouring-core/workflows/Node.js%20CI/badge.svg)

View File

@ -19,7 +19,19 @@
</Style> </Style>
<Style name="base_night_outlines"> <Style name="base_night_outlines">
<Rule> <Rule>
<LineSymbolizer stroke="#ff0000ff" stroke-width="1" /> <MaxScaleDenominator>20000</MaxScaleDenominator>
<MinScaleDenominator>1200</MinScaleDenominator>
<LineSymbolizer stroke="#0081AF" stroke-width="0.5"/>
</Rule>
<Rule>
<MaxScaleDenominator>12000</MaxScaleDenominator>
<MinScaleDenominator>8000</MinScaleDenominator>
<LineSymbolizer stroke="#0081AF" stroke-width="1.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>8000</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#0081AF" stroke-width="4.0"/>
</Rule> </Rule>
</Style> </Style>
<Style name="base_boroughs"> <Style name="base_boroughs">
@ -913,6 +925,83 @@
<LineSymbolizer stroke="#888" stroke-width="3.0"/> <LineSymbolizer stroke="#888" stroke-width="3.0"/>
</Rule> </Rule>
</Style> </Style>
<Style name="original_landuse">
<Rule>
<Filter>[typology_original_use_order] = "Agriculture And Fisheries"</Filter>
<PolygonSymbolizer fill="#73ccd1" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Minerals"</Filter>
<PolygonSymbolizer fill="#45cce3" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Unclassified, presumed non-residential"</Filter>
<PolygonSymbolizer fill="#6c6f8e" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Recreation And Leisure"</Filter>
<PolygonSymbolizer fill="#ffbfbf" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Transport"</Filter>
<PolygonSymbolizer fill="#b3de69" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Utilities And Infrastructure"</Filter>
<PolygonSymbolizer fill="#cccccc" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and not ([typology_original_use_order] = "Garden buildings") and not ([typology_original_use_order] = "Hotels, boarding and guest houses") and not ([typology_original_use_verified])</Filter>
<PolygonSymbolizer fill="#252aa6" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and not ([typology_original_use_order] = "Garden buildings") and not ([typology_original_use_order] = "Hotels, boarding and guest houses") and ([typology_original_use_verified])</Filter>
<PolygonSymbolizer fill="#7025a6" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and [typology_original_use_order] = "Hotels, boarding and guest houses"</Filter>
<PolygonSymbolizer fill="#3c4194" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and [typology_original_use_order] = "Garden buildings" </Filter>
<PolygonSymbolizer fill="#1157fa" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Community Services"</Filter>
<PolygonSymbolizer fill="#fa667d" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Retail"</Filter>
<PolygonSymbolizer fill="#ff8c00" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Industry And Business"</Filter>
<PolygonSymbolizer fill="#f5f58f" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Vacant And Derelict"</Filter>
<PolygonSymbolizer fill="#ffffff" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Defence"</Filter>
<PolygonSymbolizer fill="#898944" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Mixed Use"</Filter>
<PolygonSymbolizer fill="#e5050d" />
</Rule>
<Rule>
<MaxScaleDenominator>8530</MaxScaleDenominator>
<MinScaleDenominator>4264</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="0.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>4264</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="3.0"/>
</Rule>
</Style>
<Style name="disaster_severity"> <Style name="disaster_severity">
<Rule> <Rule>
<Filter>[disaster_severity] = "Building destroyed"</Filter> <Filter>[disaster_severity] = "Building destroyed"</Filter>
@ -973,4 +1062,139 @@
<PolygonSymbolizer fill="#ffe8a9" /> <PolygonSymbolizer fill="#ffe8a9" />
</Rule> </Rule>
</Style> </Style>
<Style name="typology_classification">
<Rule>
<Filter>[typology_classification] = "Low-rise: Not part of a group/cluster (1-3 core floors- excluding extensions)"</Filter>
<PolygonSymbolizer fill="#0311AB" />
</Rule>
<Rule>
<Filter>[typology_classification] = "Low-rise: Part of dense block/row/terrace"</Filter>
<PolygonSymbolizer fill="#3845D4" />
</Rule>
<Rule>
<Filter>[typology_classification] = "Low-rise: Part of group of widely spaced blocks (includes semi-detached houses)"</Filter>
<PolygonSymbolizer fill="#6D79FD" />
</Rule>
<Rule>
<Filter>[typology_classification] = "Mid-rise: Not part of a group/cluster (4-7 core floors)"</Filter>
<PolygonSymbolizer fill="#FF5D00" />
</Rule>
<Rule>
<Filter>[typology_classification] = "Mid-rise: Part of group of densely spaced blocks"</Filter>
<PolygonSymbolizer fill="#FF8000" />
</Rule>
<Rule>
<Filter>[typology_classification] = "Mid-rise: Part of group of widely spaced blocks"</Filter>
<PolygonSymbolizer fill="#FFA200" />
</Rule>
<Rule>
<Filter>[typology_classification] = "High rise: Not part of a group/cluster"</Filter>
<PolygonSymbolizer fill="#AB1303" />
</Rule>
<Rule>
<Filter>[typology_classification] = "High-rise: Part of group of densely spaced blocks (8 + core floors)"</Filter>
<PolygonSymbolizer fill="#D43A29" />
</Rule>
<Rule>
<Filter>[typology_classification] = "High-rise: Part of group of widely spaced blocks"</Filter>
<PolygonSymbolizer fill="#FC604F" />
</Rule>
<Rule>
<MaxScaleDenominator>17061</MaxScaleDenominator>
<MinScaleDenominator>4264</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="0.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>4264</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="2.0"/>
</Rule>
</Style>
<Style name="typology_style_period">
<Rule>
<Filter>[typology_style_period] = "43AD-410 (Roman)"</Filter>
<PolygonSymbolizer fill="#FFF739" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "410-1485 (Medieval)"</Filter>
<PolygonSymbolizer fill="#C5BD00" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1485-1603 (Tudor)"</Filter>
<PolygonSymbolizer fill="#FF9A39" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1603-1714 (Stuart)"</Filter>
<PolygonSymbolizer fill="#C56000" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1714-1837 (Georgian)"</Filter>
<PolygonSymbolizer fill="#EA8072" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1837-1901 (Victorian)"</Filter>
<PolygonSymbolizer fill="#A71200" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1901-1914 (Edwardian)"</Filter>
<PolygonSymbolizer fill="#A272D4" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1914-1945 (WWI-WWII)"</Filter>
<PolygonSymbolizer fill="#3988C5" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1946-1979 (Post war)"</Filter>
<PolygonSymbolizer fill="#5ADFA2" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "1980-1999 (Late 20th Century)"</Filter>
<PolygonSymbolizer fill="#C2F47A" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "2000-2025 (Early 21st Century)"</Filter>
<PolygonSymbolizer fill="#6FB40A" />
</Rule>
<Rule>
<MaxScaleDenominator>17061</MaxScaleDenominator>
<MinScaleDenominator>4264</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="0.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>4264</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="2.0"/>
</Rule>
</Style>
<Style name="typology_dynamic_classification">
<Rule>
<Filter>[typology_dynamic_classification] = "Repetitive small, domestic plots"</Filter>
<PolygonSymbolizer fill="#96484A" />
</Rule>
<Rule>
<Filter>[typology_dynamic_classification] = "Linear non-domestic, i.e. high streets"</Filter>
<PolygonSymbolizer fill="#4B9889" />
</Rule>
<Rule>
<Filter>[typology_dynamic_classification] = "Large plots with internal roads"</Filter>
<PolygonSymbolizer fill="#4F8DA8" />
</Rule>
<Rule>
<Filter>[typology_dynamic_classification] = "Other"</Filter>
<PolygonSymbolizer fill="#897A5D" />
</Rule>
<Rule>
<MaxScaleDenominator>17061</MaxScaleDenominator>
<MinScaleDenominator>4264</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="0.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>4264</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="2.0"/>
</Rule>
</Style>
</Map> </Map>

246
app/package-lock.json generated
View File

@ -128,9 +128,9 @@
} }
}, },
"node_modules/@babel/core/node_modules/semver": { "node_modules/@babel/core/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -196,9 +196,9 @@
} }
}, },
"node_modules/@babel/helper-compilation-targets/node_modules/semver": { "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -264,9 +264,9 @@
} }
}, },
"node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true, "peer": true,
"bin": { "bin": {
@ -1664,9 +1664,9 @@
} }
}, },
"node_modules/@babel/plugin-transform-runtime/node_modules/semver": { "node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true, "peer": true,
"bin": { "bin": {
@ -1895,9 +1895,9 @@
} }
}, },
"node_modules/@babel/preset-env/node_modules/semver": { "node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true, "peer": true,
"bin": { "bin": {
@ -4099,9 +4099,9 @@
} }
}, },
"node_modules/babel-plugin-istanbul/node_modules/semver": { "node_modules/babel-plugin-istanbul/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -4138,9 +4138,9 @@
} }
}, },
"node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true, "peer": true,
"bin": { "bin": {
@ -6160,9 +6160,9 @@
} }
}, },
"node_modules/default-gateway/node_modules/semver": { "node_modules/default-gateway/node_modules/semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver"
@ -6994,9 +6994,9 @@
} }
}, },
"node_modules/eslint-plugin-react/node_modules/semver": { "node_modules/eslint-plugin-react/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -9776,9 +9776,9 @@
} }
}, },
"node_modules/istanbul-lib-instrument/node_modules/semver": { "node_modules/istanbul-lib-instrument/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -10747,9 +10747,9 @@
} }
}, },
"node_modules/make-dir/node_modules/semver": { "node_modules/make-dir/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
@ -11523,9 +11523,9 @@
} }
}, },
"node_modules/normalize-package-data/node_modules/semver": { "node_modules/normalize-package-data/node_modules/semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver"
@ -15234,9 +15234,9 @@
} }
}, },
"node_modules/react-dev-utils/node_modules/semver": { "node_modules/react-dev-utils/node_modules/semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver"
@ -16217,9 +16217,9 @@
} }
}, },
"node_modules/sane/node_modules/semver": { "node_modules/sane/node_modules/semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver"
@ -16363,9 +16363,9 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.3.8", "version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
}, },
@ -18206,9 +18206,9 @@
} }
}, },
"node_modules/tiny-async-pool/node_modules/semver": { "node_modules/tiny-async-pool/node_modules/semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver"
@ -18311,23 +18311,24 @@
} }
}, },
"node_modules/tough-cookie": { "node_modules/tough-cookie": {
"version": "4.0.0", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"psl": "^1.1.33", "psl": "^1.1.33",
"punycode": "^2.1.1", "punycode": "^2.1.1",
"universalify": "^0.1.2" "universalify": "^0.2.0",
"url-parse": "^1.5.3"
}, },
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/tough-cookie/node_modules/universalify": { "node_modules/tough-cookie/node_modules/universalify": {
"version": "0.1.2", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 4.0.0" "node": ">= 4.0.0"
@ -19636,9 +19637,9 @@
} }
}, },
"node_modules/webpack-dev-server/node_modules/semver": { "node_modules/webpack-dev-server/node_modules/semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -20144,9 +20145,9 @@
} }
}, },
"node_modules/word-wrap": { "node_modules/word-wrap": {
"version": "1.2.3", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -20356,9 +20357,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true "dev": true
} }
} }
@ -20408,9 +20409,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true "dev": true
} }
} }
@ -20460,9 +20461,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true "peer": true
} }
@ -21429,9 +21430,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true "peer": true
} }
@ -21605,9 +21606,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true "peer": true
} }
@ -23391,9 +23392,9 @@
} }
}, },
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true "dev": true
} }
} }
@ -23423,9 +23424,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"peer": true "peer": true
} }
@ -24995,9 +24996,9 @@
"dev": true "dev": true
}, },
"semver": { "semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true "dev": true
}, },
"shebang-command": { "shebang-command": {
@ -25705,9 +25706,9 @@
} }
}, },
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true "dev": true
} }
} }
@ -27756,9 +27757,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true "dev": true
} }
} }
@ -28521,9 +28522,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
} }
} }
}, },
@ -29127,9 +29128,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true "dev": true
} }
} }
@ -31910,9 +31911,9 @@
} }
}, },
"semver": { "semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true "dev": true
}, },
"strip-ansi": { "strip-ansi": {
@ -32690,9 +32691,9 @@
"dev": true "dev": true
}, },
"semver": { "semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true "dev": true
}, },
"shebang-command": { "shebang-command": {
@ -32809,9 +32810,9 @@
} }
}, },
"semver": { "semver": {
"version": "7.3.8", "version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"requires": { "requires": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
} }
@ -34294,9 +34295,9 @@
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true "dev": true
} }
} }
@ -34379,20 +34380,21 @@
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
}, },
"tough-cookie": { "tough-cookie": {
"version": "4.0.0", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"dev": true, "dev": true,
"requires": { "requires": {
"psl": "^1.1.33", "psl": "^1.1.33",
"punycode": "^2.1.1", "punycode": "^2.1.1",
"universalify": "^0.1.2" "universalify": "^0.2.0",
"url-parse": "^1.5.3"
}, },
"dependencies": { "dependencies": {
"universalify": { "universalify": {
"version": "0.1.2", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"dev": true "dev": true
} }
} }
@ -35535,9 +35537,9 @@
} }
}, },
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true "dev": true
}, },
"sockjs-client": { "sockjs-client": {
@ -35831,9 +35833,9 @@
} }
}, },
"word-wrap": { "word-wrap": {
"version": "1.2.3", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
"dev": true "dev": true
}, },
"worker-rpc": { "worker-rpc": {

View File

@ -59,6 +59,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
location_name_link: {
edit: true,
verify: true,
},
location_number: { location_number: {
edit: true, edit: true,
verify: true, verify: true,
@ -103,6 +107,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true verify: true
}, },
location_alternative_footprint_links: {
edit: true,
verify: true
},
date_year: { date_year: {
edit: true, edit: true,
verify: true, verify: true,
@ -126,6 +134,14 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
date_source_type: {
edit: true,
verify: true,
},
date_source_links: {
edit: true,
verify: true,
},
facade_year: { facade_year: {
edit: true, edit: true,
verify: true, verify: true,
@ -254,6 +270,14 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
construction_core_material_source_type: {
edit: true,
verify: true,
},
construction_core_material_source_links: {
edit: true,
verify: true,
},
construction_secondary_materials: { construction_secondary_materials: {
edit: false, edit: false,
}, },
@ -261,6 +285,116 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
construction_roof_covering_source_type: {
edit: true,
verify: true,
},
construction_roof_covering_source_links: {
edit: true,
verify: true,
},
construction_structural_system: {
edit: true,
verify: true,
},
construction_structural_system_source_type: {
edit: true,
verify: true,
},
construction_structural_system_source_links: {
edit: true,
verify: true,
},
construction_foundation: {
edit: true,
verify: true,
},
construction_foundation_source_type: {
edit: true,
verify: true,
},
construction_foundation_source_links: {
edit: true,
verify: true,
},
construction_roof_shape: {
edit: true,
verify: true,
},
construction_roof_shape_source_type: {
edit: true,
verify: true,
},
construction_roof_shape_source_links: {
edit: true,
verify: true,
},
construction_irregularities: {
edit: true,
verify: true,
},
construction_irregularities_source_type: {
edit: true,
verify: true,
},
construction_irregularities_source_links: {
edit: true,
verify: true,
},
construction_decorative_features: {
edit: true,
verify: true,
},
construction_decorative_feature_materials: {
edit: true,
verify: true,
},
construction_decorative_feature_source_type: {
edit: true,
verify: true,
},
construction_decorative_feature_source_links: {
edit: true,
verify: true,
},
construction_external_wall: {
edit: true,
verify: true,
},
construction_external_wall_source_type: {
edit: true,
verify: true,
},
construction_external_wall_source_links: {
edit: true,
verify: true,
},
construction_internal_wall: {
edit: true,
verify: true,
},
construction_internal_wall_source_type: {
edit: true,
verify: true,
},
construction_internal_wall_source_links: {
edit: true,
verify: true,
},
construction_ground_floor: {
edit: true,
verify: true,
},
construction_ground_floor_source_type: {
edit: true,
verify: true,
},
construction_ground_floor_source_links: {
edit: true,
verify: true,
},
planning_portal_link: { planning_portal_link: {
edit: true, edit: true,
verify: true, verify: true,
@ -277,10 +411,22 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_crowdsourced_site_completion_source_type: {
edit: true,
verify: true,
},
planning_crowdsourced_site_completion_source_links: {
edit: true,
verify: true,
},
planning_crowdsourced_planning_id: { planning_crowdsourced_planning_id: {
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_in_conservation_area: {
edit: true,
verify: true,
},
planning_in_conservation_area_id: { planning_in_conservation_area_id: {
edit: true, edit: true,
verify: true, verify: true,
@ -301,6 +447,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_world_heritage_site: {
edit: true,
verify: true,
},
planning_world_list_id: { planning_world_list_id: {
edit: true, edit: true,
verify: true, verify: true,
@ -309,14 +459,26 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_in_apa: {
edit: true,
verify: true,
},
planning_in_apa_url: { planning_in_apa_url: {
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_local_list: {
edit: true,
verify: true,
},
planning_local_list_url: { planning_local_list_url: {
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_historic_area_assessment: {
edit: true,
verify: true,
},
planning_historic_area_assessment_url: { planning_historic_area_assessment_url: {
edit: true, edit: true,
verify: true, verify: true,
@ -325,6 +487,30 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
planning_missing_data: {
edit: true,
verify: true,
},
planning_missing_data_links: {
edit: true,
verify: true,
},
planning_heritage_at_risk: {
edit: true,
verify: true,
},
planning_scientific_interest: {
edit: true,
verify: true,
},
planning_scientific_interest_source_type: {
edit: true,
verify: true,
},
planning_scientific_interest_source_links: {
edit: true,
verify: true,
},
sust_breeam_rating: { sust_breeam_rating: {
edit: true, edit: true,
verify: true, verify: true,
@ -355,6 +541,14 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true, verify: true,
}, },
building_attachment_source_type: {
edit: true,
verify: true,
},
building_attachment_source_links: {
edit: true,
verify: true,
},
date_change_building_use: { date_change_building_use: {
edit: true, edit: true,
}, },
@ -482,6 +676,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true verify: true
}, },
developer_links: {
edit: true,
verify: true
},
developer_source_type: { developer_source_type: {
edit: true, edit: true,
verify: true verify: true
@ -494,6 +692,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true verify: true
}, },
landowner_links: {
edit: true,
verify: true
},
landowner_source_type: { landowner_source_type: {
edit: true, edit: true,
verify: true verify: true
@ -506,6 +708,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true verify: true
}, },
designers_links: {
edit: true,
verify: true
},
designers_source_type: { designers_source_type: {
edit: true, edit: true,
verify: true verify: true
@ -530,6 +736,10 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
edit: true, edit: true,
verify: true verify: true
}, },
builder_links: {
edit: true,
verify: true
},
builder_source_type: { builder_source_type: {
edit: true, edit: true,
verify: true verify: true
@ -685,7 +895,95 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
age_retrofit_date_source_links : { age_retrofit_date_source_links : {
edit: true, edit: true,
verify: true verify: true
} },
age_historical_raster_map_links : {
edit: true,
verify: true
},
age_historical_vectorised_footprint_links : {
edit: true,
verify: true
},
energy_solar : {
edit: true,
verify: true
},
energy_solar_source_type : {
edit: true,
verify: true
},
energy_solar_source_links : {
edit: true,
verify: true
},
energy_green_roof : {
edit: true,
verify: true
},
energy_green_roof_source_type : {
edit: true,
verify: true
},
energy_green_roof_source_links : {
edit: true,
verify: true
},
typology_classification : {
edit: true,
verify: true
},
typology_classification_source_type : {
edit: true,
verify: true
},
typology_classification_source_links: {
edit: true,
verify: true
},
typology_style_period : {
edit: true,
verify: true
},
typology_style_period_source_type : {
edit: true,
verify: true
},
typology_style_period_source_links: {
edit: true,
verify: true
},
typology_dynamic_classification : {
edit: true,
verify: true
},
typology_dynamic_classification_source_type : {
edit: true,
verify: true
},
typology_dynamic_classification_source_links: {
edit: true,
verify: true
},
typology_original_use : {
edit: true,
derivedEdit: true,
verify: true
},
typology_original_use_verified: {
edit: true,
},
typology_original_use_order : {
edit: true,
verify: true
},
typology_original_use_source_type : {
edit: true,
verify: true
},
typology_original_use_source_links: {
edit: true,
verify: true
},
}); });

View File

@ -10,6 +10,7 @@ type GetAutofillOptionsFn = (value: string, all?: boolean) => Promise<AutofillOp
const autofillFunctionMap : { [fieldName: string] : GetAutofillOptionsFn } = { const autofillFunctionMap : { [fieldName: string] : GetAutofillOptionsFn } = {
current_landuse_group: getLanduseGroupOptions, current_landuse_group: getLanduseGroupOptions,
typology_original_use: getLanduseGroupOptions,
}; };
@ -20,6 +21,7 @@ function getLanduseGroupOptions(value: string, all: boolean = false) {
landuse_id AS id, landuse_id AS id,
description AS value description AS value
FROM reference_tables.buildings_landuse_group FROM reference_tables.buildings_landuse_group
ORDER BY description
` `
); );
} }

View File

@ -9,7 +9,7 @@ import { ArgumentError } from '../../errors/general';
import { updateLandUse } from './landUse'; import { updateLandUse } from './landUse';
/** /**
* Process land use classifications - derive land use order from land use groups * Process current land use classifications - derive land use order from land use groups
*/ */
async function processCurrentLandUseClassifications( async function processCurrentLandUseClassifications(
buildingId: number, buildingId: number,
@ -40,6 +40,37 @@ async function processCurrentLandUseClassifications(
} }
} }
/**
* Process original land use classifications - derive land use order from land use groups
*/
async function processOriginalLandUseClassifications(
buildingId: number,
buildingUpdate: Partial<BuildingAttributes>,
t?: ITask<any>
): Promise<any> {
const currentBuildingData = await getBuildingData(buildingId);
try {
const currentLandUseUpdate = await updateLandUse(
{
landUseGroup: currentBuildingData.typology_original_use,
landUseOrder: currentBuildingData.typology_original_use_order
}, {
landUseGroup: buildingUpdate.typology_original_use
}
);
return Object.assign({}, buildingUpdate, {
typology_original_use: currentLandUseUpdate.landUseGroup,
typology_original_use_order: currentLandUseUpdate.landUseOrder,
});
} catch (error) {
if(error instanceof ArgumentError && error.argumentName === 'landUseUpdate') {
error.argumentName = 'buildingUpdate';
}
throw error;
}
}
/** /**
* Process Dynamics data - check field relationships and sort demolished buildings by construction date * Process Dynamics data - check field relationships and sort demolished buildings by construction date
@ -81,6 +112,9 @@ export async function processBuildingUpdate(buildingId: number, {attributes, use
if(hasAnyOwnProperty(attributes, ['current_landuse_group'])) { if(hasAnyOwnProperty(attributes, ['current_landuse_group'])) {
attributes = await processCurrentLandUseClassifications(buildingId, attributes, t); attributes = await processCurrentLandUseClassifications(buildingId, attributes, t);
} }
if(hasAnyOwnProperty(attributes, ['typology_original_use'])) {
attributes = await processOriginalLandUseClassifications(buildingId, attributes, t);
}
if(hasAnyOwnProperty(attributes, ['demolished_buildings', 'dynamics_has_demolished_buildings'])) { if(hasAnyOwnProperty(attributes, ['demolished_buildings', 'dynamics_has_demolished_buildings'])) {
attributes = await processDynamicsDemolishedBuildings(buildingId, attributes, t); attributes = await processDynamicsDemolishedBuildings(buildingId, attributes, t);
} }

View File

@ -1,7 +1,9 @@
{ {
"cityName": "Cities", "cityName": "Cities",
"projectBlurb": "Colouring {City Name} is part Colouring Cities.", "projectBlurb": "Colouring {City Name} is part Colouring Cities.",
"githubURL": "https://github.com/colouring-cities/colouring-core", "githubURL": "https://github.com/colouring-cities/colouring-core",
"manualURL": "https://github.com/colouring-cities/manual/wiki/M3.-COLOURING-BRITAIN",
"privacyStatement": "{Privacy statement goes here}", "privacyStatement": "{Privacy statement goes here}",
"initialMapPosition": [ 51.5245255, -0.1338422 ], "initialMapPosition": [ 51.5245255, -0.1338422 ],

View File

@ -1,8 +1,12 @@
import { StringNullableChain } from "lodash";
export interface CCConfig export interface CCConfig
{ {
cityName: string; // City name (i.e. "Colouring {City Name}") cityName: string; // City name (i.e. "Colouring {City Name}")
projectBlurb: string; // Description used on homepage projectBlurb: string; // Description used on homepage
githubURL: string; // URL of the project's GitHub repository githubURL: string; // URL of the project's GitHub repository
manualURL: string; // Link to the project's page in the Open Manual (i.e. https://github.com/colouring-cities/manual/wiki/M3.-COLOURING-BRITAIN)
privacyStatement: string; // Privacy statement, including where data is stored privacyStatement: string; // Privacy statement, including where data is stored
initialMapPosition: [number, number]; // Initial location of the map [latitude, longitude] initialMapPosition: [number, number]; // Initial location of the map [latitude, longitude]

View File

@ -76,6 +76,12 @@ export const MultiDataEntry: React.FC<MultiDataEntryProps> = ({
</div> </div>
} }
<ul className="data-entry-list"> <ul className="data-entry-list">
{
isEditing && isDisabled && values.length === 0 &&
<div className="input-group">
<input className="form-control no-entries" type="text" value="Please add a link below" disabled={true} />
</div>
}
{ {
values.map((val, i) => ( values.map((val, i) => (
<li className="input-group" key={i /* i as key prevents input component recreation on edit */}> <li className="input-group" key={i /* i as key prevents input component recreation on edit */}>

View File

@ -12,11 +12,20 @@ interface PatternDataEntryProps extends BaseDataEntryProps {
*/ */
pattern: string; pattern: string;
maxLength?: number; maxLength?: number;
valueTransform?: (val: string) => string;
} }
export const PatternDataEntry: React.FC<PatternDataEntryProps> = props => { export const PatternDataEntry: React.FC<PatternDataEntryProps> = props => {
const handleChange = (value: string) => { const handleChange = (value: string) => {
props.onChange?.(props.slug, value); props.onChange?.(props.slug, transformValue(value));
};
const transformValue = (value: string) => {
const transform = props.valueTransform || (x => x);
const transformedValue = value === '' ?
null :
transform(value);
return transformedValue;
}; };
return ( return (

View File

@ -99,7 +99,9 @@ const withCopyEdit: (wc: React.ComponentType<CategoryViewProps>) => DataContaine
'likes_total', 'likes_total',
'community_type_worth_keeping_total', 'community_type_worth_keeping_total',
'community_local_significance_total', 'community_local_significance_total',
'community_expected_planning_application_total' 'community_expected_planning_application_total',
'typology_original_use',
'typology_original_use_verified'
] ]
for (let key in dataFields) { for (let key in dataFields) {
let fieldName = props.building == undefined ? undefined : props.building[key]; let fieldName = props.building == undefined ? undefined : props.building[key];
@ -286,6 +288,13 @@ const withCopyEdit: (wc: React.ComponentType<CategoryViewProps>) => DataContaine
this.doSubmit(edits); this.doSubmit(edits);
} }
if (slug == 'typology_original_use'){
const edits = {
'typology_original_use_verified': true
};
this.doSubmit(edits);
}
console.log(slug + " verify button clicked") console.log(slug + " verify button clicked")
} }

View File

@ -1,7 +1,7 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import '../../map/map-button.css'; import '../../map/map-button.css';
import { dataFields } from '../../config/data-fields-config'; import { commonSourceTypes, dataFields } from '../../config/data-fields-config';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { DataEntryGroup } from '../data-components/data-entry-group'; import { DataEntryGroup } from '../data-components/data-entry-group';
import { DynamicsBuildingPane, DynamicsDataEntry } from './dynamics/dynamics-data-entry'; import { DynamicsBuildingPane, DynamicsDataEntry } from './dynamics/dynamics-data-entry';
@ -40,27 +40,49 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
const ageLinkUrl = `/${props.mode}/${Category.Age}/${props.building.building_id}`; const ageLinkUrl = `/${props.mode}/${Category.Age}/${props.building.building_id}`;
const { historicData, historicDataSwitchOnClick, darkLightTheme } = useDisplayPreferences(); const { historicData, historicDataSwitchOnClick, darkLightTheme } = useDisplayPreferences();
const { historicMap, historicMapSwitchOnClick } = useDisplayPreferences();
const switchToSurvivalMapStyle = (e) => { const switchToSurvivalMapStyle = (e) => {
e.preventDefault(); e.preventDefault();
props.onMapColourScale('survival_status');
historicMapSwitchOnClick(e);
if (props.mapColourScale == "survival_status") { if (historicData === 'enabled') {
props.onMapColourScale('date_year');
historicDataSwitchOnClick(e); historicDataSwitchOnClick(e);
} }
else { }
const switchToSurvivalDataStyle = (e) => {
e.preventDefault();
props.onMapColourScale('survival_status'); props.onMapColourScale('survival_status');
historicDataSwitchOnClick(e); historicDataSwitchOnClick(e);
if (historicMap === 'enabled') {
historicMapSwitchOnClick(e);
} }
} }
if (props.building.date_source == "Expert knowledge of building" || const switchToAgeMapStyle = (e) => {
props.building.date_source == "Expert estimate from image" || e.preventDefault();
props.building.date_source == null
){ if (historicData === 'enabled') {
historicDataSwitchOnClick(e);
}
if (historicMap === 'enabled') {
historicMapSwitchOnClick(e);
}
props.onMapColourScale('date_year');
}
const switchToStylePeriodMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('typology_style_period')
}
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Building age"> <DataEntryGroup name="Building age/construction date">
<YearDataEntry <YearDataEntry
year={props.building.date_year} year={props.building.date_year}
upper={props.building.date_upper} upper={props.building.date_upper}
@ -107,7 +129,7 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.facade_year} user_verified_as={props.user_verified.facade_year}
verified_count={props.building.verified.facade_year} verified_count={props.building.verified.facade_year}
/> />
<hr/>
<SelectDataEntry <SelectDataEntry
title={dataFields.date_source.title} title={dataFields.date_source.title}
slug="date_source" slug="date_source"
@ -116,8 +138,8 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.date_source.tooltip} tooltip={dataFields.date_source.tooltip}
placeholder={dataFields.date_source.example}
options={dataFields.date_source.items} options={dataFields.date_source.items}
placeholder={dataFields.date_source.example}
/> />
{(props.building.date_source == dataFields.date_source.items[0] || {(props.building.date_source == dataFields.date_source.items[0] ||
props.building.date_source == dataFields.date_source.items[1] || props.building.date_source == dataFields.date_source.items[1] ||
@ -137,6 +159,101 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
/> />
</> </>
} }
<hr/>
<SelectDataEntry
title={dataFields.date_source_type.title}
slug="date_source_type"
value={props.building.date_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.date_source_type.tooltip}
options={dataFields.date_source_type.items}
placeholder={dataFields.date_source_type.example}
/>
{(props.building.date_source_type == dataFields.date_source_type.items[0] ||
props.building.date_source_type == dataFields.date_source_type.items[1] ||
props.building.date_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.date_source_links.title}
slug="date_source_links"
value={props.building.date_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.date_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
{/*<DataEntry
title="Year of completion (best estimate)"
slug=""
value=""
mode='view'
tooltip='Coming Soon'
/>*/}
</DataEntryGroup>
<DataEntryGroup name="Architectural style">
{(props.mapColourScale == "typology_style_period") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAgeMapStyle}>
Click here to return to building age.
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToStylePeriodMapStyle}>
Click here to show architectural style.
</button>
}
<SelectDataEntry
title={dataFields.typology_style_period.title}
slug="typology_style_period"
value={props.building.typology_style_period}
tooltip={dataFields.typology_style_period.tooltip}
options={dataFields.typology_style_period.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="typology_style_period"
allow_verify={props.user !== undefined && props.building.typology_style_period !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("typology_style_period")}
user_verified_as={props.user_verified.typology_style_period}
verified_count={props.building.verified.typology_style_period}
/>
<SelectDataEntry
title={dataFields.typology_style_period_source_type.title}
slug="typology_style_period_source_type"
value={props.building.typology_style_period_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_style_period_source_type.tooltip}
placeholder={dataFields.typology_style_period_source_type.example}
options={dataFields.typology_style_period_source_type.items}
/>
{(props.building.typology_style_period_source_type == commonSourceTypes[0] ||
props.building.typology_style_period_source_type == commonSourceTypes[1] ||
props.building.typology_style_period_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.typology_style_period_source_links.title}
slug="typology_style_period_source_links"
value={props.building.typology_style_period_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_style_period_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Cladding, extensions and retrofits"> <DataEntryGroup name="Cladding, extensions and retrofits">
<NumericDataEntry <NumericDataEntry
@ -290,9 +407,6 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Lifespan and site history"> <DataEntryGroup name="Lifespan and site history">
<button className={`map-switcher-inline ${props.mapColourScale == "survival_status" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
{(props.mapColourScale == "is_domestic")? 'Click here to hide historical maps':'Click here to show historical maps'}
</button>
<DataEntryGroup name="Constructions and demolitions on this site" showCount={false}> <DataEntryGroup name="Constructions and demolitions on this site" showCount={false}>
<DynamicsBuildingPane> <DynamicsBuildingPane>
<label>Current building (age data <Link to={ageLinkUrl}>editable here</Link>)</label> <label>Current building (age data <Link to={ageLinkUrl}>editable here</Link>)</label>
@ -379,388 +493,31 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
Please let us know your suggestions on the <a href="https://discuss.colouring.london/t/dynamics-category-discussion/107">discussion forum</a>! (external link - save your edits first) Please let us know your suggestions on the <a href="https://discuss.colouring.london/t/dynamics-category-discussion/107">discussion forum</a>! (external link - save your edits first)
</InfoBox> </InfoBox>
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Survival and loss tracked using historical maps" collapsed={true} > <DataEntryGroup name="Survival tracking" collapsed={true} >
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}> <div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i> <i>
Can you help us create a map that shows how many buildings in London have survived since the 1890s? Can you help us create a map that shows how many buildings in London have survived since the 1890s?
Choose a colour to indicate whether the building has survived. Choose a colour to indicate whether the building has survived.
</i> </i>
</div> </div>
<button className={`map-switcher-inline ${props.mapColourScale == "survival_status" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}> {(historicMap === "enabled") ?
{(props.mapColourScale == "is_domestic")? 'Click here to hide historical maps':'Click here to show historical maps'} <button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAgeMapStyle}>
Click here to hide the 1890s OS historical map.
</button> </button>
<SelectDataEntry :
title={dataFields.survival_status.title} <button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
slug="survival_status" Click here to show the 1890s OS historical map.
value={props.building.survival_status}
tooltip={dataFields.survival_status.tooltip}
options={SurvivalStatusOptions}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<SelectDataEntry
title={dataFields.survival_source.title}
slug="survival_source"
value={props.building.survival_source}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.survival_source.tooltip}
placeholder={dataFields.survival_source.example}
options={dataFields.survival_source.items}
/>
{(props.building.survival_source == dataFields.survival_source_links[0] ||
props.building.survival_source == dataFields.survival_source_links[1] ||
props.building.survival_source == null) ? <></> :
<><MultiDataEntry
title={dataFields.survival_source_links.title}
slug="survival_source_links"
value={props.building.survival_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.survival_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
</Fragment>
);
};
return (
<Fragment>
<DataEntryGroup name="Building age">
<YearDataEntry
year={props.building.date_year}
upper={props.building.date_upper}
lower={props.building.date_lower}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
allow_verify={props.user !== undefined && props.building.date_year !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("date_year")}
user_verified_as={props.user_verified.date_year}
verified_count={props.building.verified.date_year}
allow_verify_upper={props.user !== undefined && props.building.date_upper !== null && !props.edited}
onVerify_upper={props.onVerify}
user_verified_upper={props.user_verified.hasOwnProperty("date_upper")}
user_verified_as_upper={props.user_verified.date_upper}
verified_count_upper={props.building.verified.date_upper}
allow_verify_lower={props.user !== undefined && props.building.date_lower !== null && !props.edited}
onVerify_lower={props.onVerify}
user_verified_lower={props.user_verified.hasOwnProperty("date_lower")}
user_verified_as_lower={props.user_verified.date_lower}
verified_count_lower={props.building.verified.date_lower}
/>
<NumericDataEntry
title={dataFields.facade_year.title}
slug="facade_year"
value={props.building.facade_year}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.facade_year.tooltip}
/>
<Verification
slug="facade_year"
allow_verify={props.user !== undefined && props.building.facade_year !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("facade_year")}
user_verified_as={props.user_verified.facade_year}
verified_count={props.building.verified.facade_year}
/>
<SelectDataEntry
title={dataFields.date_source.title}
slug="date_source"
value={props.building.date_source}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.date_source.tooltip}
options={dataFields.date_source.items}
placeholder={dataFields.date_source.example}
/>
{(props.building.date_source == dataFields.date_source.items[0] ||
props.building.date_source == dataFields.date_source.items[1] ||
props.building.date_source == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.date_link.title}
slug="date_link"
value={props.building.date_link}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.date_link.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Cladding, extensions and retrofits">
<NumericDataEntry
slug='age_cladding_date'
title={dataFields.age_cladding_date.title}
value={props.building.age_cladding_date}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.extension_year.tooltip}
/>
<Verification
slug="age_cladding_date"
allow_verify={props.user !== undefined && props.building.age_cladding_date !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("age_cladding_date")}
user_verified_as={props.user_verified.age_cladding_date}
verified_count={props.building.verified.age_cladding_date}
/>
<SelectDataEntry
title={dataFields.age_cladding_date_source_type.title}
slug="age_cladding_date_source_type"
value={props.building.age_cladding_date_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_cladding_date_source_type.tooltip}
options={dataFields.age_cladding_date_source_type.items}
placeholder={dataFields.age_cladding_date_source_type.example}
/>
{(props.building.age_cladding_date_source_type == dataFields.age_cladding_date_source_type.items[0] ||
props.building.age_cladding_date_source_type == dataFields.age_cladding_date_source_type.items[1] ||
props.building.age_cladding_date_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.age_cladding_date_source_links.title}
slug="age_cladding_date_source_links"
value={props.building.age_cladding_date_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_cladding_date_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<NumericDataEntry
slug='age_extension_date'
title={dataFields.age_extension_date.title}
value={props.building.age_extension_date}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.extension_year.tooltip}
/>
<Verification
slug="age_extension_date"
allow_verify={props.user !== undefined && props.building.age_extension_date !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("age_extension_date")}
user_verified_as={props.user_verified.age_extension_date}
verified_count={props.building.verified.age_extension_date}
/>
<SelectDataEntry
title={dataFields.age_extension_date_source_type.title}
slug="age_extension_date_source_type"
value={props.building.age_extension_date_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_extension_date_source_type.tooltip}
options={dataFields.age_extension_date_source_type.items}
placeholder={dataFields.age_extension_date_source_type.example}
/>
{(props.building.age_extension_date_source_type == dataFields.age_extension_date_source_type.items[0] ||
props.building.age_extension_date_source_type == dataFields.age_extension_date_source_type.items[1] ||
props.building.age_extension_date_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.age_extension_date_source_links.title}
slug="age_extension_date_source_links"
value={props.building.age_extension_date_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_extension_date_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<NumericDataEntry
slug='age_retrofit_date'
title={dataFields.age_retrofit_date.title}
value={props.building.age_retrofit_date}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.extension_year.tooltip}
/>
<Verification
slug="age_retrofit_date"
allow_verify={props.user !== undefined && props.building.age_retrofit_date !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("age_retrofit_date")}
user_verified_as={props.user_verified.age_retrofit_date}
verified_count={props.building.verified.age_retrofit_date}
/>
<SelectDataEntry
title={dataFields.age_retrofit_date_source_type.title}
slug="age_retrofit_date_source_type"
value={props.building.age_retrofit_date_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_retrofit_date_source_type.tooltip}
options={dataFields.age_retrofit_date_source_type.items}
placeholder={dataFields.age_retrofit_date_source_type.example}
/>
{(props.building.age_retrofit_date_source_type == dataFields.age_retrofit_date_source_type.items[0] ||
props.building.age_retrofit_date_source_type == dataFields.age_retrofit_date_source_type.items[1] ||
props.building.age_retrofit_date_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.age_retrofit_date_source_links.title}
slug="age_retrofit_date_source_links"
value={props.building.age_retrofit_date_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_retrofit_date_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Lifespan and site history">
<button className={`map-switcher-inline ${props.mapColourScale == "survival_status" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
{(props.mapColourScale == "is_domestic")? 'Click here to hide historical maps':'Click here to show historical maps'}
</button> </button>
<DataEntryGroup name="Constructions and demolitions on this site" showCount={false}>
<DynamicsBuildingPane>
<label>Current building (age data <Link to={ageLinkUrl}>editable here</Link>)</label>
<FieldRow>
<div>
<NumericDataEntry
slug=''
title={dataFields.demolished_buildings.items.year_constructed.title}
value={currentBuildingConstructionYear}
disabled={true}
mode='view'
/>
</div>
<div>
<NumericDataEntry
slug=''
title={dataFields.demolished_buildings.items.year_demolished.title}
value={undefined}
placeholder='---'
disabled={true}
mode='view'
/>
</div>
<div style={{flex: '0 1 27%'}}>
<DataEntry
slug=''
title='Lifespan to date'
value={ (thisYear - currentBuildingConstructionYear) + ''}
disabled={true}
mode='view'
/>
</div>
</FieldRow>
</DynamicsBuildingPane>
{
currentBuildingConstructionYear == undefined ?
<InfoBox>To add historical records, fill in the <Link to={ageLinkUrl}>Age</Link> data first.</InfoBox> :
<>
<LogicalDataEntry
slug='dynamics_has_demolished_buildings'
title={dataFields.dynamics_has_demolished_buildings.title}
value={building.dynamics_has_demolished_buildings}
disallowFalse={(building.demolished_buildings?.length ?? 0) > 0}
disallowNull={(building.demolished_buildings?.length ?? 0) > 0}
onChange={props.onSaveChange}
mode={props.mode}
copy={props.copy}
/>
{
building.dynamics_has_demolished_buildings &&
<>
<DynamicsDataEntry
/*
Will clear the edits and new record data upon navigating to another building.
Should get a better way to do this, plus a way to actually keep unsaved edits.
*/
key={building.building_id}
value={building.demolished_buildings}
editableEntries={true}
slug='demolished_buildings'
title={dataFields.demolished_buildings.title}
mode={props.mode}
onChange={props.onChange}
onSaveAdd={props.onSaveAdd}
hasEdits={props.edited}
maxYear={currentBuildingConstructionYear}
minYear={50}
/>
{
props.mode === 'view' &&
<InfoBox>Switch to edit mode to add/edit past building records</InfoBox>
} }
</> {(historicData === "enabled") ?
} <button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAgeMapStyle}>
</> Click here to hide the 1890s OS historical map with modern footprints.
}
</DataEntryGroup>
<InfoBox type='warning'>
This section is under development in collaboration with the historic environment sector.
Please let us know your suggestions on the <a href="https://discuss.colouring.london/t/dynamics-category-discussion/107">discussion forum</a>! (external link - save your edits first)
</InfoBox>
</DataEntryGroup>
<DataEntryGroup name="Survival and loss tracked using historical maps" collapsed={true} >
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i>
Can you help us create a map that shows how many buildings in London have survived since the 1890s?
Choose a colour to indicate whether the building has survived.
</i>
</div>
<button className={`map-switcher-inline ${props.mapColourScale == "survival_status" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
{(props.mapColourScale == "is_domestic")? 'Click here to hide historical maps':'Click here to show historical maps'}
</button> </button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalDataStyle}>
Click here to show the 1890s OS historical map with modern footprints.
</button>
}
<SelectDataEntry <SelectDataEntry
title={dataFields.survival_status.title} title={dataFields.survival_status.title}
slug="survival_status" slug="survival_status"
@ -800,6 +557,40 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Historical map data options" collapsed={true} >
<InfoBox type='warning'>
This section is under development
</InfoBox>
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i>
This section provides links to open digitised historical maps/mapping data that we are using in the Colouring Cities platform.
</i>
</div>
<MultiDataEntry
title={dataFields.age_historical_raster_map_links.title}
slug="age_historical_raster_map_links"
value={props.building.age_historical_raster_map_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_historical_raster_map_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
<MultiDataEntry
title={dataFields.age_historical_vectorised_footprint_links.title}
slug="age_historical_vectorised_footprint_links"
value={props.building.age_historical_vectorised_footprint_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.age_historical_vectorised_footprint_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</DataEntryGroup>
</Fragment> </Fragment>
); );
}; };

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { Fragment } from 'react';
import '../../map/map-button.css'; import '../../map/map-button.css';
import withCopyEdit from '../data-container'; import withCopyEdit from '../data-container';
@ -15,6 +15,7 @@ import SelectDataEntry from '../data-components/select-data-entry';
import Verification from '../data-components/verification'; import Verification from '../data-components/verification';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { useDisplayPreferences } from '../../displayPreferences-context'; import { useDisplayPreferences } from '../../displayPreferences-context';
import DataEntry from '../data-components/data-entry';
/** /**
* Community view/edit section * Community view/edit section
@ -38,8 +39,9 @@ const CommunityView: React.FunctionComponent<CategoryViewProps> = (props) => {
} }
const { darkLightTheme } = useDisplayPreferences(); const { darkLightTheme } = useDisplayPreferences();
const worthKeepingReasonsNonEmpty = Object.values(props.building.community_type_worth_keeping_reasons ?? {}).some(x => x); const worthKeepingReasonsNonEmpty = Object.values(props.building.community_type_worth_keeping_reasons ?? {}).some(x => x);
return <> return (
<DataEntryGroup name="Community views on building types"> <Fragment>
<DataEntryGroup name="Community views on how well buildings work">
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}> <div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i> <i>
Note: We are currently only collecting data on non-residential buildings. Note: We are currently only collecting data on non-residential buildings.
@ -133,6 +135,11 @@ const CommunityView: React.FunctionComponent<CategoryViewProps> = (props) => {
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
/> />
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i>
For more information on current planning applications, refer to the Planning Controls category.
</i>
</div>
{(props.mapColourScale == "community_expected_planning_application_total") ? {(props.mapColourScale == "community_expected_planning_application_total") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToTypologyMapStyle}> <button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToTypologyMapStyle}>
{'Click to return to liked typologies mapped.'} {'Click to return to liked typologies mapped.'}
@ -144,7 +151,7 @@ const CommunityView: React.FunctionComponent<CategoryViewProps> = (props) => {
} }
</div> </div>
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Building use for community activities"> <DataEntryGroup name="Buildings in community use">
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}> <div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i> <i>
Here we are collecting information on the location of buildings used for community activities so we can track loss of/additions to community space over time. Here we are collecting information on the location of buildings used for community activities so we can track loss of/additions to community space over time.
@ -223,7 +230,8 @@ const CommunityView: React.FunctionComponent<CategoryViewProps> = (props) => {
</button> </button>
} }
</DataEntryGroup> </DataEntryGroup>
</> </Fragment>
);
}; };
const CommunityContainer = withCopyEdit(CommunityView); const CommunityContainer = withCopyEdit(CommunityView);

View File

@ -1,6 +1,6 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { dataFields } from '../../config/data-fields-config'; import { commonSourceTypes, dataFields } from '../../config/data-fields-config';
import DataEntry from '../data-components/data-entry'; import DataEntry from '../data-components/data-entry';
import SelectDataEntry from '../data-components/select-data-entry'; import SelectDataEntry from '../data-components/select-data-entry';
import withCopyEdit from '../data-container'; import withCopyEdit from '../data-container';
@ -9,6 +9,8 @@ import Verification from '../data-components/verification';
import { CategoryViewProps } from './category-view-props'; import { CategoryViewProps } from './category-view-props';
import InfoBox from '../../components/info-box'; import InfoBox from '../../components/info-box';
import { DataEntryGroup } from '../data-components/data-entry-group'; import { DataEntryGroup } from '../data-components/data-entry-group';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { LogicalDataEntry } from '../data-components/logical-data-entry/logical-data-entry';
const ConstructionMaterialsOptions = [ const ConstructionMaterialsOptions = [
'Wood', 'Wood',
@ -21,23 +23,205 @@ const ConstructionMaterialsOptions = [
'Other Man-Made Material' 'Other Man-Made Material'
]; ];
const RoofCoveringOptions = [
'Slate',
'Clay Tile',
'Wood',
'Asphalt',
'Iron or Steel',
'Other Metal',
'Other Natural Material',
'Other Man-Made Material'
];
/** /**
* Construction view/edit section * Construction view/edit section
*/ */
const ConstructionView: React.FunctionComponent<CategoryViewProps> = (props) => { const ConstructionView: React.FunctionComponent<CategoryViewProps> = (props) => {
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Structural system">
<SelectDataEntry
title={dataFields.construction_structural_system.title}
slug="construction_structural_system"
value={props.building.construction_structural_system}
tooltip={dataFields.construction_structural_system.tooltip}
options={dataFields.construction_structural_system.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_structural_system"
allow_verify={props.user !== undefined && props.building.construction_structural_system !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_structural_system")}
user_verified_as={props.user_verified.construction_structural_system}
verified_count={props.building.verified.construction_structural_system}
/>
<SelectDataEntry
title={dataFields.construction_structural_system_source_type.title}
slug="construction_structural_system_source_type"
value={props.building.construction_structural_system_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_structural_system_source_type.tooltip}
placeholder={dataFields.construction_structural_system_source_type.example}
options={dataFields.construction_structural_system_source_type.items}
/>
{(props.building.construction_structural_system_source_type == commonSourceTypes[0] ||
props.building.construction_structural_system_source_type == commonSourceTypes[1] ||
props.building.construction_structural_system_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_structural_system_source_links.title}
slug="construction_structural_system_source_links"
value={props.building.construction_structural_system_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_structural_system_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry
title={dataFields.construction_foundation.title}
slug="construction_foundation"
value={props.building.construction_foundation}
tooltip={dataFields.construction_foundation.tooltip}
options={dataFields.construction_foundation.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_foundation"
allow_verify={props.user !== undefined && props.building.construction_foundation !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_foundation")}
user_verified_as={props.user_verified.construction_foundation}
verified_count={props.building.verified.construction_foundation}
/>
<SelectDataEntry
title={dataFields.construction_foundation_source_type.title}
slug="construction_foundation_source_type"
value={props.building.construction_foundation_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_foundation_source_type.tooltip}
placeholder={dataFields.construction_foundation_source_type.example}
options={dataFields.construction_foundation_source_type.items}
/>
{(props.building.construction_foundation_source_type == commonSourceTypes[0] ||
props.building.construction_foundation_source_type == commonSourceTypes[1] ||
props.building.construction_foundation_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_foundation_source_links.title}
slug="construction_foundation_source_links"
value={props.building.construction_foundation_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_foundation_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry
title={dataFields.construction_roof_shape.title}
slug="construction_roof_shape"
value={props.building.construction_roof_shape}
tooltip={dataFields.construction_roof_shape.tooltip}
options={dataFields.construction_roof_shape.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_roof_shape"
allow_verify={props.user !== undefined && props.building.construction_roof_shape !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_roof_shape")}
user_verified_as={props.user_verified.construction_roof_shape}
verified_count={props.building.verified.construction_roof_shape}
/>
<SelectDataEntry
title={dataFields.construction_roof_shape_source_type.title}
slug="construction_roof_shape_source_type"
value={props.building.construction_roof_shape_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_roof_shape_source_type.tooltip}
placeholder={dataFields.construction_roof_shape_source_type.example}
options={dataFields.construction_roof_shape_source_type.items}
/>
{(props.building.construction_roof_shape_source_type == commonSourceTypes[0] ||
props.building.construction_roof_shape_source_type == commonSourceTypes[1] ||
props.building.construction_roof_shape_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_roof_shape_source_links.title}
slug="construction_roof_shape_source_links"
value={props.building.construction_roof_shape_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_roof_shape_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry
title={dataFields.construction_irregularities.title}
slug="construction_irregularities"
value={props.building.construction_irregularities}
tooltip={dataFields.construction_irregularities.tooltip}
options={dataFields.construction_irregularities.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_irregularities"
allow_verify={props.user !== undefined && props.building.construction_irregularities !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_irregularities")}
user_verified_as={props.user_verified.construction_irregularities}
verified_count={props.building.verified.construction_irregularities}
/>
<SelectDataEntry
title={dataFields.construction_irregularities_source_type.title}
slug="construction_irregularities_source_type"
value={props.building.construction_irregularities_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_irregularities_source_type.tooltip}
placeholder={dataFields.construction_irregularities_source_type.example}
options={dataFields.construction_irregularities_source_type.items}
/>
{(props.building.construction_irregularities_source_type == commonSourceTypes[0] ||
props.building.construction_irregularities_source_type == commonSourceTypes[1] ||
props.building.construction_irregularities_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_irregularities_source_links.title}
slug="construction_irregularities_source_links"
value={props.building.construction_irregularities_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_irregularities_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Materials"> <DataEntryGroup name="Materials">
<SelectDataEntry <SelectDataEntry
title={dataFields.construction_core_material.title} title={dataFields.construction_core_material.title}
@ -58,22 +242,185 @@ const ConstructionView: React.FunctionComponent<CategoryViewProps> = (props) =>
verified_count={props.building.verified.construction_core_material} verified_count={props.building.verified.construction_core_material}
/> />
<SelectDataEntry <SelectDataEntry
title={dataFields.construction_secondary_materials.title} title={dataFields.construction_core_material_source_type.title}
disabled={true} slug="construction_core_material_source_type"
slug="construction_secondary_materials" value={props.building.construction_core_material_source_type}
value={props.building.construction_secondary_materials} mode={props.mode}
tooltip={dataFields.construction_secondary_materials.tooltip} copy={props.copy}
options={ConstructionMaterialsOptions} onChange={props.onChange}
tooltip={dataFields.construction_core_material_source_type.tooltip}
placeholder={dataFields.construction_core_material_source_type.example}
options={dataFields.construction_core_material_source_type.items}
/>
{(props.building.construction_core_material_source_type == commonSourceTypes[0] ||
props.building.construction_core_material_source_type == commonSourceTypes[1] ||
props.building.construction_core_material_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_core_material_source_links.title}
slug="construction_core_material_source_links"
value={props.building.construction_core_material_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_core_material_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry
title={dataFields.construction_external_wall.title}
slug="construction_external_wall"
value={props.building.construction_external_wall}
tooltip={dataFields.construction_external_wall.tooltip}
options={dataFields.construction_external_wall.items}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
/> />
<Verification
slug="construction_external_wall"
allow_verify={props.user !== undefined && props.building.construction_external_wall !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_external_wall")}
user_verified_as={props.user_verified.construction_external_wall}
verified_count={props.building.verified.construction_external_wall}
/>
<SelectDataEntry
title={dataFields.construction_external_wall_source_type.title}
slug="construction_external_wall_source_type"
value={props.building.construction_external_wall_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_external_wall_source_type.tooltip}
placeholder={dataFields.construction_external_wall_source_type.example}
options={dataFields.construction_external_wall_source_type.items}
/>
{(props.building.construction_external_wall_source_type == commonSourceTypes[0] ||
props.building.construction_external_wall_source_type == commonSourceTypes[1] ||
props.building.construction_external_wall_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_external_wall_source_links.title}
slug="construction_external_wall_source_links"
value={props.building.construction_external_wall_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_external_wall_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry
title={dataFields.construction_internal_wall.title}
slug="construction_internal_wall"
value={props.building.construction_internal_wall}
tooltip={dataFields.construction_internal_wall.tooltip}
options={dataFields.construction_internal_wall.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_internal_wall"
allow_verify={props.user !== undefined && props.building.construction_internal_wall !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_internal_wall")}
user_verified_as={props.user_verified.construction_internal_wall}
verified_count={props.building.verified.construction_internal_wall}
/>
<SelectDataEntry
title={dataFields.construction_internal_wall_source_type.title}
slug="construction_internal_wall_source_type"
value={props.building.construction_internal_wall_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_internal_wall_source_type.tooltip}
placeholder={dataFields.construction_internal_wall_source_type.example}
options={dataFields.construction_internal_wall_source_type.items}
/>
{(props.building.construction_internal_wall_source_type == commonSourceTypes[0] ||
props.building.construction_internal_wall_source_type == commonSourceTypes[1] ||
props.building.construction_internal_wall_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_internal_wall_source_links.title}
slug="construction_internal_wall_source_links"
value={props.building.construction_internal_wall_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_internal_wall_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry
title={dataFields.construction_ground_floor.title}
slug="construction_ground_floor"
value={props.building.construction_ground_floor}
tooltip={dataFields.construction_ground_floor.tooltip}
options={dataFields.construction_ground_floor.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_ground_floor"
allow_verify={props.user !== undefined && props.building.construction_ground_floor !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_ground_floor")}
user_verified_as={props.user_verified.construction_ground_floor}
verified_count={props.building.verified.construction_ground_floor}
/>
<SelectDataEntry
title={dataFields.construction_ground_floor_source_type.title}
slug="construction_ground_floor_source_type"
value={props.building.construction_ground_floor_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_ground_floor_source_type.tooltip}
placeholder={dataFields.construction_ground_floor_source_type.example}
options={dataFields.construction_ground_floor_source_type.items}
/>
{(props.building.construction_ground_floor_source_type == commonSourceTypes[0] ||
props.building.construction_ground_floor_source_type == commonSourceTypes[1] ||
props.building.construction_ground_floor_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_ground_floor_source_links.title}
slug="construction_ground_floor_source_links"
value={props.building.construction_ground_floor_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_ground_floor_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<SelectDataEntry <SelectDataEntry
title={dataFields.construction_roof_covering.title} title={dataFields.construction_roof_covering.title}
slug="construction_roof_covering" slug="construction_roof_covering"
value={props.building.construction_roof_covering} value={props.building.construction_roof_covering}
tooltip={dataFields.construction_roof_covering.tooltip} tooltip={dataFields.construction_roof_covering.tooltip}
options={RoofCoveringOptions} options={dataFields.construction_roof_covering.items}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
@ -86,14 +433,105 @@ const ConstructionView: React.FunctionComponent<CategoryViewProps> = (props) =>
user_verified_as={props.user_verified.construction_roof_covering} user_verified_as={props.user_verified.construction_roof_covering}
verified_count={props.building.verified.construction_roof_covering} verified_count={props.building.verified.construction_roof_covering}
/> />
</DataEntryGroup> <SelectDataEntry
<DataEntryGroup name="Construction sectors"> title={dataFields.construction_roof_covering_source_type.title}
<DataEntry slug="construction_roof_covering_source_type"
title="Construction system type" value={props.building.construction_roof_covering_source_type}
slug="" mode={props.mode}
value="" copy={props.copy}
mode='view' onChange={props.onChange}
tooltip={dataFields.construction_roof_covering_source_type.tooltip}
placeholder={dataFields.construction_roof_covering_source_type.example}
options={dataFields.construction_roof_covering_source_type.items}
/> />
{(props.building.construction_roof_covering_source_type == commonSourceTypes[0] ||
props.building.construction_roof_covering_source_type == commonSourceTypes[1] ||
props.building.construction_roof_covering_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_roof_covering_source_links.title}
slug="construction_roof_covering_source_links"
value={props.building.construction_roof_covering_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_roof_covering_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Decorative features">
<LogicalDataEntry
slug='construction_decorative_features'
title={dataFields.construction_decorative_features.title}
value={props.building.construction_decorative_features}
onChange={props.onSaveChange}
mode={props.mode}
copy={props.copy}
/>
<Verification
slug="construction_decorative_features"
allow_verify={props.user !== undefined && props.building.construction_decorative_features !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_decorative_features")}
user_verified_as={props.user_verified.construction_decorative_features}
verified_count={props.building.verified.construction_decorative_features}
/>
{
props.building.construction_decorative_features &&
<>
<SelectDataEntry
title={dataFields.construction_decorative_feature_materials.title}
slug="construction_decorative_feature_materials"
value={props.building.construction_decorative_feature_materials}
tooltip={dataFields.construction_decorative_feature_materials.tooltip}
options={dataFields.construction_decorative_feature_materials.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="construction_decorative_feature_materials"
allow_verify={props.user !== undefined && props.building.construction_decorative_feature_materials !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("construction_decorative_feature_materials")}
user_verified_as={props.user_verified.construction_decorative_feature_materials}
verified_count={props.building.verified.construction_decorative_feature_materials}
/>
<SelectDataEntry
title={dataFields.construction_decorative_feature_source_type.title}
slug="construction_decorative_feature_source_type"
value={props.building.construction_decorative_feature_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_decorative_feature_source_type.tooltip}
placeholder={dataFields.construction_decorative_feature_source_type.example}
options={dataFields.construction_decorative_feature_source_type.items}
/>
{(props.building.construction_decorative_feature_source_type == commonSourceTypes[0] ||
props.building.construction_decorative_feature_source_type == commonSourceTypes[1] ||
props.building.construction_decorative_feature_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.construction_decorative_feature_source_links.title}
slug="construction_decorative_feature_source_links"
value={props.building.construction_decorative_feature_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.construction_decorative_feature_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</>
}
</DataEntryGroup> </DataEntryGroup>
</Fragment> </Fragment>
); );

View File

@ -11,6 +11,7 @@ import InfoBox from '../../components/info-box';
import { CategoryViewProps } from './category-view-props'; import { CategoryViewProps } from './category-view-props';
import { DataEntryGroup } from '../data-components/data-entry-group'; import { DataEntryGroup } from '../data-components/data-entry-group';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { LogicalDataEntry } from '../data-components/logical-data-entry/logical-data-entry';
const EnergyCategoryOptions = ["A", "B", "C", "D", "E", "F", "G"]; const EnergyCategoryOptions = ["A", "B", "C", "D", "E", "F", "G"];
const BreeamRatingOptions = [ const BreeamRatingOptions = [
@ -25,9 +26,20 @@ const BreeamRatingOptions = [
* Sustainability view/edit section * Sustainability view/edit section
*/ */
const SustainabilityView: React.FunctionComponent<CategoryViewProps> = (props) => { const SustainabilityView: React.FunctionComponent<CategoryViewProps> = (props) => {
const currentYear = new Date().getFullYear();
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Energy rating data"> <DataEntryGroup name="Environmental quality rating">
<DataEntry
title="Official environmental quality rating"
slug=""
value=""
mode='view'
/>
</DataEntryGroup>
<DataEntryGroup name="Energy rating">
<SelectDataEntry <SelectDataEntry
title={dataFields.sust_breeam_rating.title} title={dataFields.sust_breeam_rating.title}
slug="sust_breeam_rating" slug="sust_breeam_rating"
@ -46,7 +58,6 @@ const SustainabilityView: React.FunctionComponent<CategoryViewProps> = (props) =
user_verified_as={props.user_verified.sust_breeam_rating} user_verified_as={props.user_verified.sust_breeam_rating}
verified_count={props.building.verified.sust_breeam_rating} verified_count={props.building.verified.sust_breeam_rating}
/> />
<SelectDataEntry <SelectDataEntry
title={dataFields.sust_dec.title} title={dataFields.sust_dec.title}
slug="sust_dec" slug="sust_dec"
@ -65,7 +76,6 @@ const SustainabilityView: React.FunctionComponent<CategoryViewProps> = (props) =
user_verified_as={props.user_verified.sust_dec} user_verified_as={props.user_verified.sust_dec}
verified_count={props.building.verified.sust_dec} verified_count={props.building.verified.sust_dec}
/> />
<SelectDataEntry <SelectDataEntry
title={dataFields.sust_aggregate_estimate_epc.title} title={dataFields.sust_aggregate_estimate_epc.title}
slug="sust_aggregate_estimate_epc" slug="sust_aggregate_estimate_epc"
@ -78,91 +88,160 @@ const SustainabilityView: React.FunctionComponent<CategoryViewProps> = (props) =
onChange={props.onChange} onChange={props.onChange}
/> />
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Retrofit Data"> <DataEntryGroup name="Retrofit history">
<NumericDataEntry <NumericDataEntry
title={dataFields.sust_retrofit_date.title} slug='age_retrofit_date'
slug="sust_retrofit_date" title={dataFields.age_retrofit_date.title}
value={props.building.sust_retrofit_date} value={props.building.age_retrofit_date}
tooltip={dataFields.sust_retrofit_date.tooltip}
step={1}
min={1086}
max={new Date().getFullYear()}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.extension_year.tooltip}
/> />
<Verification <Verification
slug="sust_retrofit_date" slug="age_retrofit_date"
allow_verify={props.user !== undefined && props.building.sust_retrofit_date !== null && !props.edited} allow_verify={props.user !== undefined && props.building.age_retrofit_date !== null && !props.edited}
onVerify={props.onVerify} onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("sust_retrofit_date")} user_verified={props.user_verified.hasOwnProperty("age_retrofit_date")}
user_verified_as={props.user_verified.sust_retrofit_date} user_verified_as={props.user_verified.age_retrofit_date}
verified_count={props.building.verified.sust_retrofit_date} verified_count={props.building.verified.age_retrofit_date}
/> />
<SelectDataEntry <SelectDataEntry
title={dataFields.sust_retrofit_source_type.title} title={dataFields.age_retrofit_date_source_type.title}
slug="sust_retrofit_source_type" slug="age_retrofit_date_source_type"
value={props.building.sust_retrofit_source_type} value={props.building.age_retrofit_date_source_type}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.sust_retrofit_source_type.tooltip} tooltip={dataFields.age_retrofit_date_source_type.tooltip}
options={dataFields.sust_retrofit_source_type.items} options={dataFields.age_retrofit_date_source_type.items}
placeholder={dataFields.sust_retrofit_source_type.example} placeholder={dataFields.age_retrofit_date_source_type.example}
/> />
{(props.building.sust_retrofit_source_type == dataFields.sust_retrofit_source_type.items[0] || {(props.building.age_retrofit_date_source_type == dataFields.age_retrofit_date_source_type.items[0] ||
props.building.sust_retrofit_source_type == dataFields.sust_retrofit_source_type.items[1] || props.building.age_retrofit_date_source_type == dataFields.age_retrofit_date_source_type.items[1] ||
props.building.sust_retrofit_source_type == null) ? <></> : props.building.age_retrofit_date_source_type == null) ? <></> :
<> <>
<MultiDataEntry <MultiDataEntry
title={dataFields.sust_retrofit_source_links.title} title={dataFields.age_retrofit_date_source_links.title}
slug="sust_retrofit_source_links" slug="age_retrofit_date_source_links"
value={props.building.sust_retrofit_source_links} value={props.building.age_retrofit_date_source_links}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.sust_retrofit_source_links.tooltip} tooltip={dataFields.age_retrofit_date_source_links.tooltip}
placeholder="https://..." placeholder="https://..."
editableEntries={true} editableEntries={true}
isUrl={true} isUrl={true}
/> />
</> </>
} }
{/* <hr/> </DataEntryGroup>
<DataEntry <DataEntryGroup name="Solar panels">
title="Date of Significant Retrofits" <LogicalDataEntry
slug="" title={dataFields.energy_solar.title}
value="" slug="energy_solar"
mode='view' value={props.building.energy_solar}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.energy_solar.tooltip}
/> />
<Verification <Verification
slug="date_link" slug="energy_solar"
allow_verify={props.user !== undefined && props.building.date_link !== null && !props.edited} allow_verify={props.user !== undefined && props.building.energy_solar !== null && !props.edited}
onVerify={props.onVerify} onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("date_link")} user_verified={props.user_verified.hasOwnProperty("energy_solar")}
user_verified_as={props.user_verified.date_link} user_verified_as={props.user_verified.energy_solar}
verified_count={props.building.verified.date_link} verified_count={props.building.verified.energy_solar}
/> />
<DataEntry {props.building.energy_solar == null ? <></> :
title="Source" <>
slug="" <SelectDataEntry
value="" title={dataFields.energy_solar_source_type.title}
mode='view' slug="energy_solar_source_type"
/> */} value={props.building.energy_solar_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.energy_solar_source_type.tooltip}
options={dataFields.energy_solar_source_type.items}
placeholder={dataFields.energy_solar_source_type.example}
/>
{(props.building.energy_solar_source_type == dataFields.energy_solar_source_type.items[0] ||
props.building.energy_solar_source_type == dataFields.energy_solar_source_type.items[1] ||
props.building.energy_solar_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.energy_solar_source_links.title}
slug="energy_solar_source_links"
value={props.building.energy_solar_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.energy_solar_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</>
}
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Other sustainability features"> <DataEntryGroup name="Green walls/roof">
<DataEntry <LogicalDataEntry
title="Does the building have Solar Panels?" title={dataFields.energy_green_roof.title}
slug="" slug="energy_green_roof"
value="" value={props.building.energy_green_roof}
mode='view' mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.energy_green_roof.tooltip}
/> />
<DataEntry <Verification
title="Does the building have Green Walls / Green Roof" slug="energy_green_roof"
slug="" allow_verify={props.user !== undefined && props.building.energy_green_roof !== null && !props.edited}
value="" onVerify={props.onVerify}
mode='view' user_verified={props.user_verified.hasOwnProperty("energy_green_roof")}
user_verified_as={props.user_verified.energy_green_roof}
verified_count={props.building.verified.energy_green_roof}
/> />
{props.building.energy_green_roof == null ? <></> :
<>
<SelectDataEntry
title={dataFields.energy_green_roof_source_type.title}
slug="energy_green_roof_source_type"
value={props.building.energy_green_roof_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.energy_green_roof_source_type.tooltip}
options={dataFields.energy_green_roof_source_type.items}
placeholder={dataFields.energy_green_roof_source_type.example}
/>
{(props.building.energy_green_roof_source_type == dataFields.energy_green_roof_source_type.items[0] ||
props.building.energy_green_roof_source_type == dataFields.energy_green_roof_source_type.items[1] ||
props.building.energy_green_roof_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.energy_green_roof_source_links.title}
slug="energy_green_roof_source_links"
value={props.building.energy_green_roof_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.energy_green_roof_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</>
}
</DataEntryGroup> </DataEntryGroup>
</Fragment> </Fragment>
); );

View File

@ -32,14 +32,14 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => {
const { darkLightTheme } = useDisplayPreferences(); const { darkLightTheme } = useDisplayPreferences();
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Residential/non-residential land use data (general)"> <DataEntryGroup name="General Land Use">
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}> <div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i> <i>
The vast majority of properties are residential (93% in the UK), so we have set 'residential' as the default value. Can you help us identify non-residential and mixed use buildings (and verify residential buildings too)? The vast majority of properties are residential (93% in the UK), so we have set 'residential' as the default value. Can you help us identify non-residential and mixed use buildings (and verify residential buildings too)?
</i> </i>
</div> </div>
<button className={`map-switcher-inline ${props.mapColourScale == "is_domestic" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToIsDomesticMapStyle}> <button className={`map-switcher-inline ${props.mapColourScale == "is_domestic" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToIsDomesticMapStyle}>
{(props.mapColourScale == "is_domestic")? 'Showing domestic, non-domestic and mixed-use buildings (click to hide)' : 'Click to see domestic, non-domestic and mixed-use buildings on the map.'} {(props.mapColourScale == "is_domestic")? 'Showing residential, non-residential and mixed-use buildings (click to hide)' : 'Click to see residential, non-residential and mixed-use buildings on the map.'}
</button> </button>
<SelectDataEntry <SelectDataEntry
title={dataFields.is_domestic.title} title={dataFields.is_domestic.title}
@ -87,7 +87,7 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => {
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Specific land use data"> <DataEntryGroup name="Specific land use(s)">
<MultiDataEntry <MultiDataEntry
title={dataFields.current_landuse_group.title} title={dataFields.current_landuse_group.title}
slug="current_landuse_group" slug="current_landuse_group"
@ -138,6 +138,7 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => {
/> />
</> </>
} }
<hr/>
{ {
props.mode != 'view' && props.mode != 'view' &&
<div> <div>

View File

@ -12,12 +12,14 @@ import SelectDataEntry from '../data-components/select-data-entry';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
const locationNumberPattern = "[1-9]\\d*[a-z]?(-([1-9]\\d*))?"; ///[1-9]\d*[a-z]?(-([1-9]\d*))?/; const locationNumberPattern = "[1-9]\\d*[a-z]?(-([1-9]\\d*))?"; ///[1-9]\d*[a-z]?(-([1-9]\d*))?/;
const postcodeCharacterPattern = "^[A-Z]{1,2}[0-9]{1,2}[A-Z]?(\\s*[0-9][A-Z]{1,2})?$";
const osmIdentifierPattern = "[0-9]{1,9}";
const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => { const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
const osm_url = "https://www.openstreetmap.org/way/"+props.building.ref_osm_id; const osm_url = "www.openstreetmap.org/way/"+props.building.ref_osm_id;
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Address data"> <DataEntryGroup name="Addresses">
<DataEntry <DataEntry
title={dataFields.location_name.title} title={dataFields.location_name.title}
slug="location_name" slug="location_name"
@ -26,8 +28,9 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.location_name.tooltip} tooltip={dataFields.location_name.tooltip}
placeholder="https://..." placeholder=""
isUrl={true} isUrl={false}
disabled={true}
/> />
<Verification <Verification
slug="location_name" slug="location_name"
@ -44,6 +47,23 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
mode='view' mode='view'
tooltip="Not yet activated.<br><br>For security reasons, we do not allow the use of free text boxes and are currently looking into alternative ways to collect this data." tooltip="Not yet activated.<br><br>For security reasons, we do not allow the use of free text boxes and are currently looking into alternative ways to collect this data."
/> />
<DataEntry
title={dataFields.location_name_link.title}
slug="location_name_link"
value={props.building.location_name_link}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.location_name_link.tooltip}
placeholder={dataFields.location_name_link.example}
isUrl={true}
/>
{
(props.building.location_name_link == null) ? <></> :
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">Source: <a href={props.building.location_name_link} target={"_blank"}>{props.building.location_name_link}</a></i>
</div>
}
<hr/> <hr/>
<PatternDataEntry <PatternDataEntry
title={dataFields.location_number.title} title={dataFields.location_number.title}
@ -54,6 +74,7 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.location_number.tooltip} tooltip={dataFields.location_number.tooltip}
maxLength={5}
/> />
<Verification <Verification
slug="location_number" slug="location_number"
@ -71,6 +92,7 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
maxLength={30} maxLength={30}
disabled={true}
/> />
<Verification <Verification
slug="location_street" slug="location_street"
@ -88,6 +110,7 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
maxLength={30} maxLength={30}
disabled={true}
/> />
<Verification <Verification
slug="location_line_two" slug="location_line_two"
@ -104,6 +127,7 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
disabled={true}
/> />
<Verification <Verification
slug="location_town" slug="location_town"
@ -113,15 +137,17 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.location_town} user_verified_as={props.user_verified.location_town}
verified_count={props.building.verified.location_town} verified_count={props.building.verified.location_town}
/> />
<DataEntry <PatternDataEntry
title={dataFields.location_postcode.title} title={dataFields.location_postcode.title}
slug="location_postcode" slug="location_postcode"
value={props.building.location_postcode} value={props.building.location_postcode}
pattern={postcodeCharacterPattern}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
maxLength={8} maxLength={8}
valueTransform={x=>x.toUpperCase()} valueTransform={x=>x.toUpperCase()}
tooltip={dataFields.location_postcode.tooltip}
/> />
<Verification <Verification
slug="location_postcode" slug="location_postcode"
@ -161,7 +187,7 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Property/footprint IDs and coordinate data"> <DataEntryGroup name="Property/footprint IDs and coordinates">
<DataEntry <DataEntry
title={dataFields.ref_toid.title} title={dataFields.ref_toid.title}
slug="ref_toid" slug="ref_toid"
@ -172,13 +198,27 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
onChange={props.onChange} onChange={props.onChange}
disabled={true} disabled={true}
/> />
{
(props.building.ref_toid == null) ? <></> :
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">Source: <a href="https://www.ordnancesurvey.co.uk/products/os-open-toid" target={"_blank"}>{"www.ordnancesurvey.co.uk/products/os-open-toid"}</a></i>
</div>
}
<hr/>
<UPRNsDataEntry <UPRNsDataEntry
title={dataFields.uprns.title} title={dataFields.uprns.title}
slug="ref_uprns" slug="ref_uprns"
value={props.building.uprns} value={props.building.uprns}
tooltip={dataFields.uprns.tooltip} tooltip={dataFields.uprns.tooltip}
/> />
<DataEntry {
(props.building.uprns == null) ? <></> :
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">Source: <a href="https://beta.ordnancesurvey.co.uk/products/os-open-uprn" target={"_blank"}>{"beta.ordnancesurvey.co.uk/products/os-open-uprn"}</a></i>
</div>
}
<hr/>
<PatternDataEntry
title={dataFields.ref_osm_id.title} title={dataFields.ref_osm_id.title}
slug="ref_osm_id" slug="ref_osm_id"
value={props.building.ref_osm_id} value={props.building.ref_osm_id}
@ -187,13 +227,8 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
tooltip={dataFields.ref_osm_id.tooltip} tooltip={dataFields.ref_osm_id.tooltip}
maxLength={20} maxLength={20}
onChange={props.onChange} onChange={props.onChange}
pattern={osmIdentifierPattern}
/> />
{
(props.building.ref_osm_id == null) ? <></> :
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">Source: <a href={osm_url} target={"_blank"}>{osm_url}</a></i>
</div>
}
<Verification <Verification
slug="ref_osm_id" slug="ref_osm_id"
allow_verify={props.user !== undefined && props.building.ref_osm_id !== null && !props.edited} allow_verify={props.user !== undefined && props.building.ref_osm_id !== null && !props.edited}
@ -202,6 +237,12 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.ref_osm_id} user_verified_as={props.user_verified.ref_osm_id}
verified_count={props.building.verified.ref_osm_id} verified_count={props.building.verified.ref_osm_id}
/> />
{
(props.building.ref_osm_id == null) ? <></> :
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">Source: <a href={"https://"+osm_url} target={"_blank"}>{osm_url}</a></i>
</div>
}
<hr/> <hr/>
<NumericDataEntry <NumericDataEntry
title={dataFields.location_latitude.title} title={dataFields.location_latitude.title}
@ -274,6 +315,19 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => {
/> />
</> </>
} }
<hr/>
<MultiDataEntry
title={dataFields.location_alternative_footprint_links.title}
slug="location_alternative_footprint_links"
value={props.building.location_alternative_footprint_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.location_alternative_footprint_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</DataEntryGroup> </DataEntryGroup>
</Fragment> </Fragment>
); );

View File

@ -19,6 +19,8 @@ import { CategoryViewProps } from './category-view-props';
import { Category } from '../../config/categories-config'; import { Category } from '../../config/categories-config';
import { useDisplayPreferences } from '../../displayPreferences-context'; import { useDisplayPreferences } from '../../displayPreferences-context';
import { processParam } from '../../../api/parameters'; import { processParam } from '../../../api/parameters';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import YearDataEntry from '../data-components/year-data-entry';
const currentTimestamp = new Date().valueOf(); const currentTimestamp = new Date().valueOf();
const milisecondsInYear = 1000 * 60 * 60 * 24 * 365; const milisecondsInYear = 1000 * 60 * 60 * 24 * 365;
@ -63,10 +65,12 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
} }
const { flood, floodSwitchOnClick, housing, housingSwitchOnClick, creative, creativeSwitchOnClick, vista, vistaSwitchOnClick, parcel, parcelSwitchOnClick, conservation, conservationSwitchOnClick, darkLightTheme } = useDisplayPreferences(); const { flood, floodSwitchOnClick, housing, housingSwitchOnClick, creative, creativeSwitchOnClick, vista, vistaSwitchOnClick, parcel, parcelSwitchOnClick, conservation, conservationSwitchOnClick, darkLightTheme } = useDisplayPreferences();
const communityLinkUrl = `/${props.mode}/${Category.Community}/${props.building.building_id}`; const communityLinkUrl = `/${props.mode}/${Category.Community}/${props.building.building_id}`;
const currentYear = new Date().getFullYear();
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Planning application information" collapsed={true} > <DataEntryGroup name="Current planning applications" collapsed={true} >
<DataEntryGroup name="Current/active applications (official data)"> <DataEntryGroup name="Official data">
<InfoBox> <InfoBox>
This section provides data on active applications. We define these as applications with any activity in the last year. This section provides data on active applications. We define these as applications with any activity in the last year.
<br /> <br />
@ -85,7 +89,119 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
: <></> : <></>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Past applications (official data)" collapsed={true} > <DataEntryGroup name="Year of completion" collapsed={true} >
<LogicalDataEntry
slug='planning_crowdsourced_site_completion_status'
title={dataFields.planning_crowdsourced_site_completion_status.title}
tooltip={dataFields.planning_crowdsourced_site_completion_status.tooltip}
value={props.building.planning_crowdsourced_site_completion_status}
copy={props.copy}
onChange={props.onChange}
mode={props.mode}
/>
<Verification
slug="planning_crowdsourced_site_completion_status"
allow_verify={props.user !== undefined && props.building.planning_crowdsourced_site_completion_status !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_crowdsourced_site_completion_status")}
user_verified_as={props.user_verified.planning_crowdsourced_site_completion_status}
verified_count={props.building.verified.planning_crowdsourced_site_completion_status}
/>
{props.building.planning_crowdsourced_site_completion_status == null ? <></> :
<>
<NumericDataEntry
title={dataFields.planning_crowdsourced_site_completion_year.title}
slug="planning_crowdsourced_site_completion_year"
value={props.building.planning_crowdsourced_site_completion_year}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.planning_crowdsourced_site_completion_year.tooltip}
/>
<Verification
slug="planning_crowdsourced_site_completion_year"
allow_verify={props.user !== undefined && props.building.planning_crowdsourced_site_completion_year !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_crowdsourced_site_completion_year")}
user_verified_as={props.user_verified.planning_crowdsourced_site_completion_year}
verified_count={props.building.verified.planning_crowdsourced_site_completion_year}
/>
<SelectDataEntry
title={dataFields.planning_crowdsourced_site_completion_source_type.title}
slug="planning_crowdsourced_site_completion_source_type"
value={props.building.planning_crowdsourced_site_completion_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.planning_crowdsourced_site_completion_source_type.tooltip}
options={dataFields.planning_crowdsourced_site_completion_source_type.items}
placeholder={dataFields.planning_crowdsourced_site_completion_source_type.example}
/>
{(props.building.planning_crowdsourced_site_completion_source_type == dataFields.planning_crowdsourced_site_completion_source_type.items[0] ||
props.building.planning_crowdsourced_site_completion_source_type == dataFields.planning_crowdsourced_site_completion_source_type.items[1] ||
props.building.planning_crowdsourced_site_completion_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.planning_crowdsourced_site_completion_source_links.title}
slug="planning_crowdsourced_site_completion_source_links"
value={props.building.planning_crowdsourced_site_completion_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.planning_crowdsourced_site_completion_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</>
}
</DataEntryGroup>
<DataEntryGroup name="Incomplete/missing data" collapsed={true} >
<LogicalDataEntry
slug='planning_missing_data'
title={dataFields.planning_missing_data.title}
tooltip={dataFields.planning_missing_data.tooltip}
value={props.building.planning_missing_data}
copy={props.copy}
onChange={props.onChange}
mode={props.mode}
/>
<Verification
slug="planning_missing_data"
allow_verify={props.user !== undefined && props.building.planning_missing_data !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_missing_data")}
user_verified_as={props.user_verified.planning_missing_data}
verified_count={props.building.verified.planning_missing_data}
/>
{props.building.planning_missing_data == null ? <></> :
<>
<MultiDataEntry
title={dataFields.planning_missing_data_links.title}
slug="planning_missing_data_links"
value={props.building.planning_missing_data_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.planning_missing_data_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<InfoBox>
If you feel there are incorrect or missing data relating to this building please contact:
planningdata@London.gov.uk
</InfoBox>
</DataEntryGroup>
</DataEntryGroup>
<DataEntryGroup name="Past applications" collapsed={true} >
<InfoBox> <InfoBox>
This section provides data on past applications where available from the GLA, including those with no decision in over a year This section provides data on past applications where available from the GLA, including those with no decision in over a year
</InfoBox> </InfoBox>
@ -102,7 +218,7 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
: <></> : <></>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Possible future applications (crowdsourced data)" collapsed={true} > <DataEntryGroup name="Possible future applications" collapsed={true} >
<InfoBox type='info'>Click and colour buildings here if you think they may be subject to a future planning application involving demolition. To add your opinion on how well this building works, please also visit the <Link to={communityLinkUrl}>Community</Link> section.</InfoBox> <InfoBox type='info'>Click and colour buildings here if you think they may be subject to a future planning application involving demolition. To add your opinion on how well this building works, please also visit the <Link to={communityLinkUrl}>Community</Link> section.</InfoBox>
{ {
props.mapColourScale != "community_expected_planning_application_total" ? props.mapColourScale != "community_expected_planning_application_total" ?
@ -127,7 +243,6 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
Further improvements to this feature are currently being made. Further improvements to this feature are currently being made.
</InfoBox> </InfoBox>
</DataEntryGroup> </DataEntryGroup>
</DataEntryGroup>
<DataEntryGroup name="Planning zones" collapsed={true} > <DataEntryGroup name="Planning zones" collapsed={true} >
<InfoBox> <InfoBox>
To view planning zone data for London click the buttons below. You may need to <u>zoom out</u>. To view planning zone data for London click the buttons below. You may need to <u>zoom out</u>.
@ -221,48 +336,26 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
<button className={`map-switcher-inline ${conservation}-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={conservationSwitchOnClick}> <button className={`map-switcher-inline ${conservation}-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={conservationSwitchOnClick}>
{(conservation === 'enabled')? 'Click to hide Conservation Areas' : 'Click to see Conservation Areas'} {(conservation === 'enabled')? 'Click to hide Conservation Areas' : 'Click to see Conservation Areas'}
</button> </button>
<NumericDataEntryWithFormattedLink <hr/>
title={dataFields.planning_list_id.title} <LogicalDataEntry
slug="planning_list_id" slug='planning_heritage_at_risk'
value={props.building.planning_list_id} title={dataFields.planning_heritage_at_risk.title}
mode={props.mode} tooltip={dataFields.planning_heritage_at_risk.tooltip}
value={props.building.planning_heritage_at_risk}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
placeholder="add ID here"
linkTargetFunction={(id: String) => { return "https://historicengland.org.uk/listing/the-list/list-entry/" + id + "?section=official-list-entry" } }
linkDescriptionFunction={(id: String) => { return "ID Link" } }
/>
<Verification
slug="planning_list_id"
allow_verify={props.user !== undefined && props.building.planning_list_id !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_list_id")}
user_verified_as={props.user_verified.planning_list_id}
verified_count={props.building.verified.planning_list_id}
/>
<SelectDataEntry
title={dataFields.planning_list_grade.title}
slug="planning_list_grade"
value={props.building.planning_list_grade}
mode={props.mode} mode={props.mode}
disabled={false}
copy={props.copy}
onChange={props.onChange}
options={[
"I",
"II*",
"II",
"None"
]}
/> />
<Verification <Verification
slug="planning_list_grade" slug="planning_heritage_at_risk"
allow_verify={props.user !== undefined && props.building.planning_list_grade !== null && !props.edited} allow_verify={props.user !== undefined && props.building.planning_heritage_at_risk !== null && !props.edited}
onVerify={props.onVerify} onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_list_grade")} user_verified={props.user_verified.hasOwnProperty("planning_heritage_at_risk")}
user_verified_as={props.user_verified.planning_list_grade} user_verified_as={props.user_verified.planning_heritage_at_risk}
verified_count={props.building.verified.planning_list_grade} verified_count={props.building.verified.planning_heritage_at_risk}
/> />
{(props.building.planning_heritage_at_risk == null || props.building.planning_heritage_at_risk == false) ? <></> :
<>
<DataEntry <DataEntry
title={dataFields.planning_heritage_at_risk_url.title} title={dataFields.planning_heritage_at_risk_url.title}
slug="planning_heritage_at_risk_url" slug="planning_heritage_at_risk_url"
@ -281,6 +374,28 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.planning_heritage_at_risk_url} user_verified_as={props.user_verified.planning_heritage_at_risk_url}
verified_count={props.building.verified.planning_heritage_at_risk_url} verified_count={props.building.verified.planning_heritage_at_risk_url}
/> />
</>
}
<hr/>
<LogicalDataEntry
slug='planning_world_heritage_site'
title={dataFields.planning_world_heritage_site.title}
tooltip={dataFields.planning_world_heritage_site.tooltip}
value={props.building.planning_world_heritage_site}
copy={props.copy}
onChange={props.onChange}
mode={props.mode}
/>
<Verification
slug="planning_world_heritage_site"
allow_verify={props.user !== undefined && props.building.planning_world_heritage_site !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_world_heritage_site")}
user_verified_as={props.user_verified.planning_world_heritage_site}
verified_count={props.building.verified.planning_world_heritage_site}
/>
{(props.building.planning_world_heritage_site == null || props.building.planning_world_heritage_site == false) ? <></> :
<>
<NumericDataEntryWithFormattedLink <NumericDataEntryWithFormattedLink
title={dataFields.planning_world_list_id.title} title={dataFields.planning_world_list_id.title}
slug="planning_world_list_id" slug="planning_world_list_id"
@ -300,6 +415,28 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.planning_world_list_id} user_verified_as={props.user_verified.planning_world_list_id}
verified_count={props.building.verified.planning_world_list_id} verified_count={props.building.verified.planning_world_list_id}
/> />
</>
}
<hr/>
<LogicalDataEntry
slug='planning_local_list'
title={dataFields.planning_local_list.title}
tooltip={dataFields.planning_local_list.tooltip}
value={props.building.planning_local_list}
copy={props.copy}
onChange={props.onChange}
mode={props.mode}
/>
<Verification
slug="planning_local_list"
allow_verify={props.user !== undefined && props.building.planning_local_list !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_local_list")}
user_verified_as={props.user_verified.planning_local_list}
verified_count={props.building.verified.planning_local_list}
/>
{(props.building.planning_local_list == null || props.building.planning_local_list == false) ? <></> :
<>
<DataEntry <DataEntry
title={dataFields.planning_local_list_url.title} title={dataFields.planning_local_list_url.title}
slug="planning_local_list_url" slug="planning_local_list_url"
@ -318,26 +455,28 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.planning_local_list_url} user_verified_as={props.user_verified.planning_local_list_url}
verified_count={props.building.verified.planning_local_list_url} verified_count={props.building.verified.planning_local_list_url}
/> />
</>
{/* }
<DataEntry <hr/>
title={dataFields.planning_in_conservation_area_id.title} <LogicalDataEntry
slug="planning_in_conservation_area_id" slug='planning_in_conservation_area'
value={props.building.planning_in_conservation_area_id} title={dataFields.planning_in_conservation_area.title}
mode={props.mode} tooltip={dataFields.planning_in_conservation_area.tooltip}
value={props.building.planning_in_conservation_area}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
placeholder="Please add Conservation Area identifier" mode={props.mode}
/> />
<Verification <Verification
slug="planning_in_conservation_area_id" slug="planning_in_conservation_area"
allow_verify={props.user !== undefined && props.building.planning_in_conservation_area_id !== null && !props.edited} allow_verify={props.user !== undefined && props.building.planning_in_conservation_area !== null && !props.edited}
onVerify={props.onVerify} onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_in_conservation_area_id")} user_verified={props.user_verified.hasOwnProperty("planning_in_conservation_area")}
user_verified_as={props.user_verified.planning_in_conservation_area_id} user_verified_as={props.user_verified.planning_in_conservation_area}
verified_count={props.building.verified.planning_in_conservation_area_id} verified_count={props.building.verified.planning_in_conservation_area}
/> />
*/} {(props.building.planning_in_conservation_area == null || props.building.planning_in_conservation_area == false) ? <></> :
<>
<DataEntry <DataEntry
title={dataFields.planning_in_conservation_area_url.title} title={dataFields.planning_in_conservation_area_url.title}
slug="planning_in_conservation_area_url" slug="planning_in_conservation_area_url"
@ -358,42 +497,28 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.planning_in_conservation_area_url} user_verified_as={props.user_verified.planning_in_conservation_area_url}
verified_count={props.building.verified.planning_in_conservation_area_url} verified_count={props.building.verified.planning_in_conservation_area_url}
/> />
{/* </>
<DataEntry }
title={dataFields.planning_conservation_area_name.title} <hr/>
slug="planning_conservation_area_name" <LogicalDataEntry
value={props.building.planning_conservation_area_name} slug='planning_in_apa'
mode={props.mode} title={dataFields.planning_in_apa.title}
tooltip={dataFields.planning_in_apa.tooltip}
value={props.building.planning_in_apa}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
/>
<Verification
slug="planning_conservation_area_name"
allow_verify={props.user !== undefined && props.building.planning_conservation_area_name !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_conservation_area_name")}
user_verified_as={props.user_verified.planning_conservation_area_name}
verified_count={props.building.verified.planning_conservation_area_name}
/>
*/}
<DataEntry
title={dataFields.planning_historic_area_assessment_url.title}
slug="planning_historic_area_assessment_url"
value={props.building.planning_historic_area_assessment_url}
mode={props.mode} mode={props.mode}
copy={props.copy}
onChange={props.onChange}
isUrl={true}
placeholder="Please add relevant link here"
/> />
<Verification <Verification
slug="planning_historic_area_assessment_url" slug="planning_in_apa"
allow_verify={props.user !== undefined && props.building.planning_historic_area_assessment_url !== null && !props.edited} allow_verify={props.user !== undefined && props.building.planning_in_apa !== null && !props.edited}
onVerify={props.onVerify} onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_historic_area_assessment_url")} user_verified={props.user_verified.hasOwnProperty("planning_in_apa")}
user_verified_as={props.user_verified.planning_historic_area_assessment_url} user_verified_as={props.user_verified.planning_in_apa}
verified_count={props.building.verified.planning_historic_area_assessment_url} verified_count={props.building.verified.planning_in_apa}
/> />
{(props.building.planning_in_apa == null || props.building.planning_in_apa == false) ? <></> :
<>
<DataEntry <DataEntry
title={dataFields.planning_in_apa_url.title} title={dataFields.planning_in_apa_url.title}
slug="planning_in_apa_url" slug="planning_in_apa_url"
@ -412,107 +537,109 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.planning_in_apa_url} user_verified_as={props.user_verified.planning_in_apa_url}
verified_count={props.building.verified.planning_in_apa_url} verified_count={props.building.verified.planning_in_apa_url}
/> />
</DataEntryGroup> </>
<DataEntryGroup name="Forthcoming data (sections to be activated)" collapsed={true} > }
<DataEntryGroup name="Active application info (crowdsourced)" collapsed={true} > <hr/>
{/* will be titled "Other active application info (crowdsourced data)" once active" */} <LogicalDataEntry
<InfoBox type='warning'> slug='planning_scientific_interest'
This category is not yet activated - Until this section is activated please report inaccuracies or problems on the <a href=" https://github.com/colouring-cities/colouring-london/discussions/categories/planning-section-comments">Discussion Forum</a>. title={dataFields.planning_scientific_interest.title}
</InfoBox> tooltip={dataFields.planning_scientific_interest.tooltip}
{/* that is placeholder display, will be replaced by actual code */} value={props.building.planning_scientific_interest}
<div className="data-title"> copy={props.copy}
<div className="data-title-text"> onChange={props.onChange}
<ul> mode={props.mode}
<li>Year of completion if known</li> />
<li>If you know of a planning application that has been recently submitted for this site, and is not listed in the blue box above, please enter its planning application ID below:</li> <Verification
<li>If any of the active planning applications are not mapped onto the correct site, please tick here</li> slug="planning_scientific_interest"
</ul> allow_verify={props.user !== undefined && props.building.planning_scientific_interest !== null && !props.edited}
</div> onVerify={props.onVerify}
</div> user_verified={props.user_verified.hasOwnProperty("planning_scientific_interest")}
{ user_verified_as={props.user_verified.planning_scientific_interest}
/* verified_count={props.building.verified.planning_scientific_interest}
<NumericDataEntry />
title={dataFields.planning_crowdsourced_site_completion_year.title} {(props.building.planning_scientific_interest == null || props.building.planning_scientific_interest == false) ? <></> :
slug="planning_crowdsourced_site_completion_year" <>
value={props.building.planning_crowdsourced_site_completion_year} <SelectDataEntry
mode={props.mode} title={dataFields.planning_scientific_interest_source_type.title}
copy={props.copy} slug="planning_scientific_interest_source_type"
onChange={props.onChange} value={props.building.planning_scientific_interest_source_type}
disabled={true} mode={props.mode}
/> copy={props.copy}
<Verification onChange={props.onChange}
slug="planning_crowdsourced_site_completion_year" tooltip={dataFields.planning_scientific_interest_source_type.tooltip}
allow_verify={false} options={dataFields.planning_scientific_interest_source_type.items}
onVerify={props.onVerify} placeholder={dataFields.planning_scientific_interest_source_type.example}
user_verified={props.user_verified.hasOwnProperty("planning_crowdsourced_site_completion_year")} />
user_verified_as={props.user_verified.planning_crowdsourced_site_completion_year} {(props.building.planning_scientific_interest_source_type == dataFields.planning_scientific_interest_source_type.items[0] ||
verified_count={props.building.verified.planning_crowdsourced_site_completion_year} props.building.planning_scientific_interest_source_type == dataFields.planning_scientific_interest_source_type.items[1] ||
/> props.building.planning_scientific_interest_source_type == null) ? <></> :
<>
<DataEntry <MultiDataEntry
title={dataFields.planning_crowdsourced_planning_id.title} title={dataFields.planning_scientific_interest_source_links.title}
slug="planning_crowdsourced_planning_id" slug="planning_scientific_interest_source_links"
value={props.building.planning_crowdsourced_planning_id} value={props.building.planning_scientific_interest_source_links}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
disabled={true} tooltip={dataFields.planning_scientific_interest_source_links.tooltip}
/> placeholder="https://..."
<Verification editableEntries={true}
slug="planning_crowdsourced_planning_id" isUrl={true}
allow_verify={false && props.user !== undefined && props.building.planning_crowdsourced_planning_id !== null && !props.edited} />
onVerify={props.onVerify} </>
user_verified={props.user_verified.hasOwnProperty("planning_crowdsourced_planning_id")} }
user_verified_as={props.user_verified.planning_crowdsourced_planning_id} </>
verified_count={props.building.verified.planning_crowdsourced_planning_id} }
/> <hr/>
<LogicalDataEntry
<LogicalDataEntry slug='planning_historic_area_assessment'
slug='community_expected_planning_application_is_inaccurate' title={dataFields.planning_historic_area_assessment.title}
title={"If any of the active planning applications are not mapped onto the correct site, please tick here"} tooltip={dataFields.planning_historic_area_assessment.tooltip}
value={null} value={props.building.planning_historic_area_assessment}
copy={props.copy}
onChange={props.onSaveChange} onChange={props.onChange}
mode={props.mode} mode={props.mode}
copy={props.copy} />
disabled={true} <Verification
/> slug="planning_historic_area_assessment"
on enabling switch it to UserOpinionEntry, remove value and restore userValue allow_verify={props.user !== undefined && props.building.planning_historic_area_assessment !== null && !props.edited}
*/ onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_historic_area_assessment")}
user_verified_as={props.user_verified.planning_historic_area_assessment}
verified_count={props.building.verified.planning_historic_area_assessment}
/>
{(props.building.planning_historic_area_assessment == null || props.building.planning_historic_area_assessment == false) ? <></> :
<>
<DataEntry
title={dataFields.planning_historic_area_assessment_url.title}
slug="planning_historic_area_assessment_url"
value={props.building.planning_historic_area_assessment_url}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
isUrl={true}
placeholder="Please add relevant link here"
/>
<Verification
slug="planning_historic_area_assessment_url"
allow_verify={props.user !== undefined && props.building.planning_historic_area_assessment_url !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("planning_historic_area_assessment_url")}
user_verified_as={props.user_verified.planning_historic_area_assessment_url}
verified_count={props.building.verified.planning_historic_area_assessment_url}
/>
</>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Land ownership type" collapsed={true} > <DataEntryGroup name="Land ownership" collapsed={true} >
<InfoBox type='warning'>
This category is not yet activated.
</InfoBox>
<InfoBox> <InfoBox>
This section is designed to provide information on land parcels and their ownership type. Can you help us to crowdsource this information? This section is designed to provide information on land parcels and their ownership type. Can you help us collect this information?
</InfoBox> </InfoBox>
<button className={`map-switcher-inline ${parcel}-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={parcelSwitchOnClick}>
{(parcel === 'enabled')? 'Click to hide sample of parcel data (in City)' : 'Click to see sample of parcel data (in City) mapped'}
</button>
<div className="data-title">
<div className="data-title-text">
<ul>
<li>What type of owner owns this land parcel?</li>
</ul>
</div>
</div>
{/*
<SelectDataEntry <SelectDataEntry
slug='community_public_ownership' slug='community_public_ownership'
title={"What type of owner owns this land parcel? "} title={dataFields.community_public_ownership.title}
value={props.building.community_public_ownership} value={props.building.community_public_ownership}
options={[ options={dataFields.community_public_ownership.items}
'Government-owned',
'Charity-owned',
'Community-owned/cooperative',
'Owned by other non-profit body',
'Not in public/community ownership',
]}
onChange={props.onChange} onChange={props.onChange}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
@ -525,9 +652,39 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.community_public_ownership} user_verified_as={props.user_verified.community_public_ownership}
verified_count={props.building.verified.community_public_ownership} verified_count={props.building.verified.community_public_ownership}
/> />
*/ <DataEntry
} title="Source Type"
</DataEntryGroup> slug=""
value=""
mode='view'
tooltip='Coming Soon'
/>
<MultiDataEntry
slug='community_public_ownership_sources'
title={dataFields.community_public_ownership_sources.title}
isUrl={true}
placeholder={'https://...'}
editableEntries={true}
value={props.building.community_public_ownership_sources}
onChange={props.onChange}
mode={props.mode}
copy={props.copy}
/>
<hr/>
<DataEntry
title={dataFields.size_parcel_geometry.title}
slug="size_parcel_geometry"
value={props.building.size_parcel_geometry}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.size_parcel_geometry.tooltip}
placeholder="https://..."
isUrl={true}
/>
<button className={`map-switcher-inline ${parcel}-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={parcelSwitchOnClick}>
{(parcel === 'enabled')? 'Click to hide sample land parcel data' : 'Click to show sample land parcel data'}
</button>
</DataEntryGroup> </DataEntryGroup>
</Fragment> </Fragment>
)}; )};

View File

@ -15,7 +15,22 @@ import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-e
/** /**
* Size view/edit section * Size view/edit section
*/ */
const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => ( const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => {
// Calculate the total number of floors
let total_floors = 0;
if (props.building.size_storeys_attic != null) {
total_floors += props.building.size_storeys_attic;
}
if (props.building.size_storeys_core != null) {
total_floors += props.building.size_storeys_core;
}
if (props.building.size_storeys_basement != null) {
total_floors += props.building.size_storeys_basement;
}
return (
<Fragment> <Fragment>
<DataEntryGroup name="Number of floors/storeys"> <DataEntryGroup name="Number of floors/storeys">
<NumericDataEntry <NumericDataEntry
@ -75,6 +90,18 @@ const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => (
user_verified_as={props.user_verified.size_storeys_basement} user_verified_as={props.user_verified.size_storeys_basement}
verified_count={props.building.verified.size_storeys_basement} verified_count={props.building.verified.size_storeys_basement}
/> />
<NumericDataEntry
title="Total number of floors"
slug="size_total_floors"
value={total_floors}
mode={props.mode}
copy={props.copy}
tooltip="Total number of floors, calculated from other values."
onChange={props.onChange}
step={1}
min={0}
disabled={true}
/>
<SelectDataEntry <SelectDataEntry
title={dataFields.size_storeys_source_type.title} title={dataFields.size_storeys_source_type.title}
slug="size_storeys_source_type" slug="size_storeys_source_type"
@ -103,7 +130,7 @@ const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => (
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Building height data"> <DataEntryGroup name="Height">
<NumericDataEntry <NumericDataEntry
title={dataFields.size_height_apex.title} title={dataFields.size_height_apex.title}
slug="size_height_apex" slug="size_height_apex"
@ -196,7 +223,7 @@ const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => (
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Floor area data"> <DataEntryGroup name="Floor area">
<NumericDataEntry <NumericDataEntry
title={dataFields.size_floor_area_ground.title} title={dataFields.size_floor_area_ground.title}
slug="size_floor_area_ground" slug="size_floor_area_ground"
@ -261,7 +288,7 @@ const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => (
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Plot size data"> <DataEntryGroup name="Plot size">
<NumericDataEntry <NumericDataEntry
title={dataFields.size_width_frontage.title} title={dataFields.size_width_frontage.title}
slug="size_width_frontage" slug="size_width_frontage"
@ -449,7 +476,8 @@ const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => (
} }
</DataEntryGroup> </DataEntryGroup>
</Fragment> </Fragment>
); )
};
const SizeContainer = withCopyEdit(SizeView); const SizeContainer = withCopyEdit(SizeView);
export default SizeContainer; export default SizeContainer;

View File

@ -1,28 +1,25 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import InfoBox from '../../components/info-box';
import { commonSourceTypes, dataFields } from '../../config/data-fields-config'; import { commonSourceTypes, dataFields } from '../../config/data-fields-config';
import DataEntry from '../data-components/data-entry'; import DataEntry from '../data-components/data-entry';
import NumericDataEntry from '../data-components/numeric-data-entry'; import NumericDataEntry from '../data-components/numeric-data-entry';
import withCopyEdit from '../data-container'; import withCopyEdit from '../data-container';
import { CategoryViewProps } from './category-view-props'; import { CategoryViewProps } from './category-view-props';
import { DataEntryGroup } from '../data-components/data-entry-group'; import { DataEntryGroup } from '../data-components/data-entry-group';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import SelectDataEntry from '../data-components/select-data-entry'; import SelectDataEntry from '../data-components/select-data-entry';
import Verification from '../data-components/verification'; import Verification from '../data-components/verification';
import { LogicalDataEntry } from '../data-components/logical-data-entry/logical-data-entry';
/** /**
* Streetscape view/edit section * Street Context view/edit section
*/ */
const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => ( const StreetContextView: React.FunctionComponent<CategoryViewProps> = (props) => (
<Fragment> <Fragment>
<DataEntryGroup name="Does the building have a garden?"> <DataEntryGroup name="Green Space">
<SelectDataEntry <LogicalDataEntry
title={dataFields.context_front_garden.title} title={dataFields.context_front_garden.title}
slug="context_front_garden" slug="context_front_garden"
value={props.building.context_front_garden} value={props.building.context_front_garden}
options={dataFields.context_front_garden.items}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
@ -36,7 +33,7 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
user_verified_as={props.user_verified.context_front_garden} user_verified_as={props.user_verified.context_front_garden}
verified_count={props.building.verified.context_front_garden} verified_count={props.building.verified.context_front_garden}
/> />
<SelectDataEntry <LogicalDataEntry
title={dataFields.context_back_garden.title} title={dataFields.context_back_garden.title}
slug="context_back_garden" slug="context_back_garden"
value={props.building.context_back_garden} value={props.building.context_back_garden}
@ -44,8 +41,6 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.context_back_garden.tooltip} tooltip={dataFields.context_back_garden.tooltip}
//placeholder={dataFields.context_back_garden.example}
options={dataFields.context_back_garden.items}
/> />
<Verification <Verification
slug="context_back_garden" slug="context_back_garden"
@ -55,7 +50,7 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
user_verified_as={props.user_verified.context_back_garden} user_verified_as={props.user_verified.context_back_garden}
verified_count={props.building.verified.context_back_garden} verified_count={props.building.verified.context_back_garden}
/> />
<SelectDataEntry <LogicalDataEntry
title={dataFields.context_flats_garden.title} title={dataFields.context_flats_garden.title}
slug="context_flats_garden" slug="context_flats_garden"
value={props.building.context_flats_garden} value={props.building.context_flats_garden}
@ -63,8 +58,6 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.context_flats_garden.tooltip} tooltip={dataFields.context_flats_garden.tooltip}
//placeholder={dataFields.context_flats_garden.example}
options={dataFields.context_flats_garden.items}
/> />
<Verification <Verification
slug="context_flats_garden" slug="context_flats_garden"
@ -74,7 +67,6 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
user_verified_as={props.user_verified.context_flats_garden} user_verified_as={props.user_verified.context_flats_garden}
verified_count={props.building.verified.context_flats_garden} verified_count={props.building.verified.context_flats_garden}
/> />
<hr/>
<SelectDataEntry <SelectDataEntry
title={dataFields.context_garden_source_type.title} title={dataFields.context_garden_source_type.title}
slug="context_garden_source_type" slug="context_garden_source_type"
@ -104,8 +96,110 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
/> />
</> </>
} }
<hr/>
<NumericDataEntry
title={dataFields.context_green_space_distance.title}
value={props.building.context_green_space_distance}
slug="context_green_space_distance"
tooltip={dataFields.context_green_space_distance.tooltip}
//placeholder={dataFields.context_green_space_distance.example}
mode={props.mode}
onChange={props.onChange}
step={1}
min={0}
/>
<Verification
slug="context_green_space_distance"
allow_verify={props.user !== undefined && props.building.context_green_space_distance !== null}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("context_green_space_distance")}
user_verified_as={props.user_verified.context_green_space_distance}
verified_count={props.building.verified.context_green_space_distance}
/>
<SelectDataEntry
title={dataFields.context_green_space_distance_source_type.title}
slug="context_green_space_distance_source_type"
value={props.building.context_green_space_distance_source_type}
options={dataFields.context_green_space_distance_source_type.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_green_space_distance_source_type.tooltip}
/>
{(props.building.context_green_space_distance_source_type == commonSourceTypes[0] ||
props.building.context_green_space_distance_source_type == commonSourceTypes[1] ||
props.building.context_green_space_distance_source_type == null) ? <></> :
<><MultiDataEntry
title={dataFields.context_green_space_distance_source_links.title}
slug="context_green_space_distance_source_links"
value={props.building.context_green_space_distance_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_green_space_distance_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<NumericDataEntry
title={dataFields.context_tree_distance.title}
value={props.building.context_tree_distance}
slug="context_tree_distance"
tooltip={dataFields.context_tree_distance.tooltip}
//placeholder={dataFields.context_tree_distance.example}
mode={props.mode}
onChange={props.onChange}
step={1}
min={0}
/>
<Verification
slug="context_tree_distance"
allow_verify={props.user !== undefined && props.building.context_tree_distance !== null}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("context_tree_distance")}
user_verified_as={props.user_verified.context_tree_distance}
verified_count={props.building.verified.context_tree_distance}
/>
<SelectDataEntry
title={dataFields.context_tree_distance_source_type.title}
slug="context_tree_distance_source_type"
value={props.building.context_tree_distance_source_type}
options={dataFields.context_tree_distance_source_type.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_tree_distance_source_type.tooltip}
/>
{(props.building.context_tree_distance_source_type == commonSourceTypes[0] ||
props.building.context_tree_distance_source_type == commonSourceTypes[1] ||
props.building.context_tree_distance_source_type == null) ? <></> :
<><MultiDataEntry
title={dataFields.context_tree_distance_source_links.title}
slug="context_tree_distance_source_links"
value={props.building.context_tree_distance_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_tree_distance_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Street/pavement properties"> <DataEntryGroup name="Street/pavement">
<DataEntry
title="Walkability Index"
slug=""
value=""
mode='view'
tooltip='Under development'
/>
<hr/>
<NumericDataEntry <NumericDataEntry
title={dataFields.context_street_width.title} title={dataFields.context_street_width.title}
value={props.building.context_street_width} value={props.building.context_street_width}
@ -247,103 +341,17 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Access to green space"> <DataEntryGroup name="Number of entrances facing street">
<NumericDataEntry <DataEntry
title={dataFields.context_green_space_distance.title} title="Number of entrances facing street"
value={props.building.context_green_space_distance} slug=""
slug="context_green_space_distance" value=""
tooltip={dataFields.context_green_space_distance.tooltip} mode='view'
//placeholder={dataFields.context_green_space_distance.example} tooltip='Under development'
mode={props.mode}
onChange={props.onChange}
step={1}
min={0}
/> />
<Verification
slug="context_green_space_distance"
allow_verify={props.user !== undefined && props.building.context_green_space_distance !== null}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("context_green_space_distance")}
user_verified_as={props.user_verified.context_green_space_distance}
verified_count={props.building.verified.context_green_space_distance}
/>
<SelectDataEntry
title={dataFields.context_green_space_distance_source_type.title}
slug="context_green_space_distance_source_type"
value={props.building.context_green_space_distance_source_type}
options={dataFields.context_green_space_distance_source_type.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_green_space_distance_source_type.tooltip}
/>
{(props.building.context_green_space_distance_source_type == commonSourceTypes[0] ||
props.building.context_green_space_distance_source_type == commonSourceTypes[1] ||
props.building.context_green_space_distance_source_type == null) ? <></> :
<><MultiDataEntry
title={dataFields.context_green_space_distance_source_links.title}
slug="context_green_space_distance_source_links"
value={props.building.context_green_space_distance_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_green_space_distance_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<NumericDataEntry
title={dataFields.context_tree_distance.title}
value={props.building.context_tree_distance}
slug="context_tree_distance"
tooltip={dataFields.context_tree_distance.tooltip}
//placeholder={dataFields.context_tree_distance.example}
mode={props.mode}
onChange={props.onChange}
step={1}
min={0}
/>
<Verification
slug="context_tree_distance"
allow_verify={props.user !== undefined && props.building.context_tree_distance !== null}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("context_tree_distance")}
user_verified_as={props.user_verified.context_tree_distance}
verified_count={props.building.verified.context_tree_distance}
/>
<SelectDataEntry
title={dataFields.context_tree_distance_source_type.title}
slug="context_tree_distance_source_type"
value={props.building.context_tree_distance_source_type}
options={dataFields.context_tree_distance_source_type.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_tree_distance_source_type.tooltip}
/>
{(props.building.context_tree_distance_source_type == commonSourceTypes[0] ||
props.building.context_tree_distance_source_type == commonSourceTypes[1] ||
props.building.context_tree_distance_source_type == null) ? <></> :
<><MultiDataEntry
title={dataFields.context_tree_distance_source_links.title}
slug="context_tree_distance_source_links"
value={props.building.context_tree_distance_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.context_tree_distance_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup> </DataEntryGroup>
</Fragment> </Fragment>
); );
const StreetscapeContainer = withCopyEdit(StreetscapeView); const StreetContextContainer = withCopyEdit(StreetContextView);
export default StreetscapeContainer; export default StreetContextContainer;

View File

@ -4,9 +4,9 @@ import { commonSourceTypes, dataFields } from '../../config/data-fields-config';
import SelectDataEntry from '../data-components/select-data-entry'; import SelectDataEntry from '../data-components/select-data-entry';
import NumericDataEntry from '../data-components/numeric-data-entry'; import NumericDataEntry from '../data-components/numeric-data-entry';
import Verification from '../data-components/verification'; import Verification from '../data-components/verification';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { LogicalDataEntry, LogicalDataEntryYesOnly } from '../data-components/logical-data-entry/logical-data-entry'; import { LogicalDataEntry, LogicalDataEntryYesOnly } from '../data-components/logical-data-entry/logical-data-entry';
import { DataEntryGroup } from '../data-components/data-entry-group'; import { DataEntryGroup } from '../data-components/data-entry-group';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import withCopyEdit from '../data-container'; import withCopyEdit from '../data-container';
@ -21,57 +21,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
const currentBuildingConstructionYear = building.date_year || undefined; const currentBuildingConstructionYear = building.date_year || undefined;
return ( return (
<form> <form>
<DataEntryGroup name="Data relating to original building or extension?"> <DataEntryGroup name="General info">
<NumericDataEntry
slug='date_year'
title={dataFields.date_year.title}
value={currentBuildingConstructionYear}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
step={1}
min={1}
max={currentYear}
tooltip={dataFields.extension_year.tooltip}
/>
<Verification
slug="date_year"
allow_verify={props.user !== undefined && props.building.date_year !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("date_year")}
user_verified_as={props.user_verified.date_year}
verified_count={props.building.verified.date_year}
/>
<SelectDataEntry
title={dataFields.date_source.title}
slug="date_source"
value={props.building.date_source}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.date_source.tooltip}
options={dataFields.date_source.items}
placeholder={dataFields.date_source.example}
/>
{(props.building.date_source == dataFields.date_source.items[0] ||
props.building.date_source == dataFields.date_source.items[1] ||
props.building.date_source == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.date_link.title}
slug="date_link"
value={props.building.date_link}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.date_link.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
<LogicalDataEntry <LogicalDataEntry
title={dataFields.has_extension.title} title={dataFields.has_extension.title}
slug="has_extension" slug="has_extension"
@ -81,7 +31,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
onChange={props.onChange} onChange={props.onChange}
tooltip={dataFields.has_extension.tooltip} tooltip={dataFields.has_extension.tooltip}
/> />
{props.building.has_extension ? ( {props.building.has_extension!=null && !props.building.has_extension ? (
<> <>
<NumericDataEntry <NumericDataEntry
slug='extension_year' slug='extension_year'
@ -135,7 +85,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
</> </>
) : (null)} ) : (null)}
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Land ownership data"> <DataEntryGroup name="Land ownership">
<MultiDataEntry <MultiDataEntry
title={dataFields.landowner.title} title={dataFields.landowner.title}
slug="landowner" slug="landowner"
@ -146,6 +96,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
tooltip={dataFields.landowner.tooltip} tooltip={dataFields.landowner.tooltip}
placeholder="" placeholder=""
editableEntries={true} editableEntries={true}
disabled={true}
/> />
<Verification <Verification
slug="landowner" slug="landowner"
@ -155,6 +106,18 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.landowner} user_verified_as={props.user_verified.landowner}
verified_count={props.building.verified.landowner} verified_count={props.building.verified.landowner}
/> />
<MultiDataEntry
title={dataFields.landowner_links.title}
slug="landowner_links"
value={props.building.landowner_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.landowner_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
<SelectDataEntry <SelectDataEntry
title={dataFields.landowner_source_type.title} title={dataFields.landowner_source_type.title}
slug="landowner_source_type" slug="landowner_source_type"
@ -185,7 +148,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Developer data"> <DataEntryGroup name="Developer">
<SelectDataEntry <SelectDataEntry
slug='developer_type' slug='developer_type'
title={dataFields.developer_type.title} title={dataFields.developer_type.title}
@ -213,6 +176,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
tooltip={dataFields.developer_name.tooltip} tooltip={dataFields.developer_name.tooltip}
placeholder="" placeholder=""
editableEntries={true} editableEntries={true}
disabled={true}
/> />
<Verification <Verification
slug="developer_name" slug="developer_name"
@ -222,6 +186,18 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.developer_name} user_verified_as={props.user_verified.developer_name}
verified_count={props.building.verified.developer_name} verified_count={props.building.verified.developer_name}
/> />
<MultiDataEntry
title={dataFields.developer_links.title}
slug="developer_links"
value={props.building.developer_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.developer_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
<SelectDataEntry <SelectDataEntry
title={dataFields.developer_source_type.title} title={dataFields.developer_source_type.title}
slug="developer_source_type" slug="developer_source_type"
@ -252,7 +228,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
</> </>
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Designer data"> <DataEntryGroup name="Designer">
<MultiDataEntry <MultiDataEntry
title={dataFields.designers.title} title={dataFields.designers.title}
slug="designers" slug="designers"
@ -263,6 +239,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
tooltip={dataFields.designers.tooltip} tooltip={dataFields.designers.tooltip}
placeholder="" placeholder=""
editableEntries={true} editableEntries={true}
disabled={true}
/> />
<Verification <Verification
slug="designers" slug="designers"
@ -272,7 +249,18 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.designers} user_verified_as={props.user_verified.designers}
verified_count={props.building.verified.designers} verified_count={props.building.verified.designers}
/> />
<MultiDataEntry
title={dataFields.designers_links.title}
slug="designers_links"
value={props.building.designers_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.designers_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
<SelectDataEntry <SelectDataEntry
slug='lead_designer_type' slug='lead_designer_type'
title={dataFields.lead_designer_type.title} title={dataFields.lead_designer_type.title}
@ -319,7 +307,70 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
/> />
</> </>
} }
<hr/> </DataEntryGroup>
<DataEntryGroup name="Builder">
<MultiDataEntry
title={dataFields.builder.title}
slug="builder"
value={props.building.builder}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.builder.tooltip}
placeholder=""
editableEntries={true}
disabled={true}
/>
<Verification
slug="builder"
allow_verify={props.user !== undefined && props.building.builder !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("builder")}
user_verified_as={props.user_verified.builder}
verified_count={props.building.verified.builder}
/>
<MultiDataEntry
title={dataFields.builder_links.title}
slug="builder_links"
value={props.building.builder_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.builder_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
<SelectDataEntry
title={dataFields.builder_source_type.title}
slug="builder_source_type"
value={props.building.builder_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.builder_source_type.tooltip}
options={dataFields.builder_source_type.items}
placeholder={dataFields.builder_source_type.example}
/>
{(props.building.builder_source_type == commonSourceTypes[0] ||
props.building.builder_source_type == commonSourceTypes[1] ||
props.building.builder_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.builder_source_link.title}
slug="builder_source_link"
value={props.building.builder_source_link}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Awards">
<LogicalDataEntryYesOnly <LogicalDataEntryYesOnly
slug='designer_awards' slug='designer_awards'
title={dataFields.designer_awards.title} title={dataFields.designer_awards.title}
@ -363,54 +414,6 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => {
) : (null) ) : (null)
} }
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Builder data">
<MultiDataEntry
title={dataFields.builder.title}
slug="builder"
value={props.building.builder}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
placeholder=""
editableEntries={true}
/>
<Verification
slug="builder"
allow_verify={props.user !== undefined && props.building.builder !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("builder")}
user_verified_as={props.user_verified.builder}
verified_count={props.building.verified.builder}
/>
<SelectDataEntry
title={dataFields.builder_source_type.title}
slug="builder_source_type"
value={props.building.builder_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.builder_source_type.tooltip}
options={dataFields.builder_source_type.items}
placeholder={dataFields.builder_source_type.example}
/>
{(props.building.builder_source_type == commonSourceTypes[0] ||
props.building.builder_source_type == commonSourceTypes[1] ||
props.building.builder_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.builder_source_link.title}
slug="builder_source_link"
value={props.building.builder_source_link}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
</form> </form>
); );
}; };

View File

@ -1,6 +1,6 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { dataFields } from '../../config/data-fields-config'; import { commonSourceTypes, dataFields } from '../../config/data-fields-config';
import DataEntry from '../data-components/data-entry'; import DataEntry from '../data-components/data-entry';
import NumericDataEntry from '../data-components/numeric-data-entry'; import NumericDataEntry from '../data-components/numeric-data-entry';
import SelectDataEntry from '../data-components/select-data-entry'; import SelectDataEntry from '../data-components/select-data-entry';
@ -10,27 +10,316 @@ import withCopyEdit from '../data-container';
import { CategoryViewProps } from './category-view-props'; import { CategoryViewProps } from './category-view-props';
import InfoBox from '../../components/info-box'; import InfoBox from '../../components/info-box';
import { DataEntryGroup } from '../data-components/data-entry-group'; import { DataEntryGroup } from '../data-components/data-entry-group';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
const AttachmentFormOptions = [ import { useDisplayPreferences } from '../../displayPreferences-context';
"Detached",
"Semi-Detached",
"End-Terrace",
"Mid-Terrace"
];
/** /**
* Type view/edit section * Type view/edit section
*/ */
const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => { const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
const { darkLightTheme } = useDisplayPreferences();
const switchToClassificationMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('typology_classification')
}
const switchToStylePeriodMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('typology_style_period')
}
const switchToDynamicClassificationMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('typology_dynamic_classification')
}
const switchToAttachmentMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('building_attachment_form')
}
const switchToLandUseMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('original_landuse')
}
return ( return (
<Fragment> <Fragment>
<DataEntryGroup name="Adjacency and building use data"> <DataEntryGroup name="Basic typology classification">
{(props.mapColourScale == "typology_classification") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToStylePeriodMapStyle}>
{'Click here to change map to show architectural style/historical period.'}
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{"Click to change map to show typology classification."}
</button>
}
<SelectDataEntry
title={dataFields.typology_classification.title}
slug="typology_classification"
value={props.building.typology_classification}
tooltip={dataFields.typology_classification.tooltip}
options={dataFields.typology_classification.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="typology_classification"
allow_verify={props.user !== undefined && props.building.typology_classification !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("typology_classification")}
user_verified_as={props.user_verified.typology_classification}
verified_count={props.building.verified.typology_classification}
/>
<SelectDataEntry
title={dataFields.typology_classification_source_type.title}
slug="typology_classification_source_type"
value={props.building.typology_classification_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_classification_source_type.tooltip}
placeholder={dataFields.typology_classification_source_type.example}
options={dataFields.typology_classification_source_type.items}
/>
{(props.building.typology_classification_source_type == commonSourceTypes[0] ||
props.building.typology_classification_source_type == commonSourceTypes[1] ||
props.building.typology_classification_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.typology_classification_source_links.title}
slug="typology_classification_source_links"
value={props.building.typology_classification_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_classification_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Architectural style">
{/*(props.mapColourScale == "typology_style_period") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{'Click to change map to show typology classification.'}
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToStylePeriodMapStyle}>
{"Click here to change map to show architectural style/historical period."}
</button>
*/}
<SelectDataEntry
title={dataFields.typology_style_period.title}
slug="typology_style_period"
value={props.building.typology_style_period}
tooltip={dataFields.typology_style_period.tooltip}
options={dataFields.typology_style_period.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
disabled={true}
/>
{/*
<Verification
slug="typology_style_period"
allow_verify={props.user !== undefined && props.building.typology_style_period !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("typology_style_period")}
user_verified_as={props.user_verified.typology_style_period}
verified_count={props.building.verified.typology_style_period}
/> */}
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">To edit the architectural style box, and to see the data mapped, please go to <a href={"/"+props.mode+"/age/"+props.building.building_id}>Age & History</a>.</i>
</div>
{/* <SelectDataEntry
title={dataFields.typology_style_period_source_type.title}
slug="typology_style_period_source_type"
value={props.building.typology_style_period_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_style_period_source_type.tooltip}
placeholder={dataFields.typology_style_period_source_type.example}
options={dataFields.typology_style_period_source_type.items}
/>
{(props.building.typology_style_period_source_type == commonSourceTypes[0] ||
props.building.typology_style_period_source_type == commonSourceTypes[1] ||
props.building.typology_style_period_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.typology_style_period_source_links.title}
slug="typology_style_period_source_links"
value={props.building.typology_style_period_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_style_period_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
} */}
</DataEntryGroup>
<DataEntryGroup name="Dynamic tissue classification">
{(props.mapColourScale == "typology_dynamic_classification") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{'Click to change map to show typology classification.'}
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToDynamicClassificationMapStyle}>
{"Click here to change map to show dynamic classification."}
</button>
}
<SelectDataEntry
title={dataFields.typology_dynamic_classification.title}
slug="typology_dynamic_classification"
value={props.building.typology_dynamic_classification}
tooltip={dataFields.typology_dynamic_classification.tooltip}
options={dataFields.typology_dynamic_classification.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="typology_dynamic_classification"
allow_verify={props.user !== undefined && props.building.typology_dynamic_classification !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("typology_dynamic_classification")}
user_verified_as={props.user_verified.typology_dynamic_classification}
verified_count={props.building.verified.typology_dynamic_classification}
/>
<SelectDataEntry
title={dataFields.typology_dynamic_classification_source_type.title}
slug="typology_dynamic_classification_source_type"
value={props.building.typology_dynamic_classification_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_dynamic_classification_source_type.tooltip}
placeholder={dataFields.typology_dynamic_classification_source_type.example}
options={dataFields.typology_dynamic_classification_source_type.items}
/>
{(props.building.typology_dynamic_classification_source_type == commonSourceTypes[0] ||
props.building.typology_dynamic_classification_source_type == commonSourceTypes[1] ||
props.building.typology_dynamic_classification_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.typology_dynamic_classification_source_links.title}
slug="typology_dynamic_classification_source_links"
value={props.building.typology_dynamic_classification_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_dynamic_classification_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Original Use">
{(props.mapColourScale == "original_landuse") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{'Click to change map to show typology classification.'}
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToLandUseMapStyle}>
{"Click here to change map to original land use."}
</button>
}
<MultiDataEntry
title={dataFields.typology_original_use.title}
slug="typology_original_use"
value={props.building.typology_original_use}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
confirmOnEnter={true}
tooltip={dataFields.typology_original_use.tooltip}
placeholder="Type new land use group here"
copyable={true}
autofill={true}
showAllOptionsOnEmpty={true}
/>
<Verification
slug="typology_original_use"
allow_verify={props.user !== undefined && props.building.typology_original_use !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("typology_original_use")}
user_verified_as={props.user_verified.typology_original_use}
verified_count={props.building.verified.typology_original_use}
/>
<SelectDataEntry
title={dataFields.typology_original_use_source_type.title}
slug="typology_original_use_source_type"
value={props.building.typology_original_use_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_original_use_source_type.tooltip}
placeholder={dataFields.typology_original_use_source_type.example}
options={dataFields.typology_original_use_source_type.items}
/>
{(props.building.typology_original_use_source_type == commonSourceTypes[0] ||
props.building.typology_original_use_source_type == commonSourceTypes[1] ||
props.building.typology_original_use_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.typology_original_use_source_links.title}
slug="typology_original_use_source_links"
value={props.building.typology_original_use_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_original_use_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
<hr/>
{
props.mode != 'view' &&
<div>
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i>
Below is a more general classification for the original land use of this building, automatically derived from the information above.
</i>
</div>
</div>
}
<DataEntry
title={dataFields.typology_original_use_order.title}
tooltip={dataFields.typology_original_use_order.tooltip}
slug="typology_original_use_order"
value={props.building.typology_original_use_order}
mode={props.mode}
disabled={true}
copy={props.copy}
onChange={props.onChange}
/>
</DataEntryGroup>
<DataEntryGroup name="Attachment/Adjacency">
{(props.mapColourScale == "building_attachment_form") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{'Click to change map to show typology classification.'}
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAttachmentMapStyle}>
{"Click here to change map to show attachment/adjacency."}
</button>
}
<SelectDataEntry <SelectDataEntry
title={dataFields.building_attachment_form.title} title={dataFields.building_attachment_form.title}
slug="building_attachment_form" slug="building_attachment_form"
value={props.building.building_attachment_form} value={props.building.building_attachment_form}
tooltip={dataFields.building_attachment_form.tooltip} tooltip={dataFields.building_attachment_form.tooltip}
options={AttachmentFormOptions} options={dataFields.building_attachment_form.items}
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
@ -43,67 +332,38 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified_as={props.user_verified.building_attachment_form} user_verified_as={props.user_verified.building_attachment_form}
verified_count={props.building.verified.building_attachment_form} verified_count={props.building.verified.building_attachment_form}
/> />
<DataEntry <SelectDataEntry
title="Source type" title={dataFields.building_attachment_source_type.title}
slug="" slug="building_attachment_source_type"
value="" value={props.building.building_attachment_source_type}
mode='view'
tooltip="Coming Soon"
/>
<DataEntry
title="Source link"
slug=""
value=""
mode='view'
tooltip="Coming Soon"
/>
<hr/>
<DataEntry
title={dataFields.original_building_use.title}
slug="original_building_use" // doesn't exist in database yet
tooltip={dataFields.original_building_use.tooltip}
value={undefined}
copy={props.copy}
mode={props.mode} mode={props.mode}
copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
disabled={true} tooltip={dataFields.building_attachment_source_type.tooltip}
placeholder={dataFields.building_attachment_source_type.example}
options={dataFields.building_attachment_source_type.items}
/> />
<Verification {(props.building.building_attachment_source_type == commonSourceTypes[0] ||
slug="building_attachment_form" props.building.building_attachment_source_type == commonSourceTypes[1] ||
allow_verify={props.user !== undefined && props.building.building_attachment_form !== null && !props.edited} props.building.building_attachment_source_type == null) ? <></> :
onVerify={props.onVerify} <>
user_verified={props.user_verified.hasOwnProperty("building_attachment_form")} <MultiDataEntry
user_verified_as={props.user_verified.building_attachment_form} title={dataFields.building_attachment_source_links.title}
verified_count={props.building.verified.building_attachment_form} slug="building_attachment_source_links"
/> value={props.building.building_attachment_source_links}
<DataEntry mode={props.mode}
title="Source type" copy={props.copy}
slug="" onChange={props.onChange}
value="" tooltip={dataFields.building_attachment_source_links.tooltip}
mode='view' placeholder="https://..."
tooltip="Coming Soon" editableEntries={true}
/> isUrl={true}
<DataEntry
title="Source link"
slug=""
value=""
mode='view'
tooltip="Coming Soon"
/> />
</>
}
</DataEntryGroup> </DataEntryGroup>
<DataEntryGroup name="Building typology and classification data"> {/*}
<DataEntry <DataEntryGroup name="Other fields (in development)">
title="Local typology/architectural style"
slug=""
value=""
mode='view'
/>
<DataEntry
title="Base type classification"
slug=""
value=""
mode='view'
/>
<SelectDataEntry <SelectDataEntry
title={dataFields.size_roof_shape.title} title={dataFields.size_roof_shape.title}
slug="size_roof_shape" slug="size_roof_shape"
@ -136,6 +396,7 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
value="" value=""
mode='view' mode='view'
/> />
{/* <NumericDataEntry {/* <NumericDataEntry
title={dataFields.date_change_building_use.title} title={dataFields.date_change_building_use.title}
slug="date_change_building_use" slug="date_change_building_use"
@ -147,8 +408,8 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
mode={props.mode} mode={props.mode}
copy={props.copy} copy={props.copy}
onChange={props.onChange} onChange={props.onChange}
/> */} />//*}
</DataEntryGroup> </DataEntryGroup>*/}
</Fragment> </Fragment>
); );
}; };

View File

@ -184,3 +184,16 @@
padding-top: 0px; padding-top: 0px;
padding-bottom: 5px; padding-bottom: 5px;
} }
dd {
margin-bottom: 15px;
}
.uprn-list {
margin-top: 10px;
margin-bottom: 0px;
}
.uprn-list li {
margin-bottom: 10px;
}

View File

@ -98,7 +98,7 @@ export const categoriesConfig: {[key in Category]: CategoryDefinition} = {
slug: 'typology', slug: 'typology',
name: 'Typology', name: 'Typology',
aboutUrl: 'https://pages.colouring.london/buildingtypology', aboutUrl: 'https://pages.colouring.london/buildingtypology',
intro: 'Note: This section is currently under development, we are working to activate it as soon as possible. This section provides open data on the typology of the building.', intro: 'This section provides open data on the typology of the building.',
}, },
[Category.LandUse]: { [Category.LandUse]: {
slug: 'land-use', slug: 'land-use',

View File

@ -48,6 +48,25 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
] ]
}, },
}, },
{
mapStyle: 'typology_style_period',
legend: {
title: 'Architectural style',
elements: [
{ color: '#FFF739', text: '43AD-410 (Roman)' },
{ color: '#C5BD00', text: '410-1485 (Medieval)' },
{ color: '#FF9A39', text: '1485-1603 (Tudor)' },
{ color: '#C56000', text: '1603-1714 (Stuart)' },
{ color: '#EA8072', text: '1714-1837 (Georgian)' },
{ color: '#A71200', text: '1837-1901 (Victorian)' },
{ color: '#A272D4', text: '1901-1914 (Edwardian)' },
{ color: '#3988C5', text: '1914-1945 (WWI-WWII)' },
{ color: '#5ADFA2', text: '1946-1979 (Post war)' },
{ color: '#C2F47A', text: '1980-1999 (Late C20)' },
{ color: '#6FB40A', text: '2000-2025 (Early C21)' },
]
}
},
{ {
mapStyle: 'survival_status', mapStyle: 'survival_status',
legend: { legend: {
@ -299,10 +318,82 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
] ]
}, },
}], }],
[Category.Typology]: [{ [Category.Typology]: [
{
mapStyle: 'typology_classification',
legend: {
title: 'Typology classification',
elements: [
{ color: '#0311AB', text: '1-3 storeys: Detached' },
{ color: '#3845D4', text: '1-3 storeys: Tightly grouped' },
{ color: '#6D79FD', text: '1-3 storeys: Loosely grouped' },
{ color: '#FF5D00', text: '4-7 storeys: Detached' },
{ color: '#FF8000', text: '4-7 storeys: Tightly grouped' },
{ color: '#FFA200', text: '4-7 storeys: Loosely grouped' },
{ color: '#AB1303', text: '8+ storeys: Detached' },
{ color: '#D43A29', text: '8+ storeys: Tightly grouped' },
{ color: '#FC604F', text: '8+ storeys: Loosely grouped' },
]
}
},
/*{
mapStyle: 'typology_style_period',
legend: {
title: 'Architectural style',
elements: [
{ color: '#FFF739', text: '43AD-410 (Roman)' },
{ color: '#C5BD00', text: '410-1485 (Medieval)' },
{ color: '#FF9A39', text: '1485-1603 (Tudor)' },
{ color: '#C56000', text: '1603-1714 (Stuart)' },
{ color: '#EA8072', text: '1714-1837 (Georgian)' },
{ color: '#A71200', text: '1837-1901 (Victorian)' },
{ color: '#A272D4', text: '1901-1914 (Edwardian)' },
{ color: '#3988C5', text: '1914-1945 (WWI-WWII)' },
{ color: '#5ADFA2', text: '1946-1979 (Post war)' },
{ color: '#C2F47A', text: '1980-1999 (Late C20)' },
{ color: '#6FB40A', text: '2000-2025 (Early C21)' },
]
}
},*/
{
mapStyle: 'typology_dynamic_classification',
legend: {
title: 'Dynamic classification',
elements: [
{ color: '#96484A', text: 'Repetitive small, domestic plots' },
{ color: '#4B9889', text: 'Linear non-domestic, i.e. high streets' },
{ color: '#4F8DA8', text: 'Large plots with internal roads' },
{ color: '#897A5D', text: 'Other' },
]
}
},
{
mapStyle: 'original_landuse',
legend: {
title: 'Original Land Use',
elements: [
{ color: '#e5050d', text: 'Mixed Use' },
{ subtitle: 'Single use:'},
{ color: '#252aa6', text: 'Residential (unverified)' },
{ color: '#7025a6', text: 'Residential (verified)' },
{ color: '#ff8c00', text: 'Retail' },
{ color: '#f5f58f', text: 'Industry & Business' },
{ color: '#fa667d', text: 'Community Services' },
{ color: '#ffbfbf', text: 'Recreation & Leisure' },
{ color: '#b3de69', text: 'Transport' },
{ color: '#cccccc', text: 'Utilities & Infrastructure' },
{ color: '#898944', text: 'Defence' },
{ color: '#73ccd1', text: 'Agriculture' },
{ color: '#45cce3', text: 'Minerals' },
{ color: '#ffffff', text: 'Vacant & Derelict' },
{ color: '#6c6f8e', text: 'Unclassified, presumed non-residential' }
]
},
},
{
mapStyle: 'building_attachment_form', mapStyle: 'building_attachment_form',
legend: { legend: {
title: 'Adjacency/Configuration', title: 'Attachment/Adjacency',
elements: [ elements: [
{ color: "#f2a2b9", text: "Detached" }, { color: "#f2a2b9", text: "Detached" },
{ color: "#ab8fb0", text: "Semi-Detached" }, { color: "#ab8fb0", text: "Semi-Detached" },
@ -310,7 +401,8 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
{ color: "#226291", text: "Mid-Terrace" } { color: "#226291", text: "Mid-Terrace" }
] ]
}, },
}], },
],
[Category.LandUse]: [ [Category.LandUse]: [
{ {
mapStyle: 'landuse', mapStyle: 'landuse',
@ -338,11 +430,11 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
{ {
mapStyle: 'is_domestic', mapStyle: 'is_domestic',
legend: { legend: {
title: 'Domestic building', title: 'Residential building',
elements: [ elements: [
{ color: '#f7ec25', text: 'Domestic' }, { color: '#f7ec25', text: 'Residential' },
{ color: '#fc9b2a', text: 'Mixed' }, { color: '#fc9b2a', text: 'Mixed' },
{ color: '#ff2121', text: 'Non-domestic' }, { color: '#ff2121', text: 'Non-residential' },
] ]
} }
} }

View File

@ -7,7 +7,7 @@ import ResilienceContainer from '../building/data-containers/resilience';
import LocationContainer from '../building/data-containers/location'; import LocationContainer from '../building/data-containers/location';
import PlanningContainer from '../building/data-containers/planning'; import PlanningContainer from '../building/data-containers/planning';
import SizeContainer from '../building/data-containers/size'; import SizeContainer from '../building/data-containers/size';
import StreetscapeContainer from '../building/data-containers/street-context'; import StreetContextContainer from '../building/data-containers/street-context';
import SustainabilityContainer from '../building/data-containers/energy-performance'; import SustainabilityContainer from '../building/data-containers/energy-performance';
import TeamContainer from '../building/data-containers/team'; import TeamContainer from '../building/data-containers/team';
import TypeContainer from '../building/data-containers/typology'; import TypeContainer from '../building/data-containers/typology';
@ -22,7 +22,7 @@ export const categoryUiConfig: {[key in Category]: DataContainerType} = {
[Category.Age]: AgeContainer, [Category.Age]: AgeContainer,
[Category.Size]: SizeContainer, [Category.Size]: SizeContainer,
[Category.Construction]: ConstructionContainer, [Category.Construction]: ConstructionContainer,
[Category.StreetContext]: StreetscapeContainer, [Category.StreetContext]: StreetContextContainer,
[Category.Team]: TeamContainer, [Category.Team]: TeamContainer,
[Category.Planning]: PlanningContainer, [Category.Planning]: PlanningContainer,
[Category.EnergyPerformance]: SustainabilityContainer, [Category.EnergyPerformance]: SustainabilityContainer,

File diff suppressed because it is too large Load Diff

View File

@ -21,10 +21,14 @@ export type BuildingMapTileset =
'sust_dec' | 'sust_dec' |
'building_attachment_form' | 'building_attachment_form' |
'landuse' | 'landuse' |
'original_landuse' |
'dynamics_demolished_count' | 'dynamics_demolished_count' |
'disaster_severity' | 'disaster_severity' |
'team' | 'team' |
'survival_status'; 'survival_status'|
'typology_classification'|
'typology_style_period' |
'typology_dynamic_classification';
export type SpecialMapTileset = 'base_light' | 'base_night' | 'base_night_outlines' | 'highlight' | 'number_labels' | 'base_boroughs'; export type SpecialMapTileset = 'base_light' | 'base_night' | 'base_night_outlines' | 'highlight' | 'number_labels' | 'base_boroughs';

View File

@ -38,6 +38,10 @@ interface DisplayPreferencesContextState {
historicDataSwitch: (e: React.FormEvent<HTMLFormElement>) => void; historicDataSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
historicDataSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>; historicDataSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
historicMap: LayerEnablementState;
historicMapSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
historicMapSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
darkLightTheme: MapTheme; darkLightTheme: MapTheme;
darkLightThemeSwitch: (e: React.FormEvent<HTMLFormElement>) => void; darkLightThemeSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
darkLightThemeSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>; darkLightThemeSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
@ -87,6 +91,10 @@ export const DisplayPreferencesContext = createContext<DisplayPreferencesContext
historicDataSwitch: stub, historicDataSwitch: stub,
historicDataSwitchOnClick: undefined, historicDataSwitchOnClick: undefined,
historicMap: undefined,
historicMapSwitch: stub,
historicMapSwitchOnClick: undefined,
darkLightTheme: undefined, darkLightTheme: undefined,
darkLightThemeSwitch: stub, darkLightThemeSwitch: stub,
darkLightThemeSwitchOnClick: undefined, darkLightThemeSwitchOnClick: undefined,
@ -107,6 +115,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
const defaultParcel = 'disabled' const defaultParcel = 'disabled'
const defaultConservation = 'disabled' const defaultConservation = 'disabled'
const defaultHistoricData = 'disabled' const defaultHistoricData = 'disabled'
const defaultHistoricMap = 'disabled'
const defaultShowLayerSelection = 'disabled' const defaultShowLayerSelection = 'disabled'
const [vista, setVista] = useState<LayerEnablementState>(defaultVista); const [vista, setVista] = useState<LayerEnablementState>(defaultVista);
const [flood, setFlood] = useState<LayerEnablementState>(defaultFlood); const [flood, setFlood] = useState<LayerEnablementState>(defaultFlood);
@ -116,6 +125,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
const [parcel, setParcel] = useState<LayerEnablementState>(defaultParcel); const [parcel, setParcel] = useState<LayerEnablementState>(defaultParcel);
const [conservation, setConservation] = useState<LayerEnablementState>(defaultConservation); const [conservation, setConservation] = useState<LayerEnablementState>(defaultConservation);
const [historicData, setHistoricData] = useState<LayerEnablementState>(defaultHistoricData); const [historicData, setHistoricData] = useState<LayerEnablementState>(defaultHistoricData);
const [historicMap, setHistoricMap] = useState<LayerEnablementState>(defaultHistoricMap);
const [darkLightTheme, setDarkLightTheme] = useState<MapTheme>('night'); const [darkLightTheme, setDarkLightTheme] = useState<MapTheme>('night');
const [showLayerSelection, setShowLayerSelection] = useState<LayerEnablementState>(defaultShowLayerSelection); const [showLayerSelection, setShowLayerSelection] = useState<LayerEnablementState>(defaultShowLayerSelection);
@ -136,6 +146,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
setParcel(defaultParcel); setParcel(defaultParcel);
setConservation(defaultConservation); setConservation(defaultConservation);
setHistoricData(defaultHistoricData); setHistoricData(defaultHistoricData);
setHistoricMap(defaultHistoricMap);
setShowLayerSelection(defaultShowLayerSelection); // reset layers + hiding this panel is integrated into one action setShowLayerSelection(defaultShowLayerSelection); // reset layers + hiding this panel is integrated into one action
//setDarkLightTheme('night'); // reset only layers //setDarkLightTheme('night'); // reset only layers
}, },
@ -167,6 +178,9 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
if(historicData != defaultHistoricData) { if(historicData != defaultHistoricData) {
return true; return true;
} }
if(historicMap != defaultHistoricMap) {
return true;
}
//darkLightTheme not handled here //darkLightTheme not handled here
return false; return false;
} }
@ -278,9 +292,12 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
const historicDataSwitch = useCallback( const historicDataSwitch = useCallback(
(e) => { (e) => {
flipHistoricData(e) if (historicMap === 'enabled') {
fliphistoricMap(e);
}
flipHistoricData(e);
}, },
[historicData], [historicData, historicMap],
) )
const historicDataSwitchOnClick = (e) => { const historicDataSwitchOnClick = (e) => {
flipHistoricData(e) flipHistoricData(e)
@ -291,6 +308,24 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
setHistoricData(newHistoric); setHistoricData(newHistoric);
} }
const historicMapSwitch = useCallback(
(e) => {
if (historicData === 'enabled') {
flipHistoricData(e);
}
fliphistoricMap(e);
},
[historicMap, historicData],
)
const historicMapSwitchOnClick = (e) => {
fliphistoricMap(e)
}
function fliphistoricMap(e) {
e.preventDefault();
const newHistoric = (historicMap === 'enabled')? 'disabled' : 'enabled';
setHistoricMap(newHistoric);
}
const darkLightThemeSwitch = useCallback( const darkLightThemeSwitch = useCallback(
(e) => { (e) => {
flipDarkLightTheme(e) flipDarkLightTheme(e)
@ -354,6 +389,10 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
historicDataSwitch, historicDataSwitch,
historicDataSwitchOnClick, historicDataSwitchOnClick,
historicMap,
historicMapSwitch,
historicMapSwitchOnClick,
darkLightTheme, darkLightTheme,
darkLightThemeSwitch, darkLightThemeSwitch,
darkLightThemeSwitchOnClick, darkLightThemeSwitchOnClick,

View File

@ -57,7 +57,8 @@ function getCurrentMenuLinks(username: string): MenuLink[][] {
}, },
{ {
to: "https://github.com/colouring-cities/manual/wiki", to: "https://github.com/colouring-cities/manual/wiki",
text: "Open Manual - Wiki", text: "Colouring Cities Open Manual/Wiki",
disabled: false,
external: true external: true
}, },
{ {
@ -65,12 +66,6 @@ function getCurrentMenuLinks(username: string): MenuLink[][] {
text: "Open code", text: "Open code",
external: true external: true
}, },
{
to: "https://github.com/colouring-cities/manual/wiki",
text: "Colouring Cities Open Manual/Wiki",
disabled: false,
external: true
},
{ {
to: "/showcase.html", to: "/showcase.html",
text: "Case Study Showcase", text: "Case Study Showcase",
@ -79,30 +74,35 @@ function getCurrentMenuLinks(username: string): MenuLink[][] {
], ],
[ [
{ {
to: "https://pages.colouring.london", to: "https://github.com/colouring-cities/manual/wiki/A.-What-is-the-CCRP%3F",
text: "About", text: "About the Colouring Cities Research Programme",
external: true external: true
}, },
{ {
to: "https://pages.colouring.london/buildingcategories", to: config.manualURL,
text: "About the Colouring " + config.cityName + " Project",
external: true
},
{
to: "https://github.com/colouring-cities/manual/wiki/A2.-How-to%3F-Guides",
text: "How to Use",
external: true
},
{
to: "https://github.com/colouring-cities/manual/wiki/I.--DATA",
text: "Data Categories", text: "Data Categories",
external: true external: true
}, },
{ {
to: "https://pages.colouring.london/whoisinvolved", to: "https://github.com/colouring-cities/manual/wiki/M3.2-Colouring-Britain:-Who's-Involved%3F",
text: "Who's Involved?", text: "Who's Involved?",
external: true external: true
}, },
{ {
to: "https://pages.colouring.london/data-ethics", to: "https://github.com/colouring-cities/manual/wiki/C.-Ethical-framework-and-ethics-policies",
text: "Data Ethics", text: "Ethical Framework",
external: true external: true
}, }
{
to: "https://pages.colouring.london/colouring-cities",
text: "Colouring Cities Research Programme",
external: true
},
], ],
[ [
{ {
@ -110,32 +110,45 @@ function getCurrentMenuLinks(username: string): MenuLink[][] {
text: "Top Contributors" text: "Top Contributors"
}, },
{ {
to: "https://discuss.colouring.london", to: config.githubURL+"/discussions",
text: "Discussion Forum", text: "Discussion Forum (GitHub)",
external: true
},
{
to: "https://discuss.colouring.london/c/blog/9",
text: "Blog",
external: true external: true
}, },
// {
// to: "https://discuss.colouring.london/c/blog/9",
// text: "Blog",
// external: true
// },
], ],
[ [
{ {
to: "/privacy-policy.html", to: "https://github.com/colouring-cities/manual/wiki/C1.-Protocols,-codes-of-conduct-&-data-sharing-agreements#ccrp-contributor-privacy-statement",
text: "Privacy Policy" text: "Privacy Policy",
external: true
}, },
{ {
to: "/contributor-agreement.html", to: "https://github.com/colouring-cities/manual/wiki/C1.-Protocols,-codes-of-conduct-&-data-sharing-agreements#ccrp-contributor--data-user-data-accuracy--ethical-use-agreement",
text: "Contributor Agreement" text: "Contributor Agreement",
external: true
}, },
{ {
to: "/code-of-conduct.html", to: "/code-of-conduct.html",
text: "Code of Conduct" text: "Code of Conduct"
}, },
{ {
to: "/data-accuracy.html", to: "https://github.com/colouring-cities/manual/wiki/C1.-Protocols,-codes-of-conduct-&-data-sharing-agreements#ccrp-contributor--data-user-data-accuracy--ethical-use-agreement",
text: "Data Accuracy Agreement" text: "Data Accuracy and Use Agreement",
external: true
},
{
to: "https://github.com/colouring-cities/manual/wiki/C1.-Protocols,-codes-of-conduct-&-data-sharing-agreements#ccrp-equality-diversity-and-inclusion-policy",
text: "Equality, Diversity and Inclusion",
external: true
},
{
to: "https://github.com/colouring-cities/manual/wiki/C1.-Protocols,-codes-of-conduct-&-data-sharing-agreements#ccrp-protocols-for-international-academic-partners",
text: "CCRP Academic Partner Protocols",
external: true
}, },
{ {
to: "/ordnance-survey-uprn.html", to: "/ordnance-survey-uprn.html",
@ -183,7 +196,7 @@ const InternalNavLink: React.FC<{to: string; onClick: () => void}> = ({ to, onCl
); );
const ExternalNavLink: React.FC<{to: string}> = ({ to, children }) => ( const ExternalNavLink: React.FC<{to: string}> = ({ to, children }) => (
<a className="nav-link" href={to}> <a className="nav-link" href={to} target="_blank">
{children} {children}
</a> </a>
); );

View File

@ -5,11 +5,12 @@ import { useDisplayPreferences } from '../displayPreferences-context';
export const HistoricDataSwitcher: React.FC<{}> = (props) => { export const HistoricDataSwitcher: React.FC<{}> = (props) => {
const { historicData, historicDataSwitch, darkLightTheme } = useDisplayPreferences(); const { historicData, historicDataSwitch, darkLightTheme } = useDisplayPreferences();
return ( return (
<form className={`historic-data-switcher map-button ${historicData}-state ${darkLightTheme}`} onSubmit={historicDataSwitch}> <form className={`historic-data-switcher map-button ${historicData}-state ${darkLightTheme}`} onSubmit={historicDataSwitch}>
<button className="btn btn-outline btn-outline-dark" <button className="btn btn-outline btn-outline-dark"
type="submit"> type="submit">
{(historicData === 'enabled')? 'The OS 1890s Historical Map on' : 'The OS 1890s Historical Map off'} {(historicData === 'enabled')? 'OS 1890s Historical Map + Footprints on' : 'OS 1890s Historical Map + Footprints off'}
</button> </button>
</form> </form>
); );

View File

@ -0,0 +1,17 @@
import React from 'react';
import './map-button.css';
import { useDisplayPreferences } from '../displayPreferences-context';
export const HistoricMapSwitcher: React.FC<{}> = (props) => {
const { historicMap, historicMapSwitch, darkLightTheme } = useDisplayPreferences();
return (
<form className={`historic-map-switcher map-button ${historicMap}-state ${darkLightTheme}`} onSubmit={historicMapSwitch}>
<button className="btn btn-outline btn-outline-dark"
type="submit">
{(historicMap === 'enabled')? 'OS 1890s Historical Map on' : 'OS 1890s Historical Map off'}
</button>
</form>
);
}

View File

@ -0,0 +1,21 @@
import * as React from 'react';
import { TileLayer } from 'react-leaflet';
import { LayerEnablementState } from '../../config/map-config';
import { BuildingBaseLayerAllZoom } from './building-base-layer-all-zoom';
import { useDisplayPreferences } from '../../displayPreferences-context';
import { BuildingDataLayer } from './building-data-layer';
export function HistoricMapLayer({revisionId}: {revisionId: string}) {
const { historicMap } = useDisplayPreferences();
if(historicMap == "enabled") {
return <>
<TileLayer
url="https://mapseries-tilesets.s3.amazonaws.com/london_1890s/{z}/{x}/{y}.png"
attribution='&copy; CC BY 4.0 - Reproduced with the permission of the <a href="https://maps.nls.uk/">National Library of Scotland</a>'
/>
</>
} else {
return null;
}
}

View File

@ -57,7 +57,7 @@
padding: 0.5rem 0.25rem; padding: 0.5rem 0.25rem;
margin: 0.25rem 0.5rem; margin: 0.25rem 0.5rem;
width: auto; width: auto;
font-size: 18px; font-size: 17px;
border: 1px solid; border: 1px solid;
border-radius: 4px; border-radius: 4px;
} }

View File

@ -111,11 +111,15 @@
} }
.historic-data-switcher { .historic-data-switcher {
top: 437px;
}
.historic-map-switcher {
top: 397px; top: 397px;
} }
.parcel-switcher { .parcel-switcher {
top: 437px; top: 477px;
} }
.map-switcher-inline { .map-switcher-inline {

View File

@ -15,6 +15,7 @@ import { BoroughBoundaryLayer } from './layers/borough-boundary-layer';
import { BoroughLabelLayer } from './layers/borough-label-layer'; import { BoroughLabelLayer } from './layers/borough-label-layer';
import { ParcelBoundaryLayer } from './layers/parcel-boundary-layer'; import { ParcelBoundaryLayer } from './layers/parcel-boundary-layer';
import { HistoricDataLayer } from './layers/historic-data-layer'; import { HistoricDataLayer } from './layers/historic-data-layer';
import { HistoricMapLayer } from './layers/historic-map-layer';
import { FloodBoundaryLayer } from './layers/flood-boundary-layer'; import { FloodBoundaryLayer } from './layers/flood-boundary-layer';
import { ConservationAreaBoundaryLayer } from './layers/conservation-boundary-layer'; import { ConservationAreaBoundaryLayer } from './layers/conservation-boundary-layer';
import { VistaBoundaryLayer } from './layers/vista-boundary-layer'; import { VistaBoundaryLayer } from './layers/vista-boundary-layer';
@ -34,6 +35,7 @@ import { ParcelSwitcher } from './parcel-switcher';
import { FloodSwitcher } from './flood-switcher'; import { FloodSwitcher } from './flood-switcher';
import { ConservationAreaSwitcher } from './conservation-switcher'; import { ConservationAreaSwitcher } from './conservation-switcher';
import { HistoricDataSwitcher } from './historic-data-switcher'; import { HistoricDataSwitcher } from './historic-data-switcher';
import { HistoricMapSwitcher } from './historic-map-switcher';
import { VistaSwitcher } from './vista-switcher'; import { VistaSwitcher } from './vista-switcher';
import { CreativeSwitcher } from './creative-switcher'; import { CreativeSwitcher } from './creative-switcher';
import { HousingSwitcher } from './housing-switcher'; import { HousingSwitcher } from './housing-switcher';
@ -129,6 +131,7 @@ export const ColouringMap : FC<ColouringMapProps> = ({
> >
<CityBoundaryLayer/> <CityBoundaryLayer/>
<HistoricDataLayer revisionId={revisionId} /> <HistoricDataLayer revisionId={revisionId} />
<HistoricMapLayer revisionId={revisionId} />
<BoroughBoundaryLayer/> <BoroughBoundaryLayer/>
<ParcelBoundaryLayer/> <ParcelBoundaryLayer/>
<FloodBoundaryLayer/> <FloodBoundaryLayer/>
@ -167,10 +170,12 @@ export const ColouringMap : FC<ColouringMapProps> = ({
<ParcelSwitcher/> <ParcelSwitcher/>
<FloodSwitcher/> <FloodSwitcher/>
<ConservationAreaSwitcher/> <ConservationAreaSwitcher/>
<HistoricMapSwitcher/>
<HistoricDataSwitcher/> <HistoricDataSwitcher/>
<VistaSwitcher /> <VistaSwitcher />
<HousingSwitcher /> <HousingSwitcher />
<CreativeSwitcher /> <CreativeSwitcher />
</> </>
: <></> : <></>
} }

View File

@ -266,6 +266,15 @@ const LAYER_QUERIES = {
buildings buildings
WHERE WHERE
current_landuse_order IS NOT NULL`, current_landuse_order IS NOT NULL`,
original_landuse: `
SELECT
geometry_id,
typology_original_use_order,
typology_original_use[1] as typology_original_use,
typology_original_use_verified
FROM
buildings
WHERE typology_original_use IS NOT NULL`,
disaster_severity: ` disaster_severity: `
SELECT SELECT
geometry_id, geometry_id,
@ -281,6 +290,27 @@ const LAYER_QUERIES = {
FROM FROM
buildings buildings
WHERE jsonb_array_length(demolished_buildings) > 0 OR dynamics_has_demolished_buildings = FALSE`, WHERE jsonb_array_length(demolished_buildings) > 0 OR dynamics_has_demolished_buildings = FALSE`,
typology_classification: `
SELECT
geometry_id,
typology_classification
FROM
buildings
WHERE typology_classification IS NOT NULL`,
typology_style_period: `
SELECT
geometry_id,
typology_style_period
FROM
buildings
WHERE typology_style_period IS NOT NULL`,
typology_dynamic_classification: `
SELECT
geometry_id,
typology_dynamic_classification
FROM
buildings
WHERE typology_dynamic_classification IS NOT NULL`,
}; };
const GEOMETRY_FIELD = 'geometry_geom'; const GEOMETRY_FIELD = 'geometry_geom';

View File

@ -326,6 +326,8 @@ pip install -r requirements.txt
To help test the Colouring Cities application, `get_test_polygons.py` will attempt to save a small (1.5km²) extract from OpenStreetMap to a format suitable for loading to the database. To help test the Colouring Cities application, `get_test_polygons.py` will attempt to save a small (1.5km²) extract from OpenStreetMap to a format suitable for loading to the database.
**NOTE:** You can edit the file by [changing this line](https://github.com/colouring-cities/colouring-core/blob/df651521983665056d442603a329a37d966aede1/etl/get_test_polygons.py#L22) to specify the latitude and longitude used to define the centre of the sample data area, as well as the distance from that point, which are used to generate the sample data. (i.e. you could change it to match the cc-config.json file - as discussed in [configuring-colouring-cities.md](https://github.com/colouring-cities/colouring-core/blob/master/docs/configuring-colouring-cities.md).
Download the test data. Download the test data.
```bash ```bash

View File

@ -1 +1,3 @@
from .filter_mastermap import filter_mastermap from .filter_mastermap import filter_mastermap
__all__ = ["filter_mastermap"]

View File

@ -20,24 +20,24 @@ def main(mastermap_path):
def filter_mastermap(mm_path): def filter_mastermap(mm_path):
output_path = str(mm_path).replace(".gml.csv", "") output_path = str(mm_path).replace(".gml.csv", "")
output_path = "{}.filtered.csv".format(output_path) output_path = "{}.filtered.csv".format(output_path)
output_fieldnames = ('WKT', 'fid', 'descriptiveGroup') output_fieldnames = ("WKT", "fid", "descriptiveGroup")
# Open the input csv with all polygons, buildings and others # Open the input csv with all polygons, buildings and others
with open(mm_path, 'r') as fh: with open(mm_path, "r") as fh:
r = csv.DictReader(fh) r = csv.DictReader(fh)
# Open a new output csv that will contain just buildings # Open a new output csv that will contain just buildings
with open(output_path, 'w') as output_fh: with open(output_path, "w") as output_fh:
w = csv.DictWriter(output_fh, fieldnames=output_fieldnames) w = csv.DictWriter(output_fh, fieldnames=output_fieldnames)
w.writeheader() w.writeheader()
for line in r: for line in r:
try: try:
if 'Building' in line['descriptiveGroup']: if "Building" in line["descriptiveGroup"]:
w.writerow(line) w.writerow(line)
# when descriptiveGroup is missing, ignore this Polygon # when descriptiveGroup is missing, ignore this Polygon
except TypeError: except TypeError:
pass pass
if __name__ == '__main__': if __name__ == "__main__":
if len(sys.argv) != 2: if len(sys.argv) != 2:
print("Usage: filter_mastermap.py ./path/to/mastermap/dir") print("Usage: filter_mastermap.py ./path/to/mastermap/dir")
exit(-1) exit(-1)

View File

@ -21,43 +21,49 @@ size = 256
# load buildings from about 1.5km² around UCL # load buildings from about 1.5km² around UCL
point = (51.524498, -0.133874) point = (51.524498, -0.133874)
dist = 612 dist = 612
gdf = osmnx.footprints_from_point(point=point, dist=dist) tags = {"building": True}
gdf = osmnx.features_from_point(point, tags, dist=dist)
# preview image # preview image
gdf_proj = osmnx.projection.project_gdf(gdf, to_crs={'init': 'epsg:3857'}) gdf_proj = osmnx.projection.project_gdf(gdf, to_crs={"init": "epsg:3857"})
gdf_proj = gdf_proj[gdf_proj.geometry.apply(lambda g: g.geom_type != 'MultiPolygon')] # noqa gdf_proj = gdf_proj[gdf_proj.geometry.type == "Polygon"]
fig, ax = osmnx.plot_footprints(gdf_proj, bgcolor='#333333', fig, ax = osmnx.plot_footprints(
color='w', figsize=(4, 4), gdf_proj,
save=True, show=False, close=True, bgcolor="#333333",
filename='test_buildings_preview', dpi=600) color="w",
figsize=(4, 4),
save=True,
show=False,
close=True,
filepath="test_buildings_preview.png",
dpi=600,
)
# save # save
test_dir = os.path.dirname(__file__) test_dir = os.path.dirname(__file__)
test_data_geojson = str(os.path.join(test_dir, 'test_buildings.geojson')) test_data_geojson = str(os.path.join(test_dir, "test_buildings.geojson"))
subprocess.run(["rm", test_data_geojson]) subprocess.run(["rm", test_data_geojson])
gdf_to_save = gdf_proj.reset_index()[["osmid", "geometry"]]
gdf_to_save = gdf_proj.reset_index( gdf_to_save.rename(columns={"osmid": "fid"}).to_file(
)[ test_data_geojson, driver="GeoJSON"
['index', 'geometry']
]
gdf_to_save.rename(
columns={'index': 'fid'}
).to_file(
test_data_geojson, driver='GeoJSON'
) )
# convert to CSV # convert to CSV
test_data_csv = str(os.path.join(test_dir, 'test_buildings.3857.csv')) test_data_csv = str(os.path.join(test_dir, "test_buildings.3857.csv"))
subprocess.run(["rm", test_data_csv]) subprocess.run(["rm", test_data_csv])
subprocess.run( subprocess.run(
["ogr2ogr", "-f", "CSV", test_data_csv, [
test_data_geojson, "-lco", "GEOMETRY=AS_WKT"] "ogr2ogr",
"-f",
"CSV",
test_data_csv,
test_data_geojson,
"-lco",
"GEOMETRY=AS_WKT",
]
) )
# add SRID for ease of loading to PostgreSQL # add SRID for ease of loading to PostgreSQL
subprocess.run( subprocess.run(["sed", "-i", 's/^"POLYGON/"SRID=3857;POLYGON/', test_data_csv])
["sed", "-i", "s/^\"POLYGON/\"SRID=3857;POLYGON/",
test_data_csv]
)

View File

@ -17,7 +17,6 @@ Then with this script:
""" """
import json
import csv import csv
import os import os
import subprocess import subprocess
@ -28,50 +27,49 @@ from tqdm import tqdm
def main(base_url, api_key, source_file): def main(base_url, api_key, source_file):
"""Read from file, update buildings """Read from file, update buildings"""
""" with open(source_file, "r") as source_fh:
with open(source_file, 'r') as source_fh:
source = csv.DictReader(source_fh) source = csv.DictReader(source_fh)
for feature in tqdm(source, total=line_count(source_file)): for feature in tqdm(source, total=line_count(source_file)):
building_id, data = process_ca(feature) building_id, data = process_ca(feature)
if building_id and building_id != 'building_id': if building_id and building_id != "building_id":
save_data(building_id, data, api_key, base_url) save_data(building_id, data, api_key, base_url)
def line_count(fname): def line_count(fname):
"""Count lines - relies on 'wc' """Count lines - relies on 'wc'"""
""" p = subprocess.run(["wc", "-l", fname], stdout=subprocess.PIPE)
p = subprocess.run(['wc', '-l', fname], stdout=subprocess.PIPE)
if p.returncode != 0: if p.returncode != 0:
raise IOError(err) raise IOError(p.returncode)
return int(p.stdout.strip().split()[0]) return int(p.stdout.strip().split()[0])
def process_ca(props): def process_ca(props):
building_id = props['building_id'] building_id = props["building_id"]
data = { data = {
'planning_in_conservation_area': True, "planning_in_conservation_area": True,
'planning_conservation_area_name': props['conservation_area_name'] "planning_conservation_area_name": props["conservation_area_name"],
} }
return building_id, data return building_id, data
def save_data(building_id, data, api_key, base_url): def save_data(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building"""
""" requests.post(
r = requests.post(
"{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key), "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
json=data json=data,
) )
if __name__ == '__main__': if __name__ == "__main__":
try: try:
url, api_key, filename = sys.argv[1], sys.argv[2], sys.argv[3] url, api_key, filename = sys.argv[1], sys.argv[2], sys.argv[3]
except IndexError: except IndexError:
print( print(
"Usage: {} <URL> <api_key> ./path/to/conservation_areas.csv".format( "Usage: {} <URL> <api_key> ./path/to/conservation_areas.csv".format(
os.path.basename(__file__) os.path.basename(__file__)
)) )
)
exit() exit()
main(url, api_key, filename) main(url, api_key, filename)

View File

@ -44,8 +44,6 @@ TODO extend to allow latitude,longitude or easting,northing columns and lookup b
""" """
import csv import csv
import json import json
import os
import sys
import argparse import argparse
import requests import requests
@ -53,9 +51,8 @@ from retrying import retry
def main(base_url, api_key, source_file, json_columns, no_overwrite=False, debug=False): def main(base_url, api_key, source_file, json_columns, no_overwrite=False, debug=False):
"""Read from file, update buildings """Read from file, update buildings"""
""" with open(source_file, "r") as source:
with open(source_file, 'r') as source:
reader = csv.DictReader(source) reader = csv.DictReader(source)
for line in reader: for line in reader:
building_id = find_building(line, base_url) building_id = find_building(line, base_url)
@ -64,78 +61,86 @@ def main(base_url, api_key, source_file, json_columns, no_overwrite=False, debug
if building_id is None: if building_id is None:
continue continue
if 'sust_dec' in line and line['sust_dec'] == '': if "sust_dec" in line and line["sust_dec"] == "":
del line['sust_dec'] del line["sust_dec"]
if no_overwrite: if no_overwrite:
try: try:
if check_data_present(building_id, line.keys(), base_url): if check_data_present(building_id, line.keys(), base_url):
print(f'Building {building_id}: Not updating to avoid overwriting existing data') print(
f"Building {building_id}: Not updating to avoid overwriting existing data"
)
continue continue
except ApiRequestError as e: except ApiRequestError as e:
print(f'Error checking existing data for building {building_id}: status {e.code}, data: {e.data}') print(
f"Error checking existing data for building {building_id}: status {e.code}, data: {e.data}"
)
raise raise
response_code, response_data = update_building(building_id, line, api_key, base_url) response_code, response_data = update_building(
building_id, line, api_key, base_url
)
if response_code != 200: if response_code != 200:
print('ERROR', building_id, response_code, response_data) print("ERROR", building_id, response_code, response_data)
elif debug: elif debug:
print('DEBUG', building_id, response_code, response_data) print("DEBUG", building_id, response_code, response_data)
class ApiRequestError(Exception): class ApiRequestError(Exception):
def __init__(self, code, data, message=''): def __init__(self, code, data, message=""):
self.code = code self.code = code
self.data = data self.data = data
super().__init__(message) super().__init__(message)
def check_data_present(building_id, fields, base_url): def check_data_present(building_id, fields, base_url):
response_code, current_state = get_building(building_id, base_url) response_code, current_state = get_building(building_id, base_url)
if response_code != 200: if response_code != 200:
raise ApiRequestError(response_code, current_state) raise ApiRequestError(response_code, current_state)
else: else:
id_fields = set(['building_id', 'toid', 'uprn']) id_fields = set(["building_id", "toid", "uprn"])
field_names_without_ids = [k for k in fields if k not in id_fields] field_names_without_ids = [k for k in fields if k not in id_fields]
return any([current_state.get(k, None) != None for k in field_names_without_ids]) return any(
[current_state.get(k, None) is not None for k in field_names_without_ids]
)
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000) @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def get_building(building_id, base_url): def get_building(building_id, base_url):
"""Get data for a building """Get data for a building"""
"""
r = requests.get(f"{base_url}/api/buildings/{building_id}.json") r = requests.get(f"{base_url}/api/buildings/{building_id}.json")
return r.status_code, r.json() return r.status_code, r.json()
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000) @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def update_building(building_id, data, api_key, base_url): def update_building(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building"""
"""
r = requests.post( r = requests.post(
"{}/api/buildings/{}.json".format(base_url, building_id), "{}/api/buildings/{}.json".format(base_url, building_id),
params={'api_key': api_key}, params={"api_key": api_key},
json=data json=data,
) )
return r.status_code, r.json() return r.status_code, r.json()
def find_building(data, base_url): def find_building(data, base_url):
if 'building_id' in data: if "building_id" in data:
building_id = data['building_id'] building_id = data["building_id"]
if building_id is not None: if building_id is not None:
print("match_by_building_id", building_id) print("match_by_building_id", building_id)
return building_id return building_id
if 'toid' in data: if "toid" in data:
building_id = find_by_reference(base_url, 'toid', data['toid']) building_id = find_by_reference(base_url, "toid", data["toid"])
if building_id is not None: if building_id is not None:
print("match_by_toid", data['toid'], building_id) print("match_by_toid", data["toid"], building_id)
return building_id return building_id
if 'uprn' in data: if "uprn" in data:
building_id = find_by_reference(base_url, 'uprn', data['uprn']) building_id = find_by_reference(base_url, "uprn", data["uprn"])
if building_id is not None: if building_id is not None:
print("match_by_uprn", data['uprn'], building_id) print("match_by_uprn", data["uprn"], building_id)
return building_id return building_id
print("no_match", data) print("no_match", data)
@ -144,21 +149,21 @@ def find_building(data, base_url):
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000) @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def find_by_reference(base_url, ref_key, ref_id): def find_by_reference(base_url, ref_key, ref_id):
"""Find building_id by TOID or UPRN """Find building_id by TOID or UPRN"""
""" r = requests.get(
r = requests.get("{}/api/buildings/reference".format(base_url), params={ "{}/api/buildings/reference".format(base_url),
'key': ref_key, params={"key": ref_key, "id": ref_id},
'id': ref_id )
})
buildings = r.json() buildings = r.json()
if buildings and 'error' not in buildings and len(buildings) == 1: if buildings and "error" not in buildings and len(buildings) == 1:
building_id = buildings[0]['building_id'] building_id = buildings[0]["building_id"]
else: else:
building_id = None building_id = None
return building_id return building_id
def parse_json_columns(row, json_columns): def parse_json_columns(row, json_columns):
for col in json_columns: for col in json_columns:
row[col] = json.loads(row[col]) row[col] = json.loads(row[col])
@ -167,28 +172,41 @@ def parse_json_columns(row, json_columns):
def list_str(values): def list_str(values):
return values.split(',') return values.split(",")
if __name__ == '__main__':
if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('url', help='URL for the app') parser.add_argument("url", help="URL for the app")
parser.add_argument('api_key', help='API key for the user') parser.add_argument("api_key", help="API key for the user")
parser.add_argument('path', help='Path to data CSV file') parser.add_argument("path", help="Path to data CSV file")
parser.add_argument('json_columns', parser.add_argument(
nargs='?', "json_columns",
nargs="?",
type=list_str, type=list_str,
default=[], default=[],
help='A comma-separated list of columns which should be parsed as JSON') help="A comma-separated list of columns which should be parsed as JSON",
)
parser.add_argument('--no-overwrite', '-n', parser.add_argument(
action='store_true', "--no-overwrite",
dest='no_overwrite', "-n",
help='Don\'t overwrite building data if any of the fields supplied is already set') action="store_true",
dest="no_overwrite",
help="Don't overwrite building data if any of the fields supplied is already set",
)
parser.add_argument('--debug', '-d', parser.add_argument(
action='store_true', "--debug", "-d", action="store_true", help="Print debug messages"
help='Print debug messages') )
args = parser.parse_args() args = parser.parse_args()
main(args.url, args.api_key, args.path, args.json_columns, args.no_overwrite, args.debug) main(
args.url,
args.api_key,
args.path,
args.json_columns,
args.no_overwrite,
args.debug,
)

View File

@ -23,18 +23,18 @@ The process:
TODO extend to allow latitude,longitude or easting,northing columns and lookup by location. TODO extend to allow latitude,longitude or easting,northing columns and lookup by location.
""" """
import csv import csv
import json
import os import os
import sys import sys
import requests import requests
session = requests.Session() session = requests.Session()
session.verify = False session.verify = False
def main(base_url, api_key, source_file): def main(base_url, api_key, source_file):
"""Read from file, update buildings """Read from file, update buildings"""
""" with open(source_file, "r") as source:
with open(source_file, 'r') as source:
reader = csv.DictReader(source) reader = csv.DictReader(source)
for line in reader: for line in reader:
building_id = find_building(line, base_url) building_id = find_building(line, base_url)
@ -42,40 +42,41 @@ def main(base_url, api_key, source_file):
if building_id is None: if building_id is None:
continue continue
response_code, response_data = update_building(building_id, line, api_key, base_url) response_code, response_data = update_building(
building_id, line, api_key, base_url
)
if response_code != 200: if response_code != 200:
print('ERROR', building_id, response_code, response_data) print("ERROR", building_id, response_code, response_data)
def update_building(building_id, data, api_key, base_url): def update_building(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building"""
"""
r = requests.post( r = requests.post(
"{}/api/buildings/{}.json".format(base_url, building_id), "{}/api/buildings/{}.json".format(base_url, building_id),
params={'api_key': api_key}, params={"api_key": api_key},
json=data, json=data,
verify=False verify=False,
) )
print(r) print(r)
return r.status_code, r.json() return r.status_code, r.json()
def find_building(data, base_url): def find_building(data, base_url):
if 'building_id' in data: if "building_id" in data:
building_id = data['building_id'] building_id = data["building_id"]
if building_id is not None: if building_id is not None:
print("match_by_building_id", building_id) print("match_by_building_id", building_id)
return building_id return building_id
if 'toid' in data: if "toid" in data:
building_id = find_by_reference(base_url, 'toid', data['toid']) building_id = find_by_reference(base_url, "toid", data["toid"])
if building_id is not None: if building_id is not None:
print("match_by_toid", data['toid'], building_id) print("match_by_toid", data["toid"], building_id)
return building_id return building_id
if 'uprn' in data: if "uprn" in data:
building_id = find_by_reference(base_url, 'uprn', data['uprn']) building_id = find_by_reference(base_url, "uprn", data["uprn"])
if building_id is not None: if building_id is not None:
print("match_by_uprn", data['uprn'], building_id) print("match_by_uprn", data["uprn"], building_id)
return building_id return building_id
print("no_match", data) print("no_match", data)
@ -83,32 +84,34 @@ def find_building(data, base_url):
def find_by_reference(base_url, ref_key, ref_id): def find_by_reference(base_url, ref_key, ref_id):
"""Find building_id by TOID or UPRN """Find building_id by TOID or UPRN"""
""" r = requests.get(
r = requests.get("{}/api/buildings/reference".format(base_url), params={ "{}/api/buildings/reference".format(base_url),
'key': ref_key, params={
'id': ref_id, "key": ref_key,
"id": ref_id,
}, },
verify=False verify=False,
) )
buildings = r.json() buildings = r.json()
if buildings and 'error' not in buildings and len(buildings) == 1: if buildings and "error" not in buildings and len(buildings) == 1:
building_id = buildings[0]['building_id'] building_id = buildings[0]["building_id"]
else: else:
building_id = None building_id = None
return building_id return building_id
if __name__ == '__main__': if __name__ == "__main__":
try: try:
url, api_key, filename = sys.argv[1], sys.argv[2], sys.argv[3] url, api_key, filename = sys.argv[1], sys.argv[2], sys.argv[3]
except IndexError: except IndexError:
print( print(
"Usage: {} <URL> <api_key> ./path/to/data.csv".format( "Usage: {} <URL> <api_key> ./path/to/data.csv".format(
os.path.basename(__file__) os.path.basename(__file__)
)) )
)
exit() exit()
main(url, api_key, filename) main(url, api_key, filename)

View File

@ -8,7 +8,6 @@ datasets for Camden (age data) and Fitzrovia (number of storeys).
- else locate building by representative point - else locate building by representative point
- update building with data - update building with data
""" """
import json
import os import os
import sys import sys
from functools import partial from functools import partial
@ -21,18 +20,15 @@ from shapely.ops import transform
osgb_to_ll = partial( osgb_to_ll = partial(
pyproj.transform, pyproj.transform, pyproj.Proj(init="epsg:27700"), pyproj.Proj(init="epsg:4326")
pyproj.Proj(init='epsg:27700'),
pyproj.Proj(init='epsg:4326')
) )
def main(base_url, api_key, process, source_file): def main(base_url, api_key, process, source_file):
"""Read from file, update buildings """Read from file, update buildings"""
""" with fiona.open(source_file, "r") as source:
with fiona.open(source_file, 'r') as source:
for feature in source: for feature in source:
props = feature['properties'] props = feature["properties"]
if process == "camden": if process == "camden":
toid, data = process_camden(props) toid, data = process_camden(props)
@ -42,7 +38,7 @@ def main(base_url, api_key, process, source_file):
if data is None: if data is None:
continue continue
building_id = find_building(toid, feature['geometry'], base_url) building_id = find_building(toid, feature["geometry"], base_url)
if not building_id: if not building_id:
print("no_match", toid, "-") print("no_match", toid, "-")
continue continue
@ -51,31 +47,22 @@ def main(base_url, api_key, process, source_file):
def process_camden(props): def process_camden(props):
toid = osgb_toid(props['TOID']) toid = osgb_toid(props["TOID"])
data = { data = {"date_year": props["Year_C"], "date_source_detail": props["Date_sou_1"]}
'date_year': props['Year_C'],
'date_source_detail': props['Date_sou_1']
}
return toid, data return toid, data
def process_fitzrovia(props): def process_fitzrovia(props):
toid = osgb_toid(props['TOID']) toid = osgb_toid(props["TOID"])
storeys = props['Storeys'] storeys = props["Storeys"]
if storeys is None: if storeys is None:
return toid, None return toid, None
if props['Basement'] == 'Yes': if props["Basement"] == "Yes":
data = { data = {"size_storeys_core": int(storeys) - 1, "size_storeys_basement": 1}
'size_storeys_core': int(storeys) - 1,
'size_storeys_basement': 1
}
else: else:
data = { data = {"size_storeys_core": int(storeys), "size_storeys_basement": 0}
'size_storeys_core': int(storeys),
'size_storeys_basement': 0
}
return toid, data return toid, data
@ -86,24 +73,21 @@ def osgb_toid(toid):
def save_data(building_id, data, api_key, base_url): def save_data(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building"""
""" requests.post(
r = requests.post(
"{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key), "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
json=data json=data,
) )
def find_building(toid, geom, base_url): def find_building(toid, geom, base_url):
"""Find building_id by TOID or location """Find building_id by TOID or location"""
""" r = requests.get(
r = requests.get(base_url + "/buildings/reference", params={ base_url + "/buildings/reference", params={"key": "toid", "id": toid}
'key': 'toid', )
'id': toid
})
buildings = r.json() buildings = r.json()
if buildings and len(buildings) == 1: if buildings and len(buildings) == 1:
bid = buildings[0]['building_id'] bid = buildings[0]["building_id"]
print("match_by_toid", toid, bid) print("match_by_toid", toid, bid)
return bid return bid
@ -114,27 +98,32 @@ def find_building(toid, geom, base_url):
point_osgb = poly.representative_point() point_osgb = poly.representative_point()
point_ll = transform(osgb_to_ll, point_osgb) point_ll = transform(osgb_to_ll, point_osgb)
r = requests.get(base_url + "/buildings/locate", params={ r = requests.get(
'lng': point_ll.x, base_url + "/buildings/locate", params={"lng": point_ll.x, "lat": point_ll.y}
'lat': point_ll.y )
})
buildings = r.json() buildings = r.json()
if buildings and len(buildings) == 1: if buildings and len(buildings) == 1:
bid = buildings[0]['building_id'] bid = buildings[0]["building_id"]
print("match_by_location", toid, bid) print("match_by_location", toid, bid)
return bid return bid
return None return None
if __name__ == '__main__': if __name__ == "__main__":
try: try:
url, api_key, process, filename = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] url, api_key, process, filename = (
sys.argv[1],
sys.argv[2],
sys.argv[3],
sys.argv[4],
)
except IndexError: except IndexError:
print( print(
"Usage: {} <URL> <api_key> <camden|fitzrovia> ./path/to/camden.shp".format( "Usage: {} <URL> <api_key> <camden|fitzrovia> ./path/to/camden.shp".format(
os.path.basename(__file__) os.path.basename(__file__)
)) )
)
exit() exit()
main(url, api_key, process, filename) main(url, api_key, process, filename)

View File

@ -8,7 +8,6 @@ datasets for Camden (age data) and Fitzrovia (number of storeys).
- else locate building by representative point - else locate building by representative point
- update building with data - update building with data
""" """
import json
import os import os
import sys import sys
from functools import partial from functools import partial
@ -21,18 +20,15 @@ from shapely.ops import transform
osgb_to_ll = partial( osgb_to_ll = partial(
pyproj.transform, pyproj.transform, pyproj.Proj(init="epsg:27700"), pyproj.Proj(init="epsg:4326")
pyproj.Proj(init='epsg:27700'),
pyproj.Proj(init='epsg:4326')
) )
def main(base_url, api_key, process, source_file): def main(base_url, api_key, process, source_file):
"""Read from file, update buildings """Read from file, update buildings"""
""" with fiona.open(source_file, "r") as source:
with fiona.open(source_file, 'r') as source:
for feature in source: for feature in source:
props = feature['properties'] props = feature["properties"]
if process == "camden": if process == "camden":
toid, data = process_camden(props) toid, data = process_camden(props)
@ -42,7 +38,7 @@ def main(base_url, api_key, process, source_file):
if data is None: if data is None:
continue continue
building_id = find_building(toid, feature['geometry'], base_url) building_id = find_building(toid, feature["geometry"], base_url)
if not building_id: if not building_id:
print("no_match", toid, "-") print("no_match", toid, "-")
continue continue
@ -51,31 +47,22 @@ def main(base_url, api_key, process, source_file):
def process_camden(props): def process_camden(props):
toid = osgb_toid(props['TOID']) toid = osgb_toid(props["TOID"])
data = { data = {"date_year": props["Year_C"], "date_source_detail": props["Date_sou_1"]}
'date_year': props['Year_C'],
'date_source_detail': props['Date_sou_1']
}
return toid, data return toid, data
def process_fitzrovia(props): def process_fitzrovia(props):
toid = osgb_toid(props['TOID']) toid = osgb_toid(props["TOID"])
storeys = props['Storeys'] storeys = props["Storeys"]
if storeys is None: if storeys is None:
return toid, None return toid, None
if props['Basement'] == 'Yes': if props["Basement"] == "Yes":
data = { data = {"size_storeys_core": int(storeys) - 1, "size_storeys_basement": 1}
'size_storeys_core': int(storeys) - 1,
'size_storeys_basement': 1
}
else: else:
data = { data = {"size_storeys_core": int(storeys), "size_storeys_basement": 0}
'size_storeys_core': int(storeys),
'size_storeys_basement': 0
}
return toid, data return toid, data
@ -86,24 +73,21 @@ def osgb_toid(toid):
def save_data(building_id, data, api_key, base_url): def save_data(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building"""
""" requests.post(
r = requests.post(
"{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key), "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
json=data json=data,
) )
def find_building(toid, geom, base_url): def find_building(toid, geom, base_url):
"""Find building_id by TOID or location """Find building_id by TOID or location"""
""" r = requests.get(
r = requests.get(base_url + "/buildings/reference", params={ base_url + "/buildings/reference", params={"key": "toid", "id": toid}
'key': 'toid', )
'id': toid
})
buildings = r.json() buildings = r.json()
if buildings and len(buildings) == 1: if buildings and len(buildings) == 1:
bid = buildings[0]['building_id'] bid = buildings[0]["building_id"]
print("match_by_toid", toid, bid) print("match_by_toid", toid, bid)
return bid return bid
@ -114,27 +98,32 @@ def find_building(toid, geom, base_url):
point_osgb = poly.representative_point() point_osgb = poly.representative_point()
point_ll = transform(osgb_to_ll, point_osgb) point_ll = transform(osgb_to_ll, point_osgb)
r = requests.get(base_url + "/buildings/locate", params={ r = requests.get(
'lng': point_ll.x, base_url + "/buildings/locate", params={"lng": point_ll.x, "lat": point_ll.y}
'lat': point_ll.y )
})
buildings = r.json() buildings = r.json()
if buildings and len(buildings) == 1: if buildings and len(buildings) == 1:
bid = buildings[0]['building_id'] bid = buildings[0]["building_id"]
print("match_by_location", toid, bid) print("match_by_location", toid, bid)
return bid return bid
return None return None
if __name__ == '__main__': if __name__ == "__main__":
try: try:
url, api_key, process, filename = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] url, api_key, process, filename = (
sys.argv[1],
sys.argv[2],
sys.argv[3],
sys.argv[4],
)
except IndexError: except IndexError:
print( print(
"Usage: {} <URL> <api_key> <camden|fitzrovia> ./path/to/camden.shp".format( "Usage: {} <URL> <api_key> <camden|fitzrovia> ./path/to/camden.shp".format(
os.path.basename(__file__) os.path.basename(__file__)
)) )
)
exit() exit()
main(url, api_key, process, filename) main(url, api_key, process, filename)

View File

@ -1,9 +1,14 @@
def planning_data_entry_to_address(element): def planning_data_entry_to_address(element):
site_name = element["_source"].get("site_name") site_name = element["_source"].get("site_name")
site_number = element["_source"].get("site_number") site_number = element["_source"].get("site_number")
street_name = element["_source"].get("street_name") # seems often misused - say "31 COPTHALL ROAD EAST" site_name getting Ickenham street_name street_name = element["_source"].get("street_name")
# seems often misused - say "31 COPTHALL ROAD EAST" site_name
# getting Ickenham street_name
secondary_street_name = element["_source"].get("secondary_street_name") secondary_street_name = element["_source"].get("secondary_street_name")
return generate_address(site_name, site_number, street_name, secondary_street_name)['result'] return generate_address(site_name, site_number, street_name, secondary_street_name)[
"result"
]
def generate_address(site_name, site_number, street_name, secondary_street_name): def generate_address(site_name, site_number, street_name, secondary_street_name):
""" """
@ -11,13 +16,13 @@ def generate_address(site_name, site_number, street_name, secondary_street_name)
sadly it does not always works well and relies on many heursitics as data quality is limited sadly it does not always works well and relies on many heursitics as data quality is limited
""" """
if site_name != None: if site_name is not None:
site_name = site_name.strip() site_name = site_name.strip()
if site_number != None: if site_number is not None:
site_number = site_number.strip() site_number = site_number.strip()
if street_name != None: if street_name is not None:
street_name = street_name.strip() street_name = street_name.strip()
if secondary_street_name != None: if secondary_street_name is not None:
secondary_street_name = secondary_street_name.strip() secondary_street_name = secondary_street_name.strip()
if site_name == "": if site_name == "":
@ -29,68 +34,80 @@ def generate_address(site_name, site_number, street_name, secondary_street_name)
if secondary_street_name == "": if secondary_street_name == "":
secondary_street_name = None secondary_street_name = None
data = { data = {
'site_name': site_name, "site_name": site_name,
'site_number': site_number, "site_number": site_number,
'street_name': street_name, "street_name": street_name,
'secondary_street_name': secondary_street_name, "secondary_street_name": secondary_street_name,
} }
if site_name == site_number == street_name == secondary_street_name == None: if site_name == site_number == street_name == secondary_street_name is None:
return {'result': None, 'data': data} return {"result": None, "data": data}
if secondary_street_name != None: if secondary_street_name is not None:
if street_name == None: if street_name is None:
print('"secondary_street_name != None, street_name == None"') print('"secondary_street_name is not None, street_name is None"')
show_data(site_name, site_number, street_name, secondary_street_name, "???????") show_data(
site_name, site_number, street_name, secondary_street_name, "???????"
)
else: else:
street_name += " - with secondary road name: " + secondary_street_name street_name += " - with secondary road name: " + secondary_street_name
if site_number != None and street_name != None: if site_number is not None and street_name is not None:
address = site_number + " " + street_name address = site_number + " " + street_name
if site_name != None: if site_name is not None:
print('"site_name != None and site_number != None and street_name != None"') print(
show_data(site_name, site_number, street_name, secondary_street_name, address) '"site_name is not None and site_number is not None and street_name is not None"'
)
show_data(
site_name, site_number, street_name, secondary_street_name, address
)
return {'result': address, 'data': data} return {"result": address, "data": data}
if site_name != None: if site_name is not None:
if street_name != None: if street_name is not None:
try: try:
if site_number == None and int(site_name): if site_number is None and int(site_name):
return {'result': site_name + " " + street_name, 'data': data} return {"result": site_name + " " + street_name, "data": data}
except ValueError: except ValueError:
pass pass
if street_name in site_name: if street_name in site_name:
site_name_without_street_name = site_name.replace(street_name, "").strip() site_name_without_street_name = site_name.replace(
street_name, ""
).strip()
try: try:
house_number = int(site_name_without_street_name) _ = int(site_name_without_street_name)
# so it appears to be case like # so it appears to be case like
# site_name: 5 Warwick Road # site_name: 5 Warwick Road
# street_name: Warwick Road # street_name: Warwick Road
# no other info provided # no other info provided
# in such case just returning site_name will work fine... # in such case just returning site_name will work fine...
return {'result': site_name, 'data': data} return {"result": site_name, "data": data}
except ValueError: except ValueError:
pass pass
print('"site_name != None and street_name != None"') print('"site_name is not None and street_name is not None"')
show_data(site_name, site_number, street_name, secondary_street_name, site_name) show_data(
if site_number != None: site_name, site_number, street_name, secondary_street_name, site_name
print('"site_name != None and site_number != None"') )
show_data(site_name, site_number, street_name, secondary_street_name, site_name) if site_number is not None:
return {'result': site_name, 'data': data} print('"site_name is not None and site_number is not None"')
show_data(
site_name, site_number, street_name, secondary_street_name, site_name
)
return {"result": site_name, "data": data}
else: else:
if street_name != None: if street_name is not None:
if site_number != None: if site_number is not None:
return {'result': site_number + " " + street_name, 'data': data} return {"result": site_number + " " + street_name, "data": data}
if street_name != None and site_number == None: if street_name is not None and site_number is None:
print('"street_name != None or site_number == None"') print('"street_name is not None or site_number is None"')
show_data(site_name, site_number, street_name, secondary_street_name, None) show_data(site_name, site_number, street_name, secondary_street_name, None)
return {'result': None, 'data': data} return {"result": None, "data": data}
if street_name == None and site_number != None: if street_name is None and site_number is not None:
print('"street_name == None or site_number != None"') print('"street_name is None or site_number is not None"')
show_data(site_name, site_number, street_name, secondary_street_name, None) show_data(site_name, site_number, street_name, secondary_street_name, None)
return {'result': None, 'data': data} return {"result": None, "data": data}
return {'result': None, 'data': data} return {"result": None, "data": data}
def show_data(site_name, site_number, street_name, secondary_street_name, address): def show_data(site_name, site_number, street_name, secondary_street_name, address):

View File

@ -5,6 +5,7 @@ import requests
import psycopg2 import psycopg2
import address_data import address_data
def main(): def main():
connection = get_connection() connection = get_connection()
cursor = connection.cursor() cursor = connection.cursor()
@ -16,10 +17,12 @@ def main():
while True: while True:
data = query(search_after).json() data = query(search_after).json()
load_data_into_database(cursor, data) load_data_into_database(cursor, data)
for entry in data['hits']['hits']: for entry in data["hits"]["hits"]:
downloaded += 1 downloaded += 1
last_sort = entry['sort'] last_sort = entry["sort"]
print("downloaded", downloaded, "last_sort", last_sort, "previous", search_after) print(
"downloaded", downloaded, "last_sort", last_sort, "previous", search_after
)
if search_after == last_sort: if search_after == last_sort:
break break
search_after = last_sort search_after = last_sort
@ -31,24 +34,30 @@ def load_data_into_database(cursor, data):
print(json.dumps(data, indent=4)) print(json.dumps(data, indent=4))
print("timed_out field missing in provided data") print("timed_out field missing in provided data")
else: else:
if data['timed_out']: if data["timed_out"]:
raise Exception("query getting livestream data has failed") raise Exception("query getting livestream data has failed")
for entry in data['hits']['hits']: for entry in data["hits"]["hits"]:
try: try:
description = None description = None
if entry['_source']['description'] != None: if entry["_source"]["description"] is not None:
description = entry['_source']['description'].strip() description = entry["_source"]["description"].strip()
application_id = entry['_source']['lpa_app_no'] application_id = entry["_source"]["lpa_app_no"]
application_id_with_borough_identifier = entry['_source']['id'] application_id_with_borough_identifier = entry["_source"]["id"]
decision_date = parse_date_string_into_date_object(entry['_source']['decision_date']) decision_date = parse_date_string_into_date_object(
last_synced_date = parse_date_string_into_date_object(entry['_source']['last_synced']) entry["_source"]["decision_date"]
uprn = entry['_source']['uprn'] )
status_before_aliasing = entry['_source']['status'] last_synced_date = parse_date_string_into_date_object(
entry["_source"]["last_synced"]
)
uprn = entry["_source"]["uprn"]
status_before_aliasing = entry["_source"]["status"]
status_info = process_status(status_before_aliasing, decision_date) status_info = process_status(status_before_aliasing, decision_date)
status = status_info["status"] status = status_info["status"]
status_explanation_note = status_info["status_explanation_note"] status_explanation_note = status_info["status_explanation_note"]
planning_url = obtain_entry_link(entry['_source']['url_planning_app'], application_id) planning_url = obtain_entry_link(
if uprn == None: entry["_source"]["url_planning_app"], application_id
)
if uprn is None:
continue continue
try: try:
uprn = int(uprn) uprn = int(uprn)
@ -61,7 +70,9 @@ def load_data_into_database(cursor, data):
"last_synced_date": last_synced_date, "last_synced_date": last_synced_date,
"application_id": application_id, "application_id": application_id,
"application_url": planning_url, "application_url": planning_url,
"registered_with_local_authority_date": parse_date_string_into_date_object(entry['_source']['valid_date']), "registered_with_local_authority_date": parse_date_string_into_date_object(
entry["_source"]["valid_date"]
),
"uprn": uprn, "uprn": uprn,
"status": status, "status": status,
"status_before_aliasing": status_before_aliasing, "status_before_aliasing": status_before_aliasing,
@ -70,13 +81,16 @@ def load_data_into_database(cursor, data):
"data_source_link": "https://www.london.gov.uk/programmes-strategies/planning/digital-planning/planning-london-datahub", "data_source_link": "https://www.london.gov.uk/programmes-strategies/planning/digital-planning/planning-london-datahub",
"address": address_data.planning_data_entry_to_address(entry), "address": address_data.planning_data_entry_to_address(entry),
} }
if entry["address"] != None: if entry["address"] is not None:
maximum_address_length = 300 maximum_address_length = 300
if len(entry["address"]) > maximum_address_length: if len(entry["address"]) > maximum_address_length:
print("address is too long, shortening", entry["address"]) print("address is too long, shortening", entry["address"])
entry["address"] = entry["address"][0:maximum_address_length] entry["address"] = entry["address"][0:maximum_address_length]
if date_in_future(entry["registered_with_local_authority_date"]): if date_in_future(entry["registered_with_local_authority_date"]):
print("registered_with_local_authority_date is treated as invalid:", entry["registered_with_local_authority_date"]) print(
"registered_with_local_authority_date is treated as invalid:",
entry["registered_with_local_authority_date"],
)
# Brent-87_0946 has "valid_date": "23/04/9187" # Brent-87_0946 has "valid_date": "23/04/9187"
entry["registered_with_local_authority_date"] = None entry["registered_with_local_authority_date"] = None
@ -85,13 +99,17 @@ def load_data_into_database(cursor, data):
entry["decision_date"] = None entry["decision_date"] = None
if date_in_future(entry["last_synced_date"]): if date_in_future(entry["last_synced_date"]):
print("last_synced_date is treated as invalid:", entry["last_synced_date"]) print(
"last_synced_date is treated as invalid:", entry["last_synced_date"]
)
entry["last_synced_date"] = None entry["last_synced_date"] = None
if "Hackney" in application_id_with_borough_identifier: if "Hackney" in application_id_with_borough_identifier:
if entry["application_url"] != None: if entry["application_url"] is not None:
if "https://" not in entry["application_url"]: if "https://" not in entry["application_url"]:
entry["application_url"] = "https://developmentandhousing.hackney.gov.uk" + entry["application_url"] entry[
"application_url"
] = f"https://developmentandhousing.hackney.gov.uk{entry['application_url']}"
insert_entry(cursor, entry) insert_entry(cursor, entry)
except TypeError as e: except TypeError as e:
print() print()
@ -104,40 +122,40 @@ def load_data_into_database(cursor, data):
def date_in_future(date): def date_in_future(date):
if date == None: if date is None:
return False return False
return date > datetime.datetime.now() return date > datetime.datetime.now()
def query(search_after): def query(search_after):
headers = { headers = {
'X-API-AllowRequest': os.environ['PLANNNING_DATA_API_ALLOW_REQUEST_CODE'], "X-API-AllowRequest": os.environ["PLANNNING_DATA_API_ALLOW_REQUEST_CODE"],
# Already added when you pass json= but not when you pass data= # Already added when you pass json= but not when you pass data=
# 'Content-Type': 'application/json', # 'Content-Type': 'application/json',
} }
json_data = { json_data = {
'size': 10000, "size": 10000,
'sort': [ "sort": [
{ {
'last_updated': { "last_updated": {
'order': 'desc', "order": "desc",
'unmapped_type': 'boolean', "unmapped_type": "boolean",
}, },
}, },
], ],
'stored_fields': [ "stored_fields": [
'*', "*",
], ],
'_source': { "_source": {
'excludes': [], "excludes": [],
}, },
'query': { "query": {
'bool': { "bool": {
'must': [ "must": [
{ {
'range': { "range": {
'valid_date': { "valid_date": {
'gte': '01/01/1021', "gte": "01/01/1021",
}, },
}, },
}, },
@ -147,18 +165,22 @@ def query(search_after):
} }
if search_after != []: if search_after != []:
json_data['search_after'] = search_after json_data["search_after"] = search_after
print(json_data) print(json_data)
return requests.post('https://planningdata.london.gov.uk/api-guest/applications/_search', headers=headers, json=json_data) return requests.post(
"https://planningdata.london.gov.uk/api-guest/applications/_search",
headers=headers,
json=json_data,
)
def get_connection(): def get_connection():
return psycopg2.connect( return psycopg2.connect(
host=os.environ['PGHOST'], host=os.environ["PGHOST"],
dbname=os.environ['PGDATABASE'], dbname=os.environ["PGDATABASE"],
user=os.environ['PGUSER'], user=os.environ["PGUSER"],
password=os.environ['PGPASSWORD'] password=os.environ["PGPASSWORD"],
) )
@ -170,15 +192,18 @@ def insert_entry(cursor, e):
try: try:
now = datetime.datetime.now() now = datetime.datetime.now()
application_url = None application_url = None
if e["application_url"] != None: if e["application_url"] is not None:
application_url = e["application_url"] application_url = e["application_url"]
cursor.execute('''INSERT INTO cursor.execute(
"""INSERT INTO
planning_data (planning_application_id, planning_application_link, description, registered_with_local_authority_date, days_since_registration_cached, decision_date, days_since_decision_date_cached, last_synced_date, status, status_before_aliasing, status_explanation_note, data_source, data_source_link, address, uprn) planning_data (planning_application_id, planning_application_link, description, registered_with_local_authority_date, days_since_registration_cached, decision_date, days_since_decision_date_cached, last_synced_date, status, status_before_aliasing, status_explanation_note, data_source, data_source_link, address, uprn)
VALUES VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
''', ( """,
(
e["application_id"], e["application_id"],
application_url, e["description"], application_url,
e["description"],
date_object_into_date_string(e["registered_with_local_authority_date"]), date_object_into_date_string(e["registered_with_local_authority_date"]),
days_since(e["registered_with_local_authority_date"], now), days_since(e["registered_with_local_authority_date"], now),
date_object_into_date_string(e["decision_date"]), date_object_into_date_string(e["decision_date"]),
@ -191,7 +216,7 @@ def insert_entry(cursor, e):
e["data_source_link"], e["data_source_link"],
e["address"], e["address"],
e["uprn"], e["uprn"],
) ),
) )
except psycopg2.errors.Error as error: except psycopg2.errors.Error as error:
show_dictionary(e) show_dictionary(e)
@ -204,30 +229,32 @@ def show_dictionary(data):
def days_since(date, now): def days_since(date, now):
if(date == None): if date is None:
return None return None
return (now - date).days return (now - date).days
def date_object_into_date_string(date): def date_object_into_date_string(date):
if(date == None): if date is None:
return None return None
return datetime.datetime.strftime(date, "%Y-%m-%d") return datetime.datetime.strftime(date, "%Y-%m-%d")
def parse_date_string_into_date_object(incoming): def parse_date_string_into_date_object(incoming):
if incoming == None: if incoming is None:
return None return None
date = None date = None
try: try:
date = datetime.datetime.strptime(incoming, "%d/%m/%Y") # '21/07/2022' date = datetime.datetime.strptime(incoming, "%d/%m/%Y") # '21/07/2022'
except ValueError: except ValueError:
date = datetime.datetime.strptime(incoming, "%Y-%m-%dT%H:%M:%S.%fZ") # '2022-08-08T20:07:22.238Z' date = datetime.datetime.strptime(
incoming, "%Y-%m-%dT%H:%M:%S.%fZ"
) # '2022-08-08T20:07:22.238Z'
return date return date
def obtain_entry_link(provided_link, application_id): def obtain_entry_link(provided_link, application_id):
if provided_link != None: if provided_link is not None:
if "Ealing" in application_id: if "Ealing" in application_id:
if ";" == provided_link[-1]: if ";" == provided_link[-1]:
return provided_link[:-1] return provided_link[:-1]
@ -237,7 +264,7 @@ def obtain_entry_link(provided_link, application_id):
# Planning application ID: Hackney-2021_2491 # Planning application ID: Hackney-2021_2491
# https://developmentandhousing.hackney.gov.uk/planning/index.html?fa=getApplication&reference=2021/2491 # https://developmentandhousing.hackney.gov.uk/planning/index.html?fa=getApplication&reference=2021/2491
ref_for_link = application_id.replace("Hackney-", "").replace("_", "/") ref_for_link = application_id.replace("Hackney-", "").replace("_", "/")
return "https://developmentandhousing.hackney.gov.uk/planning/index.html?fa=getApplication&reference=" + ref_for_link return f"https://developmentandhousing.hackney.gov.uk/planning/index.html?fa=getApplication&reference={ref_for_link}"
if "Lambeth" in application_id: if "Lambeth" in application_id:
# sadly, specific links seems impossible # sadly, specific links seems impossible
return "https://planning.lambeth.gov.uk/online-applications/refineSearch.do?action=refine" return "https://planning.lambeth.gov.uk/online-applications/refineSearch.do?action=refine"
@ -282,9 +309,16 @@ def obtain_entry_link(provided_link, application_id):
def process_status(status, decision_date): def process_status(status, decision_date):
status_length_limit = 50 # see migrations/034.planning_livestream_data.up.sql status_length_limit = 50 # see migrations/034.planning_livestream_data.up.sql
if status in ["Application Under Consideration", "Application Received"]: if status in ["Application Under Consideration", "Application Received"]:
if decision_date == None: if decision_date is None:
status = "Submitted" status = "Submitted"
if status in ["Refused", "Refusal", "Refusal (P)", "Application Invalid", "Insufficient Fee", "Dismissed"]: if status in [
"Refused",
"Refusal",
"Refusal (P)",
"Application Invalid",
"Insufficient Fee",
"Dismissed",
]:
status = "Rejected" status = "Rejected"
if status == "Appeal Received": if status == "Appeal Received":
status = "Appeal In Progress" status = "Appeal In Progress"
@ -296,16 +330,39 @@ def process_status(status, decision_date):
status = "Withdrawn" status = "Withdrawn"
if len(status) > status_length_limit: if len(status) > status_length_limit:
print("Status was too long and was skipped:", status) print("Status was too long and was skipped:", status)
return {"status": "Processing failed", "status_explanation_note": "status was unusally long and it was imposible to save it"} return {
if (status in ["Submitted", "Approved", "Rejected", "Appeal In Progress", "Withdrawn", "Unknown"]): "status": "Processing failed",
"status_explanation_note": "status was unusally long and it was imposible to save it",
}
if status in [
"Submitted",
"Approved",
"Rejected",
"Appeal In Progress",
"Withdrawn",
"Unknown",
]:
return {"status": status, "status_explanation_note": None} return {"status": status, "status_explanation_note": None}
if status in ["No Objection to Proposal (OBS only)", "Objection Raised to Proposal (OBS only)"]: if status in [
return {"status": "Approved", "status_explanation_note": "preapproved application, local authority is unable to reject it"} "No Objection to Proposal (OBS only)",
"Objection Raised to Proposal (OBS only)",
]:
return {
"status": "Approved",
"status_explanation_note": "preapproved application, local authority is unable to reject it",
}
print("Unexpected status " + status) print("Unexpected status " + status)
if status not in ["Not Required", "SECS", "Comment Issued", "ALL DECISIONS ISSUED", "Closed", "Declined to Determine"]: if status not in [
"Not Required",
"SECS",
"Comment Issued",
"ALL DECISIONS ISSUED",
"Closed",
"Declined to Determine",
]:
print("New unexpected status " + status) print("New unexpected status " + status)
return {"status": status, "status_explanation_note": None} return {"status": status, "status_explanation_note": None}
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@ -1,3 +1,3 @@
# Python packages for planning data import # Python packages for planning data import
psycopg2==2.8.6 psycopg2-binary==2.9.7
requests==2.31.0 requests==2.31.0

View File

@ -1,7 +1,7 @@
# Python packages for etl # Python packages for etl
fiona==1.7.13 fiona
osmnx==0.13 osmnx==1.6.0
psycopg2==2.7.5 psycopg2-binary==2.9.7
shapely==1.7
retrying==1.3.3 retrying==1.3.3
requests==2.31.0 requests==2.31.0
shapely

View File

@ -4,6 +4,7 @@ COPY (SELECT
ref_osm_id, ref_osm_id,
revision_id, revision_id,
location_name, location_name,
location_name_link,
location_number, location_number,
location_street, location_street,
location_line_two, location_line_two,
@ -13,6 +14,7 @@ COPY (SELECT
location_address_links, location_address_links,
location_latitude, location_latitude,
location_longitude, location_longitude,
location_alternative_footprint_links
current_landuse_group, current_landuse_group,
current_landuse_order, current_landuse_order,
building_attachment_form, building_attachment_form,

View File

@ -1,2 +1,2 @@
psycopg2==2.8.3 psycopg2-binary==2.9.7
requests==2.31.0 requests==2.31.0

View File

@ -0,0 +1,34 @@
ALTER TABLE buildings DROP COLUMN IF EXISTS location_name_link;
ALTER TABLE buildings DROP COLUMN IF EXISTS location_alternative_footprint_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS age_historical_raster_map_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS age_historical_vectorised_footprint_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS landowner_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS designers_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS builder_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS developer_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS energy_solar;
ALTER TABLE buildings DROP COLUMN IF EXISTS energy_solar_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS energy_solar_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS energy_green_roof;
ALTER TABLE buildings DROP COLUMN IF EXISTS energy_green_roof_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS energy_green_roof_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_crowdsourced_site_completion_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_crowdsourced_site_completion_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_missing_data;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_missing_data_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS date_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS date_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_heritage_at_risk;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_world_heritage_site;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_local_list;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_in_conservation_area;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_in_apa;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_historic_area_assessment;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_scientific_interest;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_scientific_interest_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_scientific_interest_source_links;

View File

@ -0,0 +1,34 @@
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS location_name_link text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS location_alternative_footprint_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS age_historical_raster_map_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS age_historical_vectorised_footprint_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS landowner_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS designers_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS builder_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS developer_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS energy_solar boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS energy_solar_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS energy_solar_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS energy_green_roof boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS energy_green_roof_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS energy_green_roof_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_crowdsourced_site_completion_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_crowdsourced_site_completion_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_missing_data boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_missing_data_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS date_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS date_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_heritage_at_risk boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_world_heritage_site boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_local_list boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_in_conservation_area boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_in_apa boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_historic_area_assessment boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_scientific_interest boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_scientific_interest_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_scientific_interest_source_links text[];

View File

@ -0,0 +1,14 @@
ALTER TABLE buildings DROP COLUMN IF EXISTS context_front_garden;
ALTER TABLE buildings DROP COLUMN IF EXISTS context_back_garden;
ALTER TABLE buildings DROP COLUMN IF EXISTS context_flats_garden;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS context_front_garden boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS context_back_garden boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS context_flats_garden boolean;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_crowdsourced_site_completion_status;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_crowdsourced_site_completion_status boolean;
ALTER TABLE buildings DROP COLUMN IF EXISTS planning_crowdsourced_site_completion_year;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS planning_crowdsourced_site_completion_year smallint;

View File

@ -0,0 +1,18 @@
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_classification;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_classification_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_classification_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_style_period;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_style_period_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_style_period_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_dynamic_classification;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_dynamic_classification_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_dynamic_classification_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_original_use;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_original_use_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_original_use_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS building_attachment_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS building_attachment_source_links;

View File

@ -0,0 +1,18 @@
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_classification text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_classification_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_classification_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_style_period text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_style_period_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_style_period_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_dynamic_classification text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_dynamic_classification_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_dynamic_classification_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_original_use text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_original_use_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_original_use_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS building_attachment_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS building_attachment_source_links text[];

View File

@ -0,0 +1,38 @@
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_structural_system;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_structural_system_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_structural_system_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_foundation;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_foundation_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_foundation_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_roof_shape;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_roof_shape_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_roof_shape_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_irregularities;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_irregularities_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_irregularities_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_roof_covering_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_roof_covering_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_decorative_features;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_decorative_feature_materials;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_decorative_feature_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_decorative_feature_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_internal_wall;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_internal_wall_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_internal_wall_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_external_wall;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_external_wall_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_external_wall_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_ground_floor;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_ground_floor_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_ground_floor_source_links;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_core_material_source_type;
ALTER TABLE buildings DROP COLUMN IF EXISTS construction_core_material_source_links;

View File

@ -0,0 +1,38 @@
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_structural_system text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_structural_system_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_structural_system_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_foundation text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_foundation_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_foundation_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_roof_shape text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_roof_shape_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_roof_shape_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_irregularities text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_irregularities_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_irregularities_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_roof_covering_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_roof_covering_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_decorative_features boolean;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_decorative_feature_materials text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_decorative_feature_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_decorative_feature_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_internal_wall text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_internal_wall_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_internal_wall_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_external_wall text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_external_wall_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_external_wall_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_ground_floor text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_ground_floor_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_ground_floor_source_links text[];
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_core_material_source_type text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS construction_core_material_source_links text[];

View File

@ -0,0 +1,2 @@
ALTER TABLE buildings DROP COLUMN IF EXISTS typology_original_use_order;
ALTER TABLE buildings DROP COLUMN IF NOT EXISTS typology_original_use_verified;

View File

@ -0,0 +1,9 @@
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_original_use_order text;
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS typology_original_use_verified BOOLEAN NOT NULL DEFAULT FALSE;
UPDATE buildings as b
SET typology_original_use_verified = TRUE
FROM building_verification as v
WHERE b.building_id = v.building_id
AND v.attribute = 'current_landuse_group';