2024-07-31 23:37:51 +08:00
|
|
|
import 'dart:math';
|
|
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:gnss/gnss.dart';
|
|
|
|
|
|
|
|
import '../Controller/gnss_controller.dart';
|
|
|
|
|
2024-08-01 15:13:18 +08:00
|
|
|
// const signalNameList = <String>["GPS", "GLONASS", "GALILEO", "BEIDOU", "QZSS"];
|
|
|
|
const Map<String, String> signalPrefixList = {
|
|
|
|
"GPS": "G",
|
|
|
|
"GLONASS": "N",
|
|
|
|
"GALILEO": "E",
|
|
|
|
"BEIDOU": "B",
|
|
|
|
"QZSS": "Q"
|
|
|
|
};
|
|
|
|
const Map<String, Color> signalColorMap = {
|
|
|
|
"GPS": Color.fromARGB(255, 255, 0, 0),
|
|
|
|
"GLONASS": Color.fromARGB(255, 0, 255, 0),
|
|
|
|
"GALILEO": Color.fromARGB(255, 0, 0, 255),
|
|
|
|
"BEIDOU": Color.fromARGB(255, 255, 255, 0),
|
|
|
|
"QZSS": Color.fromARGB(255, 0, 255, 255),
|
|
|
|
};
|
|
|
|
|
2024-07-31 23:37:51 +08:00
|
|
|
// class DrawSkyPlot {
|
|
|
|
// double width = 500;
|
|
|
|
// double height = 500;
|
|
|
|
// late Canvas ctx;
|
|
|
|
// Map legend = {};
|
|
|
|
// final Paint _paint = Paint();
|
|
|
|
// Path path = Path();
|
|
|
|
// bool isPortrait = false;
|
|
|
|
// late LocationData locationData;
|
|
|
|
// late SignalData signalData;
|
|
|
|
// late GnssController controller;
|
|
|
|
// // final GnssController controller;
|
|
|
|
|
|
|
|
// final bool isDarkMode;
|
|
|
|
// Color color = Colors.black;
|
|
|
|
// DrawSkyPlot(op, this.isDarkMode, this.controller) {
|
|
|
|
// // controller = Get.find<GnssController>(tag: 'gnss');
|
|
|
|
// ctx = op['ctx']; // canvas dom 对象
|
|
|
|
// height = op['height']; // 画布高
|
|
|
|
// width = op['width']; // 画布宽
|
|
|
|
// if (isDarkMode) {
|
|
|
|
// color = Colors.white;
|
|
|
|
// } else {
|
|
|
|
// color = Colors.black;
|
|
|
|
// }
|
|
|
|
// _paint
|
|
|
|
// ..color = color
|
|
|
|
// ..strokeWidth = 3
|
|
|
|
// ..style = PaintingStyle.stroke;
|
|
|
|
// MediaQueryData mediaQueryData =
|
|
|
|
// MediaQueryData.fromView(WidgetsBinding.instance.window);
|
|
|
|
// final orientation = mediaQueryData.orientation;
|
|
|
|
// // 横屏竖屏宽高重新设置
|
|
|
|
// if (orientation == Orientation.portrait) {
|
|
|
|
// height = op['width'];
|
|
|
|
// isPortrait = true;
|
|
|
|
// } else {
|
|
|
|
// width = op['height'];
|
|
|
|
// isPortrait = false;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// //外圈
|
|
|
|
// drawSkyPlotCircle() {
|
|
|
|
// double x = (width / 2) + 50;
|
|
|
|
// double y = (height / 2) - 15;
|
|
|
|
// double r = height / 2.5;
|
|
|
|
// ctx.drawCircle(Offset(x, y), r, _paint);
|
|
|
|
// ctx.drawCircle(Offset(x, y), r * 2 / 3, _paint);
|
|
|
|
// ctx.drawCircle(Offset(x, y), r / 3, _paint);
|
|
|
|
// path.moveTo(x - r, y);
|
|
|
|
// path.lineTo(x + r, y);
|
|
|
|
// path.moveTo(x, y - r);
|
|
|
|
// path.lineTo(x, y + r);
|
|
|
|
// path.moveTo(x + cos(30 * (pi / 180)) * r, y + sin(30 * (pi / 180)) * r);
|
|
|
|
// path.lineTo(x - cos(30 * (pi / 180)) * r, y - sin(30 * (pi / 180)) * r);
|
|
|
|
// path.moveTo(x + cos(60 * (pi / 180)) * r, y + sin(60 * (pi / 180)) * r);
|
|
|
|
// path.lineTo(x - cos(60 * (pi / 180)) * r, y - sin(60 * (pi / 180)) * r);
|
|
|
|
// path.moveTo(x + cos(60 * (pi / 180)) * r, y - sin(60 * (pi / 180)) * r);
|
|
|
|
// path.lineTo(x - cos(60 * (pi / 180)) * r, y + sin(60 * (pi / 180)) * r);
|
|
|
|
// path.moveTo(x - cos(60 * (pi / 180)) * r, y + sin(60 * (pi / 180)) * r);
|
|
|
|
// path.lineTo(x + cos(60 * (pi / 180)) * r, y - sin(60 * (pi / 180)) * r);
|
|
|
|
// path.moveTo(x + cos(30 * (pi / 180)) * r, y - sin(30 * (pi / 180)) * r);
|
|
|
|
// path.lineTo(x - cos(30 * (pi / 180)) * r, y + sin(30 * (pi / 180)) * r);
|
|
|
|
// path.moveTo(x - cos(30 * (pi / 180)) * r, y + sin(30 * (pi / 180)) * r);
|
|
|
|
// path.lineTo(x + cos(30 * (pi / 180)) * r, y - sin(30 * (pi / 180)) * r);
|
|
|
|
// ctx.drawPath(path, _paint);
|
|
|
|
|
|
|
|
// Map textList = {
|
|
|
|
// '0': Offset(x, y - r - 25),
|
|
|
|
// '30': Offset(
|
|
|
|
// x + cos(60 * (pi / 180)) * r, y - sin(60 * (pi / 180)) * r - 25),
|
|
|
|
// '60': Offset(
|
|
|
|
// x + cos(30 * (pi / 180)) * r + 10, y - sin(30 * (pi / 180)) * r - 10),
|
|
|
|
// '90': Offset(x + r + 10, y - 5),
|
|
|
|
// '120': Offset(
|
|
|
|
// x + cos(30 * (pi / 180)) * r, y + sin(30 * (pi / 180)) * r + 5),
|
|
|
|
// '150': Offset(
|
|
|
|
// x + cos(60 * (pi / 180)) * r, y + sin(60 * (pi / 180)) * r + 5),
|
|
|
|
// '180': Offset(x - 10, y + r),
|
|
|
|
// '210': Offset(
|
|
|
|
// x - cos(60 * (pi / 180)) * r - 20, y + sin(60 * (pi / 180)) * r + 10),
|
|
|
|
// '240': Offset(
|
|
|
|
// x - cos(30 * (pi / 180)) * r - 30, y + sin(30 * (pi / 180)) * r + 5),
|
|
|
|
// '270': Offset(x - r - 35, y - 5),
|
|
|
|
// '300': Offset(
|
|
|
|
// x - cos(30 * (pi / 180)) * r - 37, y - sin(30 * (pi / 180)) * r - 20),
|
|
|
|
// '330': Offset(
|
|
|
|
// x - cos(60 * (pi / 180)) * r - 30, y - sin(60 * (pi / 180)) * r - 25)
|
|
|
|
// };
|
|
|
|
// textList.forEach((key, offset) {
|
|
|
|
// TextPainter(
|
|
|
|
// text: TextSpan(text: key, style: TextStyle(color: color, fontSize: 20)),
|
|
|
|
// textDirection: TextDirection.ltr,
|
|
|
|
// )
|
|
|
|
// ..layout()
|
|
|
|
// ..paint(ctx, offset);
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
|
|
|
|
// getCircles() {
|
|
|
|
// legend = {};
|
|
|
|
// double radius = height / 2.5;
|
|
|
|
// double r = 15;
|
|
|
|
// if (controller.signalData == null) {
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// var signalData = controller.signalData!;
|
|
|
|
// if (controller.selectedSignal['GPS'] == true) {
|
|
|
|
// for (int i = 0; i < signalData.GPS.length; i++) {
|
|
|
|
// SignalGPS item = controller.signalData.GPS[i];
|
|
|
|
// double elev = item.elevation * pi / 180; // 将仰角转换为弧度
|
|
|
|
// double maxr = radius * cos(elev); // 计算该点到圆心的距离
|
|
|
|
|
|
|
|
// double azi = (90 - item.azimuth) * pi / 180; // 将方位角转换为弧度
|
|
|
|
// double cx = (maxr) * cos(azi); // 计算 x 坐标
|
|
|
|
// double cy = -(maxr) * sin(azi); // 计算 y
|
|
|
|
|
|
|
|
// _paint.color = Colors.blue;
|
|
|
|
// _paint.style = PaintingStyle.fill;
|
|
|
|
|
|
|
|
// // 绘制卫星位置
|
|
|
|
// ctx.drawCircle(Offset(cx, cy), r, _paint);
|
|
|
|
// }
|
|
|
|
// } else if (controller.selectedSignal['BDS'] == true) {
|
|
|
|
// for (int i = 0; i < signalData.BDS.length; i++) {
|
|
|
|
// SignalBDS item = controller.signalData!.BDS![i];
|
|
|
|
// double elev = item.elevation * pi / 180; // 将仰角转换为弧度
|
|
|
|
// double maxr = radius * cos(elev); // 计算该点到圆心的距离
|
|
|
|
|
|
|
|
// double azi = (90 - item.azimuth) * pi / 180; // 将方位角转换为弧度
|
|
|
|
// double cx = (maxr) * cos(azi); // 计算 x 坐标
|
|
|
|
// double cy = -(maxr) * sin(azi); // 计算 y
|
|
|
|
|
|
|
|
// _paint.color = Colors.red;
|
|
|
|
// _paint.style = PaintingStyle.fill;
|
|
|
|
// // 绘制卫星位置
|
|
|
|
// ctx.drawCircle(Offset(cx, cy), r, _paint);
|
|
|
|
// }
|
|
|
|
// } else if (controller.selectedSignal['GLO'] == true) {
|
|
|
|
// for (int i = 0; i < signalData.GLO.length; i++) {
|
|
|
|
// SignalGLO item = controller.signalData!.GLO![i];
|
|
|
|
// double elev = item.elevation * pi / 180; // 将仰角转换为弧度
|
|
|
|
// double maxr = radius * cos(elev); // 计算该点到圆心的距离
|
|
|
|
|
|
|
|
// double azi = (90 - item.azimuth) * pi / 180; // 将方位角转换为弧度
|
|
|
|
// double cx = (maxr) * cos(azi); // 计算 x 坐标
|
|
|
|
// double cy = -(maxr) * sin(azi); // 计算 y
|
|
|
|
|
|
|
|
// _paint.color = Colors.green;
|
|
|
|
// _paint.style = PaintingStyle.fill;
|
|
|
|
// // 绘制卫星位置
|
|
|
|
// ctx.drawCircle(Offset(cx, cy), r, _paint);
|
|
|
|
// }
|
|
|
|
// } else if (controller.selectedSignal['ALS'] == true) {
|
|
|
|
// for (int i = 0; i < signalData.GAL.length; i++) {
|
|
|
|
// SignalGAL item = controller.signalData!.GAL![i];
|
|
|
|
// double elev = item.elevation * pi / 180; // 将仰角转换为弧度
|
|
|
|
// double maxr = radius * cos(elev); // 计算该点到圆心的距离
|
|
|
|
|
|
|
|
// double azi = (90 - item.azimuth) * pi / 180; // 将方位角转换为弧度
|
|
|
|
// double cx = (maxr) * cos(azi); // 计算 x 坐标
|
|
|
|
// double cy = -(maxr) * sin(azi); // 计算 y
|
|
|
|
|
|
|
|
// _paint.color = Colors.orange;
|
|
|
|
// _paint.style = PaintingStyle.fill;
|
|
|
|
// // 绘制卫星位置
|
|
|
|
// ctx.drawCircle(Offset(cx, cy), r, _paint);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
class SkyPlotPage extends StatelessWidget {
|
|
|
|
late final GnssController controller;
|
|
|
|
// Map<String, ui.Image> imageList = {};
|
|
|
|
@override
|
|
|
|
SkyPlotPage({super.key}) {
|
|
|
|
controller = Get.find<GnssController>();
|
|
|
|
}
|
|
|
|
Future<ui.Image> loadImage(String path) async {
|
|
|
|
ByteData data = await rootBundle.load(path);
|
|
|
|
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
|
|
|
|
ui.FrameInfo fi = await codec.getNextFrame();
|
|
|
|
return fi.image;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
bool isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
2024-08-01 14:08:34 +08:00
|
|
|
final size = MediaQuery.of(context).size;
|
2024-07-31 23:37:51 +08:00
|
|
|
return Obx(() {
|
2024-08-01 14:08:34 +08:00
|
|
|
controller.singnalUpdate.value;
|
|
|
|
return SizedBox(
|
|
|
|
width: size.width,
|
|
|
|
height: size.height,
|
|
|
|
child: CustomPaint(
|
2024-08-01 15:13:18 +08:00
|
|
|
painter:
|
|
|
|
SkyPlotPainter(controller.signalData, controller.selectedSignal),
|
2024-08-01 14:08:34 +08:00
|
|
|
),
|
2024-07-31 23:37:51 +08:00
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// class DrawSky extends CustomPainter {
|
|
|
|
// final BuildContext context;
|
|
|
|
// final GnssController controller;
|
|
|
|
// final radius = 20.0;
|
|
|
|
|
|
|
|
// final bool isDarkMode;
|
|
|
|
// // late SignalData signalData;
|
|
|
|
// DrawSky(this.controller, this.context, this.isDarkMode);
|
|
|
|
// @override
|
|
|
|
// void paint(Canvas canvas, Size size) {
|
|
|
|
// canvas.translate(0, -15);
|
|
|
|
// final bool isPortrait =
|
|
|
|
// MediaQuery.of(context).orientation == Orientation.portrait;
|
|
|
|
// DrawSkyPlot skyPlot = DrawSkyPlot({
|
|
|
|
// 'ctx': canvas,
|
|
|
|
// 'height': size.height,
|
|
|
|
// 'width': size.width,
|
|
|
|
// }, isDarkMode, controller);
|
|
|
|
// if (!isPortrait) {
|
|
|
|
// canvas.translate(-30, 0);
|
|
|
|
// }
|
|
|
|
// skyPlot.drawSkyPlotCircle();
|
|
|
|
// // if (controller.chartData.value.time != 0) {
|
|
|
|
// if (isPortrait) {
|
|
|
|
// canvas.translate(size.width / 2, size.height / 2);
|
|
|
|
// } else {
|
|
|
|
// canvas.translate(size.width / 2, size.height / 2);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// skyPlot.getCircles();
|
|
|
|
// if (isPortrait) {
|
|
|
|
// canvas.translate(-size.width / 2, -size.height / 2);
|
|
|
|
// } else {
|
|
|
|
// canvas.translate(-size.width / 2, -size.height / 2);
|
|
|
|
// }
|
|
|
|
// // }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// @override
|
|
|
|
// bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
|
|
|
|
// }
|
|
|
|
|
|
|
|
class SkyPlotPainter extends CustomPainter {
|
2024-08-01 15:13:18 +08:00
|
|
|
final SignalData? signalData;
|
|
|
|
final Map<String, bool> selectedSignal;
|
2024-08-01 14:08:34 +08:00
|
|
|
final satelliteRadius = 12.0;
|
2024-08-01 15:13:18 +08:00
|
|
|
SkyPlotPainter(this.signalData, this.selectedSignal);
|
2024-07-31 23:37:51 +08:00
|
|
|
|
|
|
|
@override
|
|
|
|
void paint(Canvas canvas, Size size) {
|
|
|
|
final paint = Paint()
|
2024-08-01 14:08:34 +08:00
|
|
|
..color = const ui.Color.fromARGB(255, 0, 0, 0)
|
2024-07-31 23:37:51 +08:00
|
|
|
..style = PaintingStyle.stroke;
|
|
|
|
|
|
|
|
final double radius = min(size.width / 2, size.height / 2);
|
|
|
|
final Offset center = Offset(size.width / 2, size.height / 2);
|
|
|
|
|
|
|
|
// Draw concentric circles for different elevation angles
|
|
|
|
for (int i = 0; i <= 90; i += 30) {
|
|
|
|
double r = radius * (90 - i) / 90;
|
|
|
|
canvas.drawCircle(center, r, paint);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw lines for different azimuth angles
|
|
|
|
for (int i = 0; i < 360; i += 30) {
|
|
|
|
double radians = i * pi / 180;
|
|
|
|
double x = center.dx + radius * cos(radians);
|
|
|
|
double y = center.dy + radius * sin(radians);
|
|
|
|
canvas.drawLine(center, Offset(x, y), paint);
|
|
|
|
}
|
2024-08-01 15:13:18 +08:00
|
|
|
if (signalData == null) {
|
|
|
|
return;
|
|
|
|
}
|
2024-07-31 23:37:51 +08:00
|
|
|
// Draw satellite positions
|
|
|
|
final satellitePaint = Paint()..style = PaintingStyle.fill;
|
2024-08-01 15:13:18 +08:00
|
|
|
signalData!.forEach((key, value) {
|
|
|
|
if (selectedSignal[key] == true) {
|
|
|
|
satellitePaint.color = signalColorMap[key] ?? Colors.yellow;
|
|
|
|
drawGnssSignal(canvas, center, radius, value, satellitePaint, key);
|
2024-07-31 23:37:51 +08:00
|
|
|
}
|
2024-08-01 15:13:18 +08:00
|
|
|
});
|
2024-07-31 23:37:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void drawGnssSignal(
|
|
|
|
Canvas canvas,
|
|
|
|
Offset center,
|
|
|
|
double radius,
|
|
|
|
List<SignalGNSS> signals,
|
|
|
|
Paint paint,
|
2024-08-01 15:13:18 +08:00
|
|
|
String neme,
|
2024-07-31 23:37:51 +08:00
|
|
|
) {
|
|
|
|
for (final signal in signals) {
|
|
|
|
final int el = signal.elevation;
|
2024-08-01 14:08:34 +08:00
|
|
|
|
|
|
|
final double az = (360 - signal.azimuth + 90) * pi / 180;
|
2024-07-31 23:37:51 +08:00
|
|
|
|
|
|
|
final double r = radius * (90 - el) / 90;
|
|
|
|
final double x = center.dx + r * cos(az);
|
|
|
|
final double y = center.dy + r * sin(az);
|
|
|
|
|
|
|
|
canvas.drawCircle(Offset(x, y), satelliteRadius, paint);
|
2024-08-01 14:08:34 +08:00
|
|
|
TextPainter(
|
|
|
|
text: TextSpan(
|
2024-08-01 15:13:18 +08:00
|
|
|
text: signalPrefixList[neme] + signal.prn.toString().padLeft(2, '0'),
|
|
|
|
style: const TextStyle(color: Colors.white, fontSize: 12),
|
2024-08-01 14:08:34 +08:00
|
|
|
),
|
|
|
|
textDirection: TextDirection.ltr,
|
|
|
|
)
|
|
|
|
..layout()
|
|
|
|
..paint(canvas, Offset(x - 9, y - 7));
|
2024-07-31 23:37:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
2024-08-01 14:08:34 +08:00
|
|
|
return true;
|
2024-07-31 23:37:51 +08:00
|
|
|
}
|
|
|
|
}
|