surflog-tech/geospeed

measure jibe speed

Closed this issue · 4 comments

@webjay I'm happy to take a crack at this. I have a couple of questions, though.

Are all turns considered jibes?

Like this one, for example?

Screen Shot 2022-02-28 at 6 54 08 PM

Zooming in a little, I assume these variations in the line are NOT jibes. Is that correct?

Screen Shot 2022-02-28 at 6 58 23 PM

Are there turns that are not considered jibes?

What do you mean by "jibe speed"?

Is this the maximum speed detected in the jibe? Is it the speed heading into the jibe?


If you could collect a few examples of jibes in geojson format, that would be helpful for my testing. Also, if you have any examples of turns that are not jibes, that would also be helpful!

Are all turns considered jibes?
Are there turns that are not considered jibes?

A jibe should be a turn where the speed is never 0.

The jibe you emphasise is probably me attempting a jibe, but ending up in the water :)

If bearing difference is used to define a turn, I assume we need a bearing difference to look for. Can you make that configurable?

What do you mean by "jibe speed"?

This could mean:

  • average speed in the jibe.
  • lowest speed.
  • incoming speed.
  • exit speed.

At first it would be great to just be able to identify jibes, and then later extract as much data as possible, like speed and how smooth it was.

If you could collect a few examples of jibes in geojson format, that would be helpful for my testing. Also, if you have any examples of turns that are not jibes, that would also be helpful!

Yep, I am in the process of finding some.

Not sure if this is helpful, but on a sailboat, a jibe is where the mainsail will cross the center of the boat where on a windsurfer the sail will cross the front of the board.

This was my attempt:

import { useMemo } from 'react';
import turfBearing from '@turf/bearing';

const lookback = 6;
const angleDiff = 80;

function useJibes(geoJson) {
  return useMemo(() => {
    if (geoJson === undefined) return null;
    const jibeData = [];
    let indexCoordsMeta = 0;
    const { geometry: { coordinates: coordinatesMultiLine }, properties: { coordsMeta } } = geoJson;
    coordinatesMultiLine.forEach((coordinatesLine) => {
      const bearings = [];
      coordinatesLine.forEach((coordinates, indexCoord) => {
        const { speed } = coordsMeta[indexCoordsMeta];
        indexCoordsMeta += 1;
        if (indexCoord > 0) {
          const coordPrev = coordinatesLine[indexCoord - 1];
          const bearing = turfBearing(coordPrev, coordinates);
          bearings.push(bearing);
          if (indexCoord >= lookback && speed > 0) {
            const bearingLookback = bearings[indexCoord - lookback];
            const bearingDiff = Math.abs(bearingLookback - bearing);
            if (bearingDiff >= angleDiff) {
              jibeData.push({ indexCoord, speed });
            }
          }
        }
      });
    });
    const jibeSets = [[]];
    jibeData.forEach((jibe, index) => {
      if (index === 0 || jibe.indexCoord - 1 === jibeData[index - 1].indexCoord) {
        jibeSets[jibeSets.length - 1].push(jibe);
      } else {
        jibeSets.push([jibe]);
      }
    });
    const jibeSetsFiltered = jibeSets.filter((jibeSet) => {
      if (jibeSet.length < lookback) return false;
      return jibeData
        .slice(jibeSet[0].indexCoord - lookback, jibeSet[jibeSet.length - 1].indexCoord + lookback).every(({ speed }) => speed > 0);
    });
    const result = [];
    jibeSetsFiltered.map((jibeSet, index) => jibeSet.reduce((speed, jibe) => {
      if (speed === 0 || jibe.speed < speed) {
        result[index] = jibe;
        return jibe.speed;
      }
      return speed;
    }, 0));
    return result.filter(({ speed }) => speed > 0);
  }, [geoJson]);
}

export default useJibes;

5f432c76f69e799d667c54e39784af7b6a66bdf8