settings: unit system

This commit is contained in:
Thibault Deckers 2021-10-07 18:07:42 +09:00
parent 9a2451ea0c
commit 718b4749a0
10 changed files with 96 additions and 9 deletions

View file

@ -173,6 +173,11 @@
"coordinateFormatDecimal": "Decimal degrees", "coordinateFormatDecimal": "Decimal degrees",
"@coordinateFormatDecimal": {}, "@coordinateFormatDecimal": {},
"unitSystemMetric": "Metric",
"@unitSystemMetric": {},
"unitSystemImperial": "Imperial",
"@unitSystemImperial": {},
"videoLoopModeNever": "Never", "videoLoopModeNever": "Never",
"@videoLoopModeNever": {}, "@videoLoopModeNever": {},
"videoLoopModeShortOnly": "Short videos only", "videoLoopModeShortOnly": "Short videos only",
@ -835,6 +840,10 @@
"@settingsCoordinateFormatTile": {}, "@settingsCoordinateFormatTile": {},
"settingsCoordinateFormatTitle": "Coordinate Format", "settingsCoordinateFormatTitle": "Coordinate Format",
"@settingsCoordinateFormatTitle": {}, "@settingsCoordinateFormatTitle": {},
"settingsUnitSystemTile": "Units",
"@settingsUnitSystemTile": {},
"settingsUnitSystemTitle": "Units",
"@settingsUnitSystemTitle": {},
"statsPageTitle": "Stats", "statsPageTitle": "Stats",
"@statsPageTitle": {}, "@statsPageTitle": {},

View file

@ -86,6 +86,9 @@
"coordinateFormatDms": "도분초", "coordinateFormatDms": "도분초",
"coordinateFormatDecimal": "소수점", "coordinateFormatDecimal": "소수점",
"unitSystemMetric": "미터법",
"unitSystemImperial": "야드파운드법",
"videoLoopModeNever": "반복 안 함", "videoLoopModeNever": "반복 안 함",
"videoLoopModeShortOnly": "짧은 동영상만 반복", "videoLoopModeShortOnly": "짧은 동영상만 반복",
"videoLoopModeAlways": "항상 반복", "videoLoopModeAlways": "항상 반복",
@ -409,6 +412,8 @@
"settingsLanguage": "언어", "settingsLanguage": "언어",
"settingsCoordinateFormatTile": "좌표 표현", "settingsCoordinateFormatTile": "좌표 표현",
"settingsCoordinateFormatTitle": "좌표 표현", "settingsCoordinateFormatTitle": "좌표 표현",
"settingsUnitSystemTile": "단위법",
"settingsUnitSystemTitle": "단위법",
"statsPageTitle": "통계", "statsPageTitle": "통계",
"statsImage": "{count, plural, other{사진}}", "statsImage": "{count, plural, other{사진}}",

View file

@ -82,6 +82,7 @@ class SettingsDefaults {
static const infoMapStyle = EntryMapStyle.stamenWatercolor; // `infoMapStyle` has a contextual default value static const infoMapStyle = EntryMapStyle.stamenWatercolor; // `infoMapStyle` has a contextual default value
static const infoMapZoom = 12.0; static const infoMapZoom = 12.0;
static const coordinateFormat = CoordinateFormat.dms; static const coordinateFormat = CoordinateFormat.dms;
static const unitSystem = UnitSystem.metric;
// rendering // rendering
static const imageBackground = EntryBackground.white; static const imageBackground = EntryBackground.white;

View file

@ -13,4 +13,6 @@ enum EntryMapStyle { googleNormal, googleHybrid, googleTerrain, osmHot, stamenTo
enum KeepScreenOn { never, viewerOnly, always } enum KeepScreenOn { never, viewerOnly, always }
enum UnitSystem { metric, imperial }
enum VideoLoopMode { never, shortOnly, always } enum VideoLoopMode { never, shortOnly, always }

View file

@ -98,6 +98,7 @@ class Settings extends ChangeNotifier {
static const infoMapStyleKey = 'info_map_style'; static const infoMapStyleKey = 'info_map_style';
static const infoMapZoomKey = 'info_map_zoom'; static const infoMapZoomKey = 'info_map_zoom';
static const coordinateFormatKey = 'coordinates_format'; static const coordinateFormatKey = 'coordinates_format';
static const unitSystemKey = 'unit_system';
// rendering // rendering
static const imageBackgroundKey = 'image_background'; static const imageBackgroundKey = 'image_background';
@ -379,6 +380,10 @@ class Settings extends ChangeNotifier {
set coordinateFormat(CoordinateFormat newValue) => setAndNotify(coordinateFormatKey, newValue.toString()); set coordinateFormat(CoordinateFormat newValue) => setAndNotify(coordinateFormatKey, newValue.toString());
UnitSystem get unitSystem => getEnumOrDefault(unitSystemKey, SettingsDefaults.unitSystem, UnitSystem.values);
set unitSystem(UnitSystem newValue) => setAndNotify(unitSystemKey, newValue.toString());
// rendering // rendering
EntryBackground get imageBackground => getEnumOrDefault(imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values); EntryBackground get imageBackground => getEnumOrDefault(imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values);
@ -574,6 +579,7 @@ class Settings extends ChangeNotifier {
case subtitleTextAlignmentKey: case subtitleTextAlignmentKey:
case infoMapStyleKey: case infoMapStyleKey:
case coordinateFormatKey: case coordinateFormatKey:
case unitSystemKey:
case imageBackgroundKey: case imageBackgroundKey:
case accessibilityAnimationsKey: case accessibilityAnimationsKey:
case timeToTakeActionKey: case timeToTakeActionKey:

View file

@ -0,0 +1,15 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/widgets.dart';
import 'enums.dart';
extension ExtraUnitSystem on UnitSystem {
String getName(BuildContext context) {
switch (this) {
case UnitSystem.metric:
return context.l10n.unitSystemMetric;
case UnitSystem.imperial:
return context.l10n.unitSystemImperial;
}
}
}

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/settings/enums.dart'; import 'package:aves/model/settings/enums.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/utils/debouncer.dart'; import 'package:aves/utils/debouncer.dart';
import 'package:aves/widgets/common/map/buttons.dart'; import 'package:aves/widgets/common/map/buttons.dart';
@ -163,7 +164,9 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
mapController: _leafletMapController, mapController: _leafletMapController,
nonRotatedChildren: [ nonRotatedChildren: [
ScaleLayerWidget( ScaleLayerWidget(
options: ScaleLayerOptions(), options: ScaleLayerOptions(
unitSystem: settings.unitSystem,
),
), ),
], ],
children: [ children: [

View file

@ -1,5 +1,4 @@
import 'dart:math'; import 'package:aves/model/settings/enums.dart';
import 'package:aves/widgets/common/basic/outlined_text.dart'; import 'package:aves/widgets/common/basic/outlined_text.dart';
import 'package:aves/widgets/common/map/leaflet/scalebar_utils.dart'; import 'package:aves/widgets/common/map/leaflet/scalebar_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -7,10 +6,12 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_map/plugin_api.dart';
class ScaleLayerOptions extends LayerOptions { class ScaleLayerOptions extends LayerOptions {
final UnitSystem unitSystem;
final Widget Function(double width, String distance) builder; final Widget Function(double width, String distance) builder;
ScaleLayerOptions({ ScaleLayerOptions({
Key? key, Key? key,
this.unitSystem = UnitSystem.metric,
this.builder = defaultBuilder, this.builder = defaultBuilder,
rebuild, rebuild,
}) : super(key: key, rebuild: rebuild); }) : super(key: key, rebuild: rebuild);
@ -41,7 +42,7 @@ class ScaleLayer extends StatelessWidget {
// ignore: prefer_void_to_null // ignore: prefer_void_to_null
final Stream<Null> stream; final Stream<Null> stream;
final scale = [ static const List<double> scaleMeters = [
25000000, 25000000,
15000000, 15000000,
8000000, 8000000,
@ -67,6 +68,10 @@ class ScaleLayer extends StatelessWidget {
5, 5,
]; ];
static const double metersInAKilometer = 1000;
static const double metersInAMile = 1609.344;
static const double metersInAFoot = 0.3048;
ScaleLayer(this.scaleLayerOpts, this.map, this.stream) : super(key: scaleLayerOpts.key); ScaleLayer(this.scaleLayerOpts, this.map, this.stream) : super(key: scaleLayerOpts.key);
@override @override
@ -83,11 +88,33 @@ class ScaleLayer extends StatelessWidget {
: latitude > 60 : latitude > 60
? 3 ? 3
: 2); : 2);
final distance = scale[max(0, min(20, level))].toDouble(); final scaleLevel = level.clamp(0, 20);
late final double distanceMeters;
late final String displayDistance;
switch (scaleLayerOpts.unitSystem) {
case UnitSystem.metric:
// meters
distanceMeters = scaleMeters[scaleLevel];
displayDistance = distanceMeters >= metersInAKilometer ? '${(distanceMeters / metersInAKilometer).toStringAsFixed(0)} km' : '${distanceMeters.toStringAsFixed(0)} m';
break;
case UnitSystem.imperial:
if (scaleLevel < 15) {
// miles
final distanceMiles = scaleMeters[scaleLevel + 1] / 1000;
distanceMeters = distanceMiles * metersInAMile;
displayDistance = '${distanceMiles.toStringAsFixed(0)} mi';
} else {
// feet
final distanceFeet = scaleMeters[scaleLevel - 1];
distanceMeters = distanceFeet * metersInAFoot;
displayDistance = '${distanceFeet.toStringAsFixed(0)} ft';
}
break;
}
final start = map.project(center); final start = map.project(center);
final targetPoint = ScaleBarUtils.calculateEndingGlobalCoordinates(center, 90, distance); final targetPoint = ScaleBarUtils.calculateEndingGlobalCoordinates(center, 90, distanceMeters);
final end = map.project(targetPoint); final end = map.project(targetPoint);
final displayDistance = distance > 999 ? '${(distance / 1000).toStringAsFixed(0)} km' : '${distance.toStringAsFixed(0)} m';
final width = end.x - (start.x as double); final width = end.x - (start.x as double);
return scaleLayerOpts.builder(width, displayDistance); return scaleLayerOpts.builder(width, displayDistance);

View file

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
class ScaleBarUtils { class ScaleBarUtils {
static LatLng calculateEndingGlobalCoordinates(LatLng start, double startBearing, double distance) { static LatLng calculateEndingGlobalCoordinates(LatLng start, double startBearing, double distanceMeters) {
var mSemiMajorAxis = 6378137.0; //WGS84 major axis var mSemiMajorAxis = 6378137.0; //WGS84 major axis
var mSemiMinorAxis = (1.0 - 1.0 / 298.257223563) * 6378137.0; var mSemiMinorAxis = (1.0 - 1.0 / 298.257223563) * 6378137.0;
var mFlattening = 1.0 / 298.257223563; var mFlattening = 1.0 / 298.257223563;
@ -18,7 +18,7 @@ class ScaleBarUtils {
var alpha1 = degToRadian(startBearing); var alpha1 = degToRadian(startBearing);
var cosAlpha1 = cos(alpha1); var cosAlpha1 = cos(alpha1);
var sinAlpha1 = sin(alpha1); var sinAlpha1 = sin(alpha1);
var s = distance; var s = distanceMeters;
var tanU1 = (1.0 - f) * tan(phi1); var tanU1 = (1.0 - f) * tan(phi1);
var cosU1 = 1.0 / sqrt(1.0 + tanU1 * tanU1); var cosU1 = 1.0 / sqrt(1.0 + tanU1 * tanU1);
var sinU1 = tanU1 * cosU1; var sinU1 = tanU1 * cosU1;

View file

@ -1,6 +1,7 @@
import 'package:aves/model/settings/coordinate_format.dart'; import 'package:aves/model/settings/coordinate_format.dart';
import 'package:aves/model/settings/enums.dart'; import 'package:aves/model/settings/enums.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/settings/unit_system.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/color_utils.dart'; import 'package:aves/utils/color_utils.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
@ -23,6 +24,7 @@ class LanguageSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final currentCoordinateFormat = context.select<Settings, CoordinateFormat>((s) => s.coordinateFormat); final currentCoordinateFormat = context.select<Settings, CoordinateFormat>((s) => s.coordinateFormat);
final currentUnitSystem = context.select<Settings, UnitSystem>((s) => s.unitSystem);
return AvesExpansionTile( return AvesExpansionTile(
// use a fixed value instead of the title to identify this expansion tile // use a fixed value instead of the title to identify this expansion tile
@ -55,6 +57,23 @@ class LanguageSection extends StatelessWidget {
} }
}, },
), ),
ListTile(
title: Text(context.l10n.settingsUnitSystemTile),
subtitle: Text(currentUnitSystem.getName(context)),
onTap: () async {
final value = await showDialog<UnitSystem>(
context: context,
builder: (context) => AvesSelectionDialog<UnitSystem>(
initialValue: currentUnitSystem,
options: Map.fromEntries(UnitSystem.values.map((v) => MapEntry(v, v.getName(context)))),
title: context.l10n.settingsUnitSystemTitle,
),
);
if (value != null) {
settings.unitSystem = value;
}
},
),
], ],
); );
} }