Skip to content

Commit

Permalink
Correct sitesInMap calc to handle close zoom levels
Browse files Browse the repository at this point in the history
  • Loading branch information
Frencil committed Jun 17, 2020
1 parent f7aaabe commit 0996b3f
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 60 deletions.
4 changes: 2 additions & 2 deletions lib/components/SiteMap/SiteMapContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ var SiteMapContainer = function SiteMapContainer(props) {
var _SiteMapContext$useSi = _SiteMapContext.default.useSiteMapContext(),
_SiteMapContext$useSi2 = _slicedToArray(_SiteMapContext$useSi, 2),
state = _SiteMapContext$useSi2[0],
dispatch = _SiteMapContext$useSi2[1];
dispatch = _SiteMapContext$useSi2[1]; // console.log('CONTAINER STATE:', state);


console.log('CONTAINER STATE:', state);
var isLoading = state.overallFetch.expected !== state.overallFetch.completed;
var aspectRatio = state.aspectRatio,
view = state.view.current;
Expand Down
1 change: 0 additions & 1 deletion lib/components/SiteMap/SiteMapContext.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ declare function useSiteMapContext(): any[] | {
};
focusLocation: {
current: null;
isCenteredOn: boolean;
data: null;
fetch: {
status: null;
Expand Down
16 changes: 1 addition & 15 deletions lib/components/SiteMap/SiteMapContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,7 @@ var centerIsValid = function centerIsValid(center) {
};

var calculateFeatureDataFetches = function calculateFeatureDataFetches(state) {
var sitesInMap = (0, _SiteMapUtils.calculateLocationsInMap)(state.sites, state.map.bounds, true); // If we are still centered on our focus location and it has a corresponding siteCode then add the
// siteCode manually. This happens when we jump directly to a location within a site where no loc
// level data has yet been fetched and the zoom is close enough that the center point for the site
// is not in the map view.
// TODO: This approach helps catch site in map on a focus location, but quickly fails when panning
// around a site while zoomed in. Need true sitesInMap calc using sampling boundary / aq. reach

if (state.focusLocation.isCenteredOn && Object.keys(state.sites).includes(state.focusLocation.data.siteCode) && !sitesInMap.includes(state.focusLocation.data.siteCode)) {
sitesInMap.push(state.focusLocation.data.siteCode);
}
var sitesInMap = (0, _SiteMapUtils.calculateLocationsInMap)(state.sites, state.map.bounds, true, 0.06);

if (!sitesInMap.length) {
return state;
Expand Down Expand Up @@ -467,7 +458,6 @@ var reducer = function reducer(state, action) {
}

newState.map.zoomedIcons = (0, _SiteMapUtils.getZoomedIcons)(newState.map.zoom);
newState.focusLocation.isCenteredOn = false;
updateMapTileWithZoom();
return calculateFeatureDataFetches((0, _SiteMapUtils.calculateFeatureAvailability)(newState));

Expand All @@ -476,7 +466,6 @@ var reducer = function reducer(state, action) {
newState.map.bounds = action.bounds;
}

newState.focusLocation.isCenteredOn = false;
return calculateFeatureDataFetches(newState);

case 'setMapCenter':
Expand All @@ -489,7 +478,6 @@ var reducer = function reducer(state, action) {
}

newState.map.center = _toConsumableArray(action.center);
newState.focusLocation.isCenteredOn = false;
return calculateFeatureDataFetches(newState);

case 'setMapTileLayer':
Expand Down Expand Up @@ -541,7 +529,6 @@ var reducer = function reducer(state, action) {
error: null
};
newState.focusLocation.current = action.location;
newState.focusLocation.isCenteredOn = false;
newState.focusLocation.data = null;
newState.overallFetch.expected += 1;

Expand Down Expand Up @@ -588,7 +575,6 @@ var reducer = function reducer(state, action) {

completeOverallFetch();
newState.map = (0, _SiteMapUtils.getMapStateForFocusLocation)(newState);
newState.focusLocation.isCenteredOn = true;
updateMapTileWithZoom();
return calculateFeatureDataFetches((0, _SiteMapUtils.calculateFeatureAvailability)(newState));
// Fetch and Import
Expand Down
2 changes: 1 addition & 1 deletion lib/components/SiteMap/SiteMapUtils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,5 +777,5 @@ export function parseLocationHierarchy(inHierarchy: any, parent?: any): {};
export function getZoomedIcons(zoom: any): {};
export function getMapStateForFocusLocation(state?: {}): any;
export function boundsAreValid(bounds: any): boolean;
export function calculateLocationsInMap(locations: any, bounds?: any, buffer?: boolean): string[];
export function calculateLocationsInMap(locations: any, bounds?: any, extendMap?: boolean, extendPoints?: number): string[];
import PropTypes from "prop-types";
66 changes: 55 additions & 11 deletions lib/components/SiteMap/SiteMapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ var _shadow, _shadow2, _shadow3, _shadow4, _availableFeatureType, _derived;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
Expand Down Expand Up @@ -422,7 +430,7 @@ var FEATURES = {
name: 'Site Sampling Boundaries',
nameSingular: 'Site Sampling Boundary',
type: FEATURE_TYPES.BOUNDARIES,
minZoom: 8,
minZoom: 9,
dataLoadType: FEATURE_DATA_LOAD_TYPES.IMPORT,
description: 'Terrestrial and Colocated Aquatic Sites',
parent: 'TERRESTRIAL_SITE_FEATURES',
Expand Down Expand Up @@ -464,7 +472,7 @@ var FEATURES = {
TERRESTRIAL_SITE_FEATURES: {
name: 'Terrestrial Site Features',
type: FEATURE_TYPES.GROUP,
minZoom: 10,
minZoom: 9,
description: '',
dataLoadType: FEATURE_DATA_LOAD_TYPES.FETCH,
matchLocationType: 'OS Plot - all',
Expand Down Expand Up @@ -1086,8 +1094,6 @@ var DEFAULT_STATE = {
},
focusLocation: {
current: null,
isCenteredOn: false,
// Whether the map is still centered on and zoomed into focus location
data: null,
fetch: {
status: null,
Expand Down Expand Up @@ -1665,9 +1671,11 @@ var boundsAreValid = function boundsAreValid(bounds) {

exports.boundsAreValid = boundsAreValid;

var calculateLocationsInMap = function calculateLocationsInMap(locations) {
var calculateLocationsInMap = function calculateLocationsInMap(locations) // Number, a margin to add/subtract to lat/lon for a point's hit box
{
var bounds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var buffer = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var extendMap = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var extendPoints = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

if (!locations || _typeof(locations) !== 'object' || !Object.keys(locations).length) {
return [];
Expand All @@ -1681,13 +1689,49 @@ var calculateLocationsInMap = function calculateLocationsInMap(locations) {
return [];
}

var extendedBounds = !buffer ? bounds : Object.fromEntries(Object.keys(bounds).map(function (dir) {
var bufferValue = (bounds[dir][1] - bounds[dir][0]) / 2;
return [dir, [bounds[dir][0] - bufferValue, bounds[dir][1] + bufferValue]];
}));
var extendedBounds = !extendMap ? bounds : Object.fromEntries(Object.keys(bounds).map(function (dir) {
var buffer = (bounds[dir][1] - bounds[dir][0]) / 2;
return [dir, [bounds[dir][0] - buffer, bounds[dir][1] + buffer]];
})); // This function flattens a geometry object to just coordinates so we can check if a boundary
// is in the map. NOTE: extendPoints does not work with boundaries, only solitary points.

var flatten = function flatten(items) {
var isCoord = function isCoord(c) {
return Array.isArray(c) && c.length === 2 && c.every(function (x) {
return Number.isFinite(x);
});
};

var flat = [];
items.forEach(function (item) {
if (Array.isArray(item) && !isCoord(item)) {
flat.push.apply(flat, _toConsumableArray(flatten(item)));
} else {
flat.push(item);
}
});
return flat;
};

var isInBounds = function isInBounds(loc) {
return Number.isFinite(loc.latitude) && Number.isFinite(loc.longitude) && loc.latitude >= extendedBounds.lat[0] && loc.latitude <= extendedBounds.lat[1] && loc.longitude >= extendedBounds.lng[0] && loc.longitude <= extendedBounds.lng[1];
if (Number.isFinite(loc.latitude) && Number.isFinite(loc.longitude)) {
if (extendPoints > 0) {
var lats = [loc.latitude - extendPoints, loc.latitude + extendPoints];
var lngs = [loc.longitude - extendPoints, loc.longitude + extendPoints];
return !(lats[0] > extendedBounds.lat[1] || lats[1] < extendedBounds.lat[0] || lngs[0] > extendedBounds.lng[1] || lngs[1] < extendedBounds.lng[0]);
}

return loc.latitude >= extendedBounds.lat[0] && loc.latitude <= extendedBounds.lat[1] && loc.longitude >= extendedBounds.lng[0] && loc.longitude <= extendedBounds.lng[1];
}

if (loc.geometry && loc.geometry.coordinates) {
var flatCoords = flatten(loc.geometry.coordinates);
return flatCoords.some(function (coord) {
return coord.latitude >= extendedBounds.lat[0] && coord.latitude <= extendedBounds.lat[1] && coord.longitude >= extendedBounds.lng[0] && coord.longitude <= extendedBounds.lng[1];
});
}

return false;
};

return Object.keys(locations).filter(function (locId) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib_components/components/SiteMap/SiteMapContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const SiteMapContainer = (props) => {
const [neonContextState] = NeonContext.useNeonContextState();

const [state, dispatch] = SiteMapContext.useSiteMapContext();
console.log('CONTAINER STATE:', state);
// console.log('CONTAINER STATE:', state);
const isLoading = state.overallFetch.expected !== state.overallFetch.completed;

const { aspectRatio, view: { current: view } } = state;
Expand Down
18 changes: 1 addition & 17 deletions src/lib_components/components/SiteMap/SiteMapContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,7 @@ const centerIsValid = center => (
Array.isArray(center) && center.length === 2 && center.every(v => typeof v === 'number')
);
const calculateFeatureDataFetches = (state) => {
const sitesInMap = calculateLocationsInMap(state.sites, state.map.bounds, true);
// If we are still centered on our focus location and it has a corresponding siteCode then add the
// siteCode manually. This happens when we jump directly to a location within a site where no loc
// level data has yet been fetched and the zoom is close enough that the center point for the site
// is not in the map view.
// TODO: This approach helps catch site in map on a focus location, but quickly fails when panning
// around a site while zoomed in. Need true sitesInMap calc using sampling boundary / aq. reach
if (
state.focusLocation.isCenteredOn
&& Object.keys(state.sites).includes(state.focusLocation.data.siteCode)
&& !sitesInMap.includes(state.focusLocation.data.siteCode)
) { sitesInMap.push(state.focusLocation.data.siteCode); }
const sitesInMap = calculateLocationsInMap(state.sites, state.map.bounds, true, 0.06);
if (!sitesInMap.length) { return state; }
const domainsInMap = new Set();
sitesInMap
Expand Down Expand Up @@ -384,22 +373,19 @@ const reducer = (state, action) => {
if (centerIsValid(action.center)) { newState.map.center = action.center; }
if (boundsAreValid(action.bounds)) { newState.map.bounds = action.bounds; }
newState.map.zoomedIcons = getZoomedIcons(newState.map.zoom);
newState.focusLocation.isCenteredOn = false;
updateMapTileWithZoom();
return calculateFeatureDataFetches(
calculateFeatureAvailability(newState),
);

case 'setMapBounds':
if (boundsAreValid(action.bounds)) { newState.map.bounds = action.bounds; }
newState.focusLocation.isCenteredOn = false;
return calculateFeatureDataFetches(newState);

case 'setMapCenter':
if (!centerIsValid(action.center)) { return state; }
if (boundsAreValid(action.bounds)) { newState.map.bounds = action.bounds; }
newState.map.center = [...action.center];
newState.focusLocation.isCenteredOn = false;
return calculateFeatureDataFetches(newState);

case 'setMapTileLayer':
Expand Down Expand Up @@ -438,7 +424,6 @@ const reducer = (state, action) => {
case 'setNewFocusLocation':
newState.focusLocation.fetch = { status: FETCH_STATUS.AWAITING_CALL, error: null };
newState.focusLocation.current = action.location;
newState.focusLocation.isCenteredOn = false;
newState.focusLocation.data = null;
newState.overallFetch.expected += 1;
if (newState.view.current !== VIEWS.MAP) { newState.view.current = VIEWS.MAP; }
Expand Down Expand Up @@ -473,7 +458,6 @@ const reducer = (state, action) => {
}
completeOverallFetch();
newState.map = getMapStateForFocusLocation(newState);
newState.focusLocation.isCenteredOn = true;
updateMapTileWithZoom();
return calculateFeatureDataFetches(
calculateFeatureAvailability(newState),
Expand Down
61 changes: 49 additions & 12 deletions src/lib_components/components/SiteMap/SiteMapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ export const FEATURES = {
name: 'Site Sampling Boundaries',
nameSingular: 'Site Sampling Boundary',
type: FEATURE_TYPES.BOUNDARIES,
minZoom: 8,
minZoom: 9,
dataLoadType: FEATURE_DATA_LOAD_TYPES.IMPORT,
description: 'Terrestrial and Colocated Aquatic Sites',
parent: 'TERRESTRIAL_SITE_FEATURES',
Expand Down Expand Up @@ -363,7 +363,7 @@ export const FEATURES = {
TERRESTRIAL_SITE_FEATURES: {
name: 'Terrestrial Site Features',
type: FEATURE_TYPES.GROUP,
minZoom: 10,
minZoom: 9,
description: '',
dataLoadType: FEATURE_DATA_LOAD_TYPES.FETCH,
matchLocationType: 'OS Plot - all', // Fetches for TOWER_BASE_PLOTS and DISTRIBUTED_BASE_PLOTS
Expand Down Expand Up @@ -940,7 +940,6 @@ export const DEFAULT_STATE = {
},
focusLocation: {
current: null,
isCenteredOn: false, // Whether the map is still centered on and zoomed into focus location
data: null,
fetch: { status: null, error: null },
},
Expand Down Expand Up @@ -1418,22 +1417,60 @@ export const boundsAreValid = bounds => (
))
);

export const calculateLocationsInMap = (locations, bounds = null, buffer = false) => {
export const calculateLocationsInMap = (
locations,
bounds = null,
extendMap = false, // Boolean, whether or not to extend the map bounds by 50% on each dimension
extendPoints = 0, // Number, a margin to add/subtract to lat/lon for a point's hit box
) => {
if (!locations || typeof locations !== 'object' || !Object.keys(locations).length) { return []; }
if (bounds === null) { return Object.keys(locations); }
if (!boundsAreValid(bounds)) { return []; }
const extendedBounds = !buffer ? bounds
const extendedBounds = !extendMap ? bounds
: Object.fromEntries(
Object.keys(bounds)
.map((dir) => {
const bufferValue = (bounds[dir][1] - bounds[dir][0]) / 2;
return [dir, [bounds[dir][0] - bufferValue, bounds[dir][1] + bufferValue]];
const buffer = (bounds[dir][1] - bounds[dir][0]) / 2;
return [dir, [bounds[dir][0] - buffer, bounds[dir][1] + buffer]];
}),
);
const isInBounds = loc => (
Number.isFinite(loc.latitude) && Number.isFinite(loc.longitude)
&& loc.latitude >= extendedBounds.lat[0] && loc.latitude <= extendedBounds.lat[1]
&& loc.longitude >= extendedBounds.lng[0] && loc.longitude <= extendedBounds.lng[1]
);
// This function flattens a geometry object to just coordinates so we can check if a boundary
// is in the map. NOTE: extendPoints does not work with boundaries, only solitary points.
const flatten = (items) => {
const isCoord = c => Array.isArray(c) && c.length === 2 && c.every(x => Number.isFinite(x));
const flat = [];
items.forEach((item) => {
if (Array.isArray(item) && !isCoord(item)) {
flat.push(...flatten(item));
} else {
flat.push(item);
}
});
return flat;
};
const isInBounds = (loc) => {
if (Number.isFinite(loc.latitude) && Number.isFinite(loc.longitude)) {
if (extendPoints > 0) {
const lats = [loc.latitude - extendPoints, loc.latitude + extendPoints];
const lngs = [loc.longitude - extendPoints, loc.longitude + extendPoints];
return (!(
lats[0] > extendedBounds.lat[1] || lats[1] < extendedBounds.lat[0]
|| lngs[0] > extendedBounds.lng[1] || lngs[1] < extendedBounds.lng[0]
));
}
return (
loc.latitude >= extendedBounds.lat[0] && loc.latitude <= extendedBounds.lat[1]
&& loc.longitude >= extendedBounds.lng[0] && loc.longitude <= extendedBounds.lng[1]
);
}
if (loc.geometry && loc.geometry.coordinates) {
const flatCoords = flatten(loc.geometry.coordinates);
return flatCoords.some(coord => (
coord.latitude >= extendedBounds.lat[0] && coord.latitude <= extendedBounds.lat[1]
&& coord.longitude >= extendedBounds.lng[0] && coord.longitude <= extendedBounds.lng[1]
));
}
return false;
};
return Object.keys(locations).filter(locId => isInBounds(locations[locId]));
};

0 comments on commit 0996b3f

Please sign in to comment.