Rectangular Field on earth

Hello, could you please explain in detail what this code means? Although I can run this code, I am not clear about the specific meaning, so I am unable to conduct further experiments.

public class InAreaStatus implements EventHandler {
	  private boolean inArea = false;
	  public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
	    if (!increasing) {
	      System.out.println("Visibility begins at " + s.getDate());
	      inArea = true;
	    } else {
	      System.out.println("Visibility ends at " + s.getDate());
	      inArea = false;
	    return Action.CONTINUE;
	  public boolean inArea() {
	    return inArea;

public class SubPointWriter implements OrekitFixedStepHandler {
    private final InAreaStatus status;
    private final OneAxisEllipsoid earth;
    public SubPointWriter(final InAreaStatus status, final BodyShape earth2) {
      this.status = status;  = (OneAxisEllipsoid) earth2;
    public void handleStep(SpacecraftState state) {
      if (status.inArea()) {
        // we only print satellite sub-point when we know we already are in the area of interest
        GeodeticPoint gp = earth.transform(state.getPVCoordinates().getPosition(),
        System.out.format(Locale.US, "%s %10.5f %10.5f%n",

public class Test {
	public static void main(String args[]) {
		final File home       = new File(System.getProperty("user.home"));
        final File orekitData = new File(home, "orekit-data");
        if (!orekitData.exists()) {
            System.err.format(Locale.US, "Failed to find %s folder%n",
            System.err.format(Locale.US, "You need to download %s from %s, unzip it in %s and rename it 'orekit-data' for this tutorial to work%n",
                              "", "",
        final DataProvidersManager manager = DataContext.getDefault().getDataProvidersManager();
        manager.addProvider(new DirectoryCrawler(orekitData));

        //  Initial state definition : date, orbit
        final AbsoluteDate initialDate = new AbsoluteDate(2023, 10, 26, 8, 00, 00.000, TimeScalesFactory.getUTC());
        final double mu =  3.986004415e+14; // gravitation coefficient
        final Frame inertialFrame = FramesFactory.getEME2000(); // inertial frame for orbit definition
        final Vector3D position  = new Vector3D(-7518565.475, 4279265.668, 4816446.308);
        final Vector3D velocity  = new Vector3D(-5463.904, -5533.9672, 50.719);
        final PVCoordinates pvCoordinates = new PVCoordinates(position, velocity);
        final Orbit initialOrbit = new KeplerianOrbit(pvCoordinates, inertialFrame, initialDate, mu);

        // Propagator : consider a simple Keplerian motion 
        final Propagator kepler = new KeplerianPropagator(initialOrbit);

        // Earth and frame
        final Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
        final BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,

        GeodeticPoint point1 = new GeodeticPoint(Math.toRadians(0), Math.toRadians(30), 0.0);
        GeodeticPoint point2 = new GeodeticPoint(Math.toRadians(30), Math.toRadians(30), 0.0);
        GeodeticPoint point3 = new GeodeticPoint(Math.toRadians(30), Math.toRadians(0), 0.0);
        GeodeticPoint point4 = new GeodeticPoint(Math.toRadians(0), Math.toRadians(0), 0.0);

        // 创建表示地面区域的SphericalPolygonsSet
        SphericalPolygonsSet region = EllipsoidTessellator.buildSimpleZone(1.0e-10, point1, point2, point3, point4);

        // Event definition
        final double maxcheck  = 60;
        final double threshold =  0.001;
        final double elevation = FastMath.toRadians(0.0);
        final double stepSize = 100; 
        final InAreaStatus status = new InAreaStatus();
        final GeographicZoneDetector zoneDetector =
          new GeographicZoneDetector(maxcheck, threshold, earth, region, elevation)
       final SubPointWriter subPointWriter = new SubPointWriter(status, earth);
       kepler.getMultiplexer().add(stepSize, subPointWriter);


This program starts by setting up a lot of configuration for a propagator in order for it to automatically discover on the fly some events (start/end of overflying a geographic zone) and to log the satellite location with respect to ground (i.e. latitude/longitude) while in said geographic zone.

The configuration part is everything before last line. The last line (call to kepler.propagate) is where the propagator is really run and all the configured event detection/logging is really performed. The configuration is a complicated one, it involves both events handling and step handling, and they are linked together.

Events handling is what is done in the class InAreaStatus as it implements EventHandler and is registered as the event handler to be triggered automatically when the GeographicZoneDetector discover the satellite enters or leaves the region of interest. So during the configuration phase, we associate one InAreaStatus instance with one GeographicZoneDetector and we add this detector to the propagator, so when the satellite enters/leaves the region, the InAreaStatus method eventOccurred method will be called. We store the current status (inside/outside region) in the InAreaStatus instance. Please note that in another thread, I suggested to use a TimeSpanMap<Boolean> rather than a primitive boolean to store this status with its associated time in order to avoid slight problems near region boundary (points considered visible when they are not yet visible).

Logging satellite location is done in the class SubPointWriter as it implements OrekitFixedStepHandler and is registered as a step handler called regularly throughout propagation, i.e. from start to begin. What we want here, is to output the satellite location but only when the satellite is above the region of interest. So we have to associate the InAreaStatus instance (which knows when the satellite is in area and when the satellite is out of area), and we set up a condition to only output the sub-satellite point when the satellite is above region. This means that the propagator will call the handleStep method of the SubPointWriter class throughout propagation, but due to the if statement, it will only display point when the satellite is visible (or close to it, due to the synchronization problem I mentioned in another thread, and for which I suggested to use TimeSpanMap<Boolean> for more accurate results.

Thank you for your detailed answer, I will greatly benefit. Wishing you a pleasant work and life!