Skip to content

feat:connecting trawl lines between linked buoys #1276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 23, 2025

Conversation

JoshuaVulcan
Copy link
Collaborator

What does this PR do?

  • Sketches some basic trawl lines on the map for Buoy devices.

How does it look

Screenshot 2025-04-08 at 11 12 59 AM

Relevant link(s)

  • 🤷

Where / how to start reviewing (optional)

  • The logic is a little funny; we can't rely on the coordinates provided by the devices array in a subject's additional properties, so instead we match the device_ids listed to their subject names and then use the last known position for that subject. @andrejiron21 @doug-nimblegravity should definitely add story work to keep that devices data better refreshed to avoid weird data sync issues in the future.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

const subjectIsBuoyLineEligible = (subjectFeature = {}, _index, allSubjects = []) => {
const subject = subjectFeature.properties;

const is_buoy = subject.subject_subtype === 'ropeless_buoy_device';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This string should be a constant. Maybe we won't need it anywhere else so at least putting it at the top of the file like: BUOY_SUBJECT_SUBTYPE is enough.

Second question: why snake case here (and in all other variables of this method)? It's not our usual approach 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The snake case and non-constantified strings are just me doing this as a quickie/prototype.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed!

import { useSelector } from 'react-redux';

import { getMapSubjectFeatureCollectionWithVirtualPositioning } from '../selectors/subjects';
import { lineString } from '@turf/turf';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this import should go in the first block.


if (!is_line) return false;

const line_contains_valid_subjects = devices.every(({ device_id }) => allSubjects.find(({ properties }) => properties.name === device_id));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable name sounds odd, it almost sounds like a boolean for a condition: if (lineContainsValidSubjects) ... but it is not, it's just a list of subjects contained by the line. I guess it needs to be reworded, or maybe just return the one-liner since it's kind of easy to read.

Copy link
Collaborator Author

@JoshuaVulcan JoshuaVulcan Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a boolean for a condition -- it's just using Array.prototype.every to check device presence in the subject list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh right, I just understood the every and where this is being used 👍

};

const createTrawlLineGeoJSON = (buoySubjectFeatures) => {
return buoySubjectFeatures.reduce((accumulator, { properties }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I've stared at this function for a while haha. I feel like we could make it a bit easier to understand maybe by changing some variable names or not extracting properties from objects like: ({ properties }) => properties.additional... -> (buoySubjectFeature) => buoySubjectFeature.properties.additional etc...

Copy link
Collaborator Author

@JoshuaVulcan JoshuaVulcan Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary version:

We can say that the devices listed for a buoy feature's additional.devices should be the names of the subjects comprising that trawl.

We can't say that the coordinates included in the additional data devices array data is up-to-date.

So the eligibility-checker function first ensures a trawl line is completely valid and comprised of available subjects for the user.

Then createTrawlLineGeoJSON maps those device names back to their subject's last known position and builds out the linestring coordinates from there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Maybe a couple tweaks or a brief comment could make that more obvious in the code for future reference.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a descriptive inline comment

const buoySubjects = mapSubjects.features.filter(subjectIsBuoyLineEligible);
const trawlLineGeoJSON = createTrawlLineGeoJSON(buoySubjects);

useMapSources([{ id: 'trawl-lines-source', data: trawlLineGeoJSON }]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have constants for sources and layers that we should use to keep our code consistent and the ids accessible. Also, I don't know if you tested the properly addition, update and removal of these sources and layers (seeing Chromes console for warnings and errors), but as I mentioned recently, these hooks don't follow Mapbox sources and layers flows correctly from beginning to end. That's why I think it would be better to stop using them and instead use the map API directly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did test absence/presence/filtering etc, and timesliding, and it all works as expected.

I kept the names out of our constants file because I'd like this to be as self-isolated as possible. I will however make the strings constants within this file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh with tested I meant manually. In unit testing we usually use mocks for the map and other methods and the real flow of addition, interaction and removal (or not removal, causing memory leaks) of sources and layers are obscured by all the mocking. Anyway, we agreed this is out of the scope.

About the layers and sources being isolated, why's that 🤔 ? I know this is a feature for the specific scenario of buoy, but I don't get why we don't want to treat it as part of our app if now it is. Just like stationary subjects, or patrols, which are features not everyone use. I feel I'm missing some information or something haha.

Copy link
Collaborator Author

@JoshuaVulcan JoshuaVulcan Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh with tested I meant manually.

Me too :-)

"Features not everyone uses" can be considered a spectrum -- with this Buoy work being at one pretty extreme end of the spectrum, "features designed for a specific site and fringe use-cases". It's so specifically for a special sub-project, that I'd rather not pollute the broader app with random BUOY_ variables and references.

@PADAS PADAS deleted a comment from Copilot AI Apr 8, 2025
@JoshuaVulcan JoshuaVulcan requested a review from luixlive April 8, 2025 18:54
Copy link
Contributor

@luixlive luixlive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The source + layer is very simple and the processing of the source data is clear and well tested 👍

}, { type: 'FeatureCollection', features: [] });
};

const BuoyLineLayer = (_props) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why the (_props) => { instead of simply () => {?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a matter of preference!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, it's a default linter rule (no-unused-vars) that we explicitly skip by prefixing the _. It feels weird to add it intentionally for nothing 🤔

@JoshuaVulcan JoshuaVulcan merged commit c9d6107 into develop Apr 23, 2025
5 checks passed
@JoshuaVulcan JoshuaVulcan deleted the joshua-experimenting-buoy-lines branch April 23, 2025 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants