diff --git a/.github/workflows/etl.yml b/.github/workflows/etl.yml index 32893dc1..ccba08ed 100644 --- a/.github/workflows/etl.yml +++ b/.github/workflows/etl.yml @@ -5,21 +5,20 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: - Install dependencies - run: | - sudo apt-get install libgeos-dev - python -m pip install --upgrade pip - python -m pip install pytest - python -m pip install flake8 - python -m pip install -r etl/requirements.txt - - name: Run Flake8 - run: | - ls etl/*py | grep -v 'join_building_data' | xargs flake8 --exclude etl/__init__.py - - name: Run tests - run: | - python -m pytest \ No newline at end of file + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + sudo apt-get install libgeos-dev + python -m pip install --upgrade pip + python -m pip install pytest + python -m pip install flake8 + python -m pip install -r etl/requirements.txt + - name: Run Flake8 + run: | + flake8 etl --ignore=E501 + - name: Run tests + run: | + python -m pytest diff --git a/README.md b/README.md index 6de17536..08af83d2 100644 --- a/README.md +++ b/README.md @@ -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) ![Build status](https://github.com/colouring-cities/colouring-core/workflows/Node.js%20CI/badge.svg) diff --git a/app/map_styles/polygon.xml b/app/map_styles/polygon.xml index 652f698d..7f54c7d8 100644 --- a/app/map_styles/polygon.xml +++ b/app/map_styles/polygon.xml @@ -19,7 +19,19 @@ + + + + diff --git a/app/package-lock.json b/app/package-lock.json index bd06b938..dc47f5e2 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -128,9 +128,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -196,9 +196,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -264,9 +264,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true, "bin": { @@ -1664,9 +1664,9 @@ } }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true, "bin": { @@ -1895,9 +1895,9 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true, "bin": { @@ -4099,9 +4099,9 @@ } }, "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -4138,9 +4138,9 @@ } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true, "bin": { @@ -6160,9 +6160,9 @@ } }, "node_modules/default-gateway/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -6994,9 +6994,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -9776,9 +9776,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -10747,9 +10747,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -11523,9 +11523,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -15234,9 +15234,9 @@ } }, "node_modules/react-dev-utils/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -16217,9 +16217,9 @@ } }, "node_modules/sane/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -16363,9 +16363,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -18206,9 +18206,9 @@ } }, "node_modules/tiny-async-pool/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -18311,23 +18311,24 @@ } }, "node_modules/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", - "universalify": "^0.1.2" + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { "node": ">=6" } }, "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, "engines": { "node": ">= 4.0.0" @@ -19636,9 +19637,9 @@ } }, "node_modules/webpack-dev-server/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -20144,9 +20145,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -20356,9 +20357,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -20408,9 +20409,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -20460,9 +20461,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true } @@ -21429,9 +21430,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true } @@ -21605,9 +21606,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true } @@ -23391,9 +23392,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -23423,9 +23424,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "peer": true } @@ -24995,9 +24996,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -25705,9 +25706,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -27756,9 +27757,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -28521,9 +28522,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -29127,9 +29128,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -31910,9 +31911,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "strip-ansi": { @@ -32690,9 +32691,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -32809,9 +32810,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -34294,9 +34295,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -34379,20 +34380,21 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "requires": { "psl": "^1.1.33", "punycode": "^2.1.1", - "universalify": "^0.1.2" + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "dependencies": { "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true } } @@ -35535,9 +35537,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "sockjs-client": { @@ -35831,9 +35833,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "worker-rpc": { diff --git a/app/src/api/config/dataFields.ts b/app/src/api/config/dataFields.ts index 8f150f47..c0db758b 100644 --- a/app/src/api/config/dataFields.ts +++ b/app/src/api/config/dataFields.ts @@ -59,6 +59,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true, }, + location_name_link: { + edit: true, + verify: true, + }, location_number: { edit: true, verify: true, @@ -103,6 +107,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true }, + location_alternative_footprint_links: { + edit: true, + verify: true + }, date_year: { edit: true, verify: true, @@ -126,6 +134,14 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true, }, + date_source_type: { + edit: true, + verify: true, + }, + date_source_links: { + edit: true, + verify: true, + }, facade_year: { edit: true, verify: true, @@ -254,6 +270,14 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true, }, + construction_core_material_source_type: { + edit: true, + verify: true, + }, + construction_core_material_source_links: { + edit: true, + verify: true, + }, construction_secondary_materials: { edit: false, }, @@ -261,6 +285,116 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: 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: { edit: true, verify: true, @@ -277,10 +411,22 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: 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: { edit: true, verify: true, }, + planning_in_conservation_area: { + edit: true, + verify: true, + }, planning_in_conservation_area_id: { edit: true, verify: true, @@ -301,6 +447,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true, }, + planning_world_heritage_site: { + edit: true, + verify: true, + }, planning_world_list_id: { edit: true, verify: true, @@ -309,14 +459,26 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true, }, + planning_in_apa: { + edit: true, + verify: true, + }, planning_in_apa_url: { edit: true, verify: true, }, + planning_local_list: { + edit: true, + verify: true, + }, planning_local_list_url: { edit: true, verify: true, }, + planning_historic_area_assessment: { + edit: true, + verify: true, + }, planning_historic_area_assessment_url: { edit: true, verify: true, @@ -325,6 +487,30 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: 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: { edit: true, verify: true, @@ -355,6 +541,14 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true, }, + building_attachment_source_type: { + edit: true, + verify: true, + }, + building_attachment_source_links: { + edit: true, + verify: true, + }, date_change_building_use: { edit: true, }, @@ -482,6 +676,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true }, + developer_links: { + edit: true, + verify: true + }, developer_source_type: { edit: true, verify: true @@ -494,6 +692,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true }, + landowner_links: { + edit: true, + verify: true + }, landowner_source_type: { edit: true, verify: true @@ -506,6 +708,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true }, + designers_links: { + edit: true, + verify: true + }, designers_source_type: { edit: true, verify: true @@ -530,6 +736,10 @@ export const buildingAttributesConfig = valueType()({ /* eslint edit: true, verify: true }, + builder_links: { + edit: true, + verify: true + }, builder_source_type: { edit: true, verify: true @@ -685,7 +895,95 @@ export const buildingAttributesConfig = valueType()({ /* eslint age_retrofit_date_source_links : { edit: 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 + }, }); diff --git a/app/src/api/services/autofill.ts b/app/src/api/services/autofill.ts index 8021a277..32bd43f9 100644 --- a/app/src/api/services/autofill.ts +++ b/app/src/api/services/autofill.ts @@ -10,6 +10,7 @@ type GetAutofillOptionsFn = (value: string, all?: boolean) => Promise, + t?: ITask +): Promise { + 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 @@ -81,6 +112,9 @@ export async function processBuildingUpdate(buildingId: number, {attributes, use if(hasAnyOwnProperty(attributes, ['current_landuse_group'])) { 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'])) { attributes = await processDynamicsDemolishedBuildings(buildingId, attributes, t); } diff --git a/app/src/cc-config.json b/app/src/cc-config.json index dd1361ae..087f5561 100644 --- a/app/src/cc-config.json +++ b/app/src/cc-config.json @@ -1,7 +1,9 @@ { "cityName": "Cities", "projectBlurb": "Colouring {City Name} is part Colouring Cities.", + "githubURL": "https://github.com/colouring-cities/colouring-core", + "manualURL": "https://github.com/colouring-cities/manual/wiki/M3.-COLOURING-BRITAIN", "privacyStatement": "{Privacy statement goes here}", "initialMapPosition": [ 51.5245255, -0.1338422 ], diff --git a/app/src/cc-config.ts b/app/src/cc-config.ts index 54390139..df5d7a85 100644 --- a/app/src/cc-config.ts +++ b/app/src/cc-config.ts @@ -1,8 +1,12 @@ +import { StringNullableChain } from "lodash"; + export interface CCConfig { cityName: string; // City name (i.e. "Colouring {City Name}") projectBlurb: string; // Description used on homepage + 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 initialMapPosition: [number, number]; // Initial location of the map [latitude, longitude] diff --git a/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx b/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx index 318debf1..5ad08e6c 100644 --- a/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx +++ b/app/src/frontend/building/data-components/multi-data-entry/multi-data-entry.tsx @@ -69,83 +69,89 @@ export const MultiDataEntry: React.FC = ({ copy={copyable ? props.copy : undefined} />
+ { + values.length === 0 && !isEditing && +
+ +
+ } +
    { - values.length === 0 && !isEditing && + isEditing && isDisabled && values.length === 0 &&
    - +
    } -
      - { - values.map((val, i) => ( -
    • - edit(i, val)} + { + values.map((val, i) => ( +
    • + edit(i, val)} - maxLength={props.maxLength} - isUrl={props.isUrl} - valueTransform={props.valueTransform} + maxLength={props.maxLength} + isUrl={props.isUrl} + valueTransform={props.valueTransform} - autofill={props.autofill} - showAllOptionsOnEmpty={props.showAllOptionsOnEmpty} - /> - { - !isDisabled && -
      - -
      - } -
    • - )) - } - { - !isDisabled && -
    • - setNewValue(val)} - onConfirm={(_key, val) => addNew(val)} + autofill={props.autofill} + showAllOptionsOnEmpty={props.showAllOptionsOnEmpty} + /> + { + !isDisabled && +
      + +
      + } +
    • + )) + } + { + !isDisabled && +
    • + setNewValue(val)} + onConfirm={(_key, val) => addNew(val)} - maxLength={props.maxLength} - placeholder={props.placeholder} - isUrl={props.isUrl} - valueTransform={props.valueTransform} - confirmOnEnter={confirmOnEnter} + maxLength={props.maxLength} + placeholder={props.placeholder} + isUrl={props.isUrl} + valueTransform={props.valueTransform} + confirmOnEnter={confirmOnEnter} - autofill={props.autofill} - showAllOptionsOnEmpty={props.showAllOptionsOnEmpty} - confirmOnAutofillSelect={true} - /> - { - newValue != undefined && - <> -
      - -
      -
      - -
      - - } -
    • + autofill={props.autofill} + showAllOptionsOnEmpty={props.showAllOptionsOnEmpty} + confirmOnAutofillSelect={true} + /> + { + newValue != undefined && + <> +
      + +
      +
      + +
      + + } + }
diff --git a/app/src/frontend/building/data-components/pattern-data-entry.tsx b/app/src/frontend/building/data-components/pattern-data-entry.tsx index 9681e981..187fb0df 100644 --- a/app/src/frontend/building/data-components/pattern-data-entry.tsx +++ b/app/src/frontend/building/data-components/pattern-data-entry.tsx @@ -12,11 +12,20 @@ interface PatternDataEntryProps extends BaseDataEntryProps { */ pattern: string; maxLength?: number; + valueTransform?: (val: string) => string; } export const PatternDataEntry: React.FC = props => { 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 ( diff --git a/app/src/frontend/building/data-container.tsx b/app/src/frontend/building/data-container.tsx index c72fd216..e5d7604d 100644 --- a/app/src/frontend/building/data-container.tsx +++ b/app/src/frontend/building/data-container.tsx @@ -99,7 +99,9 @@ const withCopyEdit: (wc: React.ComponentType) => DataContaine 'likes_total', 'community_type_worth_keeping_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) { let fieldName = props.building == undefined ? undefined : props.building[key]; @@ -286,6 +288,13 @@ const withCopyEdit: (wc: React.ComponentType) => DataContaine this.doSubmit(edits); } + if (slug == 'typology_original_use'){ + const edits = { + 'typology_original_use_verified': true + }; + + this.doSubmit(edits); + } console.log(slug + " verify button clicked") } diff --git a/app/src/frontend/building/data-containers/age-history.tsx b/app/src/frontend/building/data-containers/age-history.tsx index 053108b0..00caa7c7 100644 --- a/app/src/frontend/building/data-containers/age-history.tsx +++ b/app/src/frontend/building/data-containers/age-history.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; 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 { DataEntryGroup } from '../data-components/data-entry-group'; import { DynamicsBuildingPane, DynamicsDataEntry } from './dynamics/dynamics-data-entry'; @@ -40,400 +40,49 @@ const AgeView: React.FunctionComponent = (props) => { const ageLinkUrl = `/${props.mode}/${Category.Age}/${props.building.building_id}`; const { historicData, historicDataSwitchOnClick, darkLightTheme } = useDisplayPreferences(); + const { historicMap, historicMapSwitchOnClick } = useDisplayPreferences(); const switchToSurvivalMapStyle = (e) => { e.preventDefault(); - - if (props.mapColourScale == "survival_status") { - props.onMapColourScale('date_year'); - historicDataSwitchOnClick(e); - } - else { - props.onMapColourScale('survival_status'); - historicDataSwitchOnClick(e); + props.onMapColourScale('survival_status'); + historicMapSwitchOnClick(e); + + if (historicData === 'enabled') { + historicDataSwitchOnClick(e); } } - if (props.building.date_source == "Expert knowledge of building" || - props.building.date_source == "Expert estimate from image" || - props.building.date_source == null - ){ - return ( - - - { + e.preventDefault(); + props.onMapColourScale('survival_status'); + historicDataSwitchOnClick(e); - 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} - /> - - + if (historicMap === 'enabled') { + historicMapSwitchOnClick(e); + } + } - - {(props.building.date_source == dataFields.date_source.items[0] || - props.building.date_source == dataFields.date_source.items[1] || - props.building.date_source == null) ? <> : - <> - - - } - - - - - - {(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) ? <> : - <> - - - } -
- - - - {(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) ? <> : - <> - - - } -
- - - - {(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) ? <> : - <> - - - } -
- - - - - - -
- -
-
- -
-
- -
-
-
- { - currentBuildingConstructionYear == undefined ? - To add historical records, fill in the Age data first. : - - <> - 0} - disallowNull={(building.demolished_buildings?.length ?? 0) > 0} + const switchToAgeMapStyle = (e) => { + e.preventDefault(); + + if (historicData === 'enabled') { + historicDataSwitchOnClick(e); + } + if (historicMap === 'enabled') { + historicMapSwitchOnClick(e); + } + + props.onMapColourScale('date_year'); + } + + const switchToStylePeriodMapStyle = (e) => { + e.preventDefault(); + props.onMapColourScale('typology_style_period') + } - onChange={props.onSaveChange} - mode={props.mode} - copy={props.copy} - /> - { - building.dynamics_has_demolished_buildings && - <> - - { - props.mode === 'view' && - Switch to edit mode to add/edit past building records - } - - } - - } -
- - This section is under development in collaboration with the historic environment sector. - Please let us know your suggestions on the discussion forum! (external link - save your edits first) - -
- -
- - 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. - -
- - - - {(props.building.survival_source == dataFields.survival_source_links[0] || - props.building.survival_source == dataFields.survival_source_links[1] || - props.building.survival_source == null) ? <> : - <> - - } -
-
- ); - }; return ( - + = (props) => { user_verified_as={props.user_verified.facade_year} verified_count={props.building.verified.facade_year} /> +
= (props) => { /> } +
+ + {(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) ? <> : + <> + + + } + {/**/} +
+ + {(props.mapColourScale == "typology_style_period") ? + + : + + } + + + + {(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) ? <> : + <> + + + } = (props) => { } - @@ -751,16 +493,31 @@ const AgeView: React.FunctionComponent = (props) => { Please let us know your suggestions on the discussion forum! (external link - save your edits first) - +
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.
- + {(historicMap === "enabled") ? + + : + + } + {(historicData === "enabled") ? + + : + + } = (props) => { }
+ + + This section is under development + +
+ + This section provides links to open digitised historical maps/mapping data that we are using in the Colouring Cities platform. + +
+ + +
); }; diff --git a/app/src/frontend/building/data-containers/community.tsx b/app/src/frontend/building/data-containers/community.tsx index 2b137ca7..6ee45f8a 100644 --- a/app/src/frontend/building/data-containers/community.tsx +++ b/app/src/frontend/building/data-containers/community.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import '../../map/map-button.css'; import withCopyEdit from '../data-container'; @@ -15,6 +15,7 @@ import SelectDataEntry from '../data-components/select-data-entry'; import Verification from '../data-components/verification'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import { useDisplayPreferences } from '../../displayPreferences-context'; +import DataEntry from '../data-components/data-entry'; /** * Community view/edit section @@ -38,192 +39,199 @@ const CommunityView: React.FunctionComponent = (props) => { } const { darkLightTheme } = useDisplayPreferences(); const worthKeepingReasonsNonEmpty = Object.values(props.building.community_type_worth_keeping_reasons ?? {}).some(x => x); - return <> - -
- - Note: We are currently only collecting data on non-residential buildings. - -
-
- {(props.building.is_domestic === "no" || props.building.is_domestic === "mixed domestic/non-domestic") ? - <> - + +
+ + Note: We are currently only collecting data on non-residential buildings. + +
+
+ {(props.building.is_domestic === "no" || props.building.is_domestic === "mixed domestic/non-domestic") ? + <> + + + : + <> + } + + {(props.mapColourScale == "typology_likes") ? + + : + + } + { + props.building.community_type_worth_keeping === true && + ({ + key, + label: definition.title + })) + } + + mode={props.mode} + /> + } +
+ - - : - <> - } - - {(props.mapColourScale == "typology_likes") ? - - : - - } - { - props.building.community_type_worth_keeping === true && - ({ - key, - label: definition.title - })) - } + {(props.mapColourScale == "community_local_significance_total") ? + + : + + } +
+ - } -
- + + For more information on current planning applications, refer to the Planning Controls category. + +
+ {(props.mapColourScale == "community_expected_planning_application_total") ? + + : + + } +
+
+ +
+ + 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. + +
+ + + + - {(props.mapColourScale == "community_local_significance_total") ? + + + + {(props.mapColourScale == "community_in_public_ownership") ? : - } -
- - {(props.mapColourScale == "community_expected_planning_application_total") ? - - : - - } - -
- -
- - 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. - -
- - - - - - - - {(props.mapColourScale == "community_in_public_ownership") ? - - : - - } -
- + + + ); }; const CommunityContainer = withCopyEdit(CommunityView); diff --git a/app/src/frontend/building/data-containers/construction.tsx b/app/src/frontend/building/data-containers/construction.tsx index 69f709a1..1111b606 100644 --- a/app/src/frontend/building/data-containers/construction.tsx +++ b/app/src/frontend/building/data-containers/construction.tsx @@ -1,6 +1,6 @@ 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 SelectDataEntry from '../data-components/select-data-entry'; import withCopyEdit from '../data-container'; @@ -9,6 +9,8 @@ import Verification from '../data-components/verification'; import { CategoryViewProps } from './category-view-props'; import InfoBox from '../../components/info-box'; 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 = [ 'Wood', @@ -21,23 +23,205 @@ const ConstructionMaterialsOptions = [ '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 */ const ConstructionView: React.FunctionComponent = (props) => { return ( + + + + + {(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) ? <> : + <> + + + } +
+ + + + {(props.building.construction_foundation_source_type == commonSourceTypes[0] || + props.building.construction_foundation_source_type == commonSourceTypes[1] || + props.building.construction_foundation_source_type == null) ? <> : + <> + + + } +
+ + + + {(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) ? <> : + <> + + + } +
+ + + + {(props.building.construction_irregularities_source_type == commonSourceTypes[0] || + props.building.construction_irregularities_source_type == commonSourceTypes[1] || + props.building.construction_irregularities_source_type == null) ? <> : + <> + + + } +
= (props) => user_verified={props.user_verified.hasOwnProperty("construction_core_material")} user_verified_as={props.user_verified.construction_core_material} verified_count={props.building.verified.construction_core_material} - /> + /> + {(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) ? <> : + <> + + + } +
+ + + + {(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) ? <> : + <> + + + } +
+ + + + {(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) ? <> : + <> + + + } +
+ + + + {(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) ? <> : + <> + + + } +
= (props) => user_verified={props.user_verified.hasOwnProperty("construction_roof_covering")} user_verified_as={props.user_verified.construction_roof_covering} verified_count={props.building.verified.construction_roof_covering} - /> -
- - + + {(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) ? <> : + <> + + + } + + + + + { + props.building.construction_decorative_features && + <> + + + + {(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) ? <> : + <> + + + } + + }
); diff --git a/app/src/frontend/building/data-containers/energy-performance.tsx b/app/src/frontend/building/data-containers/energy-performance.tsx index bdace92a..aa7f0971 100644 --- a/app/src/frontend/building/data-containers/energy-performance.tsx +++ b/app/src/frontend/building/data-containers/energy-performance.tsx @@ -11,6 +11,7 @@ import InfoBox from '../../components/info-box'; import { CategoryViewProps } from './category-view-props'; 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 EnergyCategoryOptions = ["A", "B", "C", "D", "E", "F", "G"]; const BreeamRatingOptions = [ @@ -25,9 +26,20 @@ const BreeamRatingOptions = [ * Sustainability view/edit section */ const SustainabilityView: React.FunctionComponent = (props) => { + + const currentYear = new Date().getFullYear(); + return ( - + + + + = (props) = user_verified_as={props.user_verified.sust_breeam_rating} verified_count={props.building.verified.sust_breeam_rating} /> - = (props) = user_verified_as={props.user_verified.sust_dec} verified_count={props.building.verified.sust_dec} /> - = (props) = onChange={props.onChange} /> - + + step={1} + min={1} + max={currentYear} + tooltip={dataFields.extension_year.tooltip} + /> - {(props.building.sust_retrofit_source_type == dataFields.sust_retrofit_source_type.items[0] || - props.building.sust_retrofit_source_type == dataFields.sust_retrofit_source_type.items[1] || - props.building.sust_retrofit_source_type == null) ? <> : + {(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) ? <> : <> } - {/*
- + + - */} + {props.building.energy_solar == null ? <> : + <> + + {(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) ? <> : + <> + + + } + + } - - - + + + {props.building.energy_green_roof == null ? <> : + <> + + {(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) ? <> : + <> + + + } + + }
); diff --git a/app/src/frontend/building/data-containers/land-use.tsx b/app/src/frontend/building/data-containers/land-use.tsx index 3cc4e429..56235feb 100644 --- a/app/src/frontend/building/data-containers/land-use.tsx +++ b/app/src/frontend/building/data-containers/land-use.tsx @@ -32,14 +32,14 @@ const UseView: React.FunctionComponent = (props) => { const { darkLightTheme } = useDisplayPreferences(); return ( - +
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)?
= (props) => { }
- + = (props) => { /> } +
{ props.mode != 'view' &&
diff --git a/app/src/frontend/building/data-containers/location.tsx b/app/src/frontend/building/data-containers/location.tsx index 94b63015..6df12b42 100644 --- a/app/src/frontend/building/data-containers/location.tsx +++ b/app/src/frontend/building/data-containers/location.tsx @@ -12,12 +12,14 @@ import SelectDataEntry from '../data-components/select-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 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 = (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 ( - + = (props) => { copy={props.copy} onChange={props.onChange} tooltip={dataFields.location_name.tooltip} - placeholder="https://..." - isUrl={true} + placeholder="" + isUrl={false} + disabled={true} /> = (props) => { mode='view' tooltip="Not yet activated.

For security reasons, we do not allow the use of free text boxes and are currently looking into alternative ways to collect this data." /> + + { + (props.building.location_name_link == null) ? <> : + + }
= (props) => { copy={props.copy} onChange={props.onChange} tooltip={dataFields.location_number.tooltip} + maxLength={5} /> = (props) => { copy={props.copy} onChange={props.onChange} maxLength={30} + disabled={true} /> = (props) => { copy={props.copy} onChange={props.onChange} maxLength={30} + disabled={true} /> = (props) => { mode={props.mode} copy={props.copy} onChange={props.onChange} + disabled={true} /> = (props) => { user_verified_as={props.user_verified.location_town} verified_count={props.building.verified.location_town} /> - x.toUpperCase()} + tooltip={dataFields.location_postcode.tooltip} /> = (props) => { }
- + = (props) => { onChange={props.onChange} disabled={true} /> + { + (props.building.ref_toid == null) ? <> : + + } +
- : + + } +
+ = (props) => { tooltip={dataFields.ref_osm_id.tooltip} maxLength={20} onChange={props.onChange} + pattern={osmIdentifierPattern} /> - { - (props.building.ref_osm_id == null) ? <> : -
- Source: {osm_url} -
- } = (props) => { user_verified_as={props.user_verified.ref_osm_id} verified_count={props.building.verified.ref_osm_id} /> + { + (props.building.ref_osm_id == null) ? <> : +
+ Source: {osm_url} +
+ }
= (props) => { /> } +
+
); diff --git a/app/src/frontend/building/data-containers/planning.tsx b/app/src/frontend/building/data-containers/planning.tsx index 370d51f3..138832a5 100644 --- a/app/src/frontend/building/data-containers/planning.tsx +++ b/app/src/frontend/building/data-containers/planning.tsx @@ -19,6 +19,8 @@ import { CategoryViewProps } from './category-view-props'; import { Category } from '../../config/categories-config'; import { useDisplayPreferences } from '../../displayPreferences-context'; 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 milisecondsInYear = 1000 * 60 * 60 * 24 * 365; @@ -63,10 +65,12 @@ const PlanningView: React.FunctionComponent = (props) => { } 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 currentYear = new Date().getFullYear(); + return ( - - + + This section provides data on active applications. We define these as applications with any activity in the last year.
@@ -85,49 +89,160 @@ const PlanningView: React.FunctionComponent = (props) => { : <> }
- - - This section provides data on past applications where available from the GLA, including those with no decision in over a year - - {props.building.planning_data ? - isArchived(item))} - messageOnMissingData={ - props.building.planning_data.length > 0 ? - "Only current application data is currently available for this site" - : - "No live planning data are currently available for this building from the Planning London Datahub." - } - /> - : <> - } - - - 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 Community section. - { - props.mapColourScale != "community_expected_planning_application_total" ? - - : - - } - + - - Further improvements to this feature are currently being made. + + {props.building.planning_crowdsourced_site_completion_status == null ? <> : + <> + + + + {(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) ? <> : + <> + + + } + + } + + + + + {props.building.planning_missing_data == null ? <> : + <> + + + } + + If you feel there are incorrect or missing data relating to this building please contact: + planningdata@London.gov.uk
+ + + This section provides data on past applications where available from the GLA, including those with no decision in over a year + + {props.building.planning_data ? + isArchived(item))} + messageOnMissingData={ + props.building.planning_data.length > 0 ? + "Only current application data is currently available for this site" + : + "No live planning data are currently available for this building from the Planning London Datahub." + } + /> + : <> + } + + + 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 Community section. + { + props.mapColourScale != "community_expected_planning_application_total" ? + + : + + } + + + Further improvements to this feature are currently being made. + + To view planning zone data for London click the buttons below. You may need to zoom out. @@ -141,41 +256,41 @@ const PlanningView: React.FunctionComponent = (props) => {
- +
+ { return "https://historicengland.org.uk/listing/the-list/list-entry/" + id + "?section=official-list-entry" } } - linkDescriptionFunction={(id: String) => { return "ID Link" } } + mode={props.mode} /> - - - - - { return "https://whc.unesco.org/en/list/" + id } } - linkDescriptionFunction={(id: String) => { return "ID Link" } } - /> - - - - - {/* - - - */} - - {props.building.planning_in_conservation_area_url === "" ? "Our CA map records this building as not being within a CA. To help us verify this, please click ‘verify’ or, if info is incorrect, please add the local authority’s CA appraisal link." : "" } - {props.building.planning_in_conservation_area_url === "identified as listed: please replace with links" ? "Our CA map records this building as being within a CA. To help us verify this information please add the local authority’s CA appraisal link and then click ‘verify’." : "" } - - {/* - - - */} - - - - -
- - - {/* will be titled "Other active application info (crowdsourced data)" once active" */} - - This category is not yet activated - Until this section is activated please report inaccuracies or problems on the Discussion Forum. - - {/* that is placeholder display, will be replaced by actual code */} -
-
-
    -
  • Year of completion if known
  • -
  • 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:
  • -
  • If any of the active planning applications are not mapped onto the correct site, please tick here
  • -
-
-
- { - /* - - - - - - - - on enabling switch it to UserOpinionEntry, remove value and restore userValue - */ - } - -
- - - This category is not yet activated. - - - This section is designed to provide information on land parcels and their ownership type. Can you help us to crowdsource this information? - - -
-
-
    -
  • What type of owner owns this land parcel?
  • -
-
-
- - {/* - + {(props.building.planning_heritage_at_risk == null || props.building.planning_heritage_at_risk == false) ? <> : + <> + + onChange={props.onChange} + placeholder="Please add relevant link here" + isUrl={true} + /> + + } +
+ + + {(props.building.planning_world_heritage_site == null || props.building.planning_world_heritage_site == false) ? <> : + <> + { return "https://whc.unesco.org/en/list/" + id } } + linkDescriptionFunction={(id: String) => { return "ID Link" } } + /> + + + } +
+ + + {(props.building.planning_local_list == null || props.building.planning_local_list == false) ? <> : + <> + + + + } +
+ + + {(props.building.planning_in_conservation_area == null || props.building.planning_in_conservation_area == false) ? <> : + <> + + {props.building.planning_in_conservation_area_url === "" ? "Our CA map records this building as not being within a CA. To help us verify this, please click ‘verify’ or, if info is incorrect, please add the local authority’s CA appraisal link." : "" } + {props.building.planning_in_conservation_area_url === "identified as listed: please replace with links" ? "Our CA map records this building as being within a CA. To help us verify this information please add the local authority’s CA appraisal link and then click ‘verify’." : "" } + + + } +
+ + + {(props.building.planning_in_apa == null || props.building.planning_in_apa == false) ? <> : + <> + + + + } +
+ + + {(props.building.planning_scientific_interest == null || props.building.planning_scientific_interest == false) ? <> : + <> + - */ + {(props.building.planning_scientific_interest_source_type == dataFields.planning_scientific_interest_source_type.items[0] || + props.building.planning_scientific_interest_source_type == dataFields.planning_scientific_interest_source_type.items[1] || + props.building.planning_scientific_interest_source_type == null) ? <> : + <> + + } -
+ + } +
+ + + {(props.building.planning_historic_area_assessment == null || props.building.planning_historic_area_assessment == false) ? <> : + <> + + + + } +
+ + + This section is designed to provide information on land parcels and their ownership type. Can you help us collect this information? + + + + + +
+ +
)}; diff --git a/app/src/frontend/building/data-containers/size.tsx b/app/src/frontend/building/data-containers/size.tsx index 02ab7344..763b2a5a 100644 --- a/app/src/frontend/building/data-containers/size.tsx +++ b/app/src/frontend/building/data-containers/size.tsx @@ -15,441 +15,469 @@ import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-e /** * Size view/edit section */ -const SizeView: React.FunctionComponent = (props) => ( - - - - - - - - - - {(props.building.size_storeys_source_type == commonSourceTypes[0] || - props.building.size_storeys_source_type == commonSourceTypes[1] || - props.building.size_storeys_source_type == null) ? <> : - <> = (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 ( + + + + + + + + + + - - } - - - - - - {(props.building.size_height_apex_source_type == commonSourceTypes[0] || - props.building.size_height_apex_source_type == commonSourceTypes[1] || - props.building.size_height_apex_source_type == null) ? <> : - <> : + <> + + } + + + - - } -
- - - - {(props.building.size_height_eaves_source_type == commonSourceTypes[0] || - props.building.size_height_eaves_source_type == commonSourceTypes[1] || - props.building.size_height_eaves_source_type == null) ? <> : - <> + - - } -
- - - - - - - {(props.building.size_floor_area_source_type == commonSourceTypes[0] || - props.building.size_floor_area_source_type == commonSourceTypes[1] || - props.building.size_floor_area_source_type == null) ? <> : - <> : + <> + + } +
+ - - } -
- - - - - {(props.building.size_width_frontage_source_type == commonSourceTypes[0] || - props.building.size_width_frontage_source_type == commonSourceTypes[1] || - props.building.size_width_frontage_source_type == null) ? <> : - <> + - - } -
- - - - {(props.building.size_plot_area_total_source_type == commonSourceTypes[0] || - props.building.size_plot_area_total_source_type == commonSourceTypes[1] || - props.building.size_far_ratio_source_type == null) ? <> : - <> : + <> + + } +
+ + - - } -
- - - - {(props.building.size_far_ratio_source_type == commonSourceTypes[0] || - props.building.size_far_ratio_source_type == commonSourceTypes[1] || - props.building.size_far_ratio_source_type == null) ? <> : - <> + - - } -
- - - - {(props.building.size_parcel_geometry_source_type == commonSourceTypes[0] || - props.building.size_parcel_geometry_source_type == commonSourceTypes[1] || - props.building.size_parcel_geometry_source_type == null) ? <> : - <> + + {(props.building.size_floor_area_source_type == commonSourceTypes[0] || + props.building.size_floor_area_source_type == commonSourceTypes[1] || + props.building.size_floor_area_source_type == null) ? <> : + <> + + } +
+ + + + + {(props.building.size_width_frontage_source_type == commonSourceTypes[0] || + props.building.size_width_frontage_source_type == commonSourceTypes[1] || + props.building.size_width_frontage_source_type == null) ? <> : + <> + + } +
+ + + + {(props.building.size_plot_area_total_source_type == commonSourceTypes[0] || + props.building.size_plot_area_total_source_type == commonSourceTypes[1] || + props.building.size_far_ratio_source_type == null) ? <> : + <> + + } +
+ + + + {(props.building.size_far_ratio_source_type == commonSourceTypes[0] || + props.building.size_far_ratio_source_type == commonSourceTypes[1] || + props.building.size_far_ratio_source_type == null) ? <> : + <> + + } +
+ - - } -
-
-); + + + {(props.building.size_parcel_geometry_source_type == commonSourceTypes[0] || + props.building.size_parcel_geometry_source_type == commonSourceTypes[1] || + props.building.size_parcel_geometry_source_type == null) ? <> : + <> + + } +
+
+ ) +}; const SizeContainer = withCopyEdit(SizeView); export default SizeContainer; diff --git a/app/src/frontend/building/data-containers/street-context.tsx b/app/src/frontend/building/data-containers/street-context.tsx index f0e85c38..351c4ddb 100644 --- a/app/src/frontend/building/data-containers/street-context.tsx +++ b/app/src/frontend/building/data-containers/street-context.tsx @@ -1,28 +1,25 @@ import React, { Fragment } from 'react'; -import InfoBox from '../../components/info-box'; import { commonSourceTypes, dataFields } from '../../config/data-fields-config'; import DataEntry from '../data-components/data-entry'; import NumericDataEntry from '../data-components/numeric-data-entry'; - import withCopyEdit from '../data-container'; - import { CategoryViewProps } from './category-view-props'; import { DataEntryGroup } from '../data-components/data-entry-group'; import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import SelectDataEntry from '../data-components/select-data-entry'; 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 = (props) => ( +const StreetContextView: React.FunctionComponent = (props) => ( - - + = (props) => ( user_verified_as={props.user_verified.context_front_garden} verified_count={props.building.verified.context_front_garden} /> - = (props) => ( copy={props.copy} onChange={props.onChange} tooltip={dataFields.context_back_garden.tooltip} - //placeholder={dataFields.context_back_garden.example} - options={dataFields.context_back_garden.items} /> = (props) => ( user_verified_as={props.user_verified.context_back_garden} verified_count={props.building.verified.context_back_garden} /> - = (props) => ( copy={props.copy} onChange={props.onChange} tooltip={dataFields.context_flats_garden.tooltip} - //placeholder={dataFields.context_flats_garden.example} - options={dataFields.context_flats_garden.items} /> = (props) => ( user_verified_as={props.user_verified.context_flats_garden} verified_count={props.building.verified.context_flats_garden} /> -
= (props) => ( /> } +
+ + + + {(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) ? <> : + <> + + } +
+ + + + {(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) ? <> : + <> + + }
- + + +
= (props) => ( }
- - + - - - {(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) ? <> : - <> - - } -
- - - - {(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) ? <> : - <> - - }
); -const StreetscapeContainer = withCopyEdit(StreetscapeView); +const StreetContextContainer = withCopyEdit(StreetContextView); -export default StreetscapeContainer; +export default StreetContextContainer; diff --git a/app/src/frontend/building/data-containers/team.tsx b/app/src/frontend/building/data-containers/team.tsx index 3f9a7c32..63779bd6 100644 --- a/app/src/frontend/building/data-containers/team.tsx +++ b/app/src/frontend/building/data-containers/team.tsx @@ -4,9 +4,9 @@ import { commonSourceTypes, dataFields } from '../../config/data-fields-config'; import SelectDataEntry from '../data-components/select-data-entry'; import NumericDataEntry from '../data-components/numeric-data-entry'; 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 { DataEntryGroup } from '../data-components/data-entry-group'; +import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; import withCopyEdit from '../data-container'; @@ -21,57 +21,7 @@ const TeamView: React.FunctionComponent = (props) => { const currentBuildingConstructionYear = building.date_year || undefined; return (
- - - - - {(props.building.date_source == dataFields.date_source.items[0] || - props.building.date_source == dataFields.date_source.items[1] || - props.building.date_source == null) ? <> : - <> - - - } -
+ = (props) => { onChange={props.onChange} tooltip={dataFields.has_extension.tooltip} /> - {props.building.has_extension ? ( + {props.building.has_extension!=null && !props.building.has_extension ? ( <> = (props) => { ) : (null)} - + = (props) => { tooltip={dataFields.landowner.tooltip} placeholder="" editableEntries={true} + disabled={true} /> = (props) => { user_verified_as={props.user_verified.landowner} verified_count={props.building.verified.landowner} /> + = (props) => { } - + = (props) => { tooltip={dataFields.developer_name.tooltip} placeholder="" editableEntries={true} + disabled={true} /> = (props) => { user_verified_as={props.user_verified.developer_name} verified_count={props.building.verified.developer_name} /> + = (props) => { } - + = (props) => { tooltip={dataFields.designers.tooltip} placeholder="" editableEntries={true} + disabled={true} /> = (props) => { user_verified_as={props.user_verified.designers} verified_count={props.building.verified.designers} /> - + = (props) => { /> } -
- - - {props.building.designer_awards ? ( - <> +
+ + + + - - - ) : (null) - } + {(props.building.builder_source_type == commonSourceTypes[0] || + props.building.builder_source_type == commonSourceTypes[1] || + props.building.builder_source_type == null) ? <> : + <> + + + } - - - - - {(props.building.builder_source_type == commonSourceTypes[0] || - props.building.builder_source_type == commonSourceTypes[1] || - props.building.builder_source_type == null) ? <> : - <> + + + + {props.building.designer_awards ? ( + <> - - } - + /> + + + ) : (null) + } + ); }; diff --git a/app/src/frontend/building/data-containers/typology.tsx b/app/src/frontend/building/data-containers/typology.tsx index 01ab08f6..f1d46d60 100644 --- a/app/src/frontend/building/data-containers/typology.tsx +++ b/app/src/frontend/building/data-containers/typology.tsx @@ -1,6 +1,6 @@ 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 NumericDataEntry from '../data-components/numeric-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 InfoBox from '../../components/info-box'; import { DataEntryGroup } from '../data-components/data-entry-group'; - -const AttachmentFormOptions = [ - "Detached", - "Semi-Detached", - "End-Terrace", - "Mid-Terrace" -]; +import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry'; +import { useDisplayPreferences } from '../../displayPreferences-context'; /** * Type view/edit section */ const TypeView: React.FunctionComponent = (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 ( - + + {(props.mapColourScale == "typology_classification") ? + + : + + } + + + + {(props.building.typology_classification_source_type == commonSourceTypes[0] || + props.building.typology_classification_source_type == commonSourceTypes[1] || + props.building.typology_classification_source_type == null) ? <> : + <> + + + } + + + {/*(props.mapColourScale == "typology_style_period") ? + + : + + */} + + {/* + */} +
+ To edit the architectural style box, and to see the data mapped, please go to Age & History. +
+ {/* + {(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) ? <> : + <> + + + } */} +
+ + {(props.mapColourScale == "typology_dynamic_classification") ? + + : + + } + + + + {(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) ? <> : + <> + + + } + + + {(props.mapColourScale == "original_landuse") ? + + : + + } + + + + {(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) ? <> : + <> + + + } +
+ { + props.mode != 'view' && +
+
+ + Below is a more general classification for the original land use of this building, automatically derived from the information above. + +
+
+ } + +
+ + {(props.mapColourScale == "building_attachment_form") ? + + : + + } = (props) => { user_verified_as={props.user_verified.building_attachment_form} verified_count={props.building.verified.building_attachment_form} /> - - -
- - - - + {(props.building.building_attachment_source_type == commonSourceTypes[0] || + props.building.building_attachment_source_type == commonSourceTypes[1] || + props.building.building_attachment_source_type == null) ? <> : + <> + + + }
- - - + {/*} + = (props) => { value="" mode='view' /> + {/* = (props) => { mode={props.mode} copy={props.copy} onChange={props.onChange} - /> */} - + />//*} + */}
); }; diff --git a/app/src/frontend/building/sidebar.css b/app/src/frontend/building/sidebar.css index 5f915241..74bfe22f 100644 --- a/app/src/frontend/building/sidebar.css +++ b/app/src/frontend/building/sidebar.css @@ -183,4 +183,17 @@ .source-url { padding-top: 0px; padding-bottom: 5px; +} + +dd { + margin-bottom: 15px; +} + +.uprn-list { + margin-top: 10px; + margin-bottom: 0px; +} + +.uprn-list li { + margin-bottom: 10px; } \ No newline at end of file diff --git a/app/src/frontend/config/categories-config.ts b/app/src/frontend/config/categories-config.ts index 06906091..dd021270 100644 --- a/app/src/frontend/config/categories-config.ts +++ b/app/src/frontend/config/categories-config.ts @@ -98,7 +98,7 @@ export const categoriesConfig: {[key in Category]: CategoryDefinition} = { slug: 'typology', name: 'Typology', 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]: { slug: 'land-use', diff --git a/app/src/frontend/config/category-maps-config.ts b/app/src/frontend/config/category-maps-config.ts index 4f9dfd80..ab62917d 100644 --- a/app/src/frontend/config/category-maps-config.ts +++ b/app/src/frontend/config/category-maps-config.ts @@ -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', legend: { @@ -299,18 +318,91 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} = ] }, }], - [Category.Typology]: [{ - mapStyle: 'building_attachment_form', - legend: { - title: 'Adjacency/Configuration', - elements: [ - { color: "#f2a2b9", text: "Detached" }, - { color: "#ab8fb0", text: "Semi-Detached" }, - { color: "#3891d1", text: "End-Terrace" }, - { color: "#226291", text: "Mid-Terrace" } - ] + [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', + legend: { + title: 'Attachment/Adjacency', + elements: [ + { color: "#f2a2b9", text: "Detached" }, + { color: "#ab8fb0", text: "Semi-Detached" }, + { color: "#3891d1", text: "End-Terrace" }, + { color: "#226291", text: "Mid-Terrace" } + ] + }, + }, + ], [Category.LandUse]: [ { mapStyle: 'landuse', @@ -338,11 +430,11 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} = { mapStyle: 'is_domestic', legend: { - title: 'Domestic building', + title: 'Residential building', elements: [ - { color: '#f7ec25', text: 'Domestic' }, + { color: '#f7ec25', text: 'Residential' }, { color: '#fc9b2a', text: 'Mixed' }, - { color: '#ff2121', text: 'Non-domestic' }, + { color: '#ff2121', text: 'Non-residential' }, ] } } diff --git a/app/src/frontend/config/category-ui-config.ts b/app/src/frontend/config/category-ui-config.ts index 64d751e3..fdc2d332 100644 --- a/app/src/frontend/config/category-ui-config.ts +++ b/app/src/frontend/config/category-ui-config.ts @@ -7,7 +7,7 @@ import ResilienceContainer from '../building/data-containers/resilience'; import LocationContainer from '../building/data-containers/location'; import PlanningContainer from '../building/data-containers/planning'; 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 TeamContainer from '../building/data-containers/team'; import TypeContainer from '../building/data-containers/typology'; @@ -22,7 +22,7 @@ export const categoryUiConfig: {[key in Category]: DataContainerType} = { [Category.Age]: AgeContainer, [Category.Size]: SizeContainer, [Category.Construction]: ConstructionContainer, - [Category.StreetContext]: StreetscapeContainer, + [Category.StreetContext]: StreetContextContainer, [Category.Team]: TeamContainer, [Category.Planning]: PlanningContainer, [Category.EnergyPerformance]: SustainabilityContainer, diff --git a/app/src/frontend/config/data-fields-config.ts b/app/src/frontend/config/data-fields-config.ts index ecb765cd..c5e60342 100644 --- a/app/src/frontend/config/data-fields-config.ts +++ b/app/src/frontend/config/data-fields-config.ts @@ -6,15 +6,18 @@ let ccconfig: CCConfig = require('../../cc-config.json') * Common list of Source Types, used in multiple menus */ export const commonSourceTypes = [ - "Assessed by eye", - "Assessed using expert knowledge of building or building type", - "Assessed using streetview photographs or satellite imagery", + "Assessed by eye/personal knowledge of the building", + "Assessed using professional knowledge of building or building type", + "Assessed using streetview photographs, satellite imagery or maps", "Assessed by specialist emergency group", + "Current government record/database", "Live streamed from a government source", - "Current government record/dataset", - "Independently managed public database", + "Open database", + "Other independently managed public database", "Commercial database", "Inferred computationally using existing open attribute data", + "Synthetic data", + "Other" ]; /** @@ -144,8 +147,14 @@ export const buildingUserFields = { export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ location_name: { category: Category.Location, - title: "Building name (non-domestic)", - tooltip: "Link to a website with the name of the building.

(For security reasons, we currently only collect the names of non-residential buildings).", + title: "Building name (non-residential)", + tooltip: "The name of the building.

(For security reasons, we currently only collect the names of non-residential buildings).", + example: "Broadcasting House", + }, + location_name_link: { + category: Category.Location, + title: "Building name link", + tooltip: "Link to a website with the name of the building.", example: "https://en.wikipedia.org/wiki/Palace_of_Westminster", }, location_number: { @@ -156,7 +165,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, location_street: { category: Category.Location, - title: "Street", + title: "Street name", example: "Gower Street", //tooltip: , }, @@ -176,7 +185,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ category: Category.Location, title: "Area code/"+ccconfig.postcode, example: "W1W 6TR", - //tooltip: , + tooltip: "Correctly formatted UK postcode, i.e. NW1 2FB", }, location_address_source: { category: Category.Location, @@ -194,9 +203,15 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ ref_toid: { category: Category.Location, title: "Building footprint ID", - tooltip: "Ordnance Survey Topography Layer ID (TOID) [link]", + tooltip: "Ordnance Survey Topography Layer ID (TOID)", example: "", }, + location_alternative_footprint_links: { + category: Category.Location, + title: "Alternative open building footprint links", + tooltip: "Links to alternative building footprint datasets (include the direct link to the footprint of this building where possible).", + example: ["", "", ""], + }, /** * UPRNs is not part of the buildings table, but the string fields @@ -205,7 +220,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ uprns: { category: Category.Location, title: "Unique Property Reference Number(s) (UPRN)", - tooltip: "Unique Property Reference Numbers (to be filled automatically) [LINK]", + tooltip: "Unique Property Reference Number(s) (UPRN) (derived automatically)", example: [{ uprn: "", parent_uprn: "" }, { uprn: "", parent_uprn: "" }], }, @@ -218,11 +233,10 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ ], }, - ref_osm_id: { category: Category.Location, title: "OpenStreetMap ID", - tooltip: "OpenStreetMap feature ID", + tooltip: "OpenStreetMap building ('way') ID - Numerical string of up to 9 characters", example: "", }, location_latitude: { @@ -267,15 +281,16 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ title: "Source type", tooltip: "Source for the current land use", example: "", - items: [ - "Expert/personal knowledge of building", - "Online streetview image", - "Open planning authority dataset", - "Open property tax dataset", - "Open housing dataset", - "Open address dataset", - "Other" - ], + items: commonSourceTypes + // items: [ + // "Expert/personal knowledge of building", + // "Online streetview image", + // "Open planning authority dataset", + // "Open property tax dataset", + // "Open housing dataset", + // "Open address dataset", + // "Other" + // ], }, current_landuse_source_detail: { category: Category.LandUse, @@ -296,26 +311,29 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, building_attachment_form: { category: Category.Typology, - title: "Attachment type/adjacency", + title: "Which description best explains the way the building is attached to others?", tooltip: "We have prepopulated these based on their current attachment. A building can either be detached, semi-detached or part of a terrace (middle or end)", example: "", + items: [ + "Detached", + "Semi-Detached", + "End-Terrace", + "Mid-Terrace" + ] }, - date_change_building_use: { - category: Category.Typology, - title: "When did use change?", - tooltip: "This is the date the building stopped being used for for the function it was built for. I.e. if it was Victorian warehouse which is now an office this would be when it became an office or if it was something before that, maybe a garage then the date that happened", - example: 1920, - }, - /** - * original_building_use does not exist in database yet. - * Slug needs to be adjusted if the db column will be named differently - */ - original_building_use: { - category: Category.Typology, - title: "Original building use", - tooltip: "What was the building originally used for when it was built?", + building_attachment_source_type: { + category: Category.Age, + title: "Source type", + tooltip: "Source type for the building data above", + items: commonSourceTypes, example: "", }, + building_attachment_source_links: { + category: Category.Age, + title: "Source link(s)", + tooltip: "URL for data reference", + example: ["", "", ""], + }, size_roof_shape: { category: Category.Typology, title: "Roof type", @@ -342,13 +360,13 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, facade_year: { category: Category.Age, - title: "Date of front of building", + title: "Date of front of building (best estimate)", tooltip: "Best estimate", example: 1900, }, date_source: { category: Category.Age, - title: "Source type", + title: "Historical source type", tooltip: "Source type for the building dates above", items: [ "Expert knowledge of building", @@ -375,6 +393,19 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ example: "", }, date_link: { + category: Category.Age, + title: "Historical source link(s)", + tooltip: "URL for age and date reference", + example: ["", "", ""], + }, + date_source_type: { + category: Category.Age, + title: "Source type", + tooltip: "Source type for the building dates above", + items: commonSourceTypes, + example: "", + }, + date_source_links: { category: Category.Age, title: "Source link(s)", tooltip: "URL for age and date reference", @@ -416,7 +447,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ category: Category.Size, title: "Height to apex (m)", example: 100.5, - tooltip: "i.e. the highest part of the roof.", + tooltip: "i.e. the highest part of the roof (in meters).", }, size_height_apex_source_type: { category: Category.Team, @@ -435,7 +466,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ category: Category.Size, title: "Height to eaves (m)", example: 20.33, - tooltip: "i.e. to where the top of the wall meets the roof", + tooltip: "i.e. to where the top of the wall meets the roof (in meters)", }, size_height_eaves_source_type: { category: Category.Team, @@ -479,7 +510,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ category: Category.Size, title: "Frontage width (m)", example: 12.2, - //tooltip: , + tooltip: "Size of the frontage of the building (in meters)", }, size_width_frontage_source_type: { category: Category.Team, @@ -562,10 +593,23 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ construction_core_material: { category: Category.Construction, - title: "Core material", + title: "What is the main structural material thought to be?", tooltip: "The main structural material", example: "", }, + construction_core_material_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of main structural material data", + example: "", + items: commonSourceTypes + }, + construction_core_material_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for main structural material data source(s)", + example: ["", "", ""], + }, construction_secondary_materials: { category: Category.Construction, @@ -576,26 +620,282 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ construction_roof_covering: { category: Category.Construction, - title: "Main roof covering", - tooltip: 'Main roof covering material', + title: "What is the main roof covering?", + tooltip: '', example: "", + items: [ + 'Slate', + 'Clay Tile', + 'Wood', + 'Asphalt', + 'Iron or Steel', + 'Other Metal', + 'Other Natural Material', + 'Other Man-Made Material' + ] + }, + construction_roof_covering_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of roof covering data", + example: "", + items: commonSourceTypes + }, + construction_roof_covering_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for roof covering data source(s)", + example: ["", "", ""], + }, + construction_structural_system: { + category: Category.Construction, + title: "What type of structural system does the building appear to have?", + tooltip: "Refer to GEM Taxonomy [LINK]", + example: "Solid masonry walls supporting the roof", + items: [ + "Solid masonry walls supporting the roof", + "A lateral load resisting structure (e.g. concrete or wooden frame)", + "Other" + ] + }, + construction_structural_system_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of structural system data", + example: "", + items: commonSourceTypes + }, + construction_structural_system_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for structural system data source(s)", + example: ["", "", ""], + }, + construction_foundation: { + category: Category.Construction, + title: "What is the foundation system thought to be", + tooltip: "Refer to GEM Taxonomy [LINK]", + example: "Deep Foundations with lateral support", + items: [ + "Shallow foundations with no lateral support", + "Shallow foundations with lateral support", + "Deep foundations with no lateral support", + "Deep Foundations with lateral support", + "Other" + ] + }, + construction_foundation_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of foundation system data", + example: "", + items: commonSourceTypes + }, + construction_foundation_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for foundation system data source(s)", + example: ["", "", ""], + }, + construction_roof_shape: { + category: Category.Construction, + title: "What kind of roof shape does the building have?", + tooltip: "Refer to GEM Taxonomy [LINK]", + example: "Pitched with gable ends", + items: [ + "Flat", + "Pitched with gable ends", + "Pitched and hipped", + "Pitched with dormers", + "Monopitch", + "Sawtooth", + "Curved", + "Complex regular", + "Complex irregular", + "Other" + ] + }, + construction_roof_shape_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of roof shape data", + example: "", + items: commonSourceTypes + }, + construction_roof_shape_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for roof shape data source(s)", + example: ["", "", ""], + }, + construction_irregularities: { + category: Category.Construction, + title: "Are there any structural irregularities in the shape of the building?", + tooltip: "i.e. Is one side higher than other? - Refer to GEM Taxonomy [LINK]", + example: "No irregularities", + items: [ + "Vertical irregularities", + "Horizontal irregularities", + "No irregularities" + ] + }, + construction_irregularities_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of irregularity data", + example: "", + items: commonSourceTypes + }, + construction_irregularities_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for irregularity data source(s)", + example: ["", "", ""], + }, + construction_decorative_features: { + category: Category.Construction, + title: "Are there decorative features/mouldings integrated into the facade ?", + example: true, + tooltip: "", + }, + construction_decorative_feature_materials: { + category: Category.Construction, + title: "What are these decorative features mainly made of?", + tooltip: "", + example: "Concrete", + items: [ + "Wood", + "Clay", + "Concrete", + "Glass", + "Metal", + "Other" + ] + }, + construction_decorative_feature_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of roof shape data", + example: "", + items: commonSourceTypes + }, + construction_decorative_feature_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for roof shape data source(s)", + example: ["", "", ""], + }, + construction_internal_wall: { + category: Category.Construction, + title: "What is the main internal wall material thought to be?", + tooltip: '', + example: "", + items: [ + 'Brick', + 'Stone', + 'Concrete blocks', + 'Concrete slabs/panels', + 'Wood', + 'Metal', + 'Adobe/Earth', + 'Glass', + 'Plastic', + 'Stucco on light framing', + 'Vegetative (straw, matting etc)', + 'Cement based boards', + 'Other' + ] + }, + construction_internal_wall_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of internal wall data", + example: "", + items: commonSourceTypes + }, + construction_internal_wall_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for internal wall data source(s)", + example: ["", "", ""], + }, + construction_external_wall: { + category: Category.Construction, + title: "What is the main external wall material thought to be?", + tooltip: '', + example: "", + items: [ + 'Brick', + 'Stone', + 'Concrete blocks', + 'Concrete slabs/panels', + 'Wood', + 'Metal', + 'Adobe/Earth', + 'Glass', + 'Plastic', + 'Stucco on light framing', + 'Vegetative (straw, matting etc)', + 'Cement based boards', + 'Other' + ] + }, + construction_external_wall_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of external wall data", + example: "", + items: commonSourceTypes + }, + construction_external_wall_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for external wall data source(s)", + example: ["", "", ""], + }, + construction_ground_floor: { + category: Category.Construction, + title: "What is the main ground floor material thought to be?", + tooltip: '', + example: "", + items: [ + 'Masonry', + 'Earthen', + 'Concrete', + 'Wood', + 'Metal', + 'Other', + ] + }, + construction_ground_floor_source_type: { + category: Category.Construction, + title: "Source type", + tooltip: "Source of ground floor material data", + example: "", + items: commonSourceTypes + }, + construction_ground_floor_source_links: { + category: Category.Construction, + title: "Source links", + tooltip: "URL(s) for ground floor material data source(s)", + example: ["", "", ""], }, sust_breeam_rating: { category: Category.EnergyPerformance, - title: "Official environmental quality rating", + title: "Residential energy rating", tooltip: ccconfig.energy_rating, example: "", }, sust_dec: { category: Category.EnergyPerformance, - title: "Non-domestic Building Energy Rating", + title: "Non-residential Building Energy Rating", tooltip: "Display Energy Certificate (DEC) Any public building should have (and display) a DEC. Showing how the energy use for that building compares to other buildings with same use", example: "G", }, sust_aggregate_estimate_epc: { category: Category.EnergyPerformance, - title: "Domestic Building Energy Rating", + title: "Residential Building Energy Rating", tooltip: "Energy Performance Certificate (EPC) Any premises sold or rented is required to have an EPC to show how energy efficient it is. Only buildings rate grade E or higher may be rented", example: "", }, @@ -682,13 +982,26 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ category: Category.Planning, title: "Has the work on this site been completed?", example: true, - //tooltip: , + tooltip: "Has the work on this site been completed?", }, planning_crowdsourced_site_completion_year: { category: Category.Planning, - title: "Year of completion if known", + title: "Year of completion (best estimate)", example: 2022, - //tooltip: , + tooltip: "Year of completion, if known", + }, + planning_crowdsourced_site_completion_source_type: { + category: Category.Team, + title: "Source type", + tooltip: "Source type for work complete data", + example: "", + items: commonSourceTypes + }, + planning_crowdsourced_site_completion_source_links: { + category: Category.Team, + title: "Source links", + tooltip: "URL(s) for work complete data", + example: ["", "", ""], }, planning_crowdsourced_planning_id: { category: Category.Planning, @@ -696,6 +1009,12 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ example: "1112/QWERTY", //tooltip: , }, + planning_in_conservation_area: { + category: Category.Planning, + title: "Is the building in a conservation area?", + tooltip: "Is the building in a conservation area?", + example: true, + }, planning_in_conservation_area_id: { category: Category.Planning, title: "Conservation Area identifier", @@ -716,7 +1035,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, planning_list_grade: { category: Category.Planning, - title: "What is its rating?", + title: "What is its protection rating?", example: "II", //tooltip: , }, @@ -726,9 +1045,15 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ example: "", //tooltip: , }, + planning_world_heritage_site: { + category: Category.Planning, + title: "Is the building on a World Heritage Site?", + tooltip: "Is the building on a World Heritage Site", + example: true, + }, planning_world_list_id: { category: Category.Planning, - title: "If the building is on a World Heritage Site please add the ID:", + title: "If the building is on a World Heritage Site please add the ID:", example: "488", //tooltip: , }, @@ -738,21 +1063,39 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ example: "", //tooltip: , }, + planning_in_apa: { + category: Category.Planning, + title: "Is the building in an area of archaeological priority?", + tooltip: "Is the building in an area of archaeological priority?", + example: true, + }, planning_in_apa_url: { category: Category.Planning, - title: "Is it in an area if archaeological priority?", + title: "Is the building in an area of archaeological priority?", example: "", //tooltip: , }, + planning_local_list: { + category: Category.Planning, + title: "Is the building a locally listed heritage asset?", + tooltip: "Is the building a locally listed heritage asset?", + example: true, + }, planning_local_list_url: { category: Category.Planning, - title: "Is it a locally listed heritage asset?", + title: "Is the building a locally listed heritage asset?", example: "", //tooltip: , }, + planning_historic_area_assessment: { + category: Category.Planning, + title: "Does the building have any other type of designation?", + tooltip: "Does the building have any other type of designation?", + example: true, + }, planning_historic_area_assessment_url: { category: Category.Planning, - title: "Does it have any other kind of historic area assessment?", + title: "Does it have any other type of designation?", example: "", //tooltip: , }, @@ -762,10 +1105,49 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ example: true, //tooltip: , }, + planning_missing_data: { + category: Category.Planning, + title: "Is information on a planning application relating to this building missing?", + tooltip: "Is information on a planning application relating to this building missing?", + example: true, + }, + planning_missing_data_links: { + category: Category.Team, + title: "Source links", + tooltip: "URL(s) for missing planning information", + example: ["", "", ""], + }, + planning_heritage_at_risk: { + category: Category.Planning, + title: "Is the building on a heritage at risk register?", + tooltip: "Is the building on a heritage at risk register?", + example: true, + }, + planning_scientific_interest: { + category: Category.Planning, + title: "Is the building on a site of special scientific interest?", + tooltip: "Is the building on a site of special scientific interest?", + example: true, + }, + planning_scientific_interest_source_type: { + category: Category.Team, + title: "Source type", + tooltip: "Source type for site of special scientific interest data", + example: "", + items: commonSourceTypes + }, + planning_scientific_interest_source_links: { + category: Category.Team, + title: "Source links", + tooltip: "URL(s) for site of special scientific interest data", + example: ["", "", ""], + }, + + is_domestic: { category: Category.Team, - title: "Is the building a home/domestic building?", - tooltip: "Note: Homes used as offices for working from home should be classified as domestic.", + title: "Is the building a home/residential building?", + tooltip: "Note: Homes used as offices for working from home should be classified as residential.", example: "mixed domestic/non-domestic", items: [ "Yes", @@ -776,14 +1158,14 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ is_domestic_source: { category: Category.Team, title: "Source type", - tooltip: "Source of domestic/non-domestic data", + tooltip: "Source of residential/non-residential data", example: "", items: commonSourceTypes }, is_domestic_links: { category: Category.Team, title: "Source links", - tooltip: "URL(s) for domestic/non-domestic data source(s)", + tooltip: "URL(s) for residential/non-residential data source(s)", example: ["", "", ""], }, likes_total: { @@ -811,19 +1193,19 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ community_activities_current: { category: Category.Community, - title: "Are activities open to the community currently taking place in the building?", + title: "Is this building currently used for community activities?", tooltip: "E.g. youth club, place of worship, GP surgery, pub", example: true }, community_activities: { category: Category.Community, - title: "Has this ever been used for community activities in the past?", + title: "If not been used for community activities in the past?", tooltip: "E.g. youth club, place of worship, GP surgery, pub", example: true }, community_activities_always: { category: Category.Community, - title: "Has the building always been used for community activities?", + title: "If in community use now, has it always been used for community activities?", tooltip: "E.g. youth club, place of worship, GP surgery, pub", example: true }, @@ -837,11 +1219,15 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ title: "Is the building in public/community ownership?", example: "Privately owned (non-corporate)", items: [ - 'Government-owned', - 'Charity-owned', - 'Community-owned/cooperative', - 'Owned by other non-profit body', - 'Not in public/community ownership' + "Public/State body", + "Public body with Private company", + "Charity", + "Community group/Cooperative", + "Other non-profit body", + "Privately owned company", + "Privately owned offshore company", + "Private individual", + "Other", ] }, community_public_ownership_sources: { @@ -892,8 +1278,8 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, has_extension: { category: Category.Team, - title: "Was a later extension added?", - tooltip: "", + title: "Does this information relate to the original main building?", + tooltip: "If the data in this section relates to the original main building, select \"yes\". If the data relates to a later extension/ redevelopment, select \"no\".", example: false }, extension_year: { @@ -934,7 +1320,13 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ developer_name: { category: Category.Team, title: "Who were the developer(s)?", - tooltip: "Free text. First name, space, then Last name", + tooltip: "Name(s) of the building's developers.

Free-text entry disabled for security reasons.", + example: ["", "", ""], + }, + developer_links: { + category: Category.Team, + title: "Developer link(s)", + tooltip: "Link(s) to webpage(s) explaining who developed the building.", example: ["", "", ""], }, developer_source_type: { @@ -953,7 +1345,13 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ landowner: { category: Category.Team, title: "Landowner(s) at time of construction", - tooltip: "Free text. First name, space, then Last name", + tooltip: "Land owner when the building was constructed.

Free-text entry disabled for security reasons.

For info on current land ownership, see 'Planning Controls'.", + example: ["", "", ""], + }, + landowner_links: { + category: Category.Team, + title: "Landowner link(s)", + tooltip: "Link(s) to webpage(s) explaining who owned the land when when the building was built.", example: ["", "", ""], }, landowner_source_type: { @@ -972,7 +1370,13 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ designers: { category: Category.Team, title: "Who were the main designer(s)?", - tooltip: "Free text. First name, space, then Last name", + tooltip: "Free-text entry disabled for security reasons.", + example: ["", "", ""], + }, + designers_links: { + category: Category.Team, + title: "Designer link(s)", + tooltip: "Link(s) to webpage(s) explaining who designed the building.", example: ["", "", ""], }, designers_source_type: { @@ -1003,19 +1407,26 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, designer_awards: { category: Category.Team, - title: "Did the design team win any awards for this building?", + title: "Has the building won any awards?", tooltip: "", example: false }, awards_source_link: { category: Category.Team, - title: "Source link(s) for designer award(s)", - tooltip: "URL for source for designer award(s)", + title: "Source link(s) for building award(s)", + tooltip: "URL for source for building award(s)", example: ["", "", ""], }, builder: { category: Category.Team, - title: "Name of builder/construction team", + title: "Name of builder/construction team.", + tooltip: "Free-text entry disabled for security reasons.", + example: ["", "", ""], + }, + builder_links: { + category: Category.Team, + title: "Builder link(s)", + tooltip: "Link(s) to webpage(s) explaining who built the building.", example: ["", "", ""], }, builder_source_type: { @@ -1098,31 +1509,19 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ category: Category.StreetContext, title: "Does the building have a front garden?", tooltip: "Is the front garden mainly green/planted?", - example: "", - items: [ - "Yes", - "No" - ] + example: true, }, context_back_garden: { category: Category.StreetContext, title: "Does the building have a back garden?", tooltip: "Is the back garden mainly green/planted?", - example: "", - items: [ - "Yes", - "No" - ] + example: true }, context_flats_garden: { category: Category.StreetContext, - title: "Is the building flats with a dedicated green space?", + title: "Are flats with a dedicated green space?", tooltip: "If the building is a block of flats, does it have a dedicated garden area/green space?", - example: "", - items: [ - "Yes", - "No" - ] + example: true }, context_garden_source_type: { category: Category.StreetContext, @@ -1139,8 +1538,8 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, context_street_width: { category: Category.Team, - title: "Street width (m)", - tooltip: "Width of the street in metres.", + title: "Average street width (m)", + tooltip: "Average width of the street in metres.", example: 10 }, context_street_width_source_type: { @@ -1158,8 +1557,8 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, context_pavement_width: { category: Category.Team, - title: "Pavement width (m)", - tooltip: "Width of the pavement in metres.", + title: "Average pavement width (m)", + tooltip: "Average width of the pavement in metres.", example: 10 }, context_pavement_width_source_type: { @@ -1178,7 +1577,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ context_green_space_distance: { category: Category.Team, title: "Distance to nearest green space (m)", - tooltip: "Approximate distance from the front door of the building to the nearest public green space.", + tooltip: "Approximate distance from the front door of the building to the nearest public green space (in meters).", example: 10 }, context_green_space_distance_source_type: { @@ -1197,7 +1596,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ context_tree_distance: { category: Category.Team, title: "Distance to nearest tree (m)", - tooltip: "Approximate distance from the front door of the building to the nearest tree.", + tooltip: "Approximate distance from the front door of the building to the nearest tree in meters.", example: 10 }, context_tree_distance_source_type: { @@ -1234,7 +1633,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, age_cladding_date: { category: Category.Age, - title: "Cladding date", + title: "Cladding date (best estimate)", tooltip: "Width of the street in metres.", example: 1970 }, @@ -1253,7 +1652,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, age_extension_date: { category: Category.Age, - title: "Date of significant extensions", + title: "Date of significant extensions (best estimate)", tooltip: "Width of the street in metres.", example: 1970 }, @@ -1272,7 +1671,7 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ }, age_retrofit_date: { category: Category.Age, - title: "Date of significant retrofits", + title: "Date of last significant retrofit (best estimate)", tooltip: "Width of the street in metres.", example: 1970 }, @@ -1289,6 +1688,175 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */ tooltip: "Source link(s) for street width data", example: ["", "", ""], }, + age_historical_raster_map_links: { + category: Category.Age, + title: "Historical maps links", + tooltip: "Links to rasterised historical maps", + example: ["", "", ""], + }, + age_historical_vectorised_footprint_links: { + category: Category.Age, + title: "Extracted vectorised historical footprints links", + tooltip: "Extracted vectorised historical footprints links", + example: ["", "", ""], + }, + + energy_solar: { + category: Category.EnergyPerformance, + title: "Does the building have solar panels?", + tooltip: "Are there any kinds of solar panels on the roof of the building?", + example: true + }, + energy_solar_source_type: { + category: Category.EnergyPerformance, + title: "Source type", + tooltip: "Source type for street width data", + example: "", + items: commonSourceTypes + }, + energy_solar_source_links: { + category: Category.EnergyPerformance, + title: "Source link(s)", + tooltip: "Source link(s) for street width data", + example: ["", "", ""], + }, + energy_green_roof: { + category: Category.EnergyPerformance, + title: "Does the building have green walls/green roof?", + tooltip: "Are there any green walls, or a green roof, on the building?", + example: true + }, + energy_green_roof_source_type: { + category: Category.EnergyPerformance, + title: "Source type", + tooltip: "Source type for street width data", + example: "", + items: commonSourceTypes + }, + energy_green_roof_source_links: { + category: Category.EnergyPerformance, + title: "Source link(s)", + tooltip: "Source link(s) for street width data", + example: ["", "", ""], + }, + + typology_classification: { + category: Category.Typology, + title: "Which description best suits the building and its context?", + tooltip: "HINT: Adapted from building type classifications developed in urban morphology. See https://www.smog.chalmers.se/ 'Space Matrix' for further information.", + example: "8+ storeys: Detached", + items: [ + 'Low-rise: Not part of a group/cluster (1-3 core floors- excluding extensions)', + 'Low-rise: Part of dense block/row/terrace', + 'Low-rise: Part of group of widely spaced blocks (includes semi-detached houses)', + 'Mid-rise: Not part of a group/cluster (4-7 core floors)', + 'Mid-rise: Part of group of densely spaced blocks', + 'Mid-rise: Part of group of widely spaced blocks', + 'High rise: Not part of a group/cluster', + 'High-rise: Part of group of densely spaced blocks (8 + core floors)', + 'High-rise: Part of group of widely spaced blocks', + ] + }, + typology_classification_source_type: { + category: Category.Typology, + title: "Source type", + tooltip: "Source type for street width data", + example: "", + items: commonSourceTypes + }, + typology_classification_source_links: { + category: Category.Typology, + title: "Source link(s)", + tooltip: "Source link(s) for street width data", + example: ["", "", ""], + }, + typology_style_period: { + category: Category.Typology, + title: "Which description best suits the building's architectural style/historical period?", + tooltip: "", + example: "Georgian (1714-1837)", + items: [ + '43AD-410 (Roman)', + '410-1485 (Medieval)', + '1485-1603 (Tudor)', + '1603-1714 (Stuart)', + '1714-1837 (Georgian)', + '1837-1901 (Victorian)', + '1901-1914 (Edwardian)', + '1914-1945 (WWI-WWII)',, + '1946-1979 (Post war)', + '1980-1999 (Late 20th Century)', + '2000-2025 (Early 21st Century)', + ] + }, + typology_style_period_source_type: { + category: Category.Typology, + title: "Source type", + tooltip: "Source type for street width data", + example: "", + items: commonSourceTypes + }, + typology_style_period_source_links: { + category: Category.Typology, + title: "Source link(s)", + tooltip: "Source link(s) for street width data", + example: ["", "", ""], + }, + typology_dynamic_classification: { + category: Category.Typology, + title: "Which description best suits the building's plot?", + tooltip: "HINT: Based on a dynamic classification system for urban tissue developed by Brenda Case Scheer. For further information see: https://www.researchgate.net/publication/242150847_The_Anatomy_of_Sprawl.", + example: "Large plots with internal roads", + items: [ + 'Repetitive small, domestic plots', + 'Linear non-domestic, i.e. high streets', + 'Large plots with internal roads', + 'Other' + ] + }, + typology_dynamic_classification_source_type: { + category: Category.Typology, + title: "Source type", + tooltip: "Source type for street width data", + example: "", + items: commonSourceTypes + }, + typology_dynamic_classification_source_links: { + category: Category.Typology, + title: "Source link(s)", + tooltip: "Source link(s) for street width data", + example: ["", "", ""], + }, + typology_original_use: { + category: Category.Typology, + title: "Which land use best describes the purpose for which the building was built?", + tooltip: "Land use Groups as classified by [NLUD](https://www.gov.uk/government/statistics/national-land-use-database-land-use-and-land-cover-classification)", + example: ["", ""], + }, + typology_original_use_verified: { + category: Category.LandUse, + title: 'Has this land use been manually verified?', + example: true, + }, + typology_original_use_order: { + category: Category.Typology, + title: "Original land use (order)", + tooltip: "Land use Order as classified by [NLUD](https://www.gov.uk/government/statistics/national-land-use-database-land-use-and-land-cover-classification)", + example: "", + }, + typology_original_use_source_type: { + category: Category.Typology, + title: "Source type", + tooltip: "Source type for street width data", + example: "", + items: commonSourceTypes + }, + typology_original_use_source_links: { + category: Category.Typology, + title: "Source link(s)", + tooltip: "Source link(s) for street width data", + example: ["", "", ""], + }, }; export const allFieldsConfig = { ...dataFields, ...buildingUserFields }; \ No newline at end of file diff --git a/app/src/frontend/config/tileserver-config.ts b/app/src/frontend/config/tileserver-config.ts index 990536ef..ec6a2bcc 100644 --- a/app/src/frontend/config/tileserver-config.ts +++ b/app/src/frontend/config/tileserver-config.ts @@ -21,10 +21,14 @@ export type BuildingMapTileset = 'sust_dec' | 'building_attachment_form' | 'landuse' | + 'original_landuse' | 'dynamics_demolished_count' | 'disaster_severity' | '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'; diff --git a/app/src/frontend/displayPreferences-context.tsx b/app/src/frontend/displayPreferences-context.tsx index 01a0ff3b..3bfcc1c3 100644 --- a/app/src/frontend/displayPreferences-context.tsx +++ b/app/src/frontend/displayPreferences-context.tsx @@ -38,6 +38,10 @@ interface DisplayPreferencesContextState { historicDataSwitch: (e: React.FormEvent) => void; historicDataSwitchOnClick: React.MouseEventHandler; + historicMap: LayerEnablementState; + historicMapSwitch: (e: React.FormEvent) => void; + historicMapSwitchOnClick: React.MouseEventHandler; + darkLightTheme: MapTheme; darkLightThemeSwitch: (e: React.FormEvent) => void; darkLightThemeSwitchOnClick: React.MouseEventHandler; @@ -87,6 +91,10 @@ export const DisplayPreferencesContext = createContext = ({children}) => { const defaultParcel = 'disabled' const defaultConservation = 'disabled' const defaultHistoricData = 'disabled' + const defaultHistoricMap = 'disabled' const defaultShowLayerSelection = 'disabled' const [vista, setVista] = useState(defaultVista); const [flood, setFlood] = useState(defaultFlood); @@ -116,6 +125,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => { const [parcel, setParcel] = useState(defaultParcel); const [conservation, setConservation] = useState(defaultConservation); const [historicData, setHistoricData] = useState(defaultHistoricData); + const [historicMap, setHistoricMap] = useState(defaultHistoricMap); const [darkLightTheme, setDarkLightTheme] = useState('night'); const [showLayerSelection, setShowLayerSelection] = useState(defaultShowLayerSelection); @@ -136,6 +146,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => { setParcel(defaultParcel); setConservation(defaultConservation); setHistoricData(defaultHistoricData); + setHistoricMap(defaultHistoricMap); setShowLayerSelection(defaultShowLayerSelection); // reset layers + hiding this panel is integrated into one action //setDarkLightTheme('night'); // reset only layers }, @@ -167,6 +178,9 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => { if(historicData != defaultHistoricData) { return true; } + if(historicMap != defaultHistoricMap) { + return true; + } //darkLightTheme not handled here return false; } @@ -278,9 +292,12 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => { const historicDataSwitch = useCallback( (e) => { - flipHistoricData(e) + if (historicMap === 'enabled') { + fliphistoricMap(e); + } + flipHistoricData(e); }, - [historicData], + [historicData, historicMap], ) const historicDataSwitchOnClick = (e) => { flipHistoricData(e) @@ -291,6 +308,24 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => { 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( (e) => { flipDarkLightTheme(e) @@ -354,6 +389,10 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => { historicDataSwitch, historicDataSwitchOnClick, + historicMap, + historicMapSwitch, + historicMapSwitchOnClick, + darkLightTheme, darkLightThemeSwitch, darkLightThemeSwitchOnClick, diff --git a/app/src/frontend/header.tsx b/app/src/frontend/header.tsx index 4a2e8951..e857ef24 100644 --- a/app/src/frontend/header.tsx +++ b/app/src/frontend/header.tsx @@ -57,7 +57,8 @@ function getCurrentMenuLinks(username: string): MenuLink[][] { }, { to: "https://github.com/colouring-cities/manual/wiki", - text: "Open Manual - Wiki", + text: "Colouring Cities Open Manual/Wiki", + disabled: false, external: true }, { @@ -65,12 +66,6 @@ function getCurrentMenuLinks(username: string): MenuLink[][] { text: "Open code", external: true }, - { - to: "https://github.com/colouring-cities/manual/wiki", - text: "Colouring Cities Open Manual/Wiki", - disabled: false, - external: true - }, { to: "/showcase.html", text: "Case Study Showcase", @@ -79,30 +74,35 @@ function getCurrentMenuLinks(username: string): MenuLink[][] { ], [ { - to: "https://pages.colouring.london", - text: "About", + to: "https://github.com/colouring-cities/manual/wiki/A.-What-is-the-CCRP%3F", + text: "About the Colouring Cities Research Programme", 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", 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?", external: true }, { - to: "https://pages.colouring.london/data-ethics", - text: "Data Ethics", + to: "https://github.com/colouring-cities/manual/wiki/C.-Ethical-framework-and-ethics-policies", + text: "Ethical Framework", 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" }, { - to: "https://discuss.colouring.london", - text: "Discussion Forum", - external: true - }, - { - to: "https://discuss.colouring.london/c/blog/9", - text: "Blog", + to: config.githubURL+"/discussions", + text: "Discussion Forum (GitHub)", external: true }, + // { + // to: "https://discuss.colouring.london/c/blog/9", + // text: "Blog", + // external: true + // }, ], [ { - to: "/privacy-policy.html", - text: "Privacy Policy" + to: "https://github.com/colouring-cities/manual/wiki/C1.-Protocols,-codes-of-conduct-&-data-sharing-agreements#ccrp-contributor-privacy-statement", + text: "Privacy Policy", + external: true }, { - to: "/contributor-agreement.html", - text: "Contributor Agreement" + 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", + external: true }, { to: "/code-of-conduct.html", text: "Code of Conduct" }, { - to: "/data-accuracy.html", - text: "Data Accuracy Agreement" + 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 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", @@ -183,7 +196,7 @@ const InternalNavLink: React.FC<{to: string; onClick: () => void}> = ({ to, onCl ); const ExternalNavLink: React.FC<{to: string}> = ({ to, children }) => ( - + {children} ); diff --git a/app/src/frontend/map/historic-data-switcher.tsx b/app/src/frontend/map/historic-data-switcher.tsx index c1eadb58..db64c17e 100644 --- a/app/src/frontend/map/historic-data-switcher.tsx +++ b/app/src/frontend/map/historic-data-switcher.tsx @@ -5,11 +5,12 @@ import { useDisplayPreferences } from '../displayPreferences-context'; export const HistoricDataSwitcher: React.FC<{}> = (props) => { const { historicData, historicDataSwitch, darkLightTheme } = useDisplayPreferences(); + return (
); diff --git a/app/src/frontend/map/historic-map-switcher.tsx b/app/src/frontend/map/historic-map-switcher.tsx new file mode 100644 index 00000000..6e6be073 --- /dev/null +++ b/app/src/frontend/map/historic-map-switcher.tsx @@ -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 ( +
+ +
+ ); +} \ No newline at end of file diff --git a/app/src/frontend/map/layers/historic-map-layer.tsx b/app/src/frontend/map/layers/historic-map-layer.tsx new file mode 100644 index 00000000..405fc5dc --- /dev/null +++ b/app/src/frontend/map/layers/historic-map-layer.tsx @@ -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 <> + + + } else { + return null; + } +} + diff --git a/app/src/frontend/map/legend.css b/app/src/frontend/map/legend.css index 2b95cfbe..e412cace 100644 --- a/app/src/frontend/map/legend.css +++ b/app/src/frontend/map/legend.css @@ -57,7 +57,7 @@ padding: 0.5rem 0.25rem; margin: 0.25rem 0.5rem; width: auto; - font-size: 18px; + font-size: 17px; border: 1px solid; border-radius: 4px; } diff --git a/app/src/frontend/map/map-button.css b/app/src/frontend/map/map-button.css index b77c57d8..277c49a7 100644 --- a/app/src/frontend/map/map-button.css +++ b/app/src/frontend/map/map-button.css @@ -111,11 +111,15 @@ } .historic-data-switcher { + top: 437px; +} + +.historic-map-switcher { top: 397px; } .parcel-switcher { - top: 437px; + top: 477px; } .map-switcher-inline { diff --git a/app/src/frontend/map/map.tsx b/app/src/frontend/map/map.tsx index a1cb637a..1a1e9286 100644 --- a/app/src/frontend/map/map.tsx +++ b/app/src/frontend/map/map.tsx @@ -15,6 +15,7 @@ import { BoroughBoundaryLayer } from './layers/borough-boundary-layer'; import { BoroughLabelLayer } from './layers/borough-label-layer'; import { ParcelBoundaryLayer } from './layers/parcel-boundary-layer'; import { HistoricDataLayer } from './layers/historic-data-layer'; +import { HistoricMapLayer } from './layers/historic-map-layer'; import { FloodBoundaryLayer } from './layers/flood-boundary-layer'; import { ConservationAreaBoundaryLayer } from './layers/conservation-boundary-layer'; import { VistaBoundaryLayer } from './layers/vista-boundary-layer'; @@ -34,6 +35,7 @@ import { ParcelSwitcher } from './parcel-switcher'; import { FloodSwitcher } from './flood-switcher'; import { ConservationAreaSwitcher } from './conservation-switcher'; import { HistoricDataSwitcher } from './historic-data-switcher'; +import { HistoricMapSwitcher } from './historic-map-switcher'; import { VistaSwitcher } from './vista-switcher'; import { CreativeSwitcher } from './creative-switcher'; import { HousingSwitcher } from './housing-switcher'; @@ -129,6 +131,7 @@ export const ColouringMap : FC = ({ > + @@ -167,10 +170,12 @@ export const ColouringMap : FC = ({ + + : <> } diff --git a/app/src/tiles/dataDefinition.ts b/app/src/tiles/dataDefinition.ts index b440ca5d..c2a464f9 100644 --- a/app/src/tiles/dataDefinition.ts +++ b/app/src/tiles/dataDefinition.ts @@ -266,6 +266,15 @@ const LAYER_QUERIES = { buildings WHERE 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: ` SELECT geometry_id, @@ -281,6 +290,27 @@ const LAYER_QUERIES = { FROM buildings 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'; diff --git a/docs/setup-dev-environment.md b/docs/setup-dev-environment.md index a6aaa99d..7601d2f4 100644 --- a/docs/setup-dev-environment.md +++ b/docs/setup-dev-environment.md @@ -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. +**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. ```bash diff --git a/etl/__init__.py b/etl/__init__.py index a9f46b58..757f9641 100644 --- a/etl/__init__.py +++ b/etl/__init__.py @@ -1 +1,3 @@ -from .filter_mastermap import filter_mastermap \ No newline at end of file +from .filter_mastermap import filter_mastermap + +__all__ = ["filter_mastermap"] diff --git a/etl/filter_mastermap.py b/etl/filter_mastermap.py index 847b0cf8..352f3288 100644 --- a/etl/filter_mastermap.py +++ b/etl/filter_mastermap.py @@ -20,24 +20,24 @@ def main(mastermap_path): def filter_mastermap(mm_path): output_path = str(mm_path).replace(".gml.csv", "") 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 - with open(mm_path, 'r') as fh: + with open(mm_path, "r") as fh: r = csv.DictReader(fh) # 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.writeheader() for line in r: try: - if 'Building' in line['descriptiveGroup']: + if "Building" in line["descriptiveGroup"]: w.writerow(line) # when descriptiveGroup is missing, ignore this Polygon except TypeError: pass -if __name__ == '__main__': +if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: filter_mastermap.py ./path/to/mastermap/dir") exit(-1) diff --git a/etl/get_test_polygons.py b/etl/get_test_polygons.py index 388b9872..11312f45 100644 --- a/etl/get_test_polygons.py +++ b/etl/get_test_polygons.py @@ -21,43 +21,49 @@ size = 256 # load buildings from about 1.5km² around UCL point = (51.524498, -0.133874) 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 -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 = osmnx.projection.project_gdf(gdf, to_crs={"init": "epsg:3857"}) +gdf_proj = gdf_proj[gdf_proj.geometry.type == "Polygon"] -fig, ax = osmnx.plot_footprints(gdf_proj, bgcolor='#333333', - color='w', figsize=(4, 4), - save=True, show=False, close=True, - filename='test_buildings_preview', dpi=600) +fig, ax = osmnx.plot_footprints( + gdf_proj, + bgcolor="#333333", + color="w", + figsize=(4, 4), + save=True, + show=False, + close=True, + filepath="test_buildings_preview.png", + dpi=600, +) # save 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]) +gdf_to_save = gdf_proj.reset_index()[["osmid", "geometry"]] -gdf_to_save = gdf_proj.reset_index( -)[ - ['index', 'geometry'] -] - -gdf_to_save.rename( - columns={'index': 'fid'} -).to_file( - test_data_geojson, driver='GeoJSON' +gdf_to_save.rename(columns={"osmid": "fid"}).to_file( + test_data_geojson, driver="GeoJSON" ) # 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( - ["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 -subprocess.run( - ["sed", "-i", "s/^\"POLYGON/\"SRID=3857;POLYGON/", - test_data_csv] -) +subprocess.run(["sed", "-i", 's/^"POLYGON/"SRID=3857;POLYGON/', test_data_csv]) diff --git a/etl/join_building_data/load_conservation_areas.py b/etl/join_building_data/load_conservation_areas.py index 48832423..1ebd9656 100644 --- a/etl/join_building_data/load_conservation_areas.py +++ b/etl/join_building_data/load_conservation_areas.py @@ -17,7 +17,6 @@ Then with this script: """ -import json import csv import os import subprocess @@ -28,50 +27,49 @@ from tqdm import tqdm def main(base_url, api_key, source_file): - """Read from file, update buildings - """ - with open(source_file, 'r') as source_fh: + """Read from file, update buildings""" + with open(source_file, "r") as source_fh: source = csv.DictReader(source_fh) for feature in tqdm(source, total=line_count(source_file)): 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) def line_count(fname): - """Count lines - relies on 'wc' - """ - p = subprocess.run(['wc', '-l', fname], stdout=subprocess.PIPE) + """Count lines - relies on 'wc'""" + p = subprocess.run(["wc", "-l", fname], stdout=subprocess.PIPE) if p.returncode != 0: - raise IOError(err) + raise IOError(p.returncode) return int(p.stdout.strip().split()[0]) + def process_ca(props): - building_id = props['building_id'] + building_id = props["building_id"] data = { - 'planning_in_conservation_area': True, - 'planning_conservation_area_name': props['conservation_area_name'] + "planning_in_conservation_area": True, + "planning_conservation_area_name": props["conservation_area_name"], } return building_id, data def save_data(building_id, data, api_key, base_url): - """Save data to a building - """ - r = requests.post( + """Save data to a building""" + requests.post( "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key), - json=data + json=data, ) -if __name__ == '__main__': +if __name__ == "__main__": try: url, api_key, filename = sys.argv[1], sys.argv[2], sys.argv[3] except IndexError: print( "Usage: {} ./path/to/conservation_areas.csv".format( - os.path.basename(__file__) - )) + os.path.basename(__file__) + ) + ) exit() main(url, api_key, filename) diff --git a/etl/join_building_data/load_csv.py b/etl/join_building_data/load_csv.py index 5129b02f..5bfc9617 100644 --- a/etl/join_building_data/load_csv.py +++ b/etl/join_building_data/load_csv.py @@ -44,8 +44,6 @@ TODO extend to allow latitude,longitude or easting,northing columns and lookup b """ import csv import json -import os -import sys import argparse 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): - """Read from file, update buildings - """ - with open(source_file, 'r') as source: + """Read from file, update buildings""" + with open(source_file, "r") as source: reader = csv.DictReader(source) for line in reader: 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: continue - if 'sust_dec' in line and line['sust_dec'] == '': - del line['sust_dec'] + if "sust_dec" in line and line["sust_dec"] == "": + del line["sust_dec"] if no_overwrite: try: 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 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 - 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: - print('ERROR', building_id, response_code, response_data) + print("ERROR", building_id, response_code, response_data) elif debug: - print('DEBUG', building_id, response_code, response_data) + print("DEBUG", building_id, response_code, response_data) + class ApiRequestError(Exception): - def __init__(self, code, data, message=''): + def __init__(self, code, data, message=""): self.code = code self.data = data super().__init__(message) + def check_data_present(building_id, fields, base_url): response_code, current_state = get_building(building_id, base_url) if response_code != 200: raise ApiRequestError(response_code, current_state) 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] - 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) 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") return r.status_code, r.json() @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000) def update_building(building_id, data, api_key, base_url): - """Save data to a building - """ + """Save data to a building""" r = requests.post( "{}/api/buildings/{}.json".format(base_url, building_id), - params={'api_key': api_key}, - json=data + params={"api_key": api_key}, + json=data, ) return r.status_code, r.json() def find_building(data, base_url): - if 'building_id' in data: - building_id = data['building_id'] + if "building_id" in data: + building_id = data["building_id"] if building_id is not None: print("match_by_building_id", building_id) return building_id - if 'toid' in data: - building_id = find_by_reference(base_url, 'toid', data['toid']) + if "toid" in data: + building_id = find_by_reference(base_url, "toid", data["toid"]) 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 - if 'uprn' in data: - building_id = find_by_reference(base_url, 'uprn', data['uprn']) + if "uprn" in data: + building_id = find_by_reference(base_url, "uprn", data["uprn"]) 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 print("no_match", data) @@ -144,21 +149,21 @@ def find_building(data, base_url): @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000) def find_by_reference(base_url, ref_key, ref_id): - """Find building_id by TOID or UPRN - """ - r = requests.get("{}/api/buildings/reference".format(base_url), params={ - 'key': ref_key, - 'id': ref_id - }) + """Find building_id by TOID or UPRN""" + r = requests.get( + "{}/api/buildings/reference".format(base_url), + params={"key": ref_key, "id": ref_id}, + ) buildings = r.json() - if buildings and 'error' not in buildings and len(buildings) == 1: - building_id = buildings[0]['building_id'] + if buildings and "error" not in buildings and len(buildings) == 1: + building_id = buildings[0]["building_id"] else: building_id = None return building_id + def parse_json_columns(row, json_columns): for col in json_columns: row[col] = json.loads(row[col]) @@ -167,28 +172,41 @@ def parse_json_columns(row, json_columns): def list_str(values): - return values.split(',') + return values.split(",") -if __name__ == '__main__': + +if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('url', help='URL for the app') - parser.add_argument('api_key', help='API key for the user') - parser.add_argument('path', help='Path to data CSV file') - parser.add_argument('json_columns', - nargs='?', + parser.add_argument("url", help="URL for the app") + parser.add_argument("api_key", help="API key for the user") + parser.add_argument("path", help="Path to data CSV file") + parser.add_argument( + "json_columns", + nargs="?", type=list_str, 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', - action='store_true', - dest='no_overwrite', - help='Don\'t overwrite building data if any of the fields supplied is already set') + parser.add_argument( + "--no-overwrite", + "-n", + 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', - action='store_true', - help='Print debug messages') + parser.add_argument( + "--debug", "-d", action="store_true", help="Print debug messages" + ) 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, + ) diff --git a/etl/join_building_data/load_csv_to_staging.py b/etl/join_building_data/load_csv_to_staging.py index 1b61e90d..85f91d18 100644 --- a/etl/join_building_data/load_csv_to_staging.py +++ b/etl/join_building_data/load_csv_to_staging.py @@ -23,18 +23,18 @@ The process: TODO extend to allow latitude,longitude or easting,northing columns and lookup by location. """ import csv -import json import os import sys import requests + session = requests.Session() session.verify = False + def main(base_url, api_key, source_file): - """Read from file, update buildings - """ - with open(source_file, 'r') as source: + """Read from file, update buildings""" + with open(source_file, "r") as source: reader = csv.DictReader(source) for line in reader: building_id = find_building(line, base_url) @@ -42,40 +42,41 @@ def main(base_url, api_key, source_file): if building_id is None: 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: - 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): - """Save data to a building - """ + """Save data to a building""" r = requests.post( "{}/api/buildings/{}.json".format(base_url, building_id), - params={'api_key': api_key}, + params={"api_key": api_key}, json=data, - verify=False + verify=False, ) print(r) return r.status_code, r.json() def find_building(data, base_url): - if 'building_id' in data: - building_id = data['building_id'] + if "building_id" in data: + building_id = data["building_id"] if building_id is not None: print("match_by_building_id", building_id) return building_id - if 'toid' in data: - building_id = find_by_reference(base_url, 'toid', data['toid']) + if "toid" in data: + building_id = find_by_reference(base_url, "toid", data["toid"]) 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 - if 'uprn' in data: - building_id = find_by_reference(base_url, 'uprn', data['uprn']) + if "uprn" in data: + building_id = find_by_reference(base_url, "uprn", data["uprn"]) 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 print("no_match", data) @@ -83,32 +84,34 @@ def find_building(data, base_url): def find_by_reference(base_url, ref_key, ref_id): - """Find building_id by TOID or UPRN - """ - r = requests.get("{}/api/buildings/reference".format(base_url), params={ - 'key': ref_key, - 'id': ref_id, - }, - verify=False + """Find building_id by TOID or UPRN""" + r = requests.get( + "{}/api/buildings/reference".format(base_url), + params={ + "key": ref_key, + "id": ref_id, + }, + verify=False, ) buildings = r.json() - if buildings and 'error' not in buildings and len(buildings) == 1: - building_id = buildings[0]['building_id'] + if buildings and "error" not in buildings and len(buildings) == 1: + building_id = buildings[0]["building_id"] else: building_id = None return building_id -if __name__ == '__main__': +if __name__ == "__main__": try: url, api_key, filename = sys.argv[1], sys.argv[2], sys.argv[3] except IndexError: print( "Usage: {} ./path/to/data.csv".format( - os.path.basename(__file__) - )) + os.path.basename(__file__) + ) + ) exit() main(url, api_key, filename) diff --git a/etl/join_building_data/load_shapefile.py b/etl/join_building_data/load_shapefile.py index 4b805844..15702821 100644 --- a/etl/join_building_data/load_shapefile.py +++ b/etl/join_building_data/load_shapefile.py @@ -8,7 +8,6 @@ datasets for Camden (age data) and Fitzrovia (number of storeys). - else locate building by representative point - update building with data """ -import json import os import sys from functools import partial @@ -21,18 +20,15 @@ from shapely.ops import transform osgb_to_ll = partial( - pyproj.transform, - pyproj.Proj(init='epsg:27700'), - pyproj.Proj(init='epsg:4326') + pyproj.transform, pyproj.Proj(init="epsg:27700"), pyproj.Proj(init="epsg:4326") ) def main(base_url, api_key, process, source_file): - """Read from file, update buildings - """ - with fiona.open(source_file, 'r') as source: + """Read from file, update buildings""" + with fiona.open(source_file, "r") as source: for feature in source: - props = feature['properties'] + props = feature["properties"] if process == "camden": toid, data = process_camden(props) @@ -42,7 +38,7 @@ def main(base_url, api_key, process, source_file): if data is None: continue - building_id = find_building(toid, feature['geometry'], base_url) + building_id = find_building(toid, feature["geometry"], base_url) if not building_id: print("no_match", toid, "-") continue @@ -51,31 +47,22 @@ def main(base_url, api_key, process, source_file): def process_camden(props): - toid = osgb_toid(props['TOID']) - data = { - 'date_year': props['Year_C'], - 'date_source_detail': props['Date_sou_1'] - } + toid = osgb_toid(props["TOID"]) + data = {"date_year": props["Year_C"], "date_source_detail": props["Date_sou_1"]} return toid, data def process_fitzrovia(props): - toid = osgb_toid(props['TOID']) - storeys = props['Storeys'] + toid = osgb_toid(props["TOID"]) + storeys = props["Storeys"] if storeys is None: return toid, None - if props['Basement'] == 'Yes': - data = { - 'size_storeys_core': int(storeys) - 1, - 'size_storeys_basement': 1 - } + if props["Basement"] == "Yes": + data = {"size_storeys_core": int(storeys) - 1, "size_storeys_basement": 1} else: - data = { - 'size_storeys_core': int(storeys), - 'size_storeys_basement': 0 - } + data = {"size_storeys_core": int(storeys), "size_storeys_basement": 0} return toid, data @@ -86,24 +73,21 @@ def osgb_toid(toid): def save_data(building_id, data, api_key, base_url): - """Save data to a building - """ - r = requests.post( + """Save data to a building""" + requests.post( "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key), - json=data + json=data, ) def find_building(toid, geom, base_url): - """Find building_id by TOID or location - """ - r = requests.get(base_url + "/buildings/reference", params={ - 'key': 'toid', - 'id': toid - }) + """Find building_id by TOID or location""" + r = requests.get( + base_url + "/buildings/reference", params={"key": "toid", "id": toid} + ) buildings = r.json() if buildings and len(buildings) == 1: - bid = buildings[0]['building_id'] + bid = buildings[0]["building_id"] print("match_by_toid", toid, bid) return bid @@ -114,27 +98,32 @@ def find_building(toid, geom, base_url): point_osgb = poly.representative_point() point_ll = transform(osgb_to_ll, point_osgb) - r = requests.get(base_url + "/buildings/locate", params={ - 'lng': point_ll.x, - 'lat': point_ll.y - }) + r = requests.get( + base_url + "/buildings/locate", params={"lng": point_ll.x, "lat": point_ll.y} + ) buildings = r.json() if buildings and len(buildings) == 1: - bid = buildings[0]['building_id'] + bid = buildings[0]["building_id"] print("match_by_location", toid, bid) return bid return None -if __name__ == '__main__': +if __name__ == "__main__": 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: print( "Usage: {} ./path/to/camden.shp".format( - os.path.basename(__file__) - )) + os.path.basename(__file__) + ) + ) exit() main(url, api_key, process, filename) diff --git a/etl/join_building_data/load_shapefile_to_staging.py b/etl/join_building_data/load_shapefile_to_staging.py index 4b805844..15702821 100644 --- a/etl/join_building_data/load_shapefile_to_staging.py +++ b/etl/join_building_data/load_shapefile_to_staging.py @@ -8,7 +8,6 @@ datasets for Camden (age data) and Fitzrovia (number of storeys). - else locate building by representative point - update building with data """ -import json import os import sys from functools import partial @@ -21,18 +20,15 @@ from shapely.ops import transform osgb_to_ll = partial( - pyproj.transform, - pyproj.Proj(init='epsg:27700'), - pyproj.Proj(init='epsg:4326') + pyproj.transform, pyproj.Proj(init="epsg:27700"), pyproj.Proj(init="epsg:4326") ) def main(base_url, api_key, process, source_file): - """Read from file, update buildings - """ - with fiona.open(source_file, 'r') as source: + """Read from file, update buildings""" + with fiona.open(source_file, "r") as source: for feature in source: - props = feature['properties'] + props = feature["properties"] if process == "camden": toid, data = process_camden(props) @@ -42,7 +38,7 @@ def main(base_url, api_key, process, source_file): if data is None: continue - building_id = find_building(toid, feature['geometry'], base_url) + building_id = find_building(toid, feature["geometry"], base_url) if not building_id: print("no_match", toid, "-") continue @@ -51,31 +47,22 @@ def main(base_url, api_key, process, source_file): def process_camden(props): - toid = osgb_toid(props['TOID']) - data = { - 'date_year': props['Year_C'], - 'date_source_detail': props['Date_sou_1'] - } + toid = osgb_toid(props["TOID"]) + data = {"date_year": props["Year_C"], "date_source_detail": props["Date_sou_1"]} return toid, data def process_fitzrovia(props): - toid = osgb_toid(props['TOID']) - storeys = props['Storeys'] + toid = osgb_toid(props["TOID"]) + storeys = props["Storeys"] if storeys is None: return toid, None - if props['Basement'] == 'Yes': - data = { - 'size_storeys_core': int(storeys) - 1, - 'size_storeys_basement': 1 - } + if props["Basement"] == "Yes": + data = {"size_storeys_core": int(storeys) - 1, "size_storeys_basement": 1} else: - data = { - 'size_storeys_core': int(storeys), - 'size_storeys_basement': 0 - } + data = {"size_storeys_core": int(storeys), "size_storeys_basement": 0} return toid, data @@ -86,24 +73,21 @@ def osgb_toid(toid): def save_data(building_id, data, api_key, base_url): - """Save data to a building - """ - r = requests.post( + """Save data to a building""" + requests.post( "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key), - json=data + json=data, ) def find_building(toid, geom, base_url): - """Find building_id by TOID or location - """ - r = requests.get(base_url + "/buildings/reference", params={ - 'key': 'toid', - 'id': toid - }) + """Find building_id by TOID or location""" + r = requests.get( + base_url + "/buildings/reference", params={"key": "toid", "id": toid} + ) buildings = r.json() if buildings and len(buildings) == 1: - bid = buildings[0]['building_id'] + bid = buildings[0]["building_id"] print("match_by_toid", toid, bid) return bid @@ -114,27 +98,32 @@ def find_building(toid, geom, base_url): point_osgb = poly.representative_point() point_ll = transform(osgb_to_ll, point_osgb) - r = requests.get(base_url + "/buildings/locate", params={ - 'lng': point_ll.x, - 'lat': point_ll.y - }) + r = requests.get( + base_url + "/buildings/locate", params={"lng": point_ll.x, "lat": point_ll.y} + ) buildings = r.json() if buildings and len(buildings) == 1: - bid = buildings[0]['building_id'] + bid = buildings[0]["building_id"] print("match_by_location", toid, bid) return bid return None -if __name__ == '__main__': +if __name__ == "__main__": 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: print( "Usage: {} ./path/to/camden.shp".format( - os.path.basename(__file__) - )) + os.path.basename(__file__) + ) + ) exit() main(url, api_key, process, filename) diff --git a/etl/planning_data/address_data.py b/etl/planning_data/address_data.py index 7e2b58af..c92eb87e 100644 --- a/etl/planning_data/address_data.py +++ b/etl/planning_data/address_data.py @@ -1,9 +1,14 @@ def planning_data_entry_to_address(element): site_name = element["_source"].get("site_name") 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") - 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): """ @@ -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 """ - if site_name != None: + if site_name is not None: site_name = site_name.strip() - if site_number != None: + if site_number is not None: site_number = site_number.strip() - if street_name != None: + if street_name is not None: street_name = street_name.strip() - if secondary_street_name != None: + if secondary_street_name is not None: secondary_street_name = secondary_street_name.strip() if site_name == "": @@ -29,68 +34,80 @@ def generate_address(site_name, site_number, street_name, secondary_street_name) if secondary_street_name == "": secondary_street_name = None data = { - 'site_name': site_name, - 'site_number': site_number, - 'street_name': street_name, - 'secondary_street_name': secondary_street_name, - } + "site_name": site_name, + "site_number": site_number, + "street_name": street_name, + "secondary_street_name": secondary_street_name, + } - if site_name == site_number == street_name == secondary_street_name == None: - return {'result': None, 'data': data} + if site_name == site_number == street_name == secondary_street_name is None: + return {"result": None, "data": data} - if secondary_street_name != None: - if street_name == None: - print('"secondary_street_name != None, street_name == None"') - show_data(site_name, site_number, street_name, secondary_street_name, "???????") + if secondary_street_name is not None: + if street_name is None: + print('"secondary_street_name is not None, street_name is None"') + show_data( + site_name, site_number, street_name, secondary_street_name, "???????" + ) else: 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 - if site_name != None: - print('"site_name != None and site_number != None and street_name != None"') - show_data(site_name, site_number, street_name, secondary_street_name, address) + if site_name is not None: + print( + '"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 street_name != None: + if site_name is not None: + if street_name is not None: try: - if site_number == None and int(site_name): - return {'result': site_name + " " + street_name, 'data': data} + if site_number is None and int(site_name): + return {"result": site_name + " " + street_name, "data": data} except ValueError: pass 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: - house_number = int(site_name_without_street_name) + _ = int(site_name_without_street_name) # so it appears to be case like # site_name: 5 Warwick Road # street_name: Warwick Road # no other info provided # in such case just returning site_name will work fine... - return {'result': site_name, 'data': data} + return {"result": site_name, "data": data} except ValueError: pass - print('"site_name != None and street_name != None"') - show_data(site_name, site_number, street_name, secondary_street_name, site_name) - if site_number != None: - print('"site_name != None and site_number != None"') - show_data(site_name, site_number, street_name, secondary_street_name, site_name) - return {'result': site_name, 'data': data} + 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 + ) + if site_number is not None: + 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: - if street_name != None: - if site_number != None: - return {'result': site_number + " " + street_name, 'data': data} - if street_name != None and site_number == None: - print('"street_name != None or site_number == None"') + if street_name is not None: + if site_number is not None: + return {"result": site_number + " " + street_name, "data": data} + if street_name is not None and site_number is None: + print('"street_name is not None or site_number is None"') show_data(site_name, site_number, street_name, secondary_street_name, None) - return {'result': None, 'data': data} - if street_name == None and site_number != None: - print('"street_name == None or site_number != None"') + return {"result": None, "data": data} + if street_name is None and site_number is not None: + print('"street_name is None or site_number is not 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): @@ -100,4 +117,4 @@ def show_data(site_name, site_number, street_name, secondary_street_name, addres print("secondary_street_name:", secondary_street_name) print("address generated based on this data:", address) print() - print() \ No newline at end of file + print() diff --git a/etl/planning_data/obtain_livestream_data_and_load_into_database.py b/etl/planning_data/obtain_livestream_data_and_load_into_database.py index bb0bddbf..b277a0a7 100644 --- a/etl/planning_data/obtain_livestream_data_and_load_into_database.py +++ b/etl/planning_data/obtain_livestream_data_and_load_into_database.py @@ -5,6 +5,7 @@ import requests import psycopg2 import address_data + def main(): connection = get_connection() cursor = connection.cursor() @@ -16,10 +17,12 @@ def main(): while True: data = query(search_after).json() load_data_into_database(cursor, data) - for entry in data['hits']['hits']: + for entry in data["hits"]["hits"]: downloaded += 1 - last_sort = entry['sort'] - print("downloaded", downloaded, "last_sort", last_sort, "previous", search_after) + last_sort = entry["sort"] + print( + "downloaded", downloaded, "last_sort", last_sort, "previous", search_after + ) if search_after == last_sort: break search_after = last_sort @@ -31,24 +34,30 @@ def load_data_into_database(cursor, data): print(json.dumps(data, indent=4)) print("timed_out field missing in provided data") else: - if data['timed_out']: + if data["timed_out"]: raise Exception("query getting livestream data has failed") - for entry in data['hits']['hits']: + for entry in data["hits"]["hits"]: try: description = None - if entry['_source']['description'] != None: - description = entry['_source']['description'].strip() - application_id = entry['_source']['lpa_app_no'] - application_id_with_borough_identifier = entry['_source']['id'] - decision_date = parse_date_string_into_date_object(entry['_source']['decision_date']) - last_synced_date = parse_date_string_into_date_object(entry['_source']['last_synced']) - uprn = entry['_source']['uprn'] - status_before_aliasing = entry['_source']['status'] + if entry["_source"]["description"] is not None: + description = entry["_source"]["description"].strip() + application_id = entry["_source"]["lpa_app_no"] + application_id_with_borough_identifier = entry["_source"]["id"] + decision_date = parse_date_string_into_date_object( + entry["_source"]["decision_date"] + ) + 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 = status_info["status"] status_explanation_note = status_info["status_explanation_note"] - planning_url = obtain_entry_link(entry['_source']['url_planning_app'], application_id) - if uprn == None: + planning_url = obtain_entry_link( + entry["_source"]["url_planning_app"], application_id + ) + if uprn is None: continue try: uprn = int(uprn) @@ -61,7 +70,9 @@ def load_data_into_database(cursor, data): "last_synced_date": last_synced_date, "application_id": application_id, "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, "status": status, "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", "address": address_data.planning_data_entry_to_address(entry), } - if entry["address"] != None: + if entry["address"] is not None: maximum_address_length = 300 if len(entry["address"]) > maximum_address_length: print("address is too long, shortening", entry["address"]) entry["address"] = entry["address"][0:maximum_address_length] 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" entry["registered_with_local_authority_date"] = None @@ -85,13 +99,17 @@ def load_data_into_database(cursor, data): entry["decision_date"] = None 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 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"]: - 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) except TypeError as e: print() @@ -104,40 +122,40 @@ def load_data_into_database(cursor, data): def date_in_future(date): - if date == None: + if date is None: return False return date > datetime.datetime.now() def query(search_after): 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= # 'Content-Type': 'application/json', } json_data = { - 'size': 10000, - 'sort': [ + "size": 10000, + "sort": [ { - 'last_updated': { - 'order': 'desc', - 'unmapped_type': 'boolean', + "last_updated": { + "order": "desc", + "unmapped_type": "boolean", }, }, ], - 'stored_fields': [ - '*', + "stored_fields": [ + "*", ], - '_source': { - 'excludes': [], + "_source": { + "excludes": [], }, - 'query': { - 'bool': { - 'must': [ + "query": { + "bool": { + "must": [ { - 'range': { - 'valid_date': { - 'gte': '01/01/1021', + "range": { + "valid_date": { + "gte": "01/01/1021", }, }, }, @@ -147,18 +165,22 @@ def query(search_after): } if search_after != []: - json_data['search_after'] = search_after + json_data["search_after"] = search_after 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(): return psycopg2.connect( - host=os.environ['PGHOST'], - dbname=os.environ['PGDATABASE'], - user=os.environ['PGUSER'], - password=os.environ['PGPASSWORD'] + host=os.environ["PGHOST"], + dbname=os.environ["PGDATABASE"], + user=os.environ["PGUSER"], + password=os.environ["PGPASSWORD"], ) @@ -170,28 +192,31 @@ def insert_entry(cursor, e): try: now = datetime.datetime.now() application_url = None - if e["application_url"] != None: + if e["application_url"] is not None: 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) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) - ''', ( - e["application_id"], - application_url, e["description"], - date_object_into_date_string(e["registered_with_local_authority_date"]), - days_since(e["registered_with_local_authority_date"], now), - date_object_into_date_string(e["decision_date"]), - days_since(e["decision_date"], now), - date_object_into_date_string(e["last_synced_date"]), - e["status"], - e["status_before_aliasing"], - e["status_explanation_note"], - e["data_source"], - e["data_source_link"], - e["address"], - e["uprn"], - ) + """, + ( + e["application_id"], + application_url, + e["description"], + date_object_into_date_string(e["registered_with_local_authority_date"]), + days_since(e["registered_with_local_authority_date"], now), + date_object_into_date_string(e["decision_date"]), + days_since(e["decision_date"], now), + date_object_into_date_string(e["last_synced_date"]), + e["status"], + e["status_before_aliasing"], + e["status_explanation_note"], + e["data_source"], + e["data_source_link"], + e["address"], + e["uprn"], + ), ) except psycopg2.errors.Error as error: show_dictionary(e) @@ -204,30 +229,32 @@ def show_dictionary(data): def days_since(date, now): - if(date == None): + if date is None: return None return (now - date).days def date_object_into_date_string(date): - if(date == None): + if date is None: return None return datetime.datetime.strftime(date, "%Y-%m-%d") def parse_date_string_into_date_object(incoming): - if incoming == None: + if incoming is None: return None date = None try: date = datetime.datetime.strptime(incoming, "%d/%m/%Y") # '21/07/2022' 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 def obtain_entry_link(provided_link, application_id): - if provided_link != None: + if provided_link is not None: if "Ealing" in application_id: if ";" == 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 # https://developmentandhousing.hackney.gov.uk/planning/index.html?fa=getApplication&reference=2021/2491 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: # sadly, specific links seems impossible 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): status_length_limit = 50 # see migrations/034.planning_livestream_data.up.sql if status in ["Application Under Consideration", "Application Received"]: - if decision_date == None: + if decision_date is None: 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" if status == "Appeal Received": status = "Appeal In Progress" @@ -296,16 +330,39 @@ def process_status(status, decision_date): status = "Withdrawn" if len(status) > status_length_limit: 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"} - if (status in ["Submitted", "Approved", "Rejected", "Appeal In Progress", "Withdrawn", "Unknown"]): + return { + "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} - if status in ["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"} + if status in [ + "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) - 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) return {"status": status, "status_explanation_note": None} -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/etl/planning_data/requirements.txt b/etl/planning_data/requirements.txt index 073368d2..731c082c 100644 --- a/etl/planning_data/requirements.txt +++ b/etl/planning_data/requirements.txt @@ -1,3 +1,3 @@ # Python packages for planning data import -psycopg2==2.8.6 +psycopg2-binary==2.9.7 requests==2.31.0 diff --git a/etl/requirements.txt b/etl/requirements.txt index 0dafbe99..7ceb9e1a 100644 --- a/etl/requirements.txt +++ b/etl/requirements.txt @@ -1,7 +1,7 @@ # Python packages for etl -fiona==1.7.13 -osmnx==0.13 -psycopg2==2.7.5 -shapely==1.7 +fiona +osmnx==1.6.0 +psycopg2-binary==2.9.7 retrying==1.3.3 requests==2.31.0 +shapely diff --git a/maintenance/extract_data/export_attributes.sql b/maintenance/extract_data/export_attributes.sql index b87926f7..4f5672bf 100644 --- a/maintenance/extract_data/export_attributes.sql +++ b/maintenance/extract_data/export_attributes.sql @@ -4,6 +4,7 @@ COPY (SELECT ref_osm_id, revision_id, location_name, + location_name_link, location_number, location_street, location_line_two, @@ -13,6 +14,7 @@ COPY (SELECT location_address_links, location_latitude, location_longitude, + location_alternative_footprint_links current_landuse_group, current_landuse_order, building_attachment_form, diff --git a/maintenance/requirements.txt b/maintenance/requirements.txt index d4ff7130..9e61f44e 100644 --- a/maintenance/requirements.txt +++ b/maintenance/requirements.txt @@ -1,2 +1,2 @@ -psycopg2==2.8.3 -requests==2.31.0 \ No newline at end of file +psycopg2-binary==2.9.7 +requests==2.31.0 diff --git a/migrations/041.ui_revamp_tweaks.down.sql b/migrations/041.ui_revamp_tweaks.down.sql new file mode 100644 index 00000000..6a6482d5 --- /dev/null +++ b/migrations/041.ui_revamp_tweaks.down.sql @@ -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; diff --git a/migrations/041.ui_revamp_tweaks.up.sql b/migrations/041.ui_revamp_tweaks.up.sql new file mode 100644 index 00000000..04b06baf --- /dev/null +++ b/migrations/041.ui_revamp_tweaks.up.sql @@ -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[]; diff --git a/migrations/042.ui_revamp_tweaks.refactor.sql b/migrations/042.ui_revamp_tweaks.refactor.sql new file mode 100644 index 00000000..660efc3b --- /dev/null +++ b/migrations/042.ui_revamp_tweaks.refactor.sql @@ -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; diff --git a/migrations/043.typology_updates.down.sql b/migrations/043.typology_updates.down.sql new file mode 100644 index 00000000..bc3f3b84 --- /dev/null +++ b/migrations/043.typology_updates.down.sql @@ -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; diff --git a/migrations/043.typology_updates_up.sql b/migrations/043.typology_updates_up.sql new file mode 100644 index 00000000..af0f4a72 --- /dev/null +++ b/migrations/043.typology_updates_up.sql @@ -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[]; \ No newline at end of file diff --git a/migrations/044.construction_updates.down.sql b/migrations/044.construction_updates.down.sql new file mode 100644 index 00000000..f964e8fd --- /dev/null +++ b/migrations/044.construction_updates.down.sql @@ -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; \ No newline at end of file diff --git a/migrations/044.construction_updates.up.sql b/migrations/044.construction_updates.up.sql new file mode 100644 index 00000000..35005e38 --- /dev/null +++ b/migrations/044.construction_updates.up.sql @@ -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[]; \ No newline at end of file diff --git a/migrations/045.typology_changes.down.sql b/migrations/045.typology_changes.down.sql new file mode 100644 index 00000000..9797cd12 --- /dev/null +++ b/migrations/045.typology_changes.down.sql @@ -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; \ No newline at end of file diff --git a/migrations/045.typology_changes.up.sql b/migrations/045.typology_changes.up.sql new file mode 100644 index 00000000..ca2ecbd4 --- /dev/null +++ b/migrations/045.typology_changes.up.sql @@ -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'; \ No newline at end of file