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": {},
"unitSystemMetric": "Metric",
"@unitSystemMetric": {},
"unitSystemImperial": "Imperial",
"@unitSystemImperial": {},
"videoLoopModeNever": "Never",
"@videoLoopModeNever": {},
"videoLoopModeShortOnly": "Short videos only",
@ -835,6 +840,10 @@
"@settingsCoordinateFormatTile": {},
"settingsCoordinateFormatTitle": "Coordinate Format",
"@settingsCoordinateFormatTitle": {},
"settingsUnitSystemTile": "Units",
"@settingsUnitSystemTile": {},
"settingsUnitSystemTitle": "Units",
"@settingsUnitSystemTitle": {},
"statsPageTitle": "Stats",
"@statsPageTitle": {},

View file

@ -86,6 +86,9 @@
"coordinateFormatDms": "도분초",
"coordinateFormatDecimal": "소수점",
"unitSystemMetric": "미터법",
"unitSystemImperial": "야드파운드법",
"videoLoopModeNever": "반복 안 함",
"videoLoopModeShortOnly": "짧은 동영상만 반복",
"videoLoopModeAlways": "항상 반복",
@ -409,6 +412,8 @@
"settingsLanguage": "언어",
"settingsCoordinateFormatTile": "좌표 표현",
"settingsCoordinateFormatTitle": "좌표 표현",
"settingsUnitSystemTile": "단위법",
"settingsUnitSystemTitle": "단위법",
"statsPageTitle": "통계",
"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 infoMapZoom = 12.0;
static const coordinateFormat = CoordinateFormat.dms;
static const unitSystem = UnitSystem.metric;
// rendering
static const imageBackground = EntryBackground.white;

View file

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

View file

@ -98,6 +98,7 @@ class Settings extends ChangeNotifier {
static const infoMapStyleKey = 'info_map_style';
static const infoMapZoomKey = 'info_map_zoom';
static const coordinateFormatKey = 'coordinates_format';
static const unitSystemKey = 'unit_system';
// rendering
static const imageBackgroundKey = 'image_background';
@ -379,6 +380,10 @@ class Settings extends ChangeNotifier {
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
EntryBackground get imageBackground => getEnumOrDefault(imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values);
@ -574,6 +579,7 @@ class Settings extends ChangeNotifier {
case subtitleTextAlignmentKey:
case infoMapStyleKey:
case coordinateFormatKey:
case unitSystemKey:
case imageBackgroundKey:
case accessibilityAnimationsKey:
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/settings/enums.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/utils/debouncer.dart';
import 'package:aves/widgets/common/map/buttons.dart';
@ -163,7 +164,9 @@ class _EntryLeafletMapState extends State<EntryLeafletMap> with TickerProviderSt
mapController: _leafletMapController,
nonRotatedChildren: [
ScaleLayerWidget(
options: ScaleLayerOptions(),
options: ScaleLayerOptions(
unitSystem: settings.unitSystem,
),
),
],
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/map/leaflet/scalebar_utils.dart';
import 'package:flutter/material.dart';
@ -7,10 +6,12 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/plugin_api.dart';
class ScaleLayerOptions extends LayerOptions {
final UnitSystem unitSystem;
final Widget Function(double width, String distance) builder;
ScaleLayerOptions({
Key? key,
this.unitSystem = UnitSystem.metric,
this.builder = defaultBuilder,
rebuild,
}) : super(key: key, rebuild: rebuild);
@ -41,7 +42,7 @@ class ScaleLayer extends StatelessWidget {
// ignore: prefer_void_to_null
final Stream<Null> stream;
final scale = [
static const List<double> scaleMeters = [
25000000,
15000000,
8000000,
@ -67,6 +68,10 @@ class ScaleLayer extends StatelessWidget {
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);
@override
@ -83,11 +88,33 @@ class ScaleLayer extends StatelessWidget {
: latitude > 60
? 3
: 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 targetPoint = ScaleBarUtils.calculateEndingGlobalCoordinates(center, 90, distance);
final targetPoint = ScaleBarUtils.calculateEndingGlobalCoordinates(center, 90, distanceMeters);
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);
return scaleLayerOpts.builder(width, displayDistance);

View file

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:latlong2/latlong.dart';
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 mSemiMinorAxis = (1.0 - 1.0 / 298.257223563) * 6378137.0;
var mFlattening = 1.0 / 298.257223563;
@ -18,7 +18,7 @@ class ScaleBarUtils {
var alpha1 = degToRadian(startBearing);
var cosAlpha1 = cos(alpha1);
var sinAlpha1 = sin(alpha1);
var s = distance;
var s = distanceMeters;
var tanU1 = (1.0 - f) * tan(phi1);
var cosU1 = 1.0 / sqrt(1.0 + tanU1 * tanU1);
var sinU1 = tanU1 * cosU1;

View file

@ -1,6 +1,7 @@
import 'package:aves/model/settings/coordinate_format.dart';
import 'package:aves/model/settings/enums.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/utils/color_utils.dart';
import 'package:aves/utils/constants.dart';
@ -23,6 +24,7 @@ class LanguageSection extends StatelessWidget {
@override
Widget build(BuildContext context) {
final currentCoordinateFormat = context.select<Settings, CoordinateFormat>((s) => s.coordinateFormat);
final currentUnitSystem = context.select<Settings, UnitSystem>((s) => s.unitSystem);
return AvesExpansionTile(
// 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;
}
},
),
],
);
}