microsegments 0.2.4__tar.gz → 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {microsegments-0.2.4 → microsegments-0.2.5}/PKG-INFO +1 -1
  2. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/_version.py +2 -2
  3. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/html/export.py +2 -2
  4. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/html/template.html +9 -6
  5. {microsegments-0.2.4 → microsegments-0.2.5}/.github/workflows/publish.yml +0 -0
  6. {microsegments-0.2.4 → microsegments-0.2.5}/.github/workflows/test.yml +0 -0
  7. {microsegments-0.2.4 → microsegments-0.2.5}/.gitignore +0 -0
  8. {microsegments-0.2.4 → microsegments-0.2.5}/LICENSE +0 -0
  9. {microsegments-0.2.4 → microsegments-0.2.5}/README.md +0 -0
  10. {microsegments-0.2.4 → microsegments-0.2.5}/docs/report.jpg +0 -0
  11. {microsegments-0.2.4 → microsegments-0.2.5}/examples/gtfsrt_csv.toml +0 -0
  12. {microsegments-0.2.4 → microsegments-0.2.5}/examples/make_synth.py +0 -0
  13. {microsegments-0.2.4 → microsegments-0.2.5}/examples/stib1_platform.toml +0 -0
  14. {microsegments-0.2.4 → microsegments-0.2.5}/examples/stib55.toml +0 -0
  15. {microsegments-0.2.4 → microsegments-0.2.5}/examples/stib55_full.toml +0 -0
  16. {microsegments-0.2.4 → microsegments-0.2.5}/examples/stib71_platform.toml +0 -0
  17. {microsegments-0.2.4 → microsegments-0.2.5}/examples/synth.toml +0 -0
  18. {microsegments-0.2.4 → microsegments-0.2.5}/pyproject.toml +0 -0
  19. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/__init__.py +0 -0
  20. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/aggregate.py +0 -0
  21. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/cli.py +0 -0
  22. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/compare.py +0 -0
  23. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/config.py +0 -0
  24. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/contract.py +0 -0
  25. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/hotspots.py +0 -0
  26. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/html/__init__.py +0 -0
  27. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/html/leaflet.min.css +0 -0
  28. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/__init__.py +0 -0
  29. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/coverage.py +0 -0
  30. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/events.py +0 -0
  31. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/gtfsrt.py +0 -0
  32. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/ids.py +0 -0
  33. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/stib.py +0 -0
  34. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/tabular.py +0 -0
  35. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/io/timeutil.py +0 -0
  36. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/locate/__init__.py +0 -0
  37. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/locate/common.py +0 -0
  38. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/locate/linear.py +0 -0
  39. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/locate/mapmatch.py +0 -0
  40. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/locate/resample.py +0 -0
  41. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/metrics.py +0 -0
  42. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/network/__init__.py +0 -0
  43. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/network/calendar.py +0 -0
  44. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/network/geometry.py +0 -0
  45. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/network/keys.py +0 -0
  46. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/network/patterns.py +0 -0
  47. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/pipeline.py +0 -0
  48. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/plot.py +0 -0
  49. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/py.typed +0 -0
  50. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/report.py +0 -0
  51. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/scale.py +0 -0
  52. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/schema.py +0 -0
  53. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/segments.py +0 -0
  54. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/simulate.py +0 -0
  55. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/tracks.py +0 -0
  56. {microsegments-0.2.4 → microsegments-0.2.5}/src/microsegments/tune.py +0 -0
  57. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/buckets.parquet +0 -0
  58. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/drops.parquet +0 -0
  59. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/flen.parquet +0 -0
  60. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/obs_day.parquet +0 -0
  61. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/obs_dow.parquet +0 -0
  62. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/passages.parquet +0 -0
  63. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/summary.json +0 -0
  64. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/golden/validation.parquet +0 -0
  65. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/agency.parquet +0 -0
  66. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/calendar.parquet +0 -0
  67. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/calendar_dates.parquet +0 -0
  68. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/routes.parquet +0 -0
  69. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/shapes.parquet +0 -0
  70. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/stop_times.parquet +0 -0
  71. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/stops.parquet +0 -0
  72. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/gtfs/trips.parquet +0 -0
  73. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/punctuality/route55.parquet +0 -0
  74. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/speed55_ref.json +0 -0
  75. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/vd55/20250318.parquet +0 -0
  76. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/vd55/20250318.snaps.parquet +0 -0
  77. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/vd55/20250319.parquet +0 -0
  78. {microsegments-0.2.4 → microsegments-0.2.5}/tests/fixtures/stib55/vd55/20250319.snaps.parquet +0 -0
  79. {microsegments-0.2.4 → microsegments-0.2.5}/tests/gtfs_fixtures.py +0 -0
  80. {microsegments-0.2.4 → microsegments-0.2.5}/tests/regression/make_golden.py +0 -0
  81. {microsegments-0.2.4 → microsegments-0.2.5}/tests/t4_fixtures.py +0 -0
  82. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_aggregate.py +0 -0
  83. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_compare.py +0 -0
  84. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_coverage.py +0 -0
  85. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_hotspots.py +0 -0
  86. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_hotspots_criteria.py +0 -0
  87. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_io_events.py +0 -0
  88. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_io_gtfsrt.py +0 -0
  89. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_io_stib.py +0 -0
  90. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_io_tabular.py +0 -0
  91. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_locate_linear.py +0 -0
  92. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_locate_mapmatch.py +0 -0
  93. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_metrics.py +0 -0
  94. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_network_geometry.py +0 -0
  95. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_network_patterns.py +0 -0
  96. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_pipeline_cli.py +0 -0
  97. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_regression_stib55.py +0 -0
  98. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_report_html.py +0 -0
  99. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_scaffold.py +0 -0
  100. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_segments.py +0 -0
  101. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_simulate.py +0 -0
  102. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_tracks.py +0 -0
  103. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_tune.py +0 -0
  104. {microsegments-0.2.4 → microsegments-0.2.5}/tests/test_versions_merge.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: microsegments
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Where do transit vehicles linger? Count vehicle position observations per micro-segment of a line, from GTFS + AVL / GTFS-RT positions.
5
5
  Project-URL: Homepage, https://github.com/GaspardMerten/microsegments
6
6
  Project-URL: Repository, https://github.com/GaspardMerten/microsegments
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.4'
22
- __version_tuple__ = version_tuple = (0, 2, 4)
21
+ __version__ = version = '0.2.5'
22
+ __version_tuple__ = version_tuple = (0, 2, 5)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -52,8 +52,8 @@ def render(analysis_or_contract, *, title: str | None = None, lang: str = "fr",
52
52
  scales: dict | None = None, description: str | None = None, show_hotspots: bool = True, **contract_kw) -> str:
53
53
  """The full HTML document as a string. ``contract_kw`` go to ``to_contract`` when an Analysis /
54
54
  RunResult is given. The colour scale is fixed for every page (``microsegments.scale``); ``scales`` is
55
- deprecated and ignored. ``show_hotspots=False`` hides every hotspot element of the one-period view
56
- (list, map markers and outlines, tile, method paragraph); the page reads it from the contract key
55
+ deprecated and ignored. ``show_hotspots=False`` hides every hotspot element
56
+ (list, map markers and outlines, tile, method paragraph), and the retained changes of the two-period view; the page reads it from the contract key
57
57
  ``show_hotspots``, so a page fed with its data later can set that key instead."""
58
58
  c = dict(_contract(analysis_or_contract, **contract_kw))
59
59
  c["tiles"] = bool(tiles)
@@ -33,6 +33,7 @@ h2{font:700 24px/1.2 var(--serif);margin:0 0 6px}
33
33
  p.lede{font-size:clamp(17px,1.6vw,19px);color:var(--ink-2);max-width:68ch;margin:0}
34
34
  .tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:14px;overflow:hidden;margin-top:28px}
35
35
  .tiles.t3{grid-template-columns:repeat(3,1fr)}
36
+ .tiles.t2{grid-template-columns:repeat(2,1fr)}
36
37
  .tile{background:var(--surface);padding:18px 18px 16px;min-width:0}
37
38
  .tile b{display:block;font:700 clamp(24px,3vw,32px)/1 var(--serif);font-variant-numeric:tabular-nums}
38
39
  .tile span{display:block;margin-top:8px;color:var(--ink-2);font-size:14px;line-height:1.4}
@@ -411,7 +412,7 @@ const CT = {
411
412
  tip: {a: (v, s) => `avant : ${v} obs. par passage (≈ ${s} s)`, b: (v, s) => `après : ${v} (≈ ${s} s)`,
412
413
  d: (v, m1) => `<b>${v} s par ${m1}</b>`, unsure: 'différence incertaine', absent: 'absent de la première période',
413
414
  freq: (a, b, m) => `${a} → ${b} ${m} par heure`},
414
- mxsub: (f, t, a, b, m1, stops) => [`De ${f} à ${t}.`, `Chaque case : la différence de temps par ${m1} entre ${a} et ${b}, à cette heure. Vert : plus rapide ; rouge : plus lent ; pâle : incertain${stops ? '' : ' ; hachures : zones d\'arrêt masquées'}. Contours jaunes : changements retenus. Cliquez une heure pour l'afficher sur la carte.`],
415
+ mxsub: (f, t, a, b, m1, stops, hs) => [`De ${f} à ${t}.`, `Chaque case : la différence de temps par ${m1} entre ${a} et ${b}, à cette heure. Vert : plus rapide ; rouge : plus lent ; pâle : incertain${stops ? '' : ' ; hachures : zones d\'arrêt masquées'}.${hs ? ' Contours jaunes : changements retenus.' : ''} Cliquez une heure pour l'afficher sur la carte.`],
415
416
  profsub: (f, t, w, m1, m) => [`De ${f} à ${t}, ${w}.`, `Au-dessus de zéro, chaque ${m1} met plus de temps qu'avant ; en dessous, moins. Pointillés jaunes : ${m} par heure après (échelle de droite).`],
416
417
  days: 'mêmes jours de semaine que chaque période',
417
418
  method: (m1, m, a, b) => `<b>Comparaison de deux périodes.</b> ${a} (avant) et ${b} (après), chacune sur ses propres jours et avec sa propre référence. Les microsegments sont appariés d'une période à l'autre ; ceux qui n'existent que dans une période sont écartés. La différence porte sur les observations par passage, donc le temps par ${m1}, ce qui la rend indépendante de la fréquence. L'incertitude vient d'un rééchantillonnage des jours de chaque période. Quand la fréquence change de plus de 20 %, le tronçon est signalé : un horaire différent (plus de ${m}, plus de files) peut expliquer une partie de l'écart.`,
@@ -438,7 +439,7 @@ const CT = {
438
439
  tip: {a: (v, s) => `before: ${v} obs. per passage (≈ ${s} s)`, b: (v, s) => `after: ${v} (≈ ${s} s)`,
439
440
  d: (v, m1) => `<b>${v} s per ${m1}</b>`, unsure: 'uncertain difference', absent: 'absent from the first period',
440
441
  freq: (a, b, m) => `${a} → ${b} ${m} per hour`},
441
- mxsub: (f, t, a, b, m1, stops) => [`From ${f} to ${t}.`, `Each cell: the difference in time per ${m1} between ${a} and ${b}, at that hour. Green: faster; red: slower; pale: uncertain${stops ? '' : '; hatching: stop zones hidden'}. Yellow outlines: retained changes. Click an hour to show it on the map.`],
442
+ mxsub: (f, t, a, b, m1, stops, hs) => [`From ${f} to ${t}.`, `Each cell: the difference in time per ${m1} between ${a} and ${b}, at that hour. Green: faster; red: slower; pale: uncertain${stops ? '' : '; hatching: stop zones hidden'}.${hs ? ' Yellow outlines: retained changes.' : ''} Click an hour to show it on the map.`],
442
443
  profsub: (f, t, w, m1, m) => [`From ${f} to ${t}, ${w}.`, `Above zero, each ${m1} takes longer than before; below, less. Yellow dots: ${m} per hour after (right scale).`],
443
444
  days: 'same weekdays as each period',
444
445
  method: (m1, m, a, b) => `<b>Comparing two periods.</b> ${a} (before) and ${b} (after), each on its own days and with its own reference. Micro-segments are matched from one period to the other; those that exist in one period only are left out. The difference is in observations per passage, so in time per ${m1}, which makes it independent of the frequency. The uncertainty comes from resampling the days of each period. When the frequency changes by more than 20 %, the stretch is flagged: a different timetable (more ${m}, more queues) may explain part of the difference.`,
@@ -652,7 +653,7 @@ document.addEventListener('mouseout', e => { const b = e.target.closest && e.tar
652
653
  document.addEventListener('focusin', e => { const b = e.target.closest && e.target.closest('.qm'); if (b && !popPinned && b.matches(':focus-visible')) openPop(b, false); });
653
654
  window.addEventListener('scroll', () => { if (popBtn) { if (popBtn.isConnected) openPop(popBtn, popPinned); else closePop(); } }, {passive: true});
654
655
  // stop-zone hotspots are listed only when stop zones are shown
655
- const hsOn = () => isCmp() || D.show_hotspots !== false;
656
+ const hsOn = () => D.show_hotspots !== false;
656
657
  const hotDir = () => !hsOn() ? [] : (D.hotspots || []).filter(h => (h.dir ?? h.direction_id) === st.dir && (!h.pattern_uid || h.pattern_uid === (dirOf() || {}).pattern_uid)).sort((a, b) => a.rank - b.rank);
657
658
  const hotAll = () => hotDir().filter(h => st.stops || h.zone !== 'stop');
658
659
  // the list (and the map markers) show the top hotspots unless "show all" is on
@@ -721,7 +722,7 @@ function cTipHtml(E, v) {
721
722
  if (v.stop) h += `<br><i>${l.stop}</i>`;
722
723
  return h;
723
724
  }
724
- const chgAll = () => CMP ? (CMP.changes || []).filter(c => c.dir === st.dir && (!c.pattern_uid || c.pattern_uid === (dirOf() || {}).pattern_uid)).sort((a, b) => a.rank - b.rank) : [];
725
+ const chgAll = () => CMP && hsOn() ? (CMP.changes || []).filter(c => c.dir === st.dir && (!c.pattern_uid || c.pattern_uid === (dirOf() || {}).pattern_uid)).sort((a, b) => a.rank - b.rank) : [];
725
726
  const chgShown = () => chgAll().filter(c => st.stops || c.zone !== 'stop');
726
727
  const chgDir = () => st.hotAll ? chgShown() : chgShown().slice(0, HOT_TOP);
727
728
  const signCol = s => s < 0 ? FASTC : SLOWC;
@@ -857,12 +858,14 @@ function tiles() {
857
858
  if (isCmp()) {
858
859
  const c = ctx().tiles, ch = CMP.changes || [];
859
860
  document.getElementById('tiles').classList.remove('t3');
861
+ document.getElementById('tiles').classList.toggle('t2', !hsOn());
860
862
  document.getElementById('tiles').innerHTML = [[nf(CMP.a.days, 0), `${c.a} (${perLabel(CMP.a)})`], [nf(CMP.b.days, 0), `${c.b} (${perLabel(CMP.b)})`],
861
- [nf(ch.filter(x => x.sign > 0).length, 0), c.slower], [nf(ch.filter(x => x.sign < 0).length, 0), c.faster]]
863
+ ...(hsOn() ? [[nf(ch.filter(x => x.sign > 0).length, 0), c.slower], [nf(ch.filter(x => x.sign < 0).length, 0), c.faster]] : [])]
862
864
  .map(([b, s]) => `<div class="tile"><b>${b}</b><span>${esc(s)}</span></div>`).join('');
863
865
  return;
864
866
  }
865
867
  const big = v => v >= 1e6 ? nf(v / 1e6, 1) + (st.lang === 'fr' ? ' M' : 'M') : v >= 1e4 ? nf(v / 1e3, 0) + (st.lang === 'fr' ? ' k' : 'k') : nf(v, 0);
868
+ document.getElementById('tiles').classList.remove('t2');
866
869
  document.getElementById('tiles').classList.toggle('t3', !hsOn());
867
870
  document.getElementById('tiles').innerHTML = [
868
871
  [nf(D.period.days, 0), esc(l.tiles.days(D.period.days, exclSummary(D.period.excluded)))],
@@ -1086,7 +1089,7 @@ function drawMatrix() {
1086
1089
  svg.innerHTML = s;
1087
1090
  MX = {E, rows, m, RH, W, Ltot, tip: cm ? cTipHtml : tipHtml};
1088
1091
  const what = l.metrics[st.metric].toLowerCase() + (st.per10 ? ' (10 m)' : ' (30 m)');
1089
- setSub($('mxsub'), cm ? ctx().mxsub(E.from, E.to, perLabel(CMP.a), perLabel(CMP.b), noun(false), st.stops) : l.mxsub(E.from, E.to, daysLabel(), what, st.stops, hsOn() && hotOfDir().length > 0));
1092
+ setSub($('mxsub'), cm ? ctx().mxsub(E.from, E.to, perLabel(CMP.a), perLabel(CMP.b), noun(false), st.stops, chgDir().length > 0) : l.mxsub(E.from, E.to, daysLabel(), what, st.stops, hsOn() && hotOfDir().length > 0));
1090
1093
  }
1091
1094
  $('mx').addEventListener('mousemove', e => {
1092
1095
  if (!MX) return;
File without changes
File without changes
File without changes