From aa53d519a5d84e69297a0124efacc1d192ddd527 Mon Sep 17 00:00:00 2001 From: Carlos Felix <63804190+carlosfelix2@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:44:53 -0500 Subject: [PATCH] Search by tract needs to highlight a tract 100% of the time --- client/src/components/J40Map.tsx | 34 ++++++++++--- client/src/components/MapSearch/MapSearch.tsx | 51 +++---------------- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/client/src/components/J40Map.tsx b/client/src/components/J40Map.tsx index 7d8919a5..6dce1f21 100644 --- a/client/src/components/J40Map.tsx +++ b/client/src/components/J40Map.tsx @@ -91,6 +91,7 @@ const J40Map = ({location}: IJ40Interface) => { const [transitionInProgress, setTransitionInProgress] = useState(false); const [geolocationInProgress, setGeolocationInProgress] = useState(false); const [isMobileMapState, setIsMobileMapState] = useState(false); + const [selectTractId, setSelectTractId] = useState(undefined); const {width: windowWidth} = useWindowSize(); /** @@ -123,11 +124,7 @@ const J40Map = ({location}: IJ40Interface) => { const [minLng, minLat, maxLng, maxLat] = bbox(feature); // Set the selectedFeature ID - if (feature.id !== selectedFeatureId) { - setSelectedFeature(feature); - } else { - setSelectedFeature(undefined); - } + setSelectedFeature(feature); // Go to the newly selected feature (as long as it's not an Alaska Point) goToPlace([ @@ -250,7 +247,7 @@ const J40Map = ({location}: IJ40Interface) => { * @param {LngLatBoundsLike} bounds * @param {boolean} isTerritory */ - const goToPlace = (bounds: LngLatBoundsLike, isTerritory = false) => { + const goToPlace = (bounds: LngLatBoundsLike, isTerritory = false, selectTractId: string | undefined = undefined) => { const newViewPort = new WebMercatorViewport({height: viewport.height!, width: viewport.width!}); const {longitude, latitude, zoom} = newViewPort.fitBounds( bounds as [[number, number], [number, number]], { @@ -281,6 +278,9 @@ const J40Map = ({location}: IJ40Interface) => { transitionInterpolator: new FlyToInterpolator(), transitionEasing: d3.easeCubic, }); + + // Set the tract ID to be selected if any. + setSelectTractId(selectTractId); }; const onTransitionStart = () => { @@ -289,6 +289,25 @@ const J40Map = ({location}: IJ40Interface) => { const onTransitionEnd = () => { setTransitionInProgress(false); + + /* + If there is a tract ID to be selected then do so once the map has finished moving. + Note that setting the viewpoint to move the map as done in this component does not + trigger a moveend or idle event like when using flyTo or easeTo. + */ + if (selectTractId) { + // Search for features in the map that have the tract ID. + const geoidSearchResults = mapRef.current?.getMap() + .querySourceFeatures(constants.HIGH_ZOOM_SOURCE_NAME, { + sourceLayer: constants.SCORE_SOURCE_LAYER, + validate: true, + filter: ['==', constants.GEOID_PROPERTY, selectTractId], + }); + if (geoidSearchResults && geoidSearchResults.length > 0) { + selectFeatureOnMap(geoidSearchResults[0]); + } + setSelectTractId(undefined); + } }; const onGeolocate = () => { @@ -393,8 +412,7 @@ const J40Map = ({location}: IJ40Interface) => { {/* This is the first overlayed row on the map: Search and Geolocation */}
- + {/* Geolocate Icon */}
diff --git a/client/src/components/MapSearch/MapSearch.tsx b/client/src/components/MapSearch/MapSearch.tsx index 13e600f7..a5e33849 100644 --- a/client/src/components/MapSearch/MapSearch.tsx +++ b/client/src/components/MapSearch/MapSearch.tsx @@ -4,8 +4,6 @@ import {LngLatBoundsLike} from 'maplibre-gl'; import {useIntl} from 'gatsby-plugin-intl'; import {Search} from '@trussworks/react-uswds'; import {useWindowSize} from 'react-use'; -import {RefObject} from 'react'; -import {MapRef} from 'react-map-gl'; import * as JsSearch from 'js-search'; import * as constants from '../../data/constants'; @@ -15,10 +13,7 @@ import * as styles from './MapSearch.module.scss'; import * as EXPLORE_COPY from '../../data/copy/explore'; interface IMapSearch { - goToPlace(bounds: LngLatBoundsLike):void; - mapRef:RefObject; - selectFeatureOnMap: (feature: any) => void; - selectedFeatureId: string; + goToPlace(bounds: LngLatBoundsLike, isTerritory: boolean, selectTractId: string | undefined):void; } interface ISearchTractRecord { @@ -27,7 +22,7 @@ interface ISearchTractRecord { INTPTLON10: string; } -const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IMapSearch) => { +const MapSearch = ({goToPlace}:IMapSearch) => { // State to hold if the search results are empty or not: const [isSearchResultsNull, setIsSearchResultsNull] = useState(false); const intl = useIntl(); @@ -85,33 +80,15 @@ const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IM const searchForTract = async (tract: string) => { // We create a bounding box just to get the tract in the view box. // The size is not important. - const BOUNDING_BOX_SIZE_DD = 0.1; - - /** - * Wait for the map to be done loading and moving. - * @param {function()} callback the callback to run after the map is ready - */ - const waitforMap = (callback: () => void): void => { - const isMapReady = !!mapRef.current && - mapRef.current.getMap().isStyleLoaded() && - mapRef.current.getMap().isSourceLoaded(constants.HIGH_ZOOM_SOURCE_NAME); - if (isMapReady) { - callback(); - } else { - setTimeout(() => waitforMap(callback), 200); - } - }; + const BOUNDING_BOX_SIZE_DD = 0.2; // Convert 10 digit tracts to 11. - const searchTerm = tract.length == 10 ? '0' + tract : tract; - - // If the search is for the same tract then do nothing. - if (selectedFeatureId == searchTerm) return; + const normalizedTractId = tract.length == 10 ? '0' + tract : tract; setIsSearchResultsNull(true); if (tractSearch) { - const result = tractSearch.search(searchTerm); + const result = tractSearch.search(normalizedTractId); if (result.length > 0) { const searchTractRecord = result[0] as ISearchTractRecord; const lat = Number(searchTractRecord.INTPTLAT10); @@ -126,21 +103,7 @@ const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IM setIsSearchResultsNull(false); // Now move the map and select the tract. - goToPlace([[Number(longMin), Number(latMin)], [Number(longMax), Number(latMax)]]); - waitforMap(() => { - // Set up a one-shot event handler to fire when the flyTo arrives at its destination. Once the - // tract is in view of the map. mpRef.current will always be valid here... - mapRef.current?.getMap().once('idle', () => { - const geoidSearchResults = mapRef.current?.getMap().querySourceFeatures(constants.HIGH_ZOOM_SOURCE_NAME, { - sourceLayer: constants.SCORE_SOURCE_LAYER, - validate: true, - filter: ['==', constants.GEOID_PROPERTY, searchTerm], - }); - if (geoidSearchResults && geoidSearchResults.length > 0) { - selectFeatureOnMap(geoidSearchResults[0]); - } - }); - }); + goToPlace([[Number(longMin), Number(latMin)], [Number(longMax), Number(latMax)]], false, normalizedTractId); } } }; @@ -173,7 +136,7 @@ const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IM if (searchResults && searchResults.length > 0) { setIsSearchResultsNull(false); const [latMin, latMax, longMin, longMax] = searchResults[0].boundingbox; - goToPlace([[Number(longMin), Number(latMin)], [Number(longMax), Number(latMax)]]); + goToPlace([[Number(longMin), Number(latMin)], [Number(longMax), Number(latMax)]], false, undefined); } else { setIsSearchResultsNull(true); }