cleaner trip loading indicator
Some checks failed
Some checks failed
This commit is contained in:
parent
d992b62533
commit
d4de945df8
111
backend/test.py
Normal file
111
backend/test.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def euclidean_distance(p1, p2):
|
||||||
|
print(p1, p2)
|
||||||
|
return np.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
|
||||||
|
|
||||||
|
|
||||||
|
def maximize_score(places, max_distance, fixed_entry, top_k=3):
|
||||||
|
"""
|
||||||
|
Maximizes the total score of visited places while staying below the maximum distance.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
places (list of tuples): Each tuple contains (score, (x, y), location).
|
||||||
|
max_distance (float): The maximum distance that can be traveled.
|
||||||
|
fixed_entry (tuple): The place that needs to be visited independently of its score.
|
||||||
|
top_k (int): Number of top candidates to consider in each iteration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of tuples: The visited places.
|
||||||
|
float: The total score of the visited places.
|
||||||
|
"""
|
||||||
|
# Initialize total distance and score
|
||||||
|
total_distance = 0
|
||||||
|
total_score = 0
|
||||||
|
visited_places = []
|
||||||
|
|
||||||
|
# Add the fixed entry to the visited list
|
||||||
|
score, (x, y), _ = fixed_entry
|
||||||
|
visited_places.append(fixed_entry)
|
||||||
|
total_score += score
|
||||||
|
|
||||||
|
# Remove the fixed entry from the list of places
|
||||||
|
remaining_places = [place for place in places if place != fixed_entry]
|
||||||
|
|
||||||
|
# Sort remaining places by score-to-distance ratio
|
||||||
|
|
||||||
|
remaining_places.sort(key=lambda p: p[0] / euclidean_distance((x, y), (p[1][0], p[1][1])), reverse=True)
|
||||||
|
|
||||||
|
# Add places to the visited list if they don't exceed the maximum distance
|
||||||
|
current_location = (x, y)
|
||||||
|
while remaining_places and total_distance < max_distance:
|
||||||
|
# Consider top_k candidates
|
||||||
|
candidates = remaining_places[:top_k]
|
||||||
|
best_candidate = None
|
||||||
|
best_score_increase = -np.inf
|
||||||
|
|
||||||
|
for candidate in candidates:
|
||||||
|
score, (cx, cy), location = candidate
|
||||||
|
distance = euclidean_distance(current_location, (cx, cy))
|
||||||
|
if total_distance + distance <= max_distance:
|
||||||
|
score_increase = score / distance
|
||||||
|
if score_increase > best_score_increase:
|
||||||
|
best_score_increase = score_increase
|
||||||
|
best_candidate = candidate
|
||||||
|
|
||||||
|
if best_candidate:
|
||||||
|
visited_places.append(best_candidate)
|
||||||
|
total_distance += euclidean_distance(current_location, best_candidate[1])
|
||||||
|
total_score += best_candidate[0]
|
||||||
|
current_location = best_candidate[1]
|
||||||
|
remaining_places.remove(best_candidate)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return visited_places, total_score
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
places = [
|
||||||
|
(10, (0, 0), 'A'),
|
||||||
|
(8, (4, 2), 'B'),
|
||||||
|
(15, (6, 4), 'C'),
|
||||||
|
(7, (5, 6), 'D'),
|
||||||
|
(12, (1, 8), 'E'),
|
||||||
|
(14, (34, 10), 'F'),
|
||||||
|
(15, (65, 12), 'G'),
|
||||||
|
(12, (3, 14), 'H'),
|
||||||
|
(12, (15, 1), 'I'),
|
||||||
|
(7, (17, 4), 'J'),
|
||||||
|
(12, (3, 3), 'K'),
|
||||||
|
(4, (21, 22), 'L'),
|
||||||
|
(12, (23, 24), 'M'),
|
||||||
|
(4, (25, 26), 'N'),
|
||||||
|
(2, (27, 28), 'O'),
|
||||||
|
]
|
||||||
|
fixed_entry = (10, (0, 0), 'A')
|
||||||
|
max_distance = 50
|
||||||
|
|
||||||
|
visited_places, total_score = maximize_score(places, max_distance, fixed_entry)
|
||||||
|
print("Visited Places:", visited_places)
|
||||||
|
print("Total Score:", total_score)
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Plot the route
|
||||||
|
def plot_route(visited_places):
|
||||||
|
x_coords = [place[1][0] for place in visited_places]
|
||||||
|
y_coords = [place[1][1] for place in visited_places]
|
||||||
|
labels = [place[2] for place in visited_places]
|
||||||
|
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
plt.plot(x_coords, y_coords, marker='o', linestyle='-', color='b')
|
||||||
|
for i, label in enumerate(labels):
|
||||||
|
plt.text(x_coords[i], y_coords[i], label, fontsize=12, ha='right')
|
||||||
|
|
||||||
|
plt.title('Route of Visited Places')
|
||||||
|
plt.xlabel('X Coordinate')
|
||||||
|
plt.ylabel('Y Coordinate')
|
||||||
|
plt.grid(True)
|
||||||
|
plt.savefig('route.png')
|
||||||
|
|
||||||
|
plot_route(visited_places)
|
@ -1,11 +1,20 @@
|
|||||||
import 'dart:ui';
|
import 'package:anyway/constants.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
import 'package:anyway/structs/trip.dart';
|
||||||
import 'package:anyway/pages/current_trip.dart';
|
import 'package:anyway/pages/current_trip.dart';
|
||||||
|
|
||||||
|
|
||||||
|
final List<String> statusTexts = [
|
||||||
|
'Parsing your preferences...',
|
||||||
|
'Finding the best places...',
|
||||||
|
'Crunching the numbers...',
|
||||||
|
'Calculating the best route...',
|
||||||
|
'Making sure you have a great time...',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
class CurrentTripLoadingIndicator extends StatefulWidget {
|
class CurrentTripLoadingIndicator extends StatefulWidget {
|
||||||
final Trip trip;
|
final Trip trip;
|
||||||
const CurrentTripLoadingIndicator({
|
const CurrentTripLoadingIndicator({
|
||||||
@ -18,14 +27,52 @@ class CurrentTripLoadingIndicator extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Widget bottomLoadingIndicator = Container(
|
class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicator> {
|
||||||
height: 20.0, // Increase the height to take up more vertical space
|
@override
|
||||||
|
Widget build(BuildContext context) => Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
// In the very center of the panel, show the greeter which tells the user that the trip is being generated
|
||||||
|
Center(child: loadingText(widget.trip)),
|
||||||
|
// As a gimmick, and a way to show that the app is still working, show a few loading dots
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: statusText(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatically cycle through the greeter texts
|
||||||
|
class statusText extends StatefulWidget {
|
||||||
|
const statusText({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_statusTextState createState() => _statusTextState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _statusTextState extends State<statusText> {
|
||||||
|
int statusIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
Future.delayed(Duration(seconds: 5), () {
|
||||||
|
setState(() {
|
||||||
|
statusIndex = (statusIndex + 1) % statusTexts.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AutoSizeText(
|
||||||
|
statusTexts[statusIndex],
|
||||||
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
child: ImageFiltered(
|
|
||||||
imageFilter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), // Apply blur effect
|
|
||||||
child: Padding(padding: EdgeInsets.all(10), child: CircularProgressIndicator(),)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
Widget loadingText(Trip trip) => FutureBuilder(
|
Widget loadingText(Trip trip) => FutureBuilder(
|
||||||
@ -34,43 +81,82 @@ Widget loadingText(Trip trip) => FutureBuilder(
|
|||||||
Widget greeter;
|
Widget greeter;
|
||||||
|
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
greeter = AutoSizeText(
|
greeter = AnimatedGradientText(
|
||||||
maxLines: 1,
|
text: 'Creating your trip to ${snapshot.data}...',
|
||||||
'Generating your trip to ${snapshot.data}...',
|
|
||||||
style: greeterStyle,
|
style: greeterStyle,
|
||||||
);
|
);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
// the exact error is shown in the central part of the trip overview. No need to show it here
|
// the exact error is shown in the central part of the trip overview. No need to show it here
|
||||||
greeter = AutoSizeText(
|
greeter = AnimatedGradientText(
|
||||||
maxLines: 1,
|
text: 'Error while loading trip.',
|
||||||
'Error while loading trip.',
|
|
||||||
style: greeterStyle,
|
style: greeterStyle,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
greeter = AutoSizeText(
|
greeter = AnimatedGradientText(
|
||||||
maxLines: 1,
|
text: 'Creating your trip...',
|
||||||
'Generating your trip...',
|
|
||||||
style: greeterStyle,
|
style: greeterStyle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return greeter;
|
return greeter;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
class AnimatedGradientText extends StatefulWidget {
|
||||||
|
final String text;
|
||||||
|
final TextStyle style;
|
||||||
|
|
||||||
|
const AnimatedGradientText({
|
||||||
|
Key? key,
|
||||||
|
required this.text,
|
||||||
|
required this.style,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
||||||
class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicator> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Stack(
|
_AnimatedGradientTextState createState() => _AnimatedGradientTextState();
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
Center(child: loadingText(widget.trip)),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: bottomLoadingIndicator,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _AnimatedGradientTextState extends State<AnimatedGradientText> with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
vsync: this,
|
||||||
|
)..repeat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
builder: (context, child) {
|
||||||
|
return ShaderMask(
|
||||||
|
shaderCallback: (bounds) {
|
||||||
|
return LinearGradient(
|
||||||
|
colors: [GRADIENT_START, GRADIENT_END, GRADIENT_START],
|
||||||
|
stops: [
|
||||||
|
_controller.value - 1.0,
|
||||||
|
_controller.value,
|
||||||
|
_controller.value + 1.0,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
).createShader(bounds);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
widget.text,
|
||||||
|
style: widget.style,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ final Shader textGradient = APP_GRADIENT.createShader(Rect.fromLTWH(0.0, 0.0, 20
|
|||||||
TextStyle greeterStyle = TextStyle(
|
TextStyle greeterStyle = TextStyle(
|
||||||
foreground: Paint()..shader = textGradient,
|
foreground: Paint()..shader = textGradient,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 26
|
fontSize: 25
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ class LandmarkType {
|
|||||||
LandmarkType({required this.name, this.icon = const Icon(Icons.location_on)}) {
|
LandmarkType({required this.name, this.icon = const Icon(Icons.location_on)}) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'sightseeing':
|
case 'sightseeing':
|
||||||
icon = const Icon(Icons.church);
|
icon = const Icon(Icons.castle);
|
||||||
break;
|
break;
|
||||||
case 'nature':
|
case 'nature':
|
||||||
icon = const Icon(Icons.eco);
|
icon = const Icon(Icons.eco);
|
||||||
|
@ -101,10 +101,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.19.0"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -412,18 +412,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.5"
|
version: "10.0.7"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.8"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -708,7 +708,7 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.99"
|
version: "0.0.0"
|
||||||
sliding_up_panel:
|
sliding_up_panel:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -753,10 +753,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.12.0"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -777,10 +777,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
synchronized:
|
synchronized:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -801,10 +801,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.3"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -921,10 +921,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.5"
|
version: "14.3.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
import httpx
|
|
||||||
import json
|
|
||||||
|
|
||||||
base_url = "https://en.wikipedia.org/w/api.php"
|
|
||||||
|
|
||||||
def best_page_match(title) -> int:
|
|
||||||
params = {
|
|
||||||
"action": "query",
|
|
||||||
"format": "json",
|
|
||||||
"list": "prefixsearch",
|
|
||||||
"pssearch": title,
|
|
||||||
}
|
|
||||||
response = httpx.get(base_url, params=params)
|
|
||||||
data = response.json()
|
|
||||||
data = data.get("query", {}).get("prefixsearch", [])
|
|
||||||
titles_and_ids = {d["title"]: d["pageid"] for d in data}
|
|
||||||
|
|
||||||
for t in titles_and_ids:
|
|
||||||
if title.lower() == t.lower():
|
|
||||||
print("Matched")
|
|
||||||
return titles_and_ids[t]
|
|
||||||
|
|
||||||
def get_image_url(page_id) -> str:
|
|
||||||
# https://en.wikipedia.org/w/api.php?action=query&titles=K%C3%B6lner%20Dom&prop=imageinfo&iiprop=url&format=json
|
|
||||||
params = {
|
|
||||||
"action": "query",
|
|
||||||
"format": "json",
|
|
||||||
"prop": "pageimages",
|
|
||||||
"pageids": page_id,
|
|
||||||
"pithumbsize": 500,
|
|
||||||
}
|
|
||||||
response = httpx.get(base_url, params=params)
|
|
||||||
data = response.json()
|
|
||||||
data = data.get("query", {}).get("pages", {})
|
|
||||||
data = data.get(str(page_id), {})
|
|
||||||
return data.get("thumbnail", {}).get("source")
|
|
||||||
|
|
||||||
def get_image_url_from_title(title) -> str:
|
|
||||||
page_id = best_page_match(title)
|
|
||||||
if page_id is None:
|
|
||||||
return None
|
|
||||||
return get_image_url(page_id)
|
|
||||||
|
|
||||||
|
|
||||||
print(get_image_url_from_title("kölner dom"))
|
|
||||||
print(get_image_url_from_title("grossmünster"))
|
|
||||||
print(get_image_url_from_title("eiffel tower"))
|
|
||||||
print(get_image_url_from_title("taj mahal"))
|
|
||||||
print(get_image_url_from_title("big ben"))
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user