Skip to content

Commit

Permalink
Reset Map (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
nofurtherinformation authored Nov 11, 2024
1 parent cfb933b commit f857b09
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 9 deletions.
11 changes: 3 additions & 8 deletions app/src/app/components/sidebar/ResetMapButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ import {useMapStore} from '@/app/store/mapStore';
import {Button} from '@radix-ui/themes';

export function ResetMapButton() {
const mapStore = useMapStore.getState();

const handleClickResetMap = () => {
mapStore.setFreshMap(true);
// clear map metrics
mapStore.setMapMetrics(null);
};
const handleClickResetMap = useMapStore(state => state.handleReset)
const noZonesAreAssigned = useMapStore(state => !state.zoneAssignments.size)

return (
<Button onClick={handleClickResetMap} variant={'outline'} disabled>
<Button onClick={handleClickResetMap} variant={'outline'} disabled={noZonesAreAssigned}>
Reset Map
</Button>
);
Expand Down
8 changes: 8 additions & 0 deletions app/src/app/store/mapRenderSubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
PARENT_LAYERS,
CHILD_LAYERS,
getLayerFilter,
BLOCK_SOURCE_ID,
} from '@constants/layers';
import {
ColorZoneAssignmentsState,
Expand Down Expand Up @@ -50,6 +51,13 @@ export const getRenderSubscriptions = (useMapStore: typeof _useMapStore) => {
layersToFilter.forEach(layerId =>
mapRef.setFilter(layerId, getLayerFilter(layerId, shatterIds))
);
shatterIds.parents.forEach((id) => {
mapRef?.removeFeatureState({
source: BLOCK_SOURCE_ID,
id,
sourceLayer: state.mapDocument?.parent_layer,
});
});

mapRef.once('render', () => {
setMapLock(false);
Expand Down
36 changes: 35 additions & 1 deletion app/src/app/store/mapStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import {
LayerVisibility,
PaintEventHandler,
getFeaturesInBbox,
resetZoneColors,
setZones,
} from "../utils/helpers";
import { getRenderSubscriptions } from "./mapRenderSubs";
import { patchShatter } from "../utils/api/mutations";
import { patchReset, patchShatter } from "../utils/api/mutations";
import { getSearchParamsObersver } from "../utils/api/queryParamsListener";
import { getMapMetricsSubs } from "./metricsSubs";
import { getMapEditSubs } from "./mapEditSubs";
Expand Down Expand Up @@ -80,6 +81,7 @@ export interface MapStore {
resetZoneAssignments: () => void;
zonePopulations: Map<Zone, number>;
setZonePopulations: (zone: Zone, population: number) => void;
handleReset: () => void;
accumulatedGeoids: Set<string>;
setAccumulatedGeoids: (geoids: MapStore["accumulatedGeoids"]) => void;
brushSize: number;
Expand Down Expand Up @@ -232,6 +234,38 @@ export const useMapStore = create(
zoneAssignments,
});
},
handleReset: async () => {
const {mapDocument, getMapRef, zoneAssignments, shatterIds} = get();
const document_id = mapDocument?.document_id

if (!document_id) {
console.log("No document ID to reset.");
return;
}
set({
mapLock: true,
appLoadingState: "loading",
});
const resetResponse = await patchReset.mutate(document_id);

if (resetResponse.document_id === document_id) {
const initialState = useMapStore.getInitialState();
resetZoneColors({
zoneAssignments,
mapRef: getMapRef(),
mapDocument,
shatterIds
})

set({
zonePopulations: initialState.zonePopulations,
zoneAssignments: initialState.zoneAssignments,
shatterIds: initialState.shatterIds,
appLoadingState: "loaded",
mapLock: false,
});
}
},
setShatterIds: (
existingParents,
existingChildren,
Expand Down
28 changes: 28 additions & 0 deletions app/src/app/utils/api/apiHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ export interface AssignmentsCreate {
assignments_upserted: number;
}

/**
* Reset assignments response
* @interface
* @property {boolean} success - Confirming if the operation succeeded
* @property {string} document_id - Document ID where assignments were dropped
*/
export interface AssignmentsReset {
success: boolean;
document_id: string;
}

/**
*
* @param assignments
Expand All @@ -206,6 +217,23 @@ export const patchUpdateAssignments: (
});
};

/**
*
* @param assignments
* @returns server object containing the updated assignments per geoid
*/
export const patchUpdateReset: (
document_id: string,
) => Promise<AssignmentsReset> = async (document_id) => {
return await axios
.patch(`${process.env.NEXT_PUBLIC_API_URL}/api/update_assignments/${document_id}/reset`, {
document_id
})
.then((res) => {
return res.data;
});
};

/**
* Shatter result
* @interface
Expand Down
19 changes: 19 additions & 0 deletions app/src/app/utils/api/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import {MutationObserver} from '@tanstack/query-core';
import {queryClient} from './queryClient';
import {
AssignmentsCreate,
AssignmentsReset,
createMapDocument,
patchShatterParents,
patchUpdateAssignments,
patchUpdateReset,
} from '@/app/utils/api/apiHandlers';
import {useMapStore} from '@/app/store/mapStore';
import {mapMetrics} from './queries';
Expand Down Expand Up @@ -43,6 +45,23 @@ export const patchUpdates = new MutationObserver(queryClient, {
},
});


export const patchReset = new MutationObserver(queryClient, {
mutationFn: patchUpdateReset,
onMutate: () => {
console.log("Reseting map");
},
onError: (error) => {
console.log("Error reseting map: ", error);
},
onSuccess: (data: AssignmentsReset) => {
console.log(
`Successfully reset ${data.document_id}`
);
mapMetrics.refetch();
},
});

export const document = new MutationObserver(queryClient, {
mutationFn: createMapDocument,
onMutate: () => {
Expand Down
49 changes: 49 additions & 0 deletions app/src/app/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,55 @@ export const colorZoneAssignments = (
});
};

/**
* resetZoneColors
* Resets the zone colors for the specified feature IDs on the map.
*
* This function sets the feature state for each ID in the provided set or array to indicate that
* the zone color should be reset. It checks if the map document is available and determines
* the appropriate source layer based on the existence of child layers and shatter IDs.
*
* @param {Set<string> | string[]} ids - A set or array of feature IDs for which to reset the zone colors.
* @param {ReturnType<MapStore['getMapRef']>} mapRef - The maplibre map instance used to set the feature state.
* @param {MapStore['mapDocument']} mapDocument - The map document containing layer information.
* @param {MapStore['shatterIds']} shatterIds - The shatter IDs used to determine layer types.
*/
export const resetZoneColors = ({
ids, zoneAssignments, mapRef, mapDocument, shatterIds
}: {
ids?: Set<string> | string[],
zoneAssignments?: MapStore['zoneAssignments']
mapRef: ReturnType<MapStore['getMapRef']>,
mapDocument: MapStore['mapDocument'],
shatterIds: MapStore['shatterIds']
}) => {
const idsToReset = ids ? Array.from(ids) : zoneAssignments ? Array.from(zoneAssignments.keys()) : null
if (!mapDocument || !mapRef || !idsToReset) return
const childLayerExists = mapDocument?.child_layer
const shatterIdsExist = shatterIds.parents.size
const getSourceLayer = childLayerExists && shatterIdsExist
? (id: string) => {
return shatterIds.children.has(id)
? mapDocument.child_layer!
: mapDocument.parent_layer
}
: (_: string) => mapDocument.parent_layer
idsToReset.forEach(id => {
const sourceLayer = getSourceLayer(id)
mapRef?.setFeatureState(
{
source: BLOCK_SOURCE_ID,
id,
sourceLayer,
},
{
selected: true,
zone: null,
}
);
})
}

// property changes on which to re-color assignments
export const colorZoneAssignmentTriggers = [
'zoneAssignments',
Expand Down
20 changes: 20 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,26 @@ async def shatter_parent(
return result


@app.patch(
"/api/update_assignments/{document_id}/reset", status_code=status.HTTP_200_OK
)
async def reset_map(document_id: str, session: Session = Depends(get_session)):
# Drop the partition for the given assignments
partition_name = f'"document.assignments_{document_id}"'
session.execute(text(f"DROP TABLE IF EXISTS {partition_name} CASCADE;"))

# Recreate the partition
session.execute(
text(f"""
CREATE TABLE {partition_name} PARTITION OF document.assignments
FOR VALUES IN ('{document_id}');
""")
)
session.commit()

return {"message": "Assignments partition reset", "document_id": document_id}


# called by getAssignments in apiHandlers.ts
@app.get("/api/get_assignments/{document_id}", response_model=list[AssignmentsResponse])
async def get_assignments(document_id: str, session: Session = Depends(get_session)):
Expand Down
9 changes: 9 additions & 0 deletions backend/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,15 @@ def test_patch_assignments_twice(client, document_id):
assert data[1]["geo_id"] == "202090434001003"


def test_patch_reset_assignments(client, document_id):
test_patch_assignments(client, document_id)
response = client.patch(f"/api/update_assignments/{document_id}/reset")
assert response.status_code == 200
assignments = client.get(f"/api/get_assignments/{document_id}")
assert assignments.status_code == 200
assert len(assignments.json()) == 0


def test_get_document_population_totals_null_assignments(
client, document_id, ks_demo_view_census_blocks
):
Expand Down

0 comments on commit f857b09

Please sign in to comment.