Skip to content

Commit

Permalink
Merge pull request #42 from ExpTechTW/master
Browse files Browse the repository at this point in the history
release: v1.2.1
  • Loading branch information
kamiya10 authored Apr 16, 2024
2 parents 53b8016 + 67f34fd commit e51ad89
Show file tree
Hide file tree
Showing 35 changed files with 1,672 additions and 669 deletions.
1 change: 1 addition & 0 deletions android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
android:name="flutterEmbedding"
android:value="2"/>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
10 changes: 10 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ PODS:
- Flutter (1.0.0)
- flutter_local_notifications (0.0.1):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- GoogleDataTransport (9.4.1):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0)
Expand Down Expand Up @@ -89,6 +92,7 @@ PODS:
- sqflite (0.0.3):
- Flutter
- FlutterMacOS
- Toast (4.1.1)
- url_launcher_ios (0.0.1):
- Flutter

Expand All @@ -100,6 +104,7 @@ DEPENDENCIES:
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
Expand All @@ -118,6 +123,7 @@ SPEC REPOS:
- nanopb
- PromisesObjC
- ReachabilitySwift
- Toast

EXTERNAL SOURCES:
app_settings:
Expand All @@ -134,6 +140,8 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
Expand All @@ -158,6 +166,7 @@ SPEC CHECKSUMS:
FirebaseMessaging: 9f71037fd9db3376a4caa54e5a3949d1027b4b6e
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
fluttertoast: 9f2f8e81bb5ce18facb9748d7855bf5a756fe3db
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleUtilities: d053d902a8edaa9904e1bd00c37535385b8ed152
nanopb: 438bc412db1928dac798aa6fd75726007be04262
Expand All @@ -167,6 +176,7 @@ SPEC CHECKSUMS:
ReachabilitySwift: 5ae15e16814b5f9ef568963fb2c87aeb49158c66
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586

PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048
Expand Down
29 changes: 29 additions & 0 deletions lib/core/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,27 @@ import 'dart:convert';
import 'dart:math';

import 'package:dpip/model/earthquake_report.dart';
import 'package:dpip/model/eew.dart';
import 'package:dpip/model/partial_earthquake_report.dart';
import 'package:http/http.dart' as http;

enum EewSource {
/// 交通部中央氣象署
cwa,

/// 기상청 날씨누리
kma,

/// 気象庁
jma,

/// 防災科研
nied,

/// 四川省地震局
scdzj
}

class ExpTechApi {
String? apikey;

Expand Down Expand Up @@ -42,4 +60,15 @@ class ExpTechApi {
throw Exception('The server returned a status code of ${response.statusCode}');
}
}

Future<List<Eew>> getEew(EewSource source) async {
final response = await http
.get(Uri.parse('https://api-${Random().nextInt(2) + 1}.exptech.com.tw/api/v1/eq/eew?type=${source.name}'));

if (response.statusCode == 200) {
return (jsonDecode(response.body) as List<dynamic>).map((e) => Eew.fromJson(e)).toList();
} else {
throw Exception('The server returned a status code of ${response.statusCode}');
}
}
}
7 changes: 2 additions & 5 deletions lib/core/fcm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,12 @@ Future<void> messageHandler(RemoteMessage message) async {
: (data["level"] == "1")
? Priority.defaultPriority
: Priority.max,
sound: data["sound"] != "default"
? RawResourceAndroidNotificationSound(data["sound"])
: null,
sound: data["sound"] != "default" ? RawResourceAndroidNotificationSound(data["sound"]) : null,
styleInformation: const BigTextStyleInformation(''),
),
iOS: DarwinNotificationDetails(
categoryIdentifier: darwinNotificationCategoryPlain,
sound:
data["sound"] != "default" ? "${data["sound"]}.wav" : "default",
sound: data["sound"] != "default" ? "${data["sound"]}.wav" : "default",
interruptionLevel: InterruptionLevel.timeSensitive,
),
));
Expand Down
78 changes: 49 additions & 29 deletions lib/core/utils.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'dart:convert';
import 'dart:math';

import 'package:dpip/model/town.dart';
import 'package:dpip/model/wave_time.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<dynamic> get(String uri) async {
try {
var response =
await http.get(Uri.parse(uri)).timeout(const Duration(seconds: 2));
var response = await http.get(Uri.parse(uri)).timeout(const Duration(seconds: 2));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
Expand All @@ -24,8 +25,7 @@ Future<dynamic> post(String uri, Map<String, dynamic> body) async {
String jsonBody = json.encode(body);
final encoding = Encoding.getByName('utf-8');
var response = await http
.post(Uri.parse(uri),
headers: headers, body: jsonBody, encoding: encoding)
.post(Uri.parse(uri), headers: headers, body: jsonBody, encoding: encoding)
.timeout(const Duration(seconds: 2));
if (response.statusCode == 200) {
return jsonDecode(response.body);
Expand Down Expand Up @@ -53,34 +53,56 @@ int compareVersion(String version1, String version2) {
return 0;
}

Map<String, dynamic> eewIntensity(
Map<String, dynamic> data, Map<String, dynamic> region) {
Map<String, dynamic> eewAreaPga(
double lat, double lon, double depth, double mag, Map<String, Map<String, Town>> region) {
Map<String, dynamic> json = {};
double eewMaxPga = 0;

region.forEach((city, cityData) {
cityData.forEach((town, info) {
double distSurface = sqrt(pow((data['lat'] - info['lat']) * 111, 2) +
pow((data['lon'] - info['lon']) * 101, 2));
double dist = sqrt(pow(distSurface, 2) + pow(data['depth'], 2));
double pga = 1.657 *
pow(exp(1), (1.533 * data['mag'])) *
pow(dist, -1.607) *
(info['site'] ?? 1);
if (pga > eewMaxPga) {
eewMaxPga = pga;
double eewMaxI = 0.0;

region.forEach((city, towns) {
towns.forEach((town, info) {
double distSurface = distance(lat, lon, info.lat, info.lon);
double dist = sqrt(pow(distSurface, 2) + pow(depth, 2));
double pga = 1.657 * exp(1.533 * mag) * pow(dist, -1.607);
double i = pgaToFloat(pga);
if (i >= 4.5) {
i = eewAreaPgv([lat, lon], [info.lat, info.lon], depth, mag);
}
json['$city $town'] = {
'dist': dist,
'pga': pga,
};
if (i > eewMaxI) {
eewMaxI = i;
}
json['$city $town'] = {'dist': dist, 'i': i};
});
});

json['max_pga'] = eewMaxPga;
json['max_i'] = eewMaxI;
return json;
}

double eewAreaPgv(List<double> epicenterLocation, List<double> pointLocation, double depth, double magW) {
double long = pow(10, 0.5 * magW - 1.85) / 2;
double epicenterDistance = distance(epicenterLocation[0], epicenterLocation[1], pointLocation[0], pointLocation[1]);
double hypocenterDistance = sqrt(pow(depth, 2) + pow(epicenterDistance, 2)) - long;
double x = max(hypocenterDistance, 3);
num gpv600 = pow(10, 0.58 * magW + 0.0038 * depth - 1.29 - log(x + 0.0028 * pow(10, 0.5 * magW)) - 0.002 * x);
double pgv400 = gpv600 * 1.31;
double pgv = pgv400 * 1.0;
return 2.68 + 1.72 * log(pgv) / ln10;
}

double distance(double latA, double lngA, double latB, double lngB) {
latA = latA * pi / 180;
lngA = lngA * pi / 180;
latB = latB * pi / 180;
lngB = lngB * pi / 180;

double sinLatA = sin(atan(tan(latA)));
double sinLatB = sin(atan(tan(latB)));
double cosLatA = cos(atan(tan(latA)));
double cosLatB = cos(atan(tan(latB)));

return acos(sinLatA * sinLatB + cosLatA * cosLatB * cos(lngA - lngB)) * 6371.008;
}

double pgaToFloat(double pga) {
return 2 * (log(pga) / log(10)) + 0.7;
}
Expand Down Expand Up @@ -135,7 +157,7 @@ String intensityToString(level) {
: "$level 級";
}

Map<String, double> speed(double depth, double distance) {
WaveTime calculateWaveTime(double depth, double distance) {
final double Za = 1 * depth;
double G0, G;
final double Xb = distance;
Expand Down Expand Up @@ -172,7 +194,7 @@ Map<String, double> speed(double depth, double distance) {
if (distance / Stime > 4) {
Stime = distance / 4;
}
return {'Ptime': Ptime, 'Stime': Stime};
return WaveTime(p: Ptime, s: Stime);
}

String safeBase64Encode(String input) {
Expand All @@ -181,9 +203,7 @@ String safeBase64Encode(String input) {
}

String formatToUTC(TimeOfDay time) {
final now = DateTime.now()
.toUtc()
.add(const Duration(hours: 8)); // Current time in UTC+8
final now = DateTime.now().toUtc().add(const Duration(hours: 8)); // Current time in UTC+8
DateTime dateWithTime = DateTime(
now.year,
now.month,
Expand Down
3 changes: 1 addition & 2 deletions lib/global.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ class Global {
initializeTimeZones();

// src: assets/json/region.json
Map<String, dynamic> regionRaw =
jsonDecode(await rootBundle.loadString("assets/region.json"));
Map<String, dynamic> regionRaw = jsonDecode(await rootBundle.loadString("assets/region.json"));

regionRaw.forEach((cityName, city) {
region[cityName] = <String, Town>{};
Expand Down
2 changes: 1 addition & 1 deletion lib/model/area_intensity.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:dpip/model/station_intensity.dart';
import 'package:json_annotation/json_annotation.dart';

part 'area_intensity.g.dart';

Expand Down
85 changes: 75 additions & 10 deletions lib/model/earthquake_report.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:dpip/model/area_intensity.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:timezone/timezone.dart' as tz;

part 'earthquake_report.g.dart';

Expand All @@ -26,28 +27,92 @@ class EarthquakeReport {
required this.time,
required this.trem});

factory EarthquakeReport.fromJson(Map<String, dynamic> json) =>
_$EarthquakeReportFromJson(json);
factory EarthquakeReport.fromJson(Map<String, dynamic> json) => _$EarthquakeReportFromJson(json);

Map<String, dynamic> toJson() => _$EarthquakeReportToJson(this);

String? get number {
final n = id.split("-").first;

if (!n.endsWith("000")) {
return n;
}

return null;
}

bool get hasNumber => number != null;

Uri get cwaUrl {
final arr = id.split("-");
arr.removeAt(1);
return Uri.parse(
"https://www.cwa.gov.tw/V8/C/E/EQ/EQ${arr.join('-')}.html");
return Uri.parse("https://www.cwa.gov.tw/V8/C/E/EQ/EQ${arr.join('-')}.html");
}

String? getNumber() {
final n = id.split("-").first;
String get reportImageName {
final date = tz.TZDateTime.fromMillisecondsSinceEpoch(tz.getLocation("Asia/Taipei"), time);
final month = date.month.toString().padLeft(2, '0');
final day = date.day.toString().padLeft(2, '0');
final hour = date.hour.toString().padLeft(2, '0');
final minute = date.minute.toString().padLeft(2, '0');
final mag = "${(this.mag * 10).floor()}";

if (!n.endsWith("000")) {
return n;
if (hasNumber) {
final id = number!.substring(3);
return "EC$month$day$hour$minute$mag${id}_H.png";
} else {
final year = date.year.toString();
final second = date.second.toString().padLeft(2, '0');
return "ECL$year$month$day$hour$minute$second${mag}_H.png";
}
}

return null;
String get reportImageUrl => "https://www.cwa.gov.tw/Data/earthquake/img/$reportImageName";

String get zipName {
final date = tz.TZDateTime.fromMillisecondsSinceEpoch(tz.getLocation("Asia/Taipei"), time);
final year = date.year.toString();
final month = date.month.toString().padLeft(2, '0');
final day = date.day.toString().padLeft(2, '0');
final hour = date.hour.toString().padLeft(2, '0');
final minute = date.minute.toString().padLeft(2, '0');
final second = date.second.toString().padLeft(2, '0');

return "EQ$number-$year-$month$day-$hour$minute$second";
}

String get zipUrl => "https://www.cwa.gov.tw/Data/earthquake/zip/$zipName";

String? get intensityMapImageName {
if (!hasNumber) return null;

final date = tz.TZDateTime.fromMillisecondsSinceEpoch(tz.getLocation("Asia/Taipei"), time);
final year = date.year.toString();
return "$year${number!.substring(3)}i.png";
}

String? get intensityMapImageUrl => intensityMapImageName == null ? null : "$zipUrl/$intensityMapImageName";

String? get pgaMapImageName {
if (!hasNumber) return null;

final date = tz.TZDateTime.fromMillisecondsSinceEpoch(tz.getLocation("Asia/Taipei"), time);
final year = date.year.toString();
return "$year${number!.substring(3)}a.png";
}

String? get pgaMapImageUrl => pgaMapImageName == null ? null : "$zipUrl/$pgaMapImageName";

String? get pgvMapImageName {
if (!hasNumber) return null;

final date = tz.TZDateTime.fromMillisecondsSinceEpoch(tz.getLocation("Asia/Taipei"), time);
final year = date.year.toString();
return "$year${number!.substring(3)}v.png";
}

String? get pgvMapImageUrl => pgvMapImageName == null ? null : "$zipUrl/$pgvMapImageName";

int? getMaxIntensity() {
int max = 0;

Expand Down
Loading

0 comments on commit e51ad89

Please sign in to comment.