l10n: DMS coordinate directions
This commit is contained in:
parent
4ee510d7a0
commit
c74b94fb22
11 changed files with 69 additions and 30 deletions
|
@ -176,6 +176,25 @@
|
|||
"@coordinateFormatDms": {},
|
||||
"coordinateFormatDecimal": "Decimal degrees",
|
||||
"@coordinateFormatDecimal": {},
|
||||
"coordinateDms": "{coordinate} {direction}",
|
||||
"@coordinateDms": {
|
||||
"placeholders": {
|
||||
"coordinate": {
|
||||
"type": "String"
|
||||
},
|
||||
"direction": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coordinateDmsNorth": "N",
|
||||
"@coordinateDmsNorth": {},
|
||||
"coordinateDmsSouth": "S",
|
||||
"@coordinateDmsSouth": {},
|
||||
"coordinateDmsEast": "E",
|
||||
"@coordinateDmsEast": {},
|
||||
"coordinateDmsWest": "W",
|
||||
"@coordinateDmsWest": {},
|
||||
|
||||
"unitSystemMetric": "Metric",
|
||||
"@unitSystemMetric": {},
|
||||
|
|
|
@ -87,6 +87,11 @@
|
|||
|
||||
"coordinateFormatDms": "도분초",
|
||||
"coordinateFormatDecimal": "소수점",
|
||||
"coordinateDms": "{direction} {coordinate}",
|
||||
"coordinateDmsNorth": "북위",
|
||||
"coordinateDmsSouth": "남위",
|
||||
"coordinateDmsEast": "동경",
|
||||
"coordinateDmsWest": "서경",
|
||||
|
||||
"unitSystemMetric": "미터법",
|
||||
"unitSystemImperial": "야드파운드법",
|
||||
|
|
|
@ -87,6 +87,11 @@
|
|||
|
||||
"coordinateFormatDms": "Градусы, минуты и секунды",
|
||||
"coordinateFormatDecimal": "Десятичные градусы",
|
||||
"coordinateDms": "{coordinate} {direction}",
|
||||
"coordinateDmsNorth": "с. ш.",
|
||||
"coordinateDmsSouth": "ю. ш.",
|
||||
"coordinateDmsEast": "в. д.",
|
||||
"coordinateDmsWest": "з. д.",
|
||||
|
||||
"unitSystemMetric": "Метрические",
|
||||
"unitSystemImperial": "Имперские",
|
||||
|
|
|
@ -4,8 +4,10 @@ import 'package:aves/model/settings/enums.dart';
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/geo_utils.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -37,8 +39,9 @@ class CoordinateFilter extends CollectionFilter {
|
|||
@override
|
||||
EntryFilter get test => (entry) => GeoUtils.contains(sw, ne, entry.latLng);
|
||||
|
||||
String _formatBounds(CoordinateFormat format) {
|
||||
String _formatBounds(AppLocalizations l10n, CoordinateFormat format) {
|
||||
String s(LatLng latLng) => format.format(
|
||||
l10n,
|
||||
latLng,
|
||||
minuteSecondPadding: minuteSecondPadding,
|
||||
dmsSecondDecimals: 0,
|
||||
|
@ -47,10 +50,10 @@ class CoordinateFilter extends CollectionFilter {
|
|||
}
|
||||
|
||||
@override
|
||||
String get universalLabel => _formatBounds(CoordinateFormat.decimal);
|
||||
String get universalLabel => _formatBounds(lookupAppLocalizations(AppLocalizations.supportedLocales.first), CoordinateFormat.decimal);
|
||||
|
||||
@override
|
||||
String getLabel(BuildContext context) => _formatBounds(context.read<Settings>().coordinateFormat);
|
||||
String getLabel(BuildContext context) => _formatBounds(context.l10n, context.read<Settings>().coordinateFormat);
|
||||
|
||||
@override
|
||||
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true, bool embossed = false}) => Icon(AIcons.geoBounds, size: size);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/utils/geo_utils.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
import 'enums.dart';
|
||||
|
@ -15,12 +16,24 @@ extension ExtraCoordinateFormat on CoordinateFormat {
|
|||
}
|
||||
}
|
||||
|
||||
String format(LatLng latLng, {bool minuteSecondPadding = false, int dmsSecondDecimals = 2}) {
|
||||
String format(AppLocalizations l10n, LatLng latLng, {bool minuteSecondPadding = false, int dmsSecondDecimals = 2}) {
|
||||
switch (this) {
|
||||
case CoordinateFormat.dms:
|
||||
return GeoUtils.toDMS(latLng, minuteSecondPadding: minuteSecondPadding, secondDecimals: dmsSecondDecimals).join(', ');
|
||||
return toDMS(l10n, latLng, minuteSecondPadding: minuteSecondPadding, secondDecimals: dmsSecondDecimals).join(', ');
|
||||
case CoordinateFormat.decimal:
|
||||
return [latLng.latitude, latLng.longitude].map((n) => n.toStringAsFixed(6)).join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
// returns coordinates formatted as DMS, e.g. ['41° 24′ 12.2″ N', '2° 10′ 26.5″ E']
|
||||
static List<String> toDMS(AppLocalizations l10n, LatLng latLng, {bool minuteSecondPadding = false, int secondDecimals = 2}) {
|
||||
final lat = latLng.latitude;
|
||||
final lng = latLng.longitude;
|
||||
final latSexa = GeoUtils.decimal2sexagesimal(lat, minuteSecondPadding, secondDecimals);
|
||||
final lngSexa = GeoUtils.decimal2sexagesimal(lng, minuteSecondPadding, secondDecimals);
|
||||
return [
|
||||
l10n.coordinateDms(latSexa, lat < 0 ? l10n.coordinateDmsSouth : l10n.coordinateDmsNorth),
|
||||
l10n.coordinateDms(lngSexa, lng < 0 ? l10n.coordinateDmsWest : l10n.coordinateDmsEast),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
class GeoUtils {
|
||||
static String _decimal2sexagesimal(final double degDecimal, final bool minuteSecondPadding, final int secondDecimals) {
|
||||
static String decimal2sexagesimal(final double degDecimal, final bool minuteSecondPadding, final int secondDecimals) {
|
||||
List<int> _split(final double value) {
|
||||
// NumberFormat is necessary to create digit after comma if the value
|
||||
// has no decimal point (only necessary for browser)
|
||||
|
@ -32,16 +32,6 @@ class GeoUtils {
|
|||
return '$deg° $minText′ $secText″';
|
||||
}
|
||||
|
||||
// returns coordinates formatted as DMS, e.g. ['41° 24′ 12.2″ N', '2° 10′ 26.5″ E']
|
||||
static List<String> toDMS(LatLng latLng, {bool minuteSecondPadding = false, int secondDecimals = 2}) {
|
||||
final lat = latLng.latitude;
|
||||
final lng = latLng.longitude;
|
||||
return [
|
||||
'${_decimal2sexagesimal(lat, minuteSecondPadding, secondDecimals)} ${lat < 0 ? 'S' : 'N'}',
|
||||
'${_decimal2sexagesimal(lng, minuteSecondPadding, secondDecimals)} ${lng < 0 ? 'W' : 'E'}',
|
||||
];
|
||||
}
|
||||
|
||||
static LatLng getLatLngCenter(List<LatLng> points) {
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
|
|
|
@ -121,7 +121,7 @@ class _AddressRowState extends State<_AddressRow> {
|
|||
? Constants.overlayUnknown
|
||||
: entry.hasAddress
|
||||
? entry.shortAddress
|
||||
: settings.coordinateFormat.format(entry.latLng!));
|
||||
: settings.coordinateFormat.format(context.l10n, entry.latLng!));
|
||||
return Text(
|
||||
location,
|
||||
strutStyle: Constants.overflowStrutStyle,
|
||||
|
|
|
@ -23,6 +23,7 @@ class LanguageSection extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final currentCoordinateFormat = context.select<Settings, CoordinateFormat>((s) => s.coordinateFormat);
|
||||
final currentUnitSystem = context.select<Settings, UnitSystem>((s) => s.unitSystem);
|
||||
|
||||
|
@ -34,13 +35,13 @@ class LanguageSection extends StatelessWidget {
|
|||
icon: AIcons.language,
|
||||
color: stringToColor('Language'),
|
||||
),
|
||||
title: context.l10n.settingsSectionLanguage,
|
||||
title: l10n.settingsSectionLanguage,
|
||||
expandedNotifier: expandedNotifier,
|
||||
showHighlight: false,
|
||||
children: [
|
||||
const LocaleTile(),
|
||||
ListTile(
|
||||
title: Text(context.l10n.settingsCoordinateFormatTile),
|
||||
title: Text(l10n.settingsCoordinateFormatTile),
|
||||
subtitle: Text(currentCoordinateFormat.getName(context)),
|
||||
onTap: () async {
|
||||
final value = await showDialog<CoordinateFormat>(
|
||||
|
@ -48,8 +49,8 @@ class LanguageSection extends StatelessWidget {
|
|||
builder: (context) => AvesSelectionDialog<CoordinateFormat>(
|
||||
initialValue: currentCoordinateFormat,
|
||||
options: Map.fromEntries(CoordinateFormat.values.map((v) => MapEntry(v, v.getName(context)))),
|
||||
optionSubtitleBuilder: (value) => value.format(Constants.pointNemo),
|
||||
title: context.l10n.settingsCoordinateFormatTitle,
|
||||
optionSubtitleBuilder: (value) => value.format(l10n, Constants.pointNemo),
|
||||
title: l10n.settingsCoordinateFormatTitle,
|
||||
),
|
||||
);
|
||||
if (value != null) {
|
||||
|
@ -58,7 +59,7 @@ class LanguageSection extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.l10n.settingsUnitSystemTile),
|
||||
title: Text(l10n.settingsUnitSystemTile),
|
||||
subtitle: Text(currentUnitSystem.getName(context)),
|
||||
onTap: () async {
|
||||
final value = await showDialog<UnitSystem>(
|
||||
|
@ -66,7 +67,7 @@ class LanguageSection extends StatelessWidget {
|
|||
builder: (context) => AvesSelectionDialog<UnitSystem>(
|
||||
initialValue: currentUnitSystem,
|
||||
options: Map.fromEntries(UnitSystem.values.map((v) => MapEntry(v, v.getName(context)))),
|
||||
title: context.l10n.settingsUnitSystemTitle,
|
||||
title: l10n.settingsUnitSystemTitle,
|
||||
),
|
||||
);
|
||||
if (value != null) {
|
||||
|
|
|
@ -174,7 +174,7 @@ class _AddressInfoGroupState extends State<_AddressInfoGroup> {
|
|||
final l10n = context.l10n;
|
||||
return InfoRowGroup(
|
||||
info: {
|
||||
l10n.viewerInfoLabelCoordinates: settings.coordinateFormat.format(entry.latLng!),
|
||||
l10n.viewerInfoLabelCoordinates: settings.coordinateFormat.format(l10n, entry.latLng!),
|
||||
if (address.isNotEmpty) l10n.viewerInfoLabelAddress: address,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -323,7 +323,7 @@ class _LocationRow extends AnimatedWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final location = entry.hasAddress ? entry.shortAddress : settings.coordinateFormat.format(entry.latLng!);
|
||||
final location = entry.hasAddress ? entry.shortAddress : settings.coordinateFormat.format(context.l10n, entry.latLng!);
|
||||
return Row(
|
||||
children: [
|
||||
const DecoratedIcon(AIcons.location, shadows: Constants.embossShadows, size: _iconSize),
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import 'package:aves/model/settings/coordinate_format.dart';
|
||||
import 'package:aves/utils/geo_utils.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('Decimal degrees to DMS (sexagesimal)', () {
|
||||
expect(GeoUtils.toDMS(LatLng(37.496667, 127.0275)), ['37° 29′ 48.00″ N', '127° 1′ 39.00″ E']); // Gangnam
|
||||
expect(GeoUtils.toDMS(LatLng(78.9243503, 11.9230465)), ['78° 55′ 27.66″ N', '11° 55′ 22.97″ E']); // Ny-Ålesund
|
||||
expect(GeoUtils.toDMS(LatLng(-38.6965891, 175.9830047)), ['38° 41′ 47.72″ S', '175° 58′ 58.82″ E']); // Taupo
|
||||
expect(GeoUtils.toDMS(LatLng(-64.249391, -56.6556145)), ['64° 14′ 57.81″ S', '56° 39′ 20.21″ W']); // Marambio
|
||||
expect(GeoUtils.toDMS(LatLng(0, 0)), ['0° 0′ 0.00″ N', '0° 0′ 0.00″ E']);
|
||||
final l10n = lookupAppLocalizations(AppLocalizations.supportedLocales.first);
|
||||
expect(ExtraCoordinateFormat.toDMS(l10n, LatLng(37.496667, 127.0275)), ['37° 29′ 48.00″ N', '127° 1′ 39.00″ E']); // Gangnam
|
||||
expect(ExtraCoordinateFormat.toDMS(l10n, LatLng(78.9243503, 11.9230465)), ['78° 55′ 27.66″ N', '11° 55′ 22.97″ E']); // Ny-Ålesund
|
||||
expect(ExtraCoordinateFormat.toDMS(l10n, LatLng(-38.6965891, 175.9830047)), ['38° 41′ 47.72″ S', '175° 58′ 58.82″ E']); // Taupo
|
||||
expect(ExtraCoordinateFormat.toDMS(l10n, LatLng(-64.249391, -56.6556145)), ['64° 14′ 57.81″ S', '56° 39′ 20.21″ W']); // Marambio
|
||||
expect(ExtraCoordinateFormat.toDMS(l10n, LatLng(0, 0)), ['0° 0′ 0.00″ N', '0° 0′ 0.00″ E']);
|
||||
});
|
||||
|
||||
test('bounds center', () {
|
||||
|
|
Loading…
Reference in a new issue