settings: unit system
This commit is contained in:
parent
9a2451ea0c
commit
718b4749a0
10 changed files with 96 additions and 9 deletions
|
@ -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": {},
|
||||||
|
|
|
@ -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{사진}}",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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:
|
||||||
|
|
15
lib/model/settings/unit_system.dart
Normal file
15
lib/model/settings/unit_system.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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: [
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue