feat: add button to exclude drawn areas from time range
This commit is contained in:
@@ -199,9 +199,7 @@ function PolylineWithArrows({ coords, showDirection }) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Map({end, duration, slider, mapState, setMapState, setSubmenu, showDirection}) {
|
function Map({data, loading, end, duration, slider, mapState, setMapState, setSubmenu, showDirection, setDrawnItems}) {
|
||||||
const [data, loading] = useSensor('owntracks', 'OwnTracks', end, duration);
|
|
||||||
|
|
||||||
const range = useMemo(() => parseSlider(end, duration, slider), [end, duration, slider]);
|
const range = useMemo(() => parseSlider(end, duration, slider), [end, duration, slider]);
|
||||||
|
|
||||||
const coords = useMemo(() => {
|
const coords = useMemo(() => {
|
||||||
@@ -246,14 +244,25 @@ function Map({end, duration, slider, mapState, setMapState, setSubmenu, showDire
|
|||||||
localStorage.setItem('api_key', api_key);
|
localStorage.setItem('api_key', api_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRectangleDrawn = (e) => {
|
const onCreated = (e) => {
|
||||||
const { layer } = e;
|
const { layer } = e;
|
||||||
const bounds = layer.getBounds();
|
setDrawnItems(items => [...items, {id: layer._leaflet_id, bounds: layer.getBounds()}]);
|
||||||
console.log('Rectangle drawn. Bounds:', {
|
};
|
||||||
northEast: bounds.getNorthEast(),
|
|
||||||
southWest: bounds.getSouthWest(),
|
const onEdited = (e) => {
|
||||||
|
const { layers } = e;
|
||||||
|
layers.eachLayer(layer => {
|
||||||
|
setDrawnItems(items => items.map(item =>
|
||||||
|
item.id === layer._leaflet_id ? { ...item, bounds: layer.getBounds() } : item
|
||||||
|
));
|
||||||
});
|
});
|
||||||
// In the future, we can use these bounds to filter data or perform a search.
|
};
|
||||||
|
|
||||||
|
const onDeleted = (e) => {
|
||||||
|
const { layers } = e;
|
||||||
|
const deletedIds = [];
|
||||||
|
layers.eachLayer(layer => deletedIds.push(layer._leaflet_id));
|
||||||
|
setDrawnItems(items => items.filter(item => !deletedIds.includes(item.id)));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -273,7 +282,9 @@ function Map({end, duration, slider, mapState, setMapState, setSubmenu, showDire
|
|||||||
<FeatureGroup>
|
<FeatureGroup>
|
||||||
<EditControl
|
<EditControl
|
||||||
position="topright"
|
position="topright"
|
||||||
onCreated={onRectangleDrawn}
|
onCreated={onCreated}
|
||||||
|
onEdited={onEdited}
|
||||||
|
onDeleted={onDeleted}
|
||||||
draw={{
|
draw={{
|
||||||
rectangle: true,
|
rectangle: true,
|
||||||
polyline: false,
|
polyline: false,
|
||||||
@@ -300,7 +311,7 @@ function Map({end, duration, slider, mapState, setMapState, setSubmenu, showDire
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Menu({duration, setDuration, end, setEnd, slider, setSlider, submenu, setSubmenu, showDirection, setShowDirection, setMapState, shareStart, shareEnd}) {
|
function Menu({data, duration, setDuration, end, setEnd, slider, setSlider, submenu, setSubmenu, showDirection, setShowDirection, setMapState, shareStart, shareEnd, drawnItems}) {
|
||||||
const [showRange, setShowRange] = useState(false);
|
const [showRange, setShowRange] = useState(false);
|
||||||
|
|
||||||
const chooseDuration = (x) => {
|
const chooseDuration = (x) => {
|
||||||
@@ -361,6 +372,80 @@ function Menu({duration, setDuration, end, setEnd, slider, setSlider, submenu, s
|
|||||||
setSubmenu(false);
|
setSubmenu(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const excludeArea = () => {
|
||||||
|
const drawnRectangles = drawnItems.map(item => item.bounds);
|
||||||
|
if (!drawnRectangles.length || !data || !Array.isArray(data)) {
|
||||||
|
if (!drawnRectangles.length) alert("Please draw one or more rectangles on the map first.");
|
||||||
|
setSubmenu(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInsideExclusionZone = (lat, lon) => {
|
||||||
|
for (const rect of drawnRectangles) {
|
||||||
|
if (rect.contains([lat, lon])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const goodSegments = [];
|
||||||
|
let currentSegment = null;
|
||||||
|
|
||||||
|
for (const point of data) {
|
||||||
|
if (!point || typeof point.lat !== 'number' || typeof point.lon !== 'number' || !point.time) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInside = isInsideExclusionZone(point.lat, point.lon);
|
||||||
|
|
||||||
|
if (!isInside) {
|
||||||
|
if (!currentSegment) {
|
||||||
|
currentSegment = { start: point.time, end: point.time };
|
||||||
|
} else {
|
||||||
|
currentSegment.end = point.time;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentSegment) {
|
||||||
|
goodSegments.push(currentSegment);
|
||||||
|
currentSegment = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSegment) {
|
||||||
|
goodSegments.push(currentSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goodSegments.length) {
|
||||||
|
alert("No time ranges found outside the selected area(s).");
|
||||||
|
setSubmenu(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let longestSegment = goodSegments[0];
|
||||||
|
for (let i = 1; i < goodSegments.length; i++) {
|
||||||
|
const durationCurrent = moment(longestSegment.end).diff(moment(longestSegment.start));
|
||||||
|
const durationNew = moment(goodSegments[i].end).diff(moment(goodSegments[i].start));
|
||||||
|
if (durationNew > durationCurrent) {
|
||||||
|
longestSegment = goodSegments[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startUnix = moment(longestSegment.start).unix();
|
||||||
|
const endUnix = moment(longestSegment.end).unix();
|
||||||
|
const endOfWindowUnix = end.unix();
|
||||||
|
|
||||||
|
const newSliderStart = Math.round((startUnix - endOfWindowUnix) / duration.secs + duration.num);
|
||||||
|
const newSliderEnd = Math.round((endUnix - endOfWindowUnix) / duration.secs + duration.num);
|
||||||
|
|
||||||
|
const clampedStart = Math.max(0, newSliderStart);
|
||||||
|
const clampedEnd = Math.min(duration.num, newSliderEnd);
|
||||||
|
|
||||||
|
setSlider([clampedStart, clampedEnd]);
|
||||||
|
setSubmenu(false);
|
||||||
|
};
|
||||||
|
|
||||||
const range = parseSlider(end, duration, slider);
|
const range = parseSlider(end, duration, slider);
|
||||||
const startDate = moment(end).subtract(...duration.delta);
|
const startDate = moment(end).subtract(...duration.delta);
|
||||||
|
|
||||||
@@ -530,6 +615,7 @@ function Menu({duration, setDuration, end, setEnd, slider, setSlider, submenu, s
|
|||||||
Show direction
|
Show direction
|
||||||
</label>
|
</label>
|
||||||
<button onClick={recentreView}>Recentre view</button>
|
<button onClick={recentreView}>Recentre view</button>
|
||||||
|
<button onClick={excludeArea}>Exclude area</button>
|
||||||
<button onClick={shareRange}>Share range</button>
|
<button onClick={shareRange}>Share range</button>
|
||||||
<button onClick={resetToDefaults}>Reset page</button>
|
<button onClick={resetToDefaults}>Reset page</button>
|
||||||
</>
|
</>
|
||||||
@@ -592,6 +678,9 @@ function App() {
|
|||||||
});
|
});
|
||||||
const [submenu, setSubmenu] = useState(false);
|
const [submenu, setSubmenu] = useState(false);
|
||||||
const [showDirection, setShowDirection] = useState(initialShowDirection);
|
const [showDirection, setShowDirection] = useState(initialShowDirection);
|
||||||
|
const [drawnItems, setDrawnItems] = useState([]);
|
||||||
|
|
||||||
|
const [data, loading] = useSensor('owntracks', 'OwnTracks', end, duration);
|
||||||
|
|
||||||
const shareStart = shareStartParam ? moment.unix(shareStartParam) : null;
|
const shareStart = shareStartParam ? moment.unix(shareStartParam) : null;
|
||||||
const shareEnd = shareEndParam ? moment.unix(shareEndParam) : null;
|
const shareEnd = shareEndParam ? moment.unix(shareEndParam) : null;
|
||||||
@@ -644,6 +733,8 @@ function App() {
|
|||||||
setMapState={setMapState}
|
setMapState={setMapState}
|
||||||
shareStart={shareStart}
|
shareStart={shareStart}
|
||||||
shareEnd={shareEnd}
|
shareEnd={shareEnd}
|
||||||
|
data={data}
|
||||||
|
drawnItems={drawnItems}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Map
|
<Map
|
||||||
@@ -654,6 +745,9 @@ function App() {
|
|||||||
setMapState={setMapState}
|
setMapState={setMapState}
|
||||||
setSubmenu={setSubmenu}
|
setSubmenu={setSubmenu}
|
||||||
showDirection={showDirection}
|
showDirection={showDirection}
|
||||||
|
data={data}
|
||||||
|
loading={loading}
|
||||||
|
setDrawnItems={setDrawnItems}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user