diff --git a/android/build.gradle b/android/build.gradle
index 58a8c74..713d7f6 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
delete rootProject.buildDir
}
diff --git a/lib/util/render.dart b/lib/util/render.dart
index e0ed7fc..20e351d 100644
--- a/lib/util/render.dart
+++ b/lib/util/render.dart
@@ -2,8 +2,11 @@ import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_map/flutter_map.dart';
import 'package:html/parser.dart';
import 'package:html/dom.dart' as dom;
+import 'package:latlong2/latlong.dart';
+import 'package:logger/logger.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:voyagehandbook/api/classes.dart';
@@ -30,12 +33,14 @@ import 'package:widget_zoom/widget_zoom.dart';
along with this program. If not, see .
*/
final _ignoredTags = ["style", "script"];
+final logger = Logger(printer: PrettyPrinter());
class PageRenderer {
final ColorScheme scheme;
final double height;
final double width;
final BuildContext context;
+ String? document;
PageRenderer(this.scheme, this.height, this.width, this.context);
@@ -91,6 +96,7 @@ class PageRenderer {
];
var document = parse(page.html);
var sections = document.body!.getElementsByTagName("section");
+ this.document = page.html;
for (var sec in sections) {
if (sec.localName == "section") {
out.addAll(_renderSection(sec));
@@ -329,45 +335,139 @@ class PageRenderer {
),
);
} else if (element.classes.contains("mw-kartographer-container")) {
+ logger.i("Found map container");
var imgs = element
.getElementsByTagName("div")
.first
.getElementsByTagName("a")
.first
.getElementsByTagName("img");
- if (imgs.isEmpty) break; // load maps that have a static image
- var img = imgs[0];
- var cap = element.getElementsByClassName("thumbcaption")[0];
- out.add(const SizedBox(
- height: 10,
- ));
- out.add(
- SizedBox(
- width: width * 0.8,
- height: height * 0.3,
- child: WidgetZoom(
- zoomWidget:
- CachedNetworkImage(imageUrl: img.attributes["src"]!),
- heroAnimationTag: 'tag',
- ),
- ),
- );
- out.add(
- const SizedBox(
- height: 3,
- ),
- );
- out.add(
- SelectableText(
- cap.text,
- textAlign: TextAlign.center,
- ),
- );
- out.add(
- const SizedBox(
+ if (imgs.isEmpty ||
+ (imgs.first.attributes["src"]
+ ?.startsWith("https://maps.wikimedia.org") ??
+ false)) {
+ logger.i("Rendering with FlutterMap");
+ // render map using FlutterMap
+ var dataElement = element
+ .getElementsByClassName("thumbinner")
+ .first
+ .getElementsByClassName("mw-kartographer-map")
+ .first;
+
+ var pointsRaw = RegExp(
+ r"""(.+?)<\/abbr>(.+?)<\/abbr>.+?}'>(\d+)<""")
+ .allMatches(document!); // find markers
+ logger.i("Found ${pointsRaw.length} markers");
+ if (pointsRaw.isEmpty) break;
+ var points = [];
+ for (var point in pointsRaw) {
+ // convert to FlutterMap markers
+ if (point.groups([1, 2]).any((element) => element == null)) {
+ logger.w("No lat/lon found in pointer");
+ continue;
+ }
+ // assign marker color
+ var colorMatch = RegExp(r'background: #(.+?);')
+ .firstMatch(point.group(0)!)
+ ?.group(1);
+ Color bubbleColor = (colorMatch == null)
+ ? scheme.secondary
+ : Color(int.parse("0xff$colorMatch"));
+
+ points.add(
+ Marker(
+ builder: (c) => Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ color: bubbleColor),
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text(point.group(3) ?? "X"),
+ ),
+ ),
+ point: LatLng(
+ double.parse(point.group(1)!),
+ double.parse(point.group(2)!),
+ ),
+ ),
+ );
+ }
+ out.add(const SizedBox(
height: 10,
- ),
- );
+ ));
+ out.add(
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.8,
+ height: (MediaQuery.of(context).size.width * 0.8 / 4) * 3,
+ child: FlutterMap(
+ options: MapOptions(
+ center: LatLng(
+ double.parse(
+ dataElement.attributes["data-lat"] ?? "00.0"),
+ double.parse(
+ dataElement.attributes["data-lon"] ?? "00.0"),
+ ),
+ zoom: double.parse(
+ dataElement.attributes["data-zoom"] ?? "1"),
+ ),
+ nonRotatedChildren: [
+ RichAttributionWidget(
+ attributions: [
+ TextSourceAttribution(
+ "OpenStreetMap contributors",
+ onTap: () => launchUrlString(
+ "https://openstreetmap.org/copyright"),
+ )
+ ],
+ )
+ ],
+ children: [
+ TileLayer(
+ urlTemplate:
+ "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
+ userAgentPackageName: "cafe.caras.voyagehandbook",
+ ),
+ MarkerLayer(
+ markers: points,
+ )
+ ],
+ ),
+ ),
+ );
+ } else {
+ var img = imgs[0];
+ var cap = element.getElementsByClassName("thumbcaption")[0];
+ out.add(const SizedBox(
+ height: 10,
+ ));
+ out.add(
+ SizedBox(
+ width: width * 0.8,
+ height: height * 0.3,
+ child: WidgetZoom(
+ zoomWidget:
+ CachedNetworkImage(imageUrl: img.attributes["src"]!),
+ heroAnimationTag: 'tag',
+ ),
+ ),
+ );
+ out.add(
+ const SizedBox(
+ height: 3,
+ ),
+ );
+ out.add(
+ SelectableText(
+ cap.text,
+ textAlign: TextAlign.center,
+ ),
+ );
+ out.add(
+ const SizedBox(
+ height: 10,
+ ),
+ );
+ }
}
break;
default:
@@ -561,16 +661,6 @@ class PageRenderer {
return content;
}
- final _extraColorsLight = {
- "blue": const Color.fromARGB(255, 34, 34, 157),
- "red": const Color.fromARGB(255, 152, 33, 33)
- };
-
- final _extraColorsDark = {
- "blue": const Color.fromARGB(255, 84, 95, 247),
- "red": const Color.fromARGB(255, 242, 69, 69)
- };
-
SingleChildScrollView _renderList(dom.Element element) {
var out = [];
var i = 0;
@@ -580,24 +670,19 @@ class PageRenderer {
? item.getElementsByClassName("listing-name")[0].text
: null;
- Color bubbleColor;
- if (item.innerHtml.contains("background: #0000FF")) {
- bubbleColor =
- (MediaQuery.of(context).platformBrightness == Brightness.dark)
- ? _extraColorsDark["blue"]!
- : _extraColorsLight["blue"]!;
- } else if (item.innerHtml.contains("background: #800000")) {
- bubbleColor =
- (MediaQuery.of(context).platformBrightness == Brightness.dark)
- ? _extraColorsDark["red"]!
- : _extraColorsLight["red"]!;
- } else {
- bubbleColor = scheme.secondary;
+ var colorMatch =
+ RegExp(r'background: #(.+?);').firstMatch(item.innerHtml)?.group(1);
+ Color bubbleColor = (colorMatch == null)
+ ? scheme.secondary
+ : Color(int.parse("0xff$colorMatch"));
+ if (element.getElementsByClassName("geo").isNotEmpty) {
+ element.getElementsByClassName("geo").first.remove();
+ }
+ var rest =
+ ((title != null) ? item.text.replaceAll(title, "") : item.text);
+ if (rest.startsWith("1")) {
+ rest = rest.substring(1); // TODO: figure out how to remove it better?
}
-
- var rest = (title != null)
- ? item.text.replaceAll(item.getElementsByTagName("span")[0].text, "")
- : item.text;
out.add(const SizedBox(
height: 5,
));
diff --git a/pubspec.lock b/pubspec.lock
index a775269..9f0ff98 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -318,6 +318,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.2"
+ flutter_map:
+ dependency: "direct main"
+ description:
+ name: flutter_map
+ sha256: "5286f72f87deb132daa1489442d6cc46e986fc105cb727d9ae1b602b35b1d1f3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -400,6 +408,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.3.0"
+ intl:
+ dependency: transitive
+ description:
+ name: intl
+ sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.18.1"
io:
dependency: transitive
description:
@@ -432,6 +448,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.7.1"
+ latlong2:
+ dependency: "direct main"
+ description:
+ name: latlong2
+ sha256: "18712164760cee655bc790122b0fd8f3d5b3c36da2cb7bf94b68a197fbb0811b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.0"
lints:
dependency: transitive
description:
@@ -440,6 +464,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
+ lists:
+ dependency: transitive
+ description:
+ name: lists
+ sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ logger:
+ dependency: "direct main"
+ description:
+ name: logger
+ sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
logging:
dependency: transitive
description:
@@ -472,6 +512,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
+ mgrs_dart:
+ dependency: transitive
+ description:
+ name: mgrs_dart
+ sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
mime:
dependency: transitive
description:
@@ -584,6 +632,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.7.3"
+ polylabel:
+ dependency: transitive
+ description:
+ name: polylabel
+ sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
pool:
dependency: transitive
description:
@@ -600,6 +656,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.2.4"
+ proj4dart:
+ dependency: transitive
+ description:
+ name: proj4dart
+ sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
pub_semver:
dependency: transitive
description:
@@ -813,6 +877,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
+ unicode:
+ dependency: transitive
+ description:
+ name: unicode
+ sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
url_launcher:
dependency: "direct main"
description:
@@ -925,6 +997,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.5"
+ wkt_parser:
+ dependency: transitive
+ description:
+ name: wkt_parser
+ sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
xdg_directories:
dependency: transitive
description:
@@ -951,4 +1031,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.0.0 <3.2.0"
- flutter: ">=3.4.0-17.0.pre"
+ flutter: ">=3.10.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index eb48449..44d8872 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,10 +16,10 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 1.0.0-alpha.1+1
+version: 1.0.0-alpha.2+2
environment:
- sdk: '>=2.19.4 <3.0.0'
+ sdk: '>=3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@@ -46,6 +46,9 @@ dependencies:
cached_network_image: ^3.2.3
html_unescape: ^2.0.0
widget_zoom: ^0.0.1
+ flutter_map: ^5.0.0
+ latlong2: ^0.9.0
+ logger: ^1.4.0
dev_dependencies:
flutter_test: