2024-07-31 19:04:11 +08:00
|
|
|
import 'dart:convert';
|
2024-07-30 18:20:27 +08:00
|
|
|
import 'dart:math';
|
2024-07-31 19:04:11 +08:00
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'dart:ui' as ui;
|
2024-07-30 18:20:27 +08:00
|
|
|
import 'package:flutter/material.dart';
|
2024-07-31 19:04:11 +08:00
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:gnss/gnss.dart';
|
|
|
|
|
|
|
|
import '../Controller/gnssController.dart';
|
2024-07-30 18:20:27 +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;
|
2024-07-31 19:04:11 +08:00
|
|
|
late LocationData locationData;
|
|
|
|
late SignalData signalData;
|
|
|
|
late GnssController controller;
|
|
|
|
// final GnssController controller;
|
|
|
|
|
2024-07-30 18:20:27 +08:00
|
|
|
final bool isDarkMode;
|
|
|
|
Color color = Colors.black;
|
2024-07-31 19:04:11 +08:00
|
|
|
DrawSkyPlot(op, this.isDarkMode, this.controller) {
|
|
|
|
// controller = Get.find<GnssController>(tag: 'gnss');
|
2024-07-30 18:20:27 +08:00
|
|
|
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() {
|
2024-07-31 19:04:11 +08:00
|
|
|
legend = {};
|
2024-07-30 18:20:27 +08:00
|
|
|
double radius = height / 2.5;
|
|
|
|
double r = 15;
|
2024-07-31 19:04:11 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2024-07-30 18:20:27 +08:00
|
|
|
}
|
2024-07-31 19:04:11 +08:00
|
|
|
}
|
2024-07-30 18:20:27 +08:00
|
|
|
|
2024-07-31 19:04:11 +08:00
|
|
|
class SkyPlotPage extends StatelessWidget {
|
|
|
|
late final GnssController controller;
|
|
|
|
// Map<String, ui.Image> imageList = {};
|
|
|
|
@override
|
|
|
|
SkyPlotPage() {
|
|
|
|
controller = Get.find<GnssController>(tag: 'gnss');
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
|
|
|
return Obx(() {
|
|
|
|
controller.singnalUpdate;
|
|
|
|
return CustomPaint(
|
|
|
|
painter: DrawSky(controller, context, isDarkMode),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
2024-07-30 18:20:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
class DrawSky extends CustomPainter {
|
|
|
|
final BuildContext context;
|
2024-07-31 19:04:11 +08:00
|
|
|
final GnssController controller;
|
2024-07-30 18:20:27 +08:00
|
|
|
final bool isDarkMode;
|
2024-07-31 19:04:11 +08:00
|
|
|
// late SignalData signalData;
|
|
|
|
DrawSky(this.controller, this.context, this.isDarkMode);
|
2024-07-30 18:20:27 +08:00
|
|
|
@override
|
|
|
|
void paint(Canvas canvas, Size size) {
|
|
|
|
canvas.translate(0, -15);
|
|
|
|
final bool isPortrait =
|
|
|
|
MediaQuery.of(context).orientation == Orientation.portrait;
|
2024-07-31 19:04:11 +08:00
|
|
|
DrawSkyPlot skyPlot = DrawSkyPlot({
|
|
|
|
'ctx': canvas,
|
|
|
|
'height': size.height,
|
|
|
|
'width': size.width,
|
|
|
|
}, isDarkMode, controller);
|
2024-07-30 18:20:27 +08:00
|
|
|
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;
|
|
|
|
}
|