Flutter天文计算插件sweph的使用

Flutter天文计算插件sweph的使用

Sweph

跨平台的Swiss Ephemeris API绑定,适用于Dart和Flutter。 使用该库可以创建天文学和占星学应用程序。

  • 100% API覆盖
  • Dart友好的参数和返回值
  • 支持所有平台。非Web平台使用ffi,Web平台使用wasm_ffi
  • 使用的原始Swiss Ephemeris版本作为构建号参考

参考资料

使用示例

import 'package:flutter/material.dart';
import 'package:sweph/sweph.dart';

import 'web_helper.dart' if (dart.library.ffi) 'io_helper.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final stopwatch = Stopwatch()..start();

  // 只加载所需的资产。默认情况下将加载无
  // 某些资产与Sweph捆绑在一起
  await initSweph([
    'packages/sweph/assets/ephe/seas_18.se1', // 用于房屋计算
    'packages/sweph/assets/ephe/sefstars.txt', // 用于星星位置
    'packages/sweph/assets/ephe/seasnam.txt', // 用于小行星
  ]);

  runApp(MyApp(
    timeToLoad: stopwatch.elapsed,
  ));
}

class MyApp extends StatefulWidget {
  final Duration timeToLoad;
  const MyApp({super.key, required this.timeToLoad});

  [@override](/user/override)
  // ignore: library_private_types_in_public_api
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String swephVersion = Sweph.swe_version();
  String moonPosition = getMoonPosition();
  String starDistance = getStarPosition();
  String asteroidName = getAstroidName();
  HouseCuspData houseSystemAscmc = getHouseSystemAscmc();
  CoordinatesWithSpeed chironPosition = getChironPosition();
  DegreeSplitData degreeSplitData = Sweph.swe_split_deg(
    100,
    SplitDegFlags.SE_SPLIT_DEG_ZODIACAL |
        SplitDegFlags.SE_SPLIT_DEG_ROUND_SEC |
        SplitDegFlags.SE_SPLIT_DEG_KEEP_SIGN |
        SplitDegFlags.SE_SPLIT_DEG_KEEP_DEG,
  );
  HouseCuspData houseCuspData = Sweph.swe_houses_ex2(
    Sweph.swe_julday(2000, 1, 1, 12, CalendarType.SE_GREG_CAL),
    SwephFlag.SEFLG_TROPICAL,
    30,
    60,
    Hsys.B,
  );
  List<double> utcJds = Sweph.swe_utc_to_jd(
    2000,
    1,
    1,
    12,
    0,
    0,
    CalendarType.SE_GREG_CAL,
  );

  AltitudeRefracInfo altInfo =
      Sweph.swe_refrac(80, 1013.25, 15, RefractionMode.SE_APP_TO_TRUE);
  AltitudeRefracInfo altInfoEx = Sweph.swe_refrac_extended(
      100, 200, 1013.25, 15, 0.0065, RefractionMode.SE_APP_TO_TRUE);

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  static String getMoonPosition() {
    final jd =
        Sweph.swe_julday(2022, 6, 29, (2 + 52 / 60), CalendarType.SE_GREG_CAL);
    final pos =
        Sweph.swe_calc_ut(jd, HeavenlyBody.SE_MOON, SwephFlag.SEFLG_SWIEPH);
    return 'lat=${pos.latitude.toStringAsFixed(3)} lon=${pos.longitude.toStringAsFixed(3)}';
  }

  static String getStarPosition() {
    final jd =
        Sweph.swe_julday(2022, 6, 29, (2 + 52 / 60), CalendarType.SE_GREG_CAL);
    try {
      return Sweph.swe_fixstar2_ut('Rohini', jd, SwephFlag.SEFLG_SWIEPH)
          .coordinates
          .distance
          .toStringAsFixed(3);
    } catch (e) {
      return e.toString();
    }
  }

  static String getAstroidName() {
    return Sweph.swe_get_planet_name(HeavenlyBody.SE_AST_OFFSET + 16);
  }

  static HouseCuspData getHouseSystemAscmc() {
    const year = 1947;
    const month = 8;
    const day = 15;
    const hour = 16 + (0.0 / 60.0) - 5.5;

    const longitude = 81 + 50 / 60.0;
    const latitude = 25 + 57 / 60.0;
    final julday =
        Sweph.swe_julday(year, month, day, hour, CalendarType.SE_GREG_CAL);

    Sweph.swe_set_sid_mode(SiderealMode.SE_SIDM_LAHIRI,
        SiderealModeFlag.SE_SIDBIT_NONE, 0.0 /* t0 */, 0.0 /* ayan_t0 */);
    return Sweph.swe_houses(julday, latitude, longitude, Hsys.P);
  }

  static CoordinatesWithSpeed getChironPosition() {
    final now = DateTime.now();
    final jd = Sweph.swe_julday(now.year, now.month, now.day,
        (now.hour + now.minute / 60), CalendarType.SE_GREG_CAL);
    Sweph.swe_julday(2022, 6, 29, (2 + 52 / 60), CalendarType.SE_GREG_CAL);
    return Sweph.swe_calc_ut(
        jd, HeavenlyBody.SE_CHIRON, SwephFlag.SEFLG_SWIEPH);
  }

  void _addText(List<Widget> children, String text) {
    const textStyle = TextStyle(fontSize: 25);
    const spacerSmall = SizedBox(height: 10);

    children.add(spacerSmall);
    children.add(Text(
      text,
      style: textStyle,
      textAlign: TextAlign.center,
    ));
  }

  Widget _getContent(BuildContext context) {
    List<Widget> children = [
      const Text(
        'Swiss Ephemeris Example',
        style: TextStyle(fontSize: 30),
        textAlign: TextAlign.center,
      ),
      const SizedBox(height: 10)
    ];

    _addText(children,
        'Time taken to load Sweph: ${widget.timeToLoad.inMilliseconds} ms');
    _addText(children, 'Sweph Version: $swephVersion');
    _addText(
        children, 'Moon position on 2022-06-29 02:52:00 UTC: $moonPosition');
    _addText(children, 'Distance of star Rohini: $starDistance AU');
    _addText(children, 'Name of Asteroid 16: $asteroidName');
    _addText(children, 'Position of Chiron: $chironPosition');
    _addText(children,
        'House System ASCMC[0] for custom time: ${houseSystemAscmc.ascmc[0]}');
    _addText(children, 'Degree Split Data: $degreeSplitData');
    _addText(children, 'House Cusp Data: ${houseCuspData.cusps.sublist(0, 6)}');
    _addText(children,
        'TT: ${utcJds[0]} UT1: ${utcJds[1]} UTC: ${Sweph.swe_julday(2000, 1, 1, 12, CalendarType.SE_GREG_CAL)}}');
    _addText(children, 'AltInfo: $altInfo');
    _addText(children, 'AltInfoEx: $altInfoEx');

    return Column(children: children);
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Native Packages'),
        ),
        body: SingleChildScrollView(
          child: Center(
            child: Container(
                padding: const EdgeInsets.all(10), child: _getContent(context)),
          ),
        ),
      ),
    );
  }
}

更多关于Flutter天文计算插件sweph的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter天文计算插件sweph的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于在Flutter项目中使用sweph(Swiss Ephemeris Wrapper for Python)进行天文计算,虽然sweph本身是一个Python库,但我们可以通过一些方法来在Flutter中调用Python代码。通常,这可以通过使用平台通道(Platform Channels)来实现。以下是一个基本的示例,展示如何在Flutter中调用Python脚本进行天文计算。

由于Flutter原生不支持直接运行Python代码,我们需要一个中介层,比如使用FlutterMethodChannel与原生代码(如Android的Java/Kotlin或iOS的Swift/Objective-C)进行通信,而这些原生代码再调用Python脚本。

步骤 1: 设置Flutter项目

首先,确保你已经创建了一个Flutter项目。如果还没有,可以使用以下命令创建一个新项目:

flutter create astro_app
cd astro_app

步骤 2: 在Android平台上调用Python脚本

2.1 添加依赖

android/app/build.gradle文件中,添加对chaquopy的依赖,这是一个允许在Android应用中运行Python代码的插件:

dependencies {
    implementation 'com.chaquo.python:gradle:10.1.0'
}

然后在android/build.gradlebuildscript部分添加Chaquopy的仓库和插件:

buildscript {
    repositories {
        google()
        jcenter()
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath 'com.chaquo.python:gradle:10.1.0'
    }
}

2.2 配置Chaquopy

android/app/src/main/python目录下创建一个Python脚本,比如sweph_wrapper.py,这个脚本将调用sweph库进行天文计算。

# sweph_wrapper.py
import sweph

def get_planet_position(planet, julian_date):
    # 假设sweph已经正确安装并配置
    # 这里是一个简单的示例,实际调用可能需要更多参数
    res = sweph.swe_calc(julian_date, planet, 0)
    return res

2.3 创建MethodChannel

android/app/src/main/kotlin/com/example/astro_app/MainActivity.kt(对于Kotlin)或android/app/src/main/java/com/example/astro_app/MainActivity.java(对于Java)中,创建一个MethodChannel来与Flutter通信。

Kotlin示例:

package com.example.astro_app

import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import com.chaquo.python.PyObject
import com.chaquo.python.Python

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.astro_app/channel"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "getPlanetPosition") {
                val planet = call.argument<String>("planet") ?: return@setMethodCallHandler result.error("INVALID_ARGUMENT", "Planet argument is missing", null)
                val julianDate = call.argument<Double>("julianDate") ?: return@setMethodCallHandler result.error("INVALID_ARGUMENT", "Julian Date argument is missing", null)

                val py = Python.getInstance()
                val module = py.getModule("sweph_wrapper")
                val position = module.callAttr("get_planet_position", planet, julianDate)

                result.success(position.toString())
            } else {
                result.notImplemented()
            }
        }
    }
}

Java示例:

package com.example.astro_app;

import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import com.chaquo.python.PyObject;
import com.chaquo.python.Python;

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.example.astro_app/channel";

    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                            if (call.method.equals("getPlanetPosition")) {
                                String planet = call.argument("planet");
                                if (planet == null) {
                                    result.error("INVALID_ARGUMENT", "Planet argument is missing", null);
                                    return;
                                }

                                Double julianDate = call.argument("julianDate");
                                if (julianDate == null) {
                                    result.error("INVALID_ARGUMENT", "Julian Date argument is missing", null);
                                    return;
                                }

                                Python py = Python.getInstance();
                                PyObject module = py.getModule("sweph_wrapper");
                                PyObject position = module.callAttr("get_planet_position", planet, julianDate);

                                result.success(position.toString());
                            } else {
                                result.notImplemented();
                            }
                        }
                );
    }
}

步骤 3: 在Flutter中调用MethodChannel

lib/main.dart中,使用MethodChannel来调用Android端的Python脚本。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  static const platform = MethodChannel('com.example.astro_app/channel');

  String _planetPosition = 'Unknown';

  Future<void> _getPlanetPosition(String planet, double julianDate) async {
    try {
      final String result = await platform.invokeMethod('getPlanetPosition', {'planet': planet, 'julianDate': julianDate});

      setState(() {
        _planetPosition = result;
      });
    } on PlatformException catch (e) {
      setState(() {
        _planetPosition = "Failed to get planet position: '${e.message}'.";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Astronomy App'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Planet Position:'),
              Text(_planetPosition),
              ElevatedButton(
                onPressed: () {
                  _getPlanetPosition('MERCURY', 2459100.5); // 示例:获取水星在特定儒略日的位置
                },
                child: Text('Get Planet Position'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意

  1. 上述示例仅展示了如何在Android平台上集成Python脚本。对于iOS,你需要使用其他方法,比如使用PythonKit(如果可行)或者通过其他中介服务。
  2. sweph库的安装和配置可能比较复杂,确保你已经按照sweph的文档正确安装并配置了它。
  3. 本示例中的get_planet_position函数是一个占位符,实际调用sweph可能需要更多的参数和处理。

这个示例提供了一个基本的框架,你可以在此基础上根据具体需求进行扩展和修改。

回到顶部