From be6f8303e1ea62c2506eada76b4092add15e34f1 Mon Sep 17 00:00:00 2001 From: ofritz Date: Wed, 26 Nov 2025 17:38:52 +0100 Subject: [PATCH 01/10] feat(conflation): include ConflationProjectPropertyType in ProjectTypeSpecificFields fragment --- app/utils/query.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/utils/query.ts b/app/utils/query.ts index 9a919ee..97454c5 100644 --- a/app/utils/query.ts +++ b/app/utils/query.ts @@ -56,7 +56,7 @@ fragment VectorTileServerPropertyFields on ProjectVectorTileServerConfig { export const PROJECT_TYPE_SPECIFIC_FRAGMENT = gql` ${TILE_SERVER_PROPERTY_FRAGMENT} ${VECTOR_TILE_SERVER_PROPERTY_FRAGMENT} -fragment ProjectTypeSpecificFields on CompareProjectPropertyTypeFindProjectPropertyTypeValidateProjectPropertyTypeValidateImageProjectPropertyTypeCompletenessProjectPropertyTypeStreetProjectPropertyType { +fragment ProjectTypeSpecificFields on CompareProjectPropertyTypeFindProjectPropertyTypeValidateProjectPropertyTypeValidateImageProjectPropertyTypeCompletenessProjectPropertyTypeStreetProjectPropertyTypeConflationProjectPropertyType { ... on CompareProjectPropertyType { __typename aoiGeometry @@ -146,6 +146,15 @@ fragment ProjectTypeSpecificFields on CompareProjectPropertyTypeFindProjectPrope startTime } } + ... on ConflationProjectPropertyType { + __typename + objectSource { + objectGeojsonUrl + } + tileServerProperty { + ...RasterTileServerPropertyFields + } + } } `; From e4065457bc46dab5d2e6baf7940961352758da1b Mon Sep 17 00:00:00 2001 From: ofritz Date: Tue, 2 Dec 2025 16:24:46 +0100 Subject: [PATCH 02/10] feat(conflation): conflation project creation --- .../ObjectSourceInput/index.tsx | 45 ++++++++++++ .../ObjectSourceInput/schema.ts | 34 +++++++++ .../ConflationProjectSpecifics/index.tsx | 69 +++++++++++++++++++ .../ConflationProjectSpecifics/schema.ts | 38 ++++++++++ .../EditProject/UpdateProjectForm/index.tsx | 29 +++++++- .../EditProject/UpdateProjectForm/schema.ts | 20 ++++++ .../NewProject/ProjectGeneralInputs/index.tsx | 6 ++ app/views/NewProject/index.tsx | 18 ++++- 8 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/index.tsx create mode 100644 app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/schema.ts create mode 100644 app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/index.tsx create mode 100644 app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/schema.ts diff --git a/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/index.tsx b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/index.tsx new file mode 100644 index 0000000..86f8c8f --- /dev/null +++ b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/index.tsx @@ -0,0 +1,45 @@ +import { + EntriesAsList, + getErrorObject, + LeafError, + ObjectError, +} from '@togglecorp/toggle-form'; + +import Container from '#components/Container'; +import TextInput from '#components/TextInput'; + +import { PartialConflationObjectSourceInputFields } from './schema'; + +interface Props { + value: PartialConflationObjectSourceInputFields | undefined, + error: LeafError | ObjectError, + setFieldValue: (...entries: EntriesAsList) => void; +} + +function ObjectSourceInput(props: Props) { + const { + value, + error: formError, + setFieldValue, + } = props; + + const error = getErrorObject(formError); + + return ( + + + + ); +} + +export default ObjectSourceInput; diff --git a/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/schema.ts b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/schema.ts new file mode 100644 index 0000000..d0c8907 --- /dev/null +++ b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/ObjectSourceInput/schema.ts @@ -0,0 +1,34 @@ +import { + ObjectSchema, + PartialForm, +} from '@togglecorp/toggle-form'; + +import { + ConflationObjectSourceConfig, + ConflationObjectSourceConfigInput, +} from '#generated/types/graphql'; +import { DeepNonNullable } from '#utils/types'; + +export type ConflationSourceConfigKeys = Exclude; + +export type PartialConflationObjectSourceInputFields = PartialForm< + DeepNonNullable +>; +type ConflationSourceFormSchema = ObjectSchema; +type ConflationSourceInputFields = ReturnType; + +export const defaultObjectSourceInputFormValue: PartialConflationObjectSourceInputFields = { + objectGeojsonUrl: '', +}; + +const objectSourceFormSchema: ConflationSourceFormSchema = { + fields: (): ConflationSourceInputFields => { + const schema: ConflationSourceInputFields = { + objectGeojsonUrl: { required: true }, + }; + + return schema; + }, +}; + +export default objectSourceFormSchema; diff --git a/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/index.tsx b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/index.tsx new file mode 100644 index 0000000..bf7d68f --- /dev/null +++ b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/index.tsx @@ -0,0 +1,69 @@ +import { + EntriesAsList, + getErrorObject, + LeafError, + ObjectError, + useFormObject, +} from '@togglecorp/toggle-form'; + +import RasterTileServerInput from '#components/domain/RasterTileServerInput'; +import { + defaultRasterTileServerInputValue, + type PartialRasterTileServerInputFields, +} from '#components/domain/RasterTileServerInput/schema'; + +import { + defaultObjectSourceInputFormValue, + PartialConflationObjectSourceInputFields, +} from './ObjectSourceInput/schema'; +import ObjectSourceInput from './ObjectSourceInput'; +import { type PartialConflationSpecificFields } from './schema'; + +interface Props { + value: PartialConflationSpecificFields | undefined | null; + error: LeafError | ObjectError; + setFieldValue: (...entries: EntriesAsList) => void; + disabled?: boolean; +} + +function ConflationProjectSpecifics(props: Props) { + const { + value, + error: formError, + setFieldValue, + disabled, + } = props; + + const error = getErrorObject(formError); + + const setTileServerInputFieldValue = useFormObject<'tileServerProperty', PartialRasterTileServerInputFields>( + 'tileServerProperty' as const, + setFieldValue, + defaultRasterTileServerInputValue, + ); + + const setObjectSourceInputFieldValue = useFormObject<'objectSource', PartialConflationObjectSourceInputFields>( + 'objectSource' as const, + setFieldValue, + defaultObjectSourceInputFormValue, + ); + + return ( + <> + + + + ); +} + +export default ConflationProjectSpecifics; diff --git a/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/schema.ts b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/schema.ts new file mode 100644 index 0000000..97defd4 --- /dev/null +++ b/app/views/EditProject/UpdateProjectForm/ConflationProjectSpecifics/schema.ts @@ -0,0 +1,38 @@ +import { + ObjectSchema, + PartialForm, +} from '@togglecorp/toggle-form'; + +import rasterTileServerFormSchema, { defaultRasterTileServerInputValue } from '#components/domain/RasterTileServerInput/schema'; +import { ConflationProjectPropertyInput } from '#generated/types/graphql'; +import { DeepNonNullable } from '#utils/types'; + +import { + type PartialProjectUpdateInput, + type UpdateProjectContext, +} from '../schema'; +import objectSourceFormSchema, { defaultObjectSourceInputFormValue } from './ObjectSourceInput/schema'; + +export type PartialConflationSpecificFields = PartialForm< + DeepNonNullable, + 'clientId' +>; +type ConflationSpecificFormSchema = ObjectSchema< + PartialConflationSpecificFields, + PartialProjectUpdateInput, + UpdateProjectContext +>; + +export const defaultConflationSpecificFormValue: PartialConflationSpecificFields = { + objectSource: defaultObjectSourceInputFormValue, + tileServerProperty: defaultRasterTileServerInputValue, +}; + +const conflationSpecificFormSchema: ConflationSpecificFormSchema = { + fields: (): ReturnType => ({ + objectSource: objectSourceFormSchema, + tileServerProperty: rasterTileServerFormSchema, + }), +}; + +export default conflationSpecificFormSchema; diff --git a/app/views/EditProject/UpdateProjectForm/index.tsx b/app/views/EditProject/UpdateProjectForm/index.tsx index c327432..fbe7f4d 100644 --- a/app/views/EditProject/UpdateProjectForm/index.tsx +++ b/app/views/EditProject/UpdateProjectForm/index.tsx @@ -55,6 +55,11 @@ import { defaultCompletenessSpecificFormValue, PartialCompletenessSpecificFields, } from './CompletenessProjectSpecifics/schema.ts'; +import ConflationProjectSpecifics from './ConflationProjectSpecifics/index.tsx'; +import { + defaultConflationSpecificFormValue, + PartialConflationSpecificFields, +} from './ConflationProjectSpecifics/schema.ts'; import { defaultFindSpecificFormValue, PartialFindSpecificFields, @@ -134,6 +139,10 @@ function UpdateProjectForm(props: Props) { return defaultFindSpecificFormValue; } + if (projectData.project.projectType === ProjectTypeEnum.Conflation) { + return defaultConflationSpecificFormValue; + } + if (projectData.project.projectType === ProjectTypeEnum.Compare) { return defaultCompareSpecificFormValue; } @@ -373,7 +382,7 @@ function UpdateProjectForm(props: Props) { const setValidateImageProjectSpecificsFieldValue = useFormObject<'validateImage', PartialValidateImageSpecificFields>( 'validateImage', setProjectSpecificFieldValue, - defaultValidateSpecificFormValue, + defaultValidateImageSpecificFormValue, ); const setCompletenessProjectSpecificsFieldValue = useFormObject<'completeness', PartialCompletenessSpecificFields>( @@ -385,7 +394,13 @@ function UpdateProjectForm(props: Props) { const setStreetProjectSpecificsFieldValue = useFormObject<'street', PartialStreetSpecificFields>( 'street', setProjectSpecificFieldValue, - defaultValidateSpecificFormValue, + defaultStreetSpecificFormValue, + ); + + const setConflationSpecificsFieldValue = useFormObject<'conflation', PartialConflationSpecificFields>( + 'conflation', + setProjectSpecificFieldValue, + defaultConflationSpecificFormValue, ); const pending = updateProjectPending; @@ -416,6 +431,8 @@ function UpdateProjectForm(props: Props) { ?.validateImage as PartialValidateSpecificFields | undefined; const streetProjectTypeSpecifics = value.projectTypeSpecifics ?.street as PartialStreetSpecificFields | undefined; + const conflationProjectTypeSpecifics = value.projectTypeSpecifics + ?.conflation as PartialConflationSpecificFields | undefined; return ( )} + {projectContext.projectType === ProjectTypeEnum.Conflation && ( + + )} ); diff --git a/app/views/EditProject/UpdateProjectForm/schema.ts b/app/views/EditProject/UpdateProjectForm/schema.ts index 1004f09..d19facd 100644 --- a/app/views/EditProject/UpdateProjectForm/schema.ts +++ b/app/views/EditProject/UpdateProjectForm/schema.ts @@ -14,6 +14,7 @@ import { DeepNonNullable } from '#utils/types'; import compareSpecificFormSchema from './CompareProjectSpecifics/schema'; import completenessSpecificFormSchema from './CompletenessProjectSpecifics/schema'; +import conflationSpecificFormSchema from './ConflationProjectSpecifics/schema'; import findSpecificFormSchema from './FindProjectSpecifics/schema'; import streetSpecificFormSchema from './StreetProjectSpecifics/schema.ts'; import validateImageSpecificFormSchema from './ValidateImageProjectSpecifics/schema.ts'; @@ -84,6 +85,7 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { completeness: { forceValue: undefinedValue }, validate: { forceValue: undefinedValue }, validateImage: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, }; } @@ -95,6 +97,7 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { completeness: { forceValue: undefinedValue }, validate: { forceValue: undefinedValue }, validateImage: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, }; } @@ -106,6 +109,19 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { compare: { forceValue: undefinedValue }, validate: { forceValue: undefinedValue }, validateImage: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, + }; + } + + if (context?.projectType === ProjectTypeEnum.Conflation) { + return { + street: { forceValue: undefinedValue }, + validate: { forceValue: undefinedValue }, + completeness: { forceValue: undefinedValue }, + find: { forceValue: undefinedValue }, + compare: { forceValue: undefinedValue }, + validateImage: { forceValue: undefinedValue }, + conflation: conflationSpecificFormSchema, }; } @@ -117,6 +133,7 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { find: { forceValue: undefinedValue }, compare: { forceValue: undefinedValue }, validateImage: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, }; } @@ -128,6 +145,7 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { find: { forceValue: undefinedValue }, compare: { forceValue: undefinedValue }, validate: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, }; } @@ -139,6 +157,7 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { find: { forceValue: undefinedValue }, compare: { forceValue: undefinedValue }, validate: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, }; } @@ -151,6 +170,7 @@ const projectUpdateFormSchema: ProjectUpdateFormSchema = { completeness: { forceValue: undefinedValue }, validate: { forceValue: undefinedValue }, validateImage: { forceValue: undefinedValue }, + conflation: { forceValue: undefinedValue }, }; }, }, diff --git a/app/views/NewProject/ProjectGeneralInputs/index.tsx b/app/views/NewProject/ProjectGeneralInputs/index.tsx index 7454095..637db76 100644 --- a/app/views/NewProject/ProjectGeneralInputs/index.tsx +++ b/app/views/NewProject/ProjectGeneralInputs/index.tsx @@ -60,6 +60,7 @@ const hintText: Record< [ProjectTypeEnum.ValidateImage]: 'Enter the description for your project. (markdown syntax is supported)', [ProjectTypeEnum.Completeness]: 'Enter the description for your project. (markdown syntax is supported)', [ProjectTypeEnum.Street]: 'Enter the description for your project. (markdown syntax is supported)', + [ProjectTypeEnum.Conflation]: 'Enter the description for your project. (markdown syntax is supported)', }, topic: { [ProjectTypeEnum.Find]: 'Enter the title of your project. It should begin with project type. (e.g., "Find Features") ', @@ -68,6 +69,7 @@ const hintText: Record< [ProjectTypeEnum.ValidateImage]: 'Enter the title of your project. It should begin with project type. (e.g., "Assess Images")', [ProjectTypeEnum.Completeness]: 'Enter the title of your project. It should begin with project type. (e.g., "Check Completeness")', [ProjectTypeEnum.Street]: 'Enter the title of your project. It should begin with project type. (e.g., "View Streets") ', + [ProjectTypeEnum.Street]: 'Enter the title of your project. It should begin with project type. (e.g., "Conflate Features") ', }, projectInstruction: { [ProjectTypeEnum.Find]: 'What should the users look for (e.g. You are looking for: buildings, destroyed buildings, cars, trees, etc.)', @@ -100,6 +102,7 @@ const hintText: Record< [ProjectTypeEnum.ValidateImage]: 'Is this project part of a bigger campaign with multiple projects? If so, increment this number up by one each time you create a new project in the series. ', [ProjectTypeEnum.Completeness]: 'Is this project part of a bigger campaign with multiple projects? If so, increment this number up by one each time you create a new project in the series. ', [ProjectTypeEnum.Street]: 'Is this project part of a bigger campaign with multiple projects? If so, increment this number up by one each time you create a new project in the series. ', + [ProjectTypeEnum.Conflation]: 'Is this project part of a bigger campaign with multiple projects? If so, increment this number up by one each time you create a new project in the series. ', }, region: { [ProjectTypeEnum.Find]: 'Enter the region/location of your project (eg: City, Country)', @@ -108,6 +111,7 @@ const hintText: Record< [ProjectTypeEnum.ValidateImage]: 'Enter the project location in the format "city/region, country".', [ProjectTypeEnum.Completeness]: 'Enter the region/location of your project (eg: City, Country)', [ProjectTypeEnum.Street]: 'Enter the region/location of your project (eg: City, Country)', + [ProjectTypeEnum.Conflation]: 'Enter the region/location of your project (eg: City, Country)', }, requestingOrganization: { [ProjectTypeEnum.Find]: 'Which group, institution or community is requesting this project?', @@ -116,6 +120,7 @@ const hintText: Record< [ProjectTypeEnum.ValidateImage]: 'Which group, institution, or community is requesting this project?', [ProjectTypeEnum.Completeness]: 'Which group, institution or community is requesting this project?', [ProjectTypeEnum.Street]: 'Which group, institution or community is requesting this project?', + [ProjectTypeEnum.Conflation]: 'Which group, institution or community is requesting this project?', }, team: { [ProjectTypeEnum.Find]: 'Please note that if \'private\', this project will only be visible to the selected team members', @@ -124,6 +129,7 @@ const hintText: Record< [ProjectTypeEnum.ValidateImage]: 'Please note that if selected, this project will only be visible to the team members.', [ProjectTypeEnum.Completeness]: 'Please note that if \'private\', this project will only be visible to the selected team members', [ProjectTypeEnum.Street]: 'Please note that if \'private\', this project will only be visible to the selected team members', + [ProjectTypeEnum.Conflation]: 'Please note that if \'private\', this project will only be visible to the selected team members', }, }; diff --git a/app/views/NewProject/index.tsx b/app/views/NewProject/index.tsx index d84b397..21fe166 100644 --- a/app/views/NewProject/index.tsx +++ b/app/views/NewProject/index.tsx @@ -156,7 +156,23 @@ const projectTypeDescriptions: Record = { name="street-alert" title="MapSwipe Web only" type="warning" - description="Street project are currently only available in the MapSwipe web app." + description="Street projects are currently only available in the MapSwipe web app." + fullWidth + withoutShadow + /> + + ), + [ProjectTypeEnum.Conflation]: ( + +
+ Validate AI generated features and compare with + existing features on OpenStreetMap. +
+ From 7cd1caaf192bddffe7598481d94712af45096dab Mon Sep 17 00:00:00 2001 From: ofritz Date: Wed, 3 Dec 2025 13:00:21 +0100 Subject: [PATCH 03/10] feat(conflation): update backend submodule pointer --- backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend b/backend index e1b0df8..858bfad 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit e1b0df8f3e270293b0b9fc9936254b4f859c61a9 +Subproject commit 858bfadab0f5f987f4fbef2499597f8601e2bd0c From f8dae7c2786dfffaabcefd039f801dbe60ec6e54 Mon Sep 17 00:00:00 2001 From: ofritz Date: Mon, 8 Dec 2025 17:42:36 +0100 Subject: [PATCH 04/10] feat(conflation): update backend submodule pointer --- backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend b/backend index 858bfad..7015057 160000 --- a/backend +++ b/backend @@ -1 +1 @@ -Subproject commit 858bfadab0f5f987f4fbef2499597f8601e2bd0c +Subproject commit 701505789d21b9139d1490bb6c84df06e57204ae From e6da863877eb3d3e03190e510c8790d4adfa7254 Mon Sep 17 00:00:00 2001 From: ofritz Date: Wed, 10 Dec 2025 14:08:11 +0100 Subject: [PATCH 05/10] feat(conflation): add and edit conflation tutorial --- .../ConflationPropertyInput/index.tsx | 59 +++++++++++++++++ .../ConflationPropertyInput/schema.ts | 21 ++++++ .../ConflationPropertyInput/styles.module.css | 8 +++ .../ScenarioPageInput/TaskInput/index.tsx | 16 +++++ .../ScenarioPageInput/TaskInput/schema.ts | 20 ++++++ app/views/EditTutorial/index.tsx | 64 +++++++++++++++++++ backend | 2 +- 7 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 app/views/EditTutorial/ScenarioPageInput/TaskInput/ConflationPropertyInput/index.tsx create mode 100644 app/views/EditTutorial/ScenarioPageInput/TaskInput/ConflationPropertyInput/schema.ts create mode 100644 app/views/EditTutorial/ScenarioPageInput/TaskInput/ConflationPropertyInput/styles.module.css diff --git a/app/views/EditTutorial/ScenarioPageInput/TaskInput/ConflationPropertyInput/index.tsx b/app/views/EditTutorial/ScenarioPageInput/TaskInput/ConflationPropertyInput/index.tsx new file mode 100644 index 0000000..652a0aa --- /dev/null +++ b/app/views/EditTutorial/ScenarioPageInput/TaskInput/ConflationPropertyInput/index.tsx @@ -0,0 +1,59 @@ +import { _cs } from '@togglecorp/fujs'; +import { + EntriesAsList, + getErrorObject, + LeafError, + ObjectError, +} from '@togglecorp/toggle-form'; + +import NumberInput from '#components/NumberInput'; +import TextArea from '#components/TextArea'; + +import { PartialConflationPropertyInputFields } from './schema.ts'; + +import styles from './styles.module.css'; + +interface Props { + className?: string; + value: PartialConflationPropertyInputFields | undefined; + setFieldValue: (...entries: EntriesAsList) => void; + error: LeafError | ObjectError; + disabled?: boolean; +} + +function ConflationPropertyInput(props: Props) { + const { + className, + value, + setFieldValue, + error: formError, + disabled, + } = props; + + const error = getErrorObject(formError); + + return ( +
+ +