From a0f9dd43da86a68009582b631da5c9030e5f838c Mon Sep 17 00:00:00 2001 From: Rafael <1024481@stud.hs-mannheim.de> Date: Mon, 27 May 2024 14:40:46 +0200 Subject: [PATCH] Added calculateDistance between two locations and first tests. --- lib/forms/profile_category_form.dart | 2 +- lib/models/location.dart | 6 +- lib/pages/login_page.dart | 2 +- lib/pages/register_page.dart | 2 +- lib/services/chat/chat_service.dart | 2 +- lib/{ => utils}/helper.dart | 25 +------- lib/utils/math.dart | 47 +++++++++++++++ test/helper_test.dart | 81 +++++++++++++++++++++++++ test/math_test.dart | 88 ++++++++++++++++++++++++++++ 9 files changed, 225 insertions(+), 30 deletions(-) rename lib/{ => utils}/helper.dart (63%) create mode 100644 lib/utils/math.dart create mode 100644 test/helper_test.dart create mode 100644 test/math_test.dart diff --git a/lib/forms/profile_category_form.dart b/lib/forms/profile_category_form.dart index 3b88d5a..f7f240a 100644 --- a/lib/forms/profile_category_form.dart +++ b/lib/forms/profile_category_form.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import '../helper.dart'; +import '../utils/helper.dart'; class ProfileCategoryForm extends StatefulWidget { final String title; diff --git a/lib/models/location.dart b/lib/models/location.dart index b50ce7c..dc35995 100644 --- a/lib/models/location.dart +++ b/lib/models/location.dart @@ -1,5 +1,5 @@ import '../constants.dart'; -import '../helper.dart'; +import '../utils/math.dart'; class MyLocation { final String street; @@ -43,8 +43,8 @@ class MyLocation { /// Returns: locality, country. In case of an error: latitude, longitude. String toStringDegree() { try { - String latResult = convertDecimalToDMS(latitude!); - String longResult = convertDecimalToDMS(longitude!); + String latResult = convertDecimalToDMS(latitude!, isLatitude: true); + String longResult = convertDecimalToDMS(longitude!, isLatitude: false); return '$latResult, $longResult'; } catch (e) { // on error return origin values diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 784bcfb..bdf7816 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -2,7 +2,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import '../components/my_button.dart'; import '../components/my_textfield.dart'; -import '../helper.dart'; +import '../utils/helper.dart'; import '../services/auth/auth_service.dart'; class LoginPage extends StatelessWidget { diff --git a/lib/pages/register_page.dart b/lib/pages/register_page.dart index 73a1a32..948e1b0 100644 --- a/lib/pages/register_page.dart +++ b/lib/pages/register_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import '../components/my_button.dart'; import '../components/my_textfield.dart'; -import '../helper.dart'; +import '../utils/helper.dart'; import '../services/auth/auth_service.dart'; import '../services/user_service.dart'; diff --git a/lib/services/chat/chat_service.dart b/lib/services/chat/chat_service.dart index 625b398..2aa9adf 100644 --- a/lib/services/chat/chat_service.dart +++ b/lib/services/chat/chat_service.dart @@ -2,7 +2,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import '../../constants.dart'; -import '../../helper.dart'; +import '../../utils/helper.dart'; import '../../models/message.dart'; class ChatService { diff --git a/lib/helper.dart b/lib/utils/helper.dart similarity index 63% rename from lib/helper.dart rename to lib/utils/helper.dart index 7172a7f..4cb3f4b 100644 --- a/lib/helper.dart +++ b/lib/utils/helper.dart @@ -12,32 +12,11 @@ bool equalContent(List list1, List list2) { /// Creates a composite ID from the passed [ids]. /// In the format id(1)_id(n) /// -String getCompoundId(List ids){ +String getCompoundId(List ids) { ids.sort(); // sort to ensure the result is the same for any order of ids return ids.join('_'); } -/// -/// Convert decimal coordinate to degrees minutes seconds (DMS). -/// -String convertDecimalToDMS(double decimalValue) { - bool isNegative = decimalValue < 0; - double absoluteValue = decimalValue.abs(); - - int degrees = absoluteValue.toInt(); - double minutesDecimal = (absoluteValue - degrees) * 60; - int minutes = minutesDecimal.toInt(); - double secondsDecimal = (minutesDecimal - minutes) * 60; - double seconds = double.parse(secondsDecimal.toStringAsFixed(2)); - - String direction = isNegative - ? (decimalValue < 0 ? 'W' : 'S') - : (decimalValue >= 0 ? (degrees != 0 ? 'N' : 'E') : ''); - - // return formatted string - return '${degrees.abs()}° ${minutes.abs()}\' ${seconds.abs()}" $direction'; -} - /// /// Get the [displayName] of our own Enumerations. /// @@ -72,4 +51,4 @@ void showMsg(BuildContext context, String title, String content) { ], ), ); -} \ No newline at end of file +} diff --git a/lib/utils/math.dart b/lib/utils/math.dart new file mode 100644 index 0000000..b99a219 --- /dev/null +++ b/lib/utils/math.dart @@ -0,0 +1,47 @@ +import 'dart:math'; + +/// +/// Convert decimal coordinate to degrees minutes seconds (DMS). +/// +String convertDecimalToDMS(double decimalValue, {required bool isLatitude}) { + bool isNegative = decimalValue < 0; + double absoluteValue = decimalValue.abs(); + + int degrees = absoluteValue.toInt(); + double minutesDecimal = (absoluteValue - degrees) * 60; + int minutes = minutesDecimal.toInt(); + double secondsDecimal = (minutesDecimal - minutes) * 60; + double seconds = double.parse(secondsDecimal.toStringAsFixed(2)); + + String direction; + if (isLatitude) { + direction = isNegative ? 'S' : 'N'; + } else { + direction = isNegative ? 'W' : 'E'; + } + + // return formatted string + return '${degrees.abs()}° ${minutes.abs()}\' ${seconds.abs().toStringAsFixed(2)}" $direction'; +} + +/// +/// Distance in kilometers between two location coordinates +/// +double calculateDistance(double lat1, double lon1, double lat2, double lon2) { + const R = 6371; // earth radius in kilometers + + // distance between latitudes and longitudes + final dLat = _degreesToRadians(lat2 - lat1); + final dLon = _degreesToRadians(lon2 - lon1); + + final a = sin(dLat / 2) * sin(dLat / 2) + + cos(_degreesToRadians(lat1)) * cos(_degreesToRadians(lat2)) * + sin(dLon / 2) * sin(dLon / 2); + final c = 2 * atan2(sqrt(a), sqrt(1 - a)); + + return R * c; +} + +double _degreesToRadians(double degrees) { + return degrees * pi / 180; +} diff --git a/test/helper_test.dart b/test/helper_test.dart new file mode 100644 index 0000000..eb15677 --- /dev/null +++ b/test/helper_test.dart @@ -0,0 +1,81 @@ +import 'package:cofounderella/utils/helper.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('getCompoundId', () { + test('returns correct compound ID for a sorted list', () { + List ids = ['id3', 'id1', 'id2']; + String result = getCompoundId(ids); + expect(result, 'id1_id2_id3'); + }); + + test('returns correct compound ID for an already sorted list', () { + List ids = ['id1', 'id2', 'id3']; + String result = getCompoundId(ids); + expect(result, 'id1_id2_id3'); + }); + + test('returns correct compound ID for a single element list', () { + List ids = ['id1']; + String result = getCompoundId(ids); + expect(result, 'id1'); + }); + + test('returns correct compound ID for empty elements', () { + List ids = ['id1','','id2','']; + String result = getCompoundId(ids); + expect(result, '__id1_id2'); + }); + + test('returns correct compound ID for an empty list', () { + List ids = []; + String result = getCompoundId(ids); + expect(result, ''); + }); + }); + + group('equalContent', () { + test('returns true for lists with the same content in the same order', () { + List list1 = [1, 2, 3]; + List list2 = [1, 2, 3]; + bool result = equalContent(list1, list2); + expect(result, true); + }); + + test('returns true for lists with the same content in different orders', + () { + List list1 = [1, 2, 3]; + List list2 = [3, 1, 2]; + bool result = equalContent(list1, list2); + expect(result, true); + }); + + test('returns false for lists with different content', () { + List list1 = [1, 2, 3]; + List list2 = [1, 2, 4]; + bool result = equalContent(list1, list2); + expect(result, false); + }); + + test('returns false for lists with different lengths', () { + List list1 = [1, 2, 3]; + List list2 = [1, 2]; + bool result = equalContent(list1, list2); + expect(result, false); + }); + + test('returns true for empty lists', () { + List list1 = []; + List list2 = []; + bool result = equalContent(list1, list2); + expect(result, true); + }); + + test('returns false for one empty and one non-empty list', () { + List list1 = []; + List list2 = [1]; + bool result = equalContent(list1, list2); + expect(result, false); + }); + }); +} diff --git a/test/math_test.dart b/test/math_test.dart new file mode 100644 index 0000000..31ebe2f --- /dev/null +++ b/test/math_test.dart @@ -0,0 +1,88 @@ +import 'package:cofounderella/utils/math.dart'; +import 'package:flutter_test/flutter_test.dart'; + + +void main() { + group('Distance Calculation', () { + test('returns correct distance between Berlin and Paris', () { + // Example coordinates for Berlin and Paris + double berlinLat = 52.516216; + double berlinLon = 13.377646; + double parisLat = 48.858119; + double parisLon = 2.294483; + + // Calculated distance between Berlin and Paris + double distance = calculateDistance(berlinLat, berlinLon, parisLat, parisLon); + + // Expected approximate distance in kilometers + double expectedDistance = 879.1; + + // Comparison of both distances + expect(distance, closeTo(expectedDistance, 1.0)); + }); + + test('returns correct distance between same coordinates', () { + double lat = 49.469548; + double lon = 8.48243; + + double distance = calculateDistance(lat, lon, lat, lon); + + expect(distance, equals(0.0)); + }); + + test('returns correct distance for long distances', () { + // Example coordinates for New York and Sydney + double newYorkLat = 40.7128; + double newYorkLon = -74.0060; + double sydneyLat = -33.8688; + double sydneyLon = 151.2093; + + double distance = calculateDistance(newYorkLat, newYorkLon, sydneyLat, sydneyLon); + double expectedDistance = 15988; // in kilometers + + // Comparison of the calculated and expected distance + // with a tolerance of 10 due to the enormous distance + expect(distance, closeTo(expectedDistance, 10.0)); + }); + }); + + + + group('Coordinate Conversion', () { + test('converts positive latitude correctly', () { + double decimalValue = 40.7128; + String result = convertDecimalToDMS(decimalValue, isLatitude: true); + expect(result, '40° 42\' 46.08" N'); + }); + + test('converts negative latitude correctly', () { + double decimalValue = -40.7128; + String result = convertDecimalToDMS(decimalValue, isLatitude: true); + expect(result, '40° 42\' 46.08" S'); + }); + + test('converts positive longitude correctly', () { + double decimalValue = 74.0060; + String result = convertDecimalToDMS(decimalValue, isLatitude: false); + expect(result, '74° 0\' 21.60" E'); + }); + + test('converts negative longitude correctly', () { + double decimalValue = -74.0060; + String result = convertDecimalToDMS(decimalValue, isLatitude: false); + expect(result, '74° 0\' 21.60" W'); + }); + + test('handles zero latitude correctly', () { + double decimalValue = 0.0; + String result = convertDecimalToDMS(decimalValue, isLatitude: true); + expect(result, '0° 0\' 0.00" N'); + }); + + test('handles zero longitude correctly', () { + double decimalValue = 0.0; + String result = convertDecimalToDMS(decimalValue, isLatitude: false); + expect(result, '0° 0\' 0.00" E'); + }); + }); +}