Merge pull request #1225 from colouring-cities/interface/ui-tweaks

Interface/UI tweaks
This commit is contained in:
Mike Simpson 2023-08-16 12:29:05 +01:00 committed by GitHub
commit 95d0fbd113
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 501 additions and 70 deletions

View File

@ -19,7 +19,19 @@
</Style>
<Style name="base_night_outlines">
<Rule>
<LineSymbolizer stroke="#ff0000ff" stroke-width="1" />
<MaxScaleDenominator>20000</MaxScaleDenominator>
<MinScaleDenominator>1200</MinScaleDenominator>
<LineSymbolizer stroke="#0081AF" stroke-width="0.5"/>
</Rule>
<Rule>
<MaxScaleDenominator>12000</MaxScaleDenominator>
<MinScaleDenominator>8000</MinScaleDenominator>
<LineSymbolizer stroke="#0081AF" stroke-width="1.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>8000</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#0081AF" stroke-width="4.0"/>
</Rule>
</Style>
<Style name="base_boroughs">
@ -913,6 +925,83 @@
<LineSymbolizer stroke="#888" stroke-width="3.0"/>
</Rule>
</Style>
<Style name="original_landuse">
<Rule>
<Filter>[typology_original_use_order] = "Agriculture And Fisheries"</Filter>
<PolygonSymbolizer fill="#73ccd1" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Minerals"</Filter>
<PolygonSymbolizer fill="#45cce3" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Unclassified, presumed non-residential"</Filter>
<PolygonSymbolizer fill="#6c6f8e" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Recreation And Leisure"</Filter>
<PolygonSymbolizer fill="#ffbfbf" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Transport"</Filter>
<PolygonSymbolizer fill="#b3de69" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Utilities And Infrastructure"</Filter>
<PolygonSymbolizer fill="#cccccc" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and not ([typology_original_use_order] = "Garden buildings") and not ([typology_original_use_order] = "Hotels, boarding and guest houses") and not ([typology_original_use_verified])</Filter>
<PolygonSymbolizer fill="#252aa6" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and not ([typology_original_use_order] = "Garden buildings") and not ([typology_original_use_order] = "Hotels, boarding and guest houses") and ([typology_original_use_verified])</Filter>
<PolygonSymbolizer fill="#7025a6" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and [typology_original_use_order] = "Hotels, boarding and guest houses"</Filter>
<PolygonSymbolizer fill="#3c4194" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Residential" and [typology_original_use_order] = "Garden buildings" </Filter>
<PolygonSymbolizer fill="#1157fa" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Community Services"</Filter>
<PolygonSymbolizer fill="#fa667d" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Retail"</Filter>
<PolygonSymbolizer fill="#ff8c00" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Industry And Business"</Filter>
<PolygonSymbolizer fill="#f5f58f" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Vacant And Derelict"</Filter>
<PolygonSymbolizer fill="#ffffff" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Defence"</Filter>
<PolygonSymbolizer fill="#898944" />
</Rule>
<Rule>
<Filter>[typology_original_use_order] = "Mixed Use"</Filter>
<PolygonSymbolizer fill="#e5050d" />
</Rule>
<Rule>
<MaxScaleDenominator>8530</MaxScaleDenominator>
<MinScaleDenominator>4264</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="0.8"/>
</Rule>
<Rule>
<MaxScaleDenominator>4264</MaxScaleDenominator>
<MinScaleDenominator>0</MinScaleDenominator>
<LineSymbolizer stroke="#888" stroke-width="3.0"/>
</Rule>
</Style>
<Style name="disaster_severity">
<Rule>
<Filter>[disaster_severity] = "Building destroyed"</Filter>
@ -1024,47 +1113,47 @@
</Style>
<Style name="typology_style_period">
<Rule>
<Filter>[typology_style_period] = "Roman (43AD-410)"</Filter>
<Filter>[typology_style_period] = "43AD-410 (Roman)"</Filter>
<PolygonSymbolizer fill="#FFF739" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Medieval (410-1485)"</Filter>
<Filter>[typology_style_period] = "410-1485 (Medieval)"</Filter>
<PolygonSymbolizer fill="#C5BD00" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Tudor (1485-1603)"</Filter>
<Filter>[typology_style_period] = "1485-1603 (Tudor)"</Filter>
<PolygonSymbolizer fill="#FF9A39" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Stuart (1603 -1714)"</Filter>
<Filter>[typology_style_period] = "1603-1714 (Stuart)"</Filter>
<PolygonSymbolizer fill="#C56000" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Georgian (1714-1837)"</Filter>
<Filter>[typology_style_period] = "1714-1837 (Georgian)"</Filter>
<PolygonSymbolizer fill="#EA8072" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Victorian (1837-1901)"</Filter>
<Filter>[typology_style_period] = "1837-1901 (Victorian)"</Filter>
<PolygonSymbolizer fill="#A71200" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Edwardian (1901-1914)"</Filter>
<Filter>[typology_style_period] = "1901-1914 (Edwardian)"</Filter>
<PolygonSymbolizer fill="#A272D4" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "WWI - WWII (1914-45)"</Filter>
<Filter>[typology_style_period] = "1914-1945 (WWI-WWII)"</Filter>
<PolygonSymbolizer fill="#3988C5" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Post war (1945-1975)"</Filter>
<Filter>[typology_style_period] = "1946-1979 (Post war)"</Filter>
<PolygonSymbolizer fill="#5ADFA2" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Late 20th Century (1975-2000)"</Filter>
<Filter>[typology_style_period] = "1980-1999 (Late 20th Century)"</Filter>
<PolygonSymbolizer fill="#C2F47A" />
</Rule>
<Rule>
<Filter>[typology_style_period] = "Early 21st Century (2000-Present)"</Filter>
<Filter>[typology_style_period] = "2000-2025 (Early 21st Century)"</Filter>
<PolygonSymbolizer fill="#6FB40A" />
</Rule>

View File

@ -965,6 +965,14 @@ export const buildingAttributesConfig = valueType<DataFieldConfig>()({ /* eslint
verify: true
},
typology_original_use : {
edit: true,
derivedEdit: true,
verify: true
},
typology_original_use_verified: {
edit: true,
},
typology_original_use_order : {
edit: true,
verify: true
},

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import '../../map/map-button.css';
import { dataFields } from '../../config/data-fields-config';
import { commonSourceTypes, dataFields } from '../../config/data-fields-config';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { DataEntryGroup } from '../data-components/data-entry-group';
import { DynamicsBuildingPane, DynamicsDataEntry } from './dynamics/dynamics-data-entry';
@ -40,23 +40,47 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
const ageLinkUrl = `/${props.mode}/${Category.Age}/${props.building.building_id}`;
const { historicData, historicDataSwitchOnClick, darkLightTheme } = useDisplayPreferences();
const { historicMap, historicMapSwitchOnClick } = useDisplayPreferences();
const switchToSurvivalMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('survival_status');
historicMapSwitchOnClick(e);
if (props.mapColourScale == "survival_status") {
props.onMapColourScale('date_year');
if (historicData === 'enabled') {
historicDataSwitchOnClick(e);
}
else {
}
const switchToSurvivalDataStyle = (e) => {
e.preventDefault();
props.onMapColourScale('survival_status');
historicDataSwitchOnClick(e);
if (historicMap === 'enabled') {
historicMapSwitchOnClick(e);
}
}
const switchToAgeMapStyle = (e) => {
e.preventDefault();
if (props.mapColourScale == "survival_status")
{
historicDataSwitchOnClick(e);
}
props.onMapColourScale('date_year')
}
const switchToStylePeriodMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('typology_style_period')
}
return (
<Fragment>
<DataEntryGroup name="Building age">
<DataEntryGroup name="Building age/construction date">
<YearDataEntry
year={props.building.date_year}
upper={props.building.date_upper}
@ -171,6 +195,64 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
tooltip='Coming Soon'
/>*/}
</DataEntryGroup>
<DataEntryGroup name="Architectural style">
{(props.mapColourScale == "typology_style_period") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAgeMapStyle}>
Click here to return to building age.
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToStylePeriodMapStyle}>
Click here to show architectural style.
</button>
}
<SelectDataEntry
title={dataFields.typology_style_period.title}
slug="typology_style_period"
value={props.building.typology_style_period}
tooltip={dataFields.typology_style_period.tooltip}
options={dataFields.typology_style_period.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
/>
<Verification
slug="typology_style_period"
allow_verify={props.user !== undefined && props.building.typology_style_period !== null && !props.edited}
onVerify={props.onVerify}
user_verified={props.user_verified.hasOwnProperty("typology_style_period")}
user_verified_as={props.user_verified.typology_style_period}
verified_count={props.building.verified.typology_style_period}
/>
<SelectDataEntry
title={dataFields.typology_style_period_source_type.title}
slug="typology_style_period_source_type"
value={props.building.typology_style_period_source_type}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_style_period_source_type.tooltip}
placeholder={dataFields.typology_style_period_source_type.example}
options={dataFields.typology_style_period_source_type.items}
/>
{(props.building.typology_style_period_source_type == commonSourceTypes[0] ||
props.building.typology_style_period_source_type == commonSourceTypes[1] ||
props.building.typology_style_period_source_type == null) ? <></> :
<>
<MultiDataEntry
title={dataFields.typology_style_period_source_links.title}
slug="typology_style_period_source_links"
value={props.building.typology_style_period_source_links}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
tooltip={dataFields.typology_style_period_source_links.tooltip}
placeholder="https://..."
editableEntries={true}
isUrl={true}
/>
</>
}
</DataEntryGroup>
<DataEntryGroup name="Cladding, extensions and retrofits">
<NumericDataEntry
slug='age_cladding_date'
@ -323,9 +405,6 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
}
</DataEntryGroup>
<DataEntryGroup name="Lifespan and site history">
<button className={`map-switcher-inline ${props.mapColourScale == "survival_status" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
{(props.mapColourScale == "is_domestic")? 'Click here to hide historical maps':'Click here to show historical maps'}
</button>
<DataEntryGroup name="Constructions and demolitions on this site" showCount={false}>
<DynamicsBuildingPane>
<label>Current building (age data <Link to={ageLinkUrl}>editable here</Link>)</label>
@ -419,9 +498,24 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => {
Choose a colour to indicate whether the building has survived.
</i>
</div>
<button className={`map-switcher-inline ${props.mapColourScale == "survival_status" ? "enabled-state" : "disabled-state"} btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
{(props.mapColourScale == "is_domestic")? 'Click here to hide historical maps':'Click here to show historical maps'}
{(historicMap == "enabled") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAgeMapStyle}>
Click here to hide the 1890s OS historical map.
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalMapStyle}>
Click here to show the 1890s OS historical map.
</button>
}
{(historicData == "enabled") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToAgeMapStyle}>
Click here to hide the 1890s OS historical map with modern footprints.
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToSurvivalDataStyle}>
Click here to show the 1890s OS historical map with modern footprints.
</button>
}
<SelectDataEntry
title={dataFields.survival_status.title}
slug="survival_status"

View File

@ -138,6 +138,7 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => {
/>
</>
}
<hr/>
{
props.mode != 'view' &&
<div>

View File

@ -13,13 +13,6 @@ import { DataEntryGroup } from '../data-components/data-entry-group';
import { MultiDataEntry } from '../data-components/multi-data-entry/multi-data-entry';
import { useDisplayPreferences } from '../../displayPreferences-context';
const AttachmentFormOptions = [
"Detached",
"Semi-Detached",
"End-Terrace",
"Mid-Terrace"
];
/**
* Type view/edit section
*/
@ -42,6 +35,10 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
e.preventDefault();
props.onMapColourScale('building_attachment_form')
}
const switchToLandUseMapStyle = (e) => {
e.preventDefault();
props.onMapColourScale('original_landuse')
}
return (
<Fragment>
@ -103,8 +100,8 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
</>
}
</DataEntryGroup>
<DataEntryGroup name="Architectural style/Historical period">
{(props.mapColourScale == "typology_style_period") ?
<DataEntryGroup name="Architectural style">
{/*(props.mapColourScale == "typology_style_period") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{'Click to change map to show typology classification.'}
</button>
@ -112,7 +109,7 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToStylePeriodMapStyle}>
{"Click here to change map to show architectural style/historical period."}
</button>
}
*/}
<SelectDataEntry
title={dataFields.typology_style_period.title}
slug="typology_style_period"
@ -122,7 +119,9 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
mode={props.mode}
copy={props.copy}
onChange={props.onChange}
disabled={true}
/>
{/*
<Verification
slug="typology_style_period"
allow_verify={props.user !== undefined && props.building.typology_style_period !== null && !props.edited}
@ -130,11 +129,11 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
user_verified={props.user_verified.hasOwnProperty("typology_style_period")}
user_verified_as={props.user_verified.typology_style_period}
verified_count={props.building.verified.typology_style_period}
/>
/> */}
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 14, backgroundColor: "#f6f8f9" }}>
<i className="source-url">For building age by year see <a href={"http://localhost:8080/edit/age/"+props.building.building_id}>Age & History</a>.</i>
<i className="source-url">To edit the architectural style box, and to see the data mapped, please go to <a href={"/"+props.mode+"/age/"+props.building.building_id}>Age & History</a>.</i>
</div>
<SelectDataEntry
{/* <SelectDataEntry
title={dataFields.typology_style_period_source_type.title}
slug="typology_style_period_source_type"
value={props.building.typology_style_period_source_type}
@ -162,7 +161,7 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
isUrl={true}
/>
</>
}
} */}
</DataEntryGroup>
<DataEntryGroup name="Dynamic tissue classification">
{(props.mapColourScale == "typology_dynamic_classification") ?
@ -223,6 +222,15 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
}
</DataEntryGroup>
<DataEntryGroup name="Original Use">
{(props.mapColourScale == "original_landuse") ?
<button className={`map-switcher-inline enabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToClassificationMapStyle}>
{'Click to change map to show typology classification.'}
</button>
:
<button className={`map-switcher-inline disabled-state btn btn-outline btn-outline-dark ${darkLightTheme}`} onClick={switchToLandUseMapStyle}>
{"Click here to change map to original land use."}
</button>
}
<MultiDataEntry
title={dataFields.typology_original_use.title}
slug="typology_original_use"
@ -274,6 +282,27 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
/>
</>
}
<hr/>
{
props.mode != 'view' &&
<div>
<div className={`alert alert-dark`} role="alert" style={{ fontSize: 13, backgroundColor: "#f6f8f9" }}>
<i>
Below is a more general classification for the original land use of this building, automatically derived from the information above.
</i>
</div>
</div>
}
<DataEntry
title={dataFields.typology_original_use_order.title}
tooltip={dataFields.typology_original_use_order.tooltip}
slug="typology_original_use_order"
value={props.building.typology_original_use_order}
mode={props.mode}
disabled={true}
copy={props.copy}
onChange={props.onChange}
/>
</DataEntryGroup>
<DataEntryGroup name="Attachment/Adjacency">
{(props.mapColourScale == "building_attachment_form") ?
@ -290,7 +319,7 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
slug="building_attachment_form"
value={props.building.building_attachment_form}
tooltip={dataFields.building_attachment_form.tooltip}
options={AttachmentFormOptions}
options={dataFields.building_attachment_form.items}
mode={props.mode}
copy={props.copy}
onChange={props.onChange}

View File

@ -48,6 +48,25 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
]
},
},
{
mapStyle: 'typology_style_period',
legend: {
title: 'Architectural style',
elements: [
{ color: '#FFF739', text: '43AD-410 (Roman)' },
{ color: '#C5BD00', text: '410-1485 (Medieval)' },
{ color: '#FF9A39', text: '1485-1603 (Tudor)' },
{ color: '#C56000', text: '1603-1714 (Stuart)' },
{ color: '#EA8072', text: '1714-1837 (Georgian)' },
{ color: '#A71200', text: '1837-1901 (Victorian)' },
{ color: '#A272D4', text: '1901-1914 (Edwardian)' },
{ color: '#3988C5', text: '1914-1945 (WWI-WWII)' },
{ color: '#5ADFA2', text: '1946-1979 (Post war)' },
{ color: '#C2F47A', text: '1980-1999 (Late C20)' },
{ color: '#6FB40A', text: '2000-2025 (Early C21)' },
]
}
},
{
mapStyle: 'survival_status',
legend: {
@ -317,25 +336,25 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
]
}
},
{
/*{
mapStyle: 'typology_style_period',
legend: {
title: 'Architectural style',
elements: [
{ color: '#FFF739', text: 'Roman (43AD-410)' },
{ color: '#C5BD00', text: 'Medieval (410-1485)' },
{ color: '#FF9A39', text: 'Tudor (1485-1603)' },
{ color: '#C56000', text: 'Stuart (1603 -1714)' },
{ color: '#EA8072', text: 'Georgian (1714-1837)' },
{ color: '#A71200', text: 'Victorian (1837-1901)' },
{ color: '#A272D4', text: 'Edwardian (1901-1914)' },
{ color: '#3988C5', text: 'WWI - WWII (1914-45)' },
{ color: '#5ADFA2', text: 'Post war (1945-1975)' },
{ color: '#C2F47A', text: 'Late C20th (1975-2000)' },
{ color: '#6FB40A', text: 'Early C21st (2000- )' },
{ color: '#FFF739', text: '43AD-410 (Roman)' },
{ color: '#C5BD00', text: '410-1485 (Medieval)' },
{ color: '#FF9A39', text: '1485-1603 (Tudor)' },
{ color: '#C56000', text: '1603-1714 (Stuart)' },
{ color: '#EA8072', text: '1714-1837 (Georgian)' },
{ color: '#A71200', text: '1837-1901 (Victorian)' },
{ color: '#A272D4', text: '1901-1914 (Edwardian)' },
{ color: '#3988C5', text: '1914-1945 (WWI-WWII)' },
{ color: '#5ADFA2', text: '1946-1979 (Post war)' },
{ color: '#C2F47A', text: '1980-1999 (Late C20)' },
{ color: '#6FB40A', text: '2000-2025 (Early C21)' },
]
}
},
},*/
{
mapStyle: 'typology_dynamic_classification',
legend: {
@ -348,6 +367,29 @@ export const categoryMapsConfig: {[key in Category]: CategoryMapDefinition[]} =
]
}
},
{
mapStyle: 'original_landuse',
legend: {
title: 'Original Land Use',
elements: [
{ color: '#e5050d', text: 'Mixed Use' },
{ subtitle: 'Single use:'},
{ color: '#252aa6', text: 'Residential (unverified)' },
{ color: '#7025a6', text: 'Residential (verified)' },
{ color: '#ff8c00', text: 'Retail' },
{ color: '#f5f58f', text: 'Industry & Business' },
{ color: '#fa667d', text: 'Community Services' },
{ color: '#ffbfbf', text: 'Recreation & Leisure' },
{ color: '#b3de69', text: 'Transport' },
{ color: '#cccccc', text: 'Utilities & Infrastructure' },
{ color: '#898944', text: 'Defence' },
{ color: '#73ccd1', text: 'Agriculture' },
{ color: '#45cce3', text: 'Minerals' },
{ color: '#ffffff', text: 'Vacant & Derelict' },
{ color: '#6c6f8e', text: 'Unclassified, presumed non-residential' }
]
},
},
{
mapStyle: 'building_attachment_form',
legend: {

View File

@ -314,6 +314,12 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */
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"
]
},
building_attachment_source_type: {
category: Category.Age,
@ -1770,17 +1776,17 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */
tooltip: "",
example: "Georgian (1714-1837)",
items: [
'Roman (43AD-410)',
'Medieval (410-1485)',
'Tudor (1485-1603)',
'Stuart (1603 -1714)',
'Georgian (1714-1837)',
'Victorian (1837-1901)',
'Edwardian (1901-1914)',
'WWI - WWII (1914-45)',,
'Post war (1945-1975)',
'Late 20th Century (1975-2000)',
'Early 21st Century (2000-Present)',
'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: {
@ -1827,6 +1833,17 @@ export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */
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",

View File

@ -21,6 +21,7 @@ export type BuildingMapTileset =
'sust_dec' |
'building_attachment_form' |
'landuse' |
'original_landuse' |
'dynamics_demolished_count' |
'disaster_severity' |
'team' |

View File

@ -38,6 +38,10 @@ interface DisplayPreferencesContextState {
historicDataSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
historicDataSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
historicMap: LayerEnablementState;
historicMapSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
historicMapSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
darkLightTheme: MapTheme;
darkLightThemeSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
darkLightThemeSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
@ -87,6 +91,10 @@ export const DisplayPreferencesContext = createContext<DisplayPreferencesContext
historicDataSwitch: stub,
historicDataSwitchOnClick: undefined,
historicMap: undefined,
historicMapSwitch: stub,
historicMapSwitchOnClick: undefined,
darkLightTheme: undefined,
darkLightThemeSwitch: stub,
darkLightThemeSwitchOnClick: undefined,
@ -107,6 +115,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
const defaultParcel = 'disabled'
const defaultConservation = 'disabled'
const defaultHistoricData = 'disabled'
const defaultHistoricMap = 'disabled'
const defaultShowLayerSelection = 'disabled'
const [vista, setVista] = useState<LayerEnablementState>(defaultVista);
const [flood, setFlood] = useState<LayerEnablementState>(defaultFlood);
@ -116,6 +125,7 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
const [parcel, setParcel] = useState<LayerEnablementState>(defaultParcel);
const [conservation, setConservation] = useState<LayerEnablementState>(defaultConservation);
const [historicData, setHistoricData] = useState<LayerEnablementState>(defaultHistoricData);
const [historicMap, setHistoricMap] = useState<LayerEnablementState>(defaultHistoricMap);
const [darkLightTheme, setDarkLightTheme] = useState<MapTheme>('night');
const [showLayerSelection, setShowLayerSelection] = useState<LayerEnablementState>(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,7 +292,10 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
const historicDataSwitch = useCallback(
(e) => {
flipHistoricData(e)
if (historicMap === 'enabled') {
fliphistoricMap(e);
}
flipHistoricData(e);
},
[historicData],
)
@ -291,6 +308,24 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
setHistoricData(newHistoric);
}
const historicMapSwitch = useCallback(
(e) => {
if (historicData === 'enabled') {
flipHistoricData(e);
}
fliphistoricMap(e);
},
[historicMap],
)
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,

View File

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

View File

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

View File

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

View File

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

View File

@ -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<ColouringMapProps> = ({
>
<CityBoundaryLayer/>
<HistoricDataLayer revisionId={revisionId} />
<HistoricMapLayer revisionId={revisionId} />
<BoroughBoundaryLayer/>
<ParcelBoundaryLayer/>
<FloodBoundaryLayer/>
@ -167,10 +170,12 @@ export const ColouringMap : FC<ColouringMapProps> = ({
<ParcelSwitcher/>
<FloodSwitcher/>
<ConservationAreaSwitcher/>
<HistoricMapSwitcher/>
<HistoricDataSwitcher/>
<VistaSwitcher />
<HousingSwitcher />
<CreativeSwitcher />
</>
: <></>
}

View File

@ -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,

View File

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

View File

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