-
-
- {
- 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.
-
-
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.
-
-
+
+ Here we are collecting information on the location of buildings used for community activities so we can track loss of/additions to community space over time.
+
+
-
- Here we are collecting information on the location of buildings used for community activities so we can track loss of/additions to community space over time.
-
-
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.mapColourScale == "is_domestic")? 'Showing domestic, non-domestic and mixed-use buildings (click to hide)' : 'Click to see domestic, non-domestic and mixed-use buildings on the map.'}
+ {(props.mapColourScale == "is_domestic")? 'Showing residential, non-residential and mixed-use buildings (click to hide)' : 'Click to see residential, non-residential and mixed-use buildings on the map.'}
= (props) => {
>
}
-
+ = (props) => {
/>
>
}
+
{
props.mode != 'view' &&
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) => {
/>
>
}
+
+
);
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" ?
-
- {'Click here to view possible locations of future applications'}
-
- :
-
- {'Click to see planning applications'}
-
- }
-
+
-
- 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" ?
+
+ {'Click here to view possible locations of future applications'}
+
+ :
+
+ {'Click to see planning applications'}
+
+ }
+
+
+ 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) => {
{(flood === 'enabled')? 'Click to hide Flood Zones' : 'Click to see Flood Zones mapped'}
{(housing === 'enabled')? 'Click to hide Housing Zones' : 'Click to see Housing Zones mapped'}
{(creative === 'enabled')? 'Click to hide Creative Enterprise Zones' : 'Click to see Creative Enterprise Zones'}
{(vista === 'enabled')? 'Click to hide Protected Vistas' : 'Click to see Protected Vistas'}
@@ -220,314 +335,356 @@ const PlanningView: React.FunctionComponent = (props) => {
}
{(conservation === 'enabled')? 'Click to hide Conservation Areas' : 'Click to see Conservation Areas'}
-
-
+
+ { 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?
-
-
- {(parcel === 'enabled')? 'Click to hide sample of parcel data (in City)' : 'Click to see sample of parcel data (in City) mapped'}
-
-
(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