All projects

Soundmind

Odiya Parent App

GPS is often wrong. Instead of dressing up wrong data to look plausible, I built screens that say we don't know when we don't know.

Role
Location interpretation logic and screen design · implementation
Period
2025.07 ~ Present
Stack
React NativeTypeScriptTanStack QueryZustandNaver MapSTOMP
App / clientNativeServerWorkerStorageExternal
The parent app polls the server for locations; push only advances the next poll. UI fixes ship over the air without store review.Hover a block to highlight its flows. Drag to pan.

Outcome

  • 89

    Unit tests covering the location interpretation logic

  • 956 stations

    Station data used for subway inference (47 lines)

  • 3 stages

    Filters a transport-mode guess must pass

01The problem

A parent's screen is useless if it just plots the raw coordinates the child's device uploads. They only become information once translated into statements like stayed home for two hours, seems to have moved by subway, or currently near school. The problem was that the raw data is not clean. GPS drifts indoors and drops out entirely on the subway. If the interpretation is wrong, parents end up believing false information as fact, and in a service tied directly to a child's safety, that is worse than having no feature at all.

02Constraints

The biggest constraint was on the user side, not the data side. The child's device app ships through the app store, and most devices actually in the field were running old versions. The newly built interpretation screens needed fields that old versions do not send. Real-time tracking had a different problem: the app and the server each judged the state on their own, so they frequently fell out of sync.

  • Indoor GPS drift split a single stay at one place into several fragments on screen
  • Subway segments have no coordinates at all, so inference is the only option
  • Old versions of the child app do not send the fields the interpretation needs
  • In real-time tracking, the app and the server each made their own judgment and the states diverged

03Alternatives considered

The core question was how to represent GPS-dead segments on screen. Leave the gap empty and parents get anxious; fill it in and it can be wrong.

OptionStrengthsDrawbacks
Connect gaps with a straight lineThe path stays unbroken and looks naturalDraws straight lines through buildings and presents routes never actually taken as if they were fact
Leave gaps emptyNothing can be wrong and the implementation is simpleA 30-minute subway trip disappears entirely, so parents conclude location tracking is broken
Infer only when every condition is metChosenShows information only when it is right, so trust is preserved, and leaves gaps otherwise, so no false impressions are createdReal trips that fail the filters also go unshown, so some coverage must be given up

04Decision and rationale

I chose to give up some coverage. On a screen tied directly to a child's safety, a single wrong display costs more than a missing feature, because once parents start doubting the screen, they stop trusting even the correct information. So I made it a principle to show nothing rather than show something wrong, and applied the same standard at every point where interpretation happens.

before a subway trip is shown

Coverage is traded away so whatever appears can be trusted.

  • Subway trips are shown only after passing all three conditions: station matching, distance, and travel time
  • Gap detection along a path is computed in exactly one place, preventing overlapping lines drawn from different values
  • Stay clusters split only after exceeding 3x the radius from the starting point, and stays crossing midnight are split by date
  • For users on old child-app versions, the interpretation screens are disabled entirely
  • In real-time tracking the server session is the source of truth, and push messages are used only as a signal to fetch sooner

05Implementation and trial and error

The interpretation rules were easy to state in words but hard to uphold in code. Fixing one condition kept quietly breaking another screen, so I pulled the rules out of the screens into pure functions and pinned them down with tests.

  • ① I first built stay detection as a fixed radius around the starting point, and slowly moving segments got lumped into a single place. I changed it to follow the current center but split once it exceeds 3x the radius from the starting point.
  • ② A stay crossing midnight was counted at full duration on both dates. I fixed it to cut at the date boundary and sum each side separately.
  • ③ Computing path gaps separately per screen drew overlapping lines on the same segment. I unified the computation into a single place.
  • ④ Chat messages arrived truncated midway. I confirmed strings were being cut at the frame terminator character and resolved it by changing the transmission method.
  • ⑤ Labels on the map overlapped under the OS font-scaling setting. I wrapped the text component to neutralize the scaling; the previous approach had silently stopped working.

06Outcome

Every interpretation rule was extracted into pure functions with 89 unit tests attached, because rules like what radius multiple to use, how to handle midnight, and where to detect gaps are subtle enough that code alone does not keep them. The fragmenting of indoor stays and the wrong subway guesses disappeared, and old-version users no longer saw blank screens. As a bonus, I traced why chat messages arrived truncated, confirmed that React Native cuts strings at a particular character when sending, and resolved it by changing the transmission method.