Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/core/settings/setting_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ abstract final class SettingKeys {
'map.radarShowLightning',
);

/// Whether the radar echo also draws the station wind arrows of the frame it
/// is showing (absent = false, for the same reason as the lightning one).
/// Mutually exclusive with [mapRadarShowLightning] — the two sets of marks
/// cover each other, so `RadarMapLayer` turns one off when the other goes on
/// and both may be saved off, never both on.
static const SettingKey<bool> mapRadarShowWind = SettingKey<bool>._(
'map.radarShowWind',
);

/// Saved Home township codes (ordered list). See `RegionStore`.
static const SettingKey<List<String>> savedRegionCodes =
SettingKey<List<String>>._('home.savedRegionCodes');
Expand Down
32 changes: 26 additions & 6 deletions lib/features/data/presentation/pages/data_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,15 +246,35 @@ class _SeismicCard extends StatelessWidget {
/// tall and its single line of label floated in the middle of a sea of tonal
/// grey.
///
/// The height is stated outright instead, and follows the text scale rather
/// than the window: the tile holds a 34 pt icon badge beside a label of at most
/// two lines, so that is what it is tall enough for at any text size. The 80
/// floor is the height a phone drew before this, kept so the phone layout is
/// Two columns is a floor, not an outcome of that division. A 340 pt tile
/// against the 343 pt of content a 375 pt iPhone SE has left after the page
/// padding divides to **one** column, so the SE drew full-width bars where the
/// 390 pt iPhone 12 next to it drew a grid — the same page, unrecognisable on
/// the smaller phone. The width still decides how many columns a wider window
/// gets; it just may not decide fewer than two.
///
/// The height is stated outright, and follows the text scale rather than the
/// window: the tile holds a 34 pt icon badge beside a label of at most two
/// lines, so that is what it is tall enough for at any text size. The 80 floor
/// is the height a phone drew before this, kept so the phone layout is
/// untouched.
SliverGridDelegate _rankingGrid(BuildContext context) {
const twoLabelLines = 44.0;
return SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 340,
const maxTileWidth = 340.0;
// Measured the way the grid is actually laid out: the window, less the
// display cutouts [SafeArea] takes off the sides in landscape, less the
// page's own horizontal padding.
final insets = MediaQuery.paddingOf(context);
final width =
MediaQuery.sizeOf(context).width -
insets.left -
insets.right -
AppSpacing.lg * 2;
return SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: math.max(
2,
(width / (maxTileWidth + AppSpacing.sm)).ceil(),
),
crossAxisSpacing: AppSpacing.sm,
mainAxisSpacing: AppSpacing.sm,
mainAxisExtent: math.max(
Expand Down
68 changes: 42 additions & 26 deletions lib/features/earthquake/presentation/pages/report_replay_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import 'package:dpip/shared/map/map_style.dart'
townLabelLayerId;
import 'package:dpip/shared/seismic/intensity_colors.dart';
import 'package:dpip/shared/widgets/frosted_surface.dart';
import 'package:dpip/shared/widgets/collapsible_map_legend.dart';
import 'package:dpip/shared/widgets/intensity_legend.dart';
import 'package:dpip/shared/widgets/map_color_legend.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -202,18 +203,29 @@ class _ReportReplayPageState extends State<ReportReplayPage> {
// the same intensity legend the live monitor carries,
// switching to the EEW felt-scale while an alert is up
// (the legacy monitor did exactly this on active EEW).
ListenableBuilder(
listenable: _session.eew,
builder: (context, _) {
final hasEew = _session.eew.alerts.isNotEmpty;
return MapLegendCard(
child: IntensityLegend(
mode: hasEew
? IntensityLegendMode.eew
: IntensityLegendMode.rts,
),
);
},
//
// Collapsible, and collapsed to a chip to start with,
// exactly as [MapScaffold] mounts every layer's legend:
// this page is a full-screen map too, and an eleven-row
// scale pinned open covers the north-west corner of the
// island for the whole replay. The wrap sits *outside*
// the mode swap on purpose — an alert arriving mid-replay
// changes the scale being shown, not whether the user
// asked to see it.
CollapsibleMapLegend(
legend: ListenableBuilder(
listenable: _session.eew,
builder: (context, _) {
final hasEew = _session.eew.alerts.isNotEmpty;
return MapLegendCard(
child: IntensityLegend(
mode: hasEew
? IntensityLegendMode.eew
: IntensityLegendMode.rts,
),
);
},
),
),
],
),
Expand Down Expand Up @@ -944,9 +956,11 @@ class _ReplayMapState extends State<_ReplayMap> {
/// Tints the whole island by estimated shaking while an EEW alert is up —
/// the legacy monitor's county/town fill behaviour, driven by the same
/// [`EewEstimator.areaPga`] math. The base style's own `town` fill layer is
/// recoloured with a `match` on each township's `CODE` (hidden counties
/// beneath), so the felt-intensity wash reads over the base map without a
/// second geometry source; when the alerts clear the fills are restored.
/// recoloured with a `match` on each township's `CODE`, so the felt-intensity
/// wash reads over the base map without a second geometry source; when the
/// alerts clear the wash is cleared. The county fill underneath stays the
/// opaque grey island throughout — the wash is transparent wherever the
/// estimate reads 0, and something has to be opaque over Taiwan there.
///
/// With two simultaneous quakes this must use whichever alert
/// [_ReplayMap.eewIndex] currently selects (the same one the card above is
Expand All @@ -966,11 +980,17 @@ class _ReplayMapState extends State<_ReplayMap> {

final baseFill = MapColors.of(Theme.of(context).brightness).fill;
try {
// The opaque grey island is this layer's job in *every* state, alert or
// not, so it is restored before the branch rather than hidden under the
// wash. Hiding it during an alert left nothing opaque over Taiwan
// wherever the estimate reads 0 — the wash falls back to transparent
// there — and the EEW S-wave disc, anchored below [landLayerId] so it
// washes open sea only, came through the island instead.
await controller.setLayerProperties(
countyFillLayerId,
FillLayerProperties(fillColor: baseFill, fillOpacity: 1),
);
if (selected == null) {
await controller.setLayerProperties(
countyFillLayerId,
FillLayerProperties(fillColor: baseFill, fillOpacity: 1),
);
// Back to the baked default. This layer is the wash and nothing else:
// with no alert up it paints nothing, so the OSM detailed ground —
// which mounts directly beneath it — keeps showing. The grey island is
Expand Down Expand Up @@ -1002,10 +1022,6 @@ class _ReplayMapState extends State<_ReplayMap> {
});
if (entries.isEmpty) return;

await controller.setLayerProperties(
countyFillLayerId,
const FillLayerProperties(fillColor: '#00000000', fillOpacity: 0),
);
await controller.setLayerProperties(
townFillLayerId,
FillLayerProperties(
Expand All @@ -1015,9 +1031,9 @@ class _ReplayMapState extends State<_ReplayMap> {
...entries,
// Transparent, not the palette grey: a township the estimate puts
// at 0 has to leave whatever is under it showing — the OSM
// detailed ground when that layer is on, the grey `land` fill when
// it is not. Falling back to grey painted a flat sheet over the
// detailed map everywhere the shaking was 0.
// detailed ground when that layer is on, the county fill's grey
// when it is not. Falling back to grey painted a flat sheet over
// the detailed map everywhere the shaking was 0.
'rgba(0, 0, 0, 0)',
],
fillOpacity: 1,
Expand Down
Loading
Loading