Exemplo n.º 1
0
int main(int argc, char **argv)
{
  Args args(argc, argv, "PATH");
  const char *path = args.ExpectNext();
  args.ExpectEnd();

  FileLineReader reader(path, ConvertLineReader::AUTO);
  if (reader.error()) {
    fprintf(stderr, "Failed to open input file\n");
    return 1;
  }

  Airspaces airspaces;
  AirspaceParser parser(airspaces);

  NullOperationEnvironment operation;
  if (!parser.Parse(reader, operation)) {
    fprintf(stderr, "Failed to parse input file\n");
    return 1;
  }

  airspaces.Optimise();

  printf("OK\n");

  return 0;
}
Exemplo n.º 2
0
void scan_airspaces(const AircraftState state,
                    const Airspaces& airspaces,
                    const AirspaceAircraftPerformance& perf,
                    bool do_report,
                    const GeoPoint &target)
{
    const fixed range(20000.0);

    Directory::Create(Path(_T("output/results")));

    {
        AirspaceVisitorPrint pvisitor("output/results/res-bb-range.txt",
                                      do_report);
        for (const auto &i : airspaces.QueryWithinRange(state.location, range)) {
            const AbstractAirspace &airspace = i.GetAirspace();
            pvisitor.Visit(airspace);
        }
    }

    {
        AirspaceVisitorClosest pvisitor("output/results/res-bb-closest.txt",
                                        airspaces.GetProjection(), state, perf);
        for (const auto &i : airspaces.QueryWithinRange(state.location, range)) {
            const AbstractAirspace &airspace = i.GetAirspace();
            pvisitor.Visit(airspace);
        }
    }

    {
        AirspaceVisitorPrint pvi("output/results/res-bb-inside.txt",
                                 do_report);
        for (const auto &a : airspaces.QueryInside(state))
            pvi.Visit(a.GetAirspace());
    }

    {
        AirspaceIntersectionVisitorPrint ivisitor("output/results/res-bb-intersects.txt",
                "output/results/res-bb-intersected.txt",
                "output/results/res-bb-intercepts.txt",
                do_report,
                state, perf);
        airspaces.VisitIntersecting(state.location, target, ivisitor);
    }

    {
        const auto *as = FindSoonestAirspace(airspaces, state, perf,
                                             AirspacePredicateTrue());
        if (do_report) {
            std::ofstream fout("output/results/res-bb-sortedsoonest.txt");
            if (as) {
                fout << *as << "\n";
            } else {
                fout << "# no soonest found\n";
            }
        }
    }
}
Exemplo n.º 3
0
bool test_airspace_extra(Airspaces &airspaces) {
    // try adding a null polygon

    AbstractAirspace* as;
    std::vector<GeoPoint> pts;
    as = new AirspacePolygon(pts);
    airspaces.Add(as);

    // try clearing now (we haven't called optimise())

    airspaces.Clear();
    return true;
}
Exemplo n.º 4
0
void scan_airspaces(const AircraftState state, 
                    const Airspaces& airspaces,
                    const AirspaceAircraftPerformance& perf,
                    bool do_report,
                    const GeoPoint &target) 
{
  const fixed range(20000.0);

  Directory::Create(Path(_T("output/results")));

  {
    AirspaceVisitorPrint pvisitor("output/results/res-bb-range.txt",
                                  do_report);
    airspaces.VisitWithinRange(state.location, range, pvisitor);
  }

  {
    AirspaceVisitorClosest pvisitor("output/results/res-bb-closest.txt",
                                    airspaces.GetProjection(), state, perf);
    airspaces.VisitWithinRange(state.location, range, pvisitor);
  }

  {
    const std::vector<Airspace> vi = airspaces.FindInside(state);
    AirspaceVisitorPrint pvi("output/results/res-bb-inside.txt",
                             do_report);
    std::for_each(vi.begin(), vi.end(), CallVisitor<AirspaceVisitor>(pvi));
  }
  
  {
    AirspaceIntersectionVisitorPrint ivisitor("output/results/res-bb-intersects.txt",
                                              "output/results/res-bb-intersected.txt",
                                              "output/results/res-bb-intercepts.txt",
                                              do_report,
                                              state, perf);
    airspaces.VisitIntersecting(state.location, target, ivisitor);
  }

  {
    const auto *as = FindSoonestAirspace(airspaces, state, perf,
                                         AirspacePredicateTrue());
    if (do_report) {
      std::ofstream fout("output/results/res-bb-sortedsoonest.txt");
      if (as) {
        fout << *as << "\n";
      } else {
        fout << "# no soonest found\n";
      }
    }
  }
}
Exemplo n.º 5
0
void setup_airspaces(Airspaces& airspaces, const GeoPoint& center, const unsigned n) {
    std::ofstream *fin = NULL;

    if (verbose) {
        Directory::Create(Path(_T("output/results")));
        fin = new std::ofstream("output/results/res-bb-in.txt");
    }

    for (unsigned i=0; i<n; i++) {
        AbstractAirspace* as;
        if (rand()%4!=0) {
            GeoPoint c;
            c.longitude = Angle::Degrees(fixed((rand()%1200-600)/1000.0))+center.longitude;
            c.latitude = Angle::Degrees(fixed((rand()%1200-600)/1000.0))+center.latitude;
            fixed radius(10000.0*(0.2+(rand()%12)/12.0));
            as = new AirspaceCircle(c,radius);
        } else {

            // just for testing, create a random polygon from a convex hull around
            // random points
            const unsigned num = rand()%10+5;
            GeoPoint c;
            c.longitude = Angle::Degrees(fixed((rand()%1200-600)/1000.0))+center.longitude;
            c.latitude = Angle::Degrees(fixed((rand()%1200-600)/1000.0))+center.latitude;

            std::vector<GeoPoint> pts;
            for (unsigned j=0; j<num; j++) {
                GeoPoint p=c;
                p.longitude += Angle::Degrees(fixed((rand()%200)/1000.0));
                p.latitude += Angle::Degrees(fixed((rand()%200)/1000.0));
                pts.push_back(p);
            }
            as = new AirspacePolygon(pts,true);
        }
        airspace_random_properties(*as);
        airspaces.Add(as);
        if (fin)
            *fin << *as;
    }

    delete fin;

    // try inserting nothing
    airspaces.Add(NULL);

    airspaces.Optimise();

}
Exemplo n.º 6
0
bool
Airspaces::SynchroniseInRange(const Airspaces &master,
                              const GeoPoint &location,
                              const double range,
                              const AirspacePredicate &condition)
{
  qnh = master.qnh;
  activity_mask = master.activity_mask;
  task_projection = master.task_projection;

  AirspaceVector contents_master;
  for (const auto &i : master.QueryWithinRange(location, range))
    if (condition(i.GetAirspace()))
      contents_master.push_back(i);

  if (CompareAirspaceVectors(contents_master, AsVector()))
    return false;

  for (auto &i : QueryAll())
    i.ClearClearance();
  airspace_tree.clear();

  for (const auto &i : contents_master)
    airspace_tree.insert(i);

  ++serial;

  return true;
}
Exemplo n.º 7
0
 void
 AddPolygon(Airspaces &airspace_database)
 {
   AbstractAirspace *as = new AirspacePolygon(points);
   as->set_properties(Name, Type, Base, Top);
   airspace_database.insert(as);
 }
Exemplo n.º 8
0
void
MapItemListBuilder::AddVisibleAirspace(
    const Airspaces &airspaces,
    const ProtectedAirspaceWarningManager *warning_manager,
    const AirspaceComputerSettings &computer_settings,
    const AirspaceRendererSettings &renderer_settings,
    const MoreData &basic, const DerivedInfo &calculated)
{
  AirspaceWarningList warnings;
  if (warning_manager != nullptr)
    warnings.Fill(*warning_manager);

  const AircraftState aircraft = ToAircraftState(basic, calculated);
  AirspaceAtPointPredicate predicate(computer_settings, renderer_settings,
                                     aircraft,
                                     warnings, location);

  for (const auto &i : airspaces.QueryWithinRange(location, 100)) {
    if (list.full())
      break;

    const AbstractAirspace &airspace = i.GetAirspace();
    if (predicate(airspace))
      list.append(new AirspaceMapItem(airspace));
  }
}
Exemplo n.º 9
0
 void
 AddCircle(Airspaces &airspace_database)
 {
   AbstractAirspace *as = new AirspaceCircle(Center, Radius);
   as->set_properties(Name, Type, Base, Top);
   airspace_database.insert(as);
 }
Exemplo n.º 10
0
bool
Airspaces::SynchroniseInRange(const Airspaces& master,
                                const GeoPoint &location,
                                const fixed range,
                                const AirspacePredicate &condition)
{
  bool changed = false;
  const AirspaceVector contents_master = master.ScanRange(location, range, condition);
  AirspaceVector contents_self;
  contents_self.reserve(std::max(airspace_tree.size(), contents_master.size()));

  task_projection = master.task_projection; // ensure these are up to date

  for (const auto &v : airspace_tree)
    contents_self.push_back(v);

  // find items to add
  for (const auto &v : contents_master) {
    const AbstractAirspace* other = v.GetAirspace();

    bool found = false;
    for (auto s = contents_self.begin(); s != contents_self.end(); ++s) {
      const AbstractAirspace* self = s->GetAirspace();
      if (self == other) {
        found = true;
        contents_self.erase(s);
        break;
      }
    }
    if (!found && other->IsActive()) {
      Add(v.GetAirspace());
      changed = true;
    }
  }

  // anything left in the self list are items that were not in the query,
  // so delete them --- including the clearances!
  for (auto v = contents_self.begin(); v != contents_self.end();) {
    bool found = false;
    for (auto t = airspace_tree.begin(); t != airspace_tree.end(); ) {
      if (t->GetAirspace() == v->GetAirspace()) {
        AirspaceTree::const_iterator new_t = t;
        ++new_t;
        airspace_tree.erase_exact(*t);
        t = new_t;
        found = true;
      } else {
        ++t;
      }
    }
    assert(found);
    v->ClearClearance();
    v = contents_self.erase(v);
    changed = true;
  }
  if (changed)
    Optimise();
  return changed;
}
Exemplo n.º 11
0
 void
 AddPolygon(Airspaces &airspace_database)
 {
   AbstractAirspace *as = new AirspacePolygon(points);
   as->set_properties(Name, Type, Base, Top);
   as->set_radio(Radio);
   as->set_days(days_of_operation);
   airspace_database.insert(as);
 }
Exemplo n.º 12
0
 void
 AddPolygon(Airspaces &airspace_database)
 {
   AbstractAirspace *as = new AirspacePolygon(points);
   as->SetProperties(name, type, base, top);
   as->SetRadio(radio);
   as->SetDays(days_of_operation);
   airspace_database.Add(as);
 }
Exemplo n.º 13
0
 void
 AddCircle(Airspaces &airspace_database)
 {
   AbstractAirspace *as = new AirspaceCircle(center, radius);
   as->SetProperties(std::move(name), type, base, top);
   as->SetRadio(radio);
   as->SetDays(days_of_operation);
   airspace_database.Add(as);
 }
Exemplo n.º 14
0
AirspaceSelectInfoVector
FilterAirspaces(const Airspaces &airspaces, const GeoPoint &location,
                const AirspaceFilterData &filter)
{
  AirspaceFilterVisitor visitor(location, airspaces.GetProjection(), filter);

  if (!negative(filter.distance))
    airspaces.VisitWithinRange(location, filter.distance, visitor);
  else
    for (const auto &i : airspaces)
      visitor.Visit(i.GetAirspace());

  if (filter.direction.IsNegative() && negative(filter.distance))
    SortByName(visitor.result);
  else
    SortByDistance(visitor.result, location, airspaces.GetProjection());

  return visitor.result;
}
Exemplo n.º 15
0
void
AirspaceNearestSort::populate_queue(const Airspaces &airspaces,
                                    const fixed range)
{
  AirspacesInterface::AirspaceVector vectors =
    airspaces.ScanRange(m_location,
                        range,
                        m_condition);

  for (const Airspace &airspace : vectors) {
    const AbstractAirspace &as = airspace.GetAirspace();
    const AirspaceInterceptSolution ais =
      solve_intercept(as, airspaces.GetProjection());
    const fixed value = metric(ais);
    if (!negative(value))
      m_q.push(std::make_pair(value,
                              std::make_pair(ais, &as)));
  }
}
Exemplo n.º 16
0
void
ReadAirspace(Airspaces &airspaces,
             RasterTerrain *terrain,
             const AtmosphericPressure &press,
             OperationEnvironment &operation)
{
  LogStartUp(_T("ReadAirspace"));
  operation.SetText(_("Loading Airspace File..."));

  bool airspace_ok = false;

  // Read the airspace filenames from the registry
  TLineReader *reader =
    OpenConfiguredTextFile(szProfileAirspaceFile, _T("airspace.txt"));
  if (reader != NULL) {
    if (!ReadAirspace(airspaces, *reader, operation))
      LogStartUp(_T("No airspace file 1"));
    else
      airspace_ok =  true;

    delete reader;
  }

  reader = OpenConfiguredTextFile(szProfileAdditionalAirspaceFile);
  if (reader != NULL) {
    if (!ReadAirspace(airspaces, *reader, operation))
      LogStartUp(_T("No airspace file 2"));
    else
      airspace_ok = true;

    delete reader;
  }

  if (airspace_ok) {
    airspaces.optimise();
    airspaces.set_flight_levels(press);

    if (terrain != NULL)
      airspaces.set_ground_levels(*terrain);
  } else
    // there was a problem
    airspaces.clear();
}
Exemplo n.º 17
0
void setup_airspaces(Airspaces& airspaces, const unsigned n) {
#ifdef DO_PRINT
  std::ofstream fin("results/res-bb-in.txt");
#endif
  for (unsigned i=0; i<n; i++) {
    AbstractAirspace* as;
    if (rand()%4!=0) {
      GeoPoint c;
      c.Longitude = Angle::degrees(fixed((rand()%1200-600)/1000.0+0.5));
      c.Latitude = Angle::degrees(fixed((rand()%1200-600)/1000.0+0.5));
      fixed radius(10000.0*(0.2+(rand()%12)/12.0));
      as = new AirspaceCircle(c,radius);
    } else {

      // just for testing, create a random polygon from a convex hull around
      // random points
      const unsigned num = rand()%10+5;
      GeoPoint c;
      c.Longitude = Angle::degrees(fixed((rand()%1200-600)/1000.0+0.5));
      c.Latitude = Angle::degrees(fixed((rand()%1200-600)/1000.0+0.5));
      
      std::vector<GeoPoint> pts;
      for (unsigned j=0; j<num; j++) {
        GeoPoint p=c;
        p.Longitude += Angle::degrees(fixed((rand()%200)/1000.0));
        p.Latitude += Angle::degrees(fixed((rand()%200)/1000.0));
        pts.push_back(p);
      }
      as = new AirspacePolygon(pts,true);
    }
    airspace_random_properties(*as);
    airspaces.insert(as);
#ifdef DO_PRINT
    fin << *as;
#endif
  }

  // try inserting nothing
  airspaces.insert(NULL);

  airspaces.optimise();

}
Exemplo n.º 18
0
  void
  AddPolygon(Airspaces &airspace_database)
  {
    if (points.size() < 3)
      return;

    AbstractAirspace *as = new AirspacePolygon(points);
    as->SetProperties(std::move(name), type, base, top);
    as->SetRadio(radio);
    as->SetDays(days_of_operation);
    airspace_database.Add(as);
  }
Exemplo n.º 19
0
void 
AirspaceNearestSort::populate_queue(const Airspaces &airspaces,
                                    const fixed range)
{
  AirspacesInterface::AirspaceVector vectors = 
    airspaces.ScanRange(m_location,
                         range,
                         m_condition);

  for (auto v = vectors.begin(); v != vectors.end(); ++v) {
    const AbstractAirspace *as = v->get_airspace();
    if (as != NULL) {
      const AirspaceInterceptSolution ais =
        solve_intercept(*as, airspaces.GetProjection());
      const fixed value = metric(ais);
      if (!negative(value)) {
        m_q.push(std::make_pair(m_reverse? -value:value, std::make_pair(ais, *v)));
      }
    }
  }
}
Exemplo n.º 20
0
gcc_pure
static NearestAirspace
FindHorizontal(const GeoPoint &location,
               const Airspaces &airspace_database,
               const AirspacePredicate &predicate)
{
  const auto &projection = airspace_database.GetProjection();
  return FindMinimum(airspace_database, location, 30000, predicate,
                     [&location, &projection](const AbstractAirspace &airspace){
                       return CalculateNearestAirspaceHorizontal(location, projection, airspace);
                     },
                     CompareNearestAirspace());
}
Exemplo n.º 21
0
static void
LoadFiles(Airspaces &airspace_database)
{
  NullOperationEnvironment operation;

  std::unique_ptr<TLineReader> reader(OpenConfiguredTextFile(ProfileKeys::AirspaceFile,
                                                             Charset::AUTO));
  if (reader) {
    AirspaceParser parser(airspace_database);
    parser.Parse(*reader, operation);
    airspace_database.Optimise();
  }
}
Exemplo n.º 22
0
gcc_pure
NearestAirspace
NearestAirspace::FindVertical(const MoreData &basic,
                      const DerivedInfo &calculated,
                      const ProtectedAirspaceWarningManager &airspace_warnings,
                      const Airspaces &airspace_database)
{
  if (!basic.location_available ||
      (!basic.baro_altitude_available && !basic.gps_altitude_available))
    /* can't check for airspaces without a GPS fix and altitude
       value */
    return NearestAirspace();

  /* find the nearest airspace */

  AltitudeState altitude;
  altitude.altitude = basic.nav_altitude;
  altitude.altitude_agl = calculated.altitude_agl;

  const AbstractAirspace *nearest = nullptr;
  double nearest_delta = 100000;
  const ActiveAirspacePredicate active_predicate(&airspace_warnings);

  for (const auto &i : airspace_database.QueryInside(basic.location)) {
    const AbstractAirspace &airspace = i.GetAirspace();

    if (!active_predicate(airspace))
      continue;

    /* check delta below */
    auto base = airspace.GetBase().GetAltitude(altitude);
    auto base_delta = base - altitude.altitude;
    if (base_delta >= 0 && base_delta < fabs(nearest_delta)) {
      nearest = &airspace;
      nearest_delta = base_delta;
    }

    /* check delta above */
    auto top = airspace.GetTop().GetAltitude(altitude);
    auto top_delta = altitude.altitude - top;
    if (top_delta >= 0 && top_delta < fabs(nearest_delta)) {
      nearest = &airspace;
      nearest_delta = -top_delta;
    }
  }

  if (nearest == nullptr)
    return NearestAirspace();

  return NearestAirspace(*nearest, nearest_delta);
}
Exemplo n.º 23
0
static void
LoadFiles(Airspaces &airspace_database)
{
  NullOperationEnvironment operation;

  TLineReader *reader = OpenConfiguredTextFile(szProfileAirspaceFile);
  if (reader != NULL) {
    AirspaceParser parser(airspace_database);
    parser.Parse(*reader, operation);
    delete reader;

    airspace_database.optimise();
  }
}
Exemplo n.º 24
0
static bool
ParseFile(Path path, Airspaces &airspaces)
{
  FileLineReader reader(path, Charset::AUTO);

  AirspaceParser parser(airspaces);
  NullOperationEnvironment operation;

  if (!ok1(parser.Parse(reader, operation)))
    return false;

  airspaces.Optimise();
  return true;
}
Exemplo n.º 25
0
void
AirspaceXSRenderer::Draw(Canvas &canvas, const ChartRenderer &chart,
                         const Airspaces &database, const GeoPoint &start,
                         const GeoVector &vec, const AircraftState &state) const
{
  canvas.Select(*look.name_font);

  // Create IntersectionVisitor to render to the canvas
  AirspaceIntersectionVisitorSlice ivisitor(
      canvas, chart, settings, look, start, state);

  // Call visitor with intersecting airspaces
  database.VisitIntersecting(start, vec.EndPoint(start), ivisitor);
}
Exemplo n.º 26
0
void
ReadAirspace(Airspaces &airspaces,
             RasterTerrain *terrain,
             const AtmosphericPressure &press,
             OperationEnvironment &operation)
{
    LogFormat("ReadAirspace");
    operation.SetText(_("Loading Airspace File..."));

    bool airspace_ok = false;

    AirspaceParser parser(airspaces);

    // Read the airspace filenames from the registry
    auto path = Profile::GetPath(ProfileKeys::AirspaceFile);
    if (!path.IsNull())
        airspace_ok |= ParseAirspaceFile(parser, path, operation);

    path = Profile::GetPath(ProfileKeys::AdditionalAirspaceFile);
    if (!path.IsNull())
        airspace_ok |= ParseAirspaceFile(parser, path, operation);

    auto archive = OpenMapFile();
    if (archive)
        airspace_ok |= ParseAirspaceFile(parser, archive->get(), "airspace.txt",
                                         operation);

    if (airspace_ok) {
        airspaces.Optimise();
        airspaces.SetFlightLevels(press);

        if (terrain != NULL)
            airspaces.SetGroundLevels(*terrain);
    } else
        // there was a problem
        airspaces.Clear();
}
Exemplo n.º 27
0
gcc_pure
NearestAirspace
NearestAirspace::FindHorizontal(const MoreData &basic,
                                const ProtectedAirspaceWarningManager &airspace_warnings,
                                const Airspaces &airspace_database)
{
  if (!basic.location_available)
    /* can't check for airspaces without a GPS fix */
    return NearestAirspace();

  /* find the nearest airspace */
  //consider only active airspaces
  const WrapAirspacePredicate<ActiveAirspacePredicate> active_predicate(&airspace_warnings);
  const WrapAirspacePredicate<OutsideAirspacePredicate> outside_predicate(AGeoPoint(basic.location, RoughAltitude(0)));
  const AndAirspacePredicate outside_and_active_predicate(active_predicate, outside_predicate);
  const Airspace *airspace;

  //if altitude is available, filter airspaces in same height as airplane
  if (basic.NavAltitudeAvailable()) {
    /* check altitude; hard-coded margin of 50m (for now) */
    WrapAirspacePredicate<AirspacePredicateHeightRange> height_range_predicate(RoughAltitude(basic.nav_altitude-fixed(50)),
                                                                               RoughAltitude(basic.nav_altitude+fixed(50)));
     AndAirspacePredicate and_predicate(outside_and_active_predicate, height_range_predicate);
     airspace = airspace_database.FindNearest(basic.location, and_predicate);
  } else //only filter outside and active
    airspace = airspace_database.FindNearest(basic.location, outside_and_active_predicate);

  if (airspace == nullptr)
    return NearestAirspace();

  const AbstractAirspace &as = airspace->GetAirspace();

  /* calculate distance to the nearest point */
  const GeoPoint closest = as.ClosestPoint(basic.location,
                                           airspace_database.GetProjection());
  return NearestAirspace(as, basic.location.Distance(closest));
}
Exemplo n.º 28
0
AirspaceSorter::AirspaceSorter(const Airspaces &airspaces,
                               const GeoPoint &Location,
                               const fixed distance_factor)
{
  airspaces.lock();
  m_airspaces_all.reserve(airspaces.size());

  for (Airspaces::AirspaceTree::const_iterator it = airspaces.begin();
       it != airspaces.end(); ++it) {

    AirspaceSelectInfo info;
    
    const AbstractAirspace &airspace = *it->get_airspace();

    info.airspace = &airspace;

    const GeoPoint closest_loc = airspace.closest_point(Location);
    const GeoVector vec(Location, closest_loc);

    info.Distance = vec.Distance*distance_factor;
    info.Direction = vec.Bearing;

    tstring Name = airspace.get_name_text(true);

    info.FourChars = 
      ((Name.c_str()[0] & 0xff) << 24)
      +((Name.c_str()[1] & 0xff) << 16)
      +((Name.c_str()[2] & 0xff) << 8)
      +((Name.c_str()[3] & 0xff));

    m_airspaces_all.push_back(info);
  }
  airspaces.unlock();

  sort_name(m_airspaces_all);
}
Exemplo n.º 29
0
gcc_pure
static inline Result
FindMinimum(const Airspaces &airspaces, const GeoPoint &location, double range,
            const AirspacePredicate &predicate,
            Func &&func,
            Cmp &&cmp=Cmp())
{
  Result minimum;
  for (const auto &i : airspaces.QueryWithinRange(location, range)) {
    const AbstractAirspace &aa = i.GetAirspace();
    Result result = func(aa);
    if (cmp(result, minimum))
      minimum = result;
  }

  return minimum;
}
Exemplo n.º 30
0
static bool
ParseFile(const TCHAR *path, Airspaces &airspaces)
{
  FileLineReader reader(path, ConvertLineReader::AUTO);

  if (!ok1(!reader.error())) {
    skip(1, 0, "Failed to read input file");
    return false;
  }

  AirspaceParser parser(airspaces);
  NullOperationEnvironment operation;

  if (!ok1(parser.Parse(reader, operation)))
    return false;

  airspaces.Optimise();
  return true;
}