mirror of
https://github.com/DOI-DO/j40-cejst-2.git
synced 2025-08-07 03:54:19 -07:00
Search by tract needs to highlight a tract 100% of the time
This commit is contained in:
parent
5ec0d228f7
commit
aa53d519a5
2 changed files with 33 additions and 52 deletions
|
@ -91,6 +91,7 @@ const J40Map = ({location}: IJ40Interface) => {
|
||||||
const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false);
|
const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false);
|
||||||
const [geolocationInProgress, setGeolocationInProgress] = useState<boolean>(false);
|
const [geolocationInProgress, setGeolocationInProgress] = useState<boolean>(false);
|
||||||
const [isMobileMapState, setIsMobileMapState] = useState<boolean>(false);
|
const [isMobileMapState, setIsMobileMapState] = useState<boolean>(false);
|
||||||
|
const [selectTractId, setSelectTractId] = useState<string | undefined>(undefined);
|
||||||
const {width: windowWidth} = useWindowSize();
|
const {width: windowWidth} = useWindowSize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,11 +124,7 @@ const J40Map = ({location}: IJ40Interface) => {
|
||||||
const [minLng, minLat, maxLng, maxLat] = bbox(feature);
|
const [minLng, minLat, maxLng, maxLat] = bbox(feature);
|
||||||
|
|
||||||
// Set the selectedFeature ID
|
// Set the selectedFeature ID
|
||||||
if (feature.id !== selectedFeatureId) {
|
setSelectedFeature(feature);
|
||||||
setSelectedFeature(feature);
|
|
||||||
} else {
|
|
||||||
setSelectedFeature(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the newly selected feature (as long as it's not an Alaska Point)
|
// Go to the newly selected feature (as long as it's not an Alaska Point)
|
||||||
goToPlace([
|
goToPlace([
|
||||||
|
@ -250,7 +247,7 @@ const J40Map = ({location}: IJ40Interface) => {
|
||||||
* @param {LngLatBoundsLike} bounds
|
* @param {LngLatBoundsLike} bounds
|
||||||
* @param {boolean} isTerritory
|
* @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 newViewPort = new WebMercatorViewport({height: viewport.height!, width: viewport.width!});
|
||||||
const {longitude, latitude, zoom} = newViewPort.fitBounds(
|
const {longitude, latitude, zoom} = newViewPort.fitBounds(
|
||||||
bounds as [[number, number], [number, number]], {
|
bounds as [[number, number], [number, number]], {
|
||||||
|
@ -281,6 +278,9 @@ const J40Map = ({location}: IJ40Interface) => {
|
||||||
transitionInterpolator: new FlyToInterpolator(),
|
transitionInterpolator: new FlyToInterpolator(),
|
||||||
transitionEasing: d3.easeCubic,
|
transitionEasing: d3.easeCubic,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set the tract ID to be selected if any.
|
||||||
|
setSelectTractId(selectTractId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTransitionStart = () => {
|
const onTransitionStart = () => {
|
||||||
|
@ -289,6 +289,25 @@ const J40Map = ({location}: IJ40Interface) => {
|
||||||
|
|
||||||
const onTransitionEnd = () => {
|
const onTransitionEnd = () => {
|
||||||
setTransitionInProgress(false);
|
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 = () => {
|
const onGeolocate = () => {
|
||||||
|
@ -393,8 +412,7 @@ const J40Map = ({location}: IJ40Interface) => {
|
||||||
|
|
||||||
{/* This is the first overlayed row on the map: Search and Geolocation */}
|
{/* This is the first overlayed row on the map: Search and Geolocation */}
|
||||||
<div className={styles.mapHeaderRow}>
|
<div className={styles.mapHeaderRow}>
|
||||||
<MapSearch goToPlace={goToPlace} mapRef={mapRef} selectFeatureOnMap={selectFeatureOnMap}
|
<MapSearch goToPlace={goToPlace}/>
|
||||||
selectedFeatureId={selectedFeatureId}/>
|
|
||||||
|
|
||||||
{/* Geolocate Icon */}
|
{/* Geolocate Icon */}
|
||||||
<div className={styles.geolocateBox}>
|
<div className={styles.geolocateBox}>
|
||||||
|
|
|
@ -4,8 +4,6 @@ import {LngLatBoundsLike} from 'maplibre-gl';
|
||||||
import {useIntl} from 'gatsby-plugin-intl';
|
import {useIntl} from 'gatsby-plugin-intl';
|
||||||
import {Search} from '@trussworks/react-uswds';
|
import {Search} from '@trussworks/react-uswds';
|
||||||
import {useWindowSize} from 'react-use';
|
import {useWindowSize} from 'react-use';
|
||||||
import {RefObject} from 'react';
|
|
||||||
import {MapRef} from 'react-map-gl';
|
|
||||||
import * as JsSearch from 'js-search';
|
import * as JsSearch from 'js-search';
|
||||||
import * as constants from '../../data/constants';
|
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';
|
import * as EXPLORE_COPY from '../../data/copy/explore';
|
||||||
|
|
||||||
interface IMapSearch {
|
interface IMapSearch {
|
||||||
goToPlace(bounds: LngLatBoundsLike):void;
|
goToPlace(bounds: LngLatBoundsLike, isTerritory: boolean, selectTractId: string | undefined):void;
|
||||||
mapRef:RefObject<MapRef>;
|
|
||||||
selectFeatureOnMap: (feature: any) => void;
|
|
||||||
selectedFeatureId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISearchTractRecord {
|
interface ISearchTractRecord {
|
||||||
|
@ -27,7 +22,7 @@ interface ISearchTractRecord {
|
||||||
INTPTLON10: string;
|
INTPTLON10: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IMapSearch) => {
|
const MapSearch = ({goToPlace}:IMapSearch) => {
|
||||||
// State to hold if the search results are empty or not:
|
// State to hold if the search results are empty or not:
|
||||||
const [isSearchResultsNull, setIsSearchResultsNull] = useState(false);
|
const [isSearchResultsNull, setIsSearchResultsNull] = useState(false);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -85,33 +80,15 @@ const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IM
|
||||||
const searchForTract = async (tract: string) => {
|
const searchForTract = async (tract: string) => {
|
||||||
// We create a bounding box just to get the tract in the view box.
|
// We create a bounding box just to get the tract in the view box.
|
||||||
// The size is not important.
|
// The size is not important.
|
||||||
const BOUNDING_BOX_SIZE_DD = 0.1;
|
const BOUNDING_BOX_SIZE_DD = 0.2;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert 10 digit tracts to 11.
|
// Convert 10 digit tracts to 11.
|
||||||
const searchTerm = tract.length == 10 ? '0' + tract : tract;
|
const normalizedTractId = tract.length == 10 ? '0' + tract : tract;
|
||||||
|
|
||||||
// If the search is for the same tract then do nothing.
|
|
||||||
if (selectedFeatureId == searchTerm) return;
|
|
||||||
|
|
||||||
setIsSearchResultsNull(true);
|
setIsSearchResultsNull(true);
|
||||||
|
|
||||||
if (tractSearch) {
|
if (tractSearch) {
|
||||||
const result = tractSearch.search(searchTerm);
|
const result = tractSearch.search(normalizedTractId);
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
const searchTractRecord = result[0] as ISearchTractRecord;
|
const searchTractRecord = result[0] as ISearchTractRecord;
|
||||||
const lat = Number(searchTractRecord.INTPTLAT10);
|
const lat = Number(searchTractRecord.INTPTLAT10);
|
||||||
|
@ -126,21 +103,7 @@ const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IM
|
||||||
setIsSearchResultsNull(false);
|
setIsSearchResultsNull(false);
|
||||||
|
|
||||||
// Now move the map and select the tract.
|
// Now move the map and select the tract.
|
||||||
goToPlace([[Number(longMin), Number(latMin)], [Number(longMax), Number(latMax)]]);
|
goToPlace([[Number(longMin), Number(latMin)], [Number(longMax), Number(latMax)]], false, normalizedTractId);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -173,7 +136,7 @@ const MapSearch = ({goToPlace, mapRef, selectFeatureOnMap, selectedFeatureId}:IM
|
||||||
if (searchResults && searchResults.length > 0) {
|
if (searchResults && searchResults.length > 0) {
|
||||||
setIsSearchResultsNull(false);
|
setIsSearchResultsNull(false);
|
||||||
const [latMin, latMax, longMin, longMax] = searchResults[0].boundingbox;
|
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 {
|
} else {
|
||||||
setIsSearchResultsNull(true);
|
setIsSearchResultsNull(true);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue