@Override
  public TripRouter instantiateAndConfigureTripRouter(RoutingContext routingContext) {

    TripRouter instance = this.delegateFactory.instantiateAndConfigureTripRouter(routingContext);

    Network network = this.scenario.getNetwork();
    PopulationFactory populationFactory = this.scenario.getPopulation().getFactory();
    ModeRouteFactory modeRouteFactory =
        ((PopulationFactoryImpl) populationFactory).getModeRouteFactory();

    MultiModalConfigGroup multiModalConfigGroup =
        (MultiModalConfigGroup) scenario.getConfig().getModule(MultiModalConfigGroup.GROUP_NAME);
    Set<String> simulatedModes =
        CollectionUtils.stringToSet(multiModalConfigGroup.getSimulatedModes());
    for (String mode : simulatedModes) {

      if (instance.getRegisteredModes().contains(mode)) {
        log.warn("A routing algorithm for " + mode + " is already registered. It is replaced!");
      }

      TravelTime travelTime = this.multimodalTravelTimes.get(mode);
      if (travelTime == null) {
        throw new RuntimeException(
            "No travel time object was found for mode " + mode + "! Aborting.");
      }

      Network subNetwork = multimodalSubNetworks.get(mode);
      if (subNetwork == null) {
        subNetwork = NetworkImpl.createNetwork();
        Set<String> restrictions = new HashSet<>();
        restrictions.add(mode);
        TransportModeNetworkFilter networkFilter = new TransportModeNetworkFilter(network);
        networkFilter.filter(subNetwork, restrictions);
        this.multimodalSubNetworks.put(mode, subNetwork);
      }

      /*
       * We cannot use the travel disutility object from the routingContext since it
       * has not been created for the modes used here.
       */
      TravelDisutility travelDisutility =
          this.travelDisutilityFactory.createTravelDisutility(
              travelTime, scenario.getConfig().planCalcScore());
      LeastCostPathCalculator routeAlgo =
          this.leastCostPathCalculatorFactory.createPathCalculator(
              subNetwork, travelDisutility, travelTime);
      RoutingModule legRouterWrapper =
          DefaultRoutingModules.createNetworkRouter(mode, populationFactory, subNetwork, routeAlgo);
      instance.setRoutingModule(mode, legRouterWrapper);
    }

    return instance;
  }
 @StringSetter("modes")
 private void setStringModes(final String modes) {
   this.modes = CollectionUtils.stringToSet(modes);
 }
 @StringGetter("modes")
 private String getStringModes() {
   return CollectionUtils.setToString(modes);
 }
  private static void makePopulation(
      final int nOfPersonFromEachHome, final String networkFilename, final String filename) {
    Scenario sc = ScenarioUtils.createScenario(ConfigUtils.createConfig());
    Population pop = sc.getPopulation();
    PopulationFactory pf = pop.getFactory();

    Random r = new Random(4711);
    int pId = 0;

    double baseX = 1000;
    double baseY = 1000;
    for (int i = 0; i < nOfPersonFromEachHome; i++) {
      pId++;
      String mode = (r.nextDouble() < 0.50 ? TransportMode.car : TransportMode.pt);

      Person person = pf.createPerson(Id.create(pId, Person.class));
      Plan plan = pf.createPlan();
      Coord homeCoord =
          new Coord(
              (double) (int) (baseX - 450 + 900 * r.nextDouble()),
              (double) (int) (baseY - 450 + 900 * r.nextDouble()));
      fillPlan(plan, mode, r, pf, homeCoord);
      person.addPlan(plan);
      pop.addPerson(person);
    }

    baseX = 1000;
    baseY = 4000;
    for (int i = 0; i < nOfPersonFromEachHome; i++) {
      pId++;
      String mode = (r.nextDouble() < 0.50 ? TransportMode.car : TransportMode.pt);

      Person person = pf.createPerson(Id.create(pId, Person.class));
      Plan plan = pf.createPlan();
      Coord homeCoord =
          new Coord(
              (double) (int) (baseX - 450 + 900 * r.nextDouble()),
              (double) (int) (baseY - 450 + 900 * r.nextDouble()));
      fillPlan(plan, mode, r, pf, homeCoord);
      person.addPlan(plan);
      pop.addPerson(person);
    }

    baseX = 4000;
    baseY = 4000;
    for (int i = 0; i < nOfPersonFromEachHome; i++) {
      pId++;
      String mode = (r.nextDouble() < 0.50 ? TransportMode.car : TransportMode.pt);

      Person person = pf.createPerson(Id.create(pId, Person.class));
      Plan plan = pf.createPlan();
      Coord homeCoord =
          new Coord(
              (double) (int) (baseX - 450 + 900.0 * r.nextDouble()),
              (double) (int) (baseY - 450 + 900.0 * r.nextDouble()));
      fillPlan(plan, mode, r, pf, homeCoord);
      person.addPlan(plan);
      pop.addPerson(person);
    }

    new MatsimNetworkReader(sc.getNetwork()).readFile(networkFilename);
    NetworkImpl carNetwork = NetworkImpl.createNetwork();
    new TransportModeNetworkFilter(sc.getNetwork())
        .filter(carNetwork, CollectionUtils.stringToSet(TransportMode.car));
    new XY2Links(carNetwork, null).run(pop);

    new PopulationWriter(pop, null).write(filename);
  }
/**
 * This class creates facilities for each zone. Available activity options for each facilities are
 * defined based on information from a file containing zonal attributes.
 *
 * <p>So far, no information related to buildings in the zones is available. Therefore, random
 * coordinates within each zone are drawn. The number of buildings is defined by a config parameter.
 * For each of this coordinates, a facility is created and attached to the nearest link with a valid
 * link type. By default, e.g. highways are ignored for this assignment.
 *
 * <p>If a more detailed network is available, this class can be simply re-run to create a new
 * mapping from facilities to links.
 *
 * <p>Note that zonal shp file has to use WGS 84 coordinates!
 *
 * @author cdobler
 */
public class FacilitiesCreator {

  private static final Logger log = Logger.getLogger(FacilitiesCreator.class);

  public static String ttaActivityType = "tta";
  public static String TAZObjectAttributesName = "taz";
  /*
   * For each taz, we also have a unique index. In contrast to the taz values,
   * the indices are enumerated from 0 onwards. The order of the indices equals
   * the order of the taz values.
   */
  public static String indexObjectAttributesName = "index";

  private static String ITM = "EPSG:2039"; // network coding String

  private static String basePath = "";
  private static String networkFile = "";
  private static String zonalAttributesFile = "";
  private static String zonalSHPFile = "";
  private static String facilitiesFile = "";
  private static String facilitiesAttributesFile = "";
  private static String f2lFile = "";

  private static int facilitiesPerZone = 100;
  private static double capacity = 1000000.0;

  private static Set<String> validLinkTypes = CollectionUtils.stringToSet("4,5,6");

  private static String separator = ",";

  private static Random random = MatsimRandom.getLocalInstance();
  private static GeometryFactory geometryFactory = new GeometryFactory();
  private static CoordinateTransformation fromWGS84CoordinateTransformation =
      new GeotoolsTransformation("WGS84", ITM);
  private static CoordinateTransformation toWGS84CoordinateTransformation =
      new GeotoolsTransformation(ITM, "WGS84");

  public static void main(String[] args) {
    try {
      if (args.length > 0) {
        String file = args[0];
        Map<String, String> parameterMap = new XMLParameterParser().parseFile(file);
        String value;

        value = parameterMap.remove("basePath");
        if (value != null) basePath = value;

        value = parameterMap.remove("networkFile");
        if (value != null) networkFile = value;

        value = parameterMap.remove("zonalAttributesFile");
        if (value != null) zonalAttributesFile = value;

        value = parameterMap.remove("separator");
        if (value != null) separator = value;

        value = parameterMap.remove("zonalSHPFile");
        if (value != null) zonalSHPFile = value;

        value = parameterMap.remove("facilitiesFile");
        if (value != null) facilitiesFile = value;

        value = parameterMap.remove("facilitiesAttributesFile");
        if (value != null) facilitiesAttributesFile = value;

        value = parameterMap.remove("f2lFile");
        if (value != null) f2lFile = value;

        value = parameterMap.remove("facilitiesPerZone");
        if (value != null) facilitiesPerZone = Integer.parseInt(value);

        value = parameterMap.remove("validLinkTypes");
        if (value != null) validLinkTypes = CollectionUtils.stringToSet(value);

        for (String key : parameterMap.keySet())
          log.warn("Found parameter " + key + " which is not handled!");
      } else {
        log.error("No input config file was given. Therefore cannot proceed. Aborting!");
        return;
      }

      log.info("loading network ...");
      Config config = ConfigUtils.createConfig();
      config.network().setInputFile(basePath + networkFile);
      Scenario scenario = ScenarioUtils.loadScenario(config);
      log.info("done.\n");

      log.info("loading zonal attributes ...");
      boolean skipHeader = true;
      Map<Integer, Emme2Zone> zonalAttributes =
          new Emme2ZonesFileParser(basePath + zonalAttributesFile, separator).readFile(skipHeader);
      log.info("done.\n");

      log.info("loading zonal shp file ...");
      // use a TreeMap to be deterministic
      Map<Integer, SimpleFeature> zonalShapes = new TreeMap<Integer, SimpleFeature>();
      for (SimpleFeature feature : ShapeFileReader.getAllFeatures(basePath + zonalSHPFile)) {
        zonalShapes.put((Integer) feature.getAttribute(3), feature);
      }
      log.info("done.\n");

      log.info("identify nodes outside the model area ...");
      Set<Id<Node>> externalNodes = getExternalNodes(scenario, zonalShapes);
      log.info("\tfound " + externalNodes.size() + " nodes outside the mapped area");
      log.info("done.\n");

      /*
       * We have to create tta activities BEFORE filtering the network. They might also start
       * and end at highways. We do not know their real start and end positions. The coordinate
       * we know might only be the place where the agents enter the modeled area, which will
       * probably be by using a highway.
       */
      log.info("creating external facilities for tta activities ...");
      createExternalFacilities(scenario, externalNodes);
      log.info("done.\n");

      /*
       * Before creating the internal facilities, we can perform the links filtering.
       */
      log.info("removing links from network where no facilities should be attached to ...");
      List<Id<Link>> linksToRemove = new ArrayList<Id<Link>>();
      for (Link link : scenario.getNetwork().getLinks().values()) {
        String type = ((LinkImpl) link).getType();
        if (!validLinkTypes.contains(type)) linksToRemove.add(link.getId());
      }
      log.info("\tfound " + linksToRemove.size() + " links which do not match the criteria");
      for (Id<Link> linkId : linksToRemove)
        ((NetworkImpl) scenario.getNetwork()).removeLink(linkId);
      log.info(
          "\tprocessed network contains "
              + scenario.getNetwork().getLinks().size()
              + " valid links");
      log.info("done.\n");

      log.info("creating internal facilities ...");
      createInternalFacilities(scenario, zonalAttributes, zonalShapes);
      log.info("done.\n");

      log.info("writing facilities to links mapping to a file ...");
      createAndWriteF2LMapping(scenario);
      log.info("done.\n");

      log.info(
          "writing "
              + scenario.getActivityFacilities().getFacilities().size()
              + " facilities to a file ...");
      FacilitiesWriter facilitiesWriter = new FacilitiesWriter(scenario.getActivityFacilities());
      facilitiesWriter.write(basePath + facilitiesFile);
      new ObjectAttributesXmlWriter(scenario.getActivityFacilities().getFacilityAttributes())
          .writeFile(basePath + facilitiesAttributesFile);
      log.info("done.\n");
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  // Create Facilities inside the simulated area.
  private static void createInternalFacilities(
      Scenario scenario,
      Map<Integer, Emme2Zone> zonalAttributes,
      Map<Integer, SimpleFeature> zonalShapes) {

    // create indices for the zones
    Map<Integer, Integer> indices = new HashMap<Integer, Integer>();
    int index = 0;
    for (Integer taz : zonalShapes.keySet()) {
      indices.put(taz, index);
      index++;
    }

    NetworkImpl network = (NetworkImpl) scenario.getNetwork();

    ActivityFacilities activityFacilities = scenario.getActivityFacilities();
    ObjectAttributes facilitiesAttributes = activityFacilities.getFacilityAttributes();
    ActivityFacilitiesFactory factory = activityFacilities.getFactory();

    for (Entry<Integer, SimpleFeature> entry : zonalShapes.entrySet()) {
      int taz = entry.getKey();
      SimpleFeature feature = entry.getValue();
      Geometry geometry = (Geometry) feature.getDefaultGeometry();
      List<Coord> coordinates = getRandomCoordinatesInZone(facilitiesPerZone, geometry, random);

      int i = 0;
      for (Coord coord : coordinates) {
        Id<ActivityFacility> id = Id.create(taz + "_" + i, ActivityFacility.class);
        ActivityFacility facility = factory.createActivityFacility(id, coord);
        createAndAddActivityOptions(scenario, facility, zonalAttributes.get(taz));
        activityFacilities.addActivityFacility(facility);
        Link link = network.getNearestLinkExactly(coord);
        ((ActivityFacilityImpl) facility).setLinkId(link.getId());
        i++;

        // Also add a tta activity to all facilities.
        ActivityOption activityOption = factory.createActivityOption(ttaActivityType);
        activityOption.addOpeningTime(new OpeningTimeImpl(0 * 3600, 24 * 3600));
        activityOption.setCapacity(capacity);
        facility.addActivityOption(activityOption);

        facilitiesAttributes.putAttribute(id.toString(), TAZObjectAttributesName, taz);
        facilitiesAttributes.putAttribute(
            id.toString(), indexObjectAttributesName, indices.get(taz));
      }
    }
  }

  // Create external Facilities that are used by transit traffic agents.
  private static void createExternalFacilities(Scenario scenario, Set<Id<Node>> externalNodes) {

    ActivityFacilities activityFacilities = scenario.getActivityFacilities();
    ActivityFacilitiesFactory factory = activityFacilities.getFactory();

    /*
     * We check for all OutLinks of all external nodes if they already host a facility. If not,
     * a new facility with a tta ActivityOption will be created and added.
     */
    for (Id<Node> id : externalNodes) {
      Node externalNode = scenario.getNetwork().getNodes().get(id);

      for (Link externalLink : externalNode.getOutLinks().values()) {
        ActivityFacility facility = activityFacilities.getFacilities().get(externalLink.getId());

        // if already a facility exists we have nothing left to do
        if (facility != null) continue;

        /*
         * No Facility exists at that link therefore we create and add a new one.
         */
        double fromX = externalLink.getFromNode().getCoord().getX();
        double fromY = externalLink.getFromNode().getCoord().getY();
        double toX = externalLink.getToNode().getCoord().getX();
        double toY = externalLink.getToNode().getCoord().getY();

        double dX = toX - fromX;
        double dY = toY - fromY;

        double length = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));

        double centerX = externalLink.getCoord().getX();
        double centerY = externalLink.getCoord().getY();

        /*
         * Unit vector that directs with an angle of 90° away from the link.
         */
        double unitVectorX = dY / length;
        double unitVectorY = -dX / length;

        Coord coord = new Coord(centerX + unitVectorX, centerY + unitVectorY);

        facility =
            activityFacilities
                .getFactory()
                .createActivityFacility(
                    Id.create(externalLink.getId().toString(), ActivityFacility.class), coord);
        activityFacilities.addActivityFacility(facility);
        ((ActivityFacilityImpl) facility).setLinkId(externalLink.getId());

        ActivityOption activityOption = factory.createActivityOption(ttaActivityType);
        activityOption.addOpeningTime(new OpeningTimeImpl(0 * 3600, 24 * 3600));
        activityOption.setCapacity(capacity);
        facility.addActivityOption(activityOption);
      }
    }
  }

  /*
   * Creates and adds the possible activities to the facility. The capacities
   * have to defined elsewhere...
   *
   * home	/	no (Activity)	/	0 .. 24
   * work	/	work	/	8 .. 18
   * education	/	study	/	8 .. 18
   * shopping	/	shopping	/	9 .. 19
   * leisure	/	other	6 .. 22
   *
   * Mapping from the zones file:
   *
   * Cultural Areas -> leisure, work
   * Education -> education_university, education_highschool, education_elementaryschool, work
   * Office -> work
   * Shopping -> leisure, work
   * Health Institutions -> work, leisure
   * Urban Cores -> ignore
   * Religions Character -> ignore
   * Transportation -> work, leisure (airport, big train stations, etc.)
   */
  private static void createAndAddActivityOptions(
      Scenario scenario, ActivityFacility facility, Emme2Zone zone) {

    boolean hasHome = false;
    boolean hasWork = false;
    boolean hasEducationUniversity = false;
    boolean hasEducationHighSchool = false;
    boolean hasEducationElementarySchool = false;
    boolean hasShopping = false;
    boolean hasLeisure = false;

    hasHome = zone.hasHome();
    hasWork = zone.hasWork();
    hasEducationUniversity = zone.hasEducationUniversity();
    hasEducationHighSchool = zone.hasEducationHighSchool();
    hasEducationElementarySchool = zone.hasEducationElementarySchool();
    hasShopping = zone.hasShopping();
    hasLeisure = zone.hasLeisure();
    //		if (zone.POPULATION > 0) { hasHome = true; }
    //		if (zone.CULTURAL > 0) { hasLeisure = true; hasWork = true; }
    //		if (zone.EDUCATION == 1) { hasEducationUniversity = true; hasWork = true; }
    //		if (zone.EDUCATION == 2) { hasEducationHighSchool = true; hasWork = true; }
    //		if (zone.EDUCATION == 3) { hasEducationElementarySchool = true; hasWork = true; }
    //		if (zone.OFFICE > 0) { hasWork = true; }
    //		if (zone.SHOPPING > 0) { hasShopping = true; hasWork = true; }
    //		if (zone.HEALTH > 0) { hasLeisure = true; hasWork = true; }
    //		if (zone.TRANSPORTA > 0) { hasLeisure = true; hasWork = true; }
    //		if (zone.EMPL_TOT > 0) { hasWork = true; }

    // "Other" activities - should be possible in every zone.
    //		hasLeisure = true;

    // "Shopping" activities - should be possible in every zone.
    //		hasShopping = true;

    ActivityOption activityOption;

    ActivityFacilitiesFactory factory = scenario.getActivityFacilities().getFactory();

    if (hasHome) {
      activityOption = factory.createActivityOption("home");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(0 * 3600, 24 * 3600));
      activityOption.setCapacity(capacity);
    }

    if (hasWork) {
      activityOption = factory.createActivityOption("work");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(8 * 3600, 18 * 3600));
      activityOption.setCapacity(capacity);
    }

    if (hasEducationUniversity) {
      activityOption = factory.createActivityOption("education_university");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(9 * 3600, 18 * 3600));
      activityOption.setCapacity(capacity);
    }

    if (hasEducationHighSchool) {
      activityOption = factory.createActivityOption("education_highschool");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(8 * 3600, 16 * 3600));
      activityOption.setCapacity(capacity);
    }

    if (hasEducationElementarySchool) {
      activityOption = factory.createActivityOption("education_elementaryschool");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(8 * 3600, 14 * 3600));
      activityOption.setCapacity(capacity);
    }

    if (hasShopping) {
      activityOption = factory.createActivityOption("shopping");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(9 * 3600, 19 * 3600));
      activityOption.setCapacity(capacity);
    }

    if (hasLeisure) {
      activityOption = factory.createActivityOption("leisure");
      facility.addActivityOption(activityOption);
      activityOption.addOpeningTime(new OpeningTimeImpl(6 * 3600, 22 * 3600));
      activityOption.setCapacity(capacity);
    }
  }

  // create f2l mapping file
  private static void createAndWriteF2LMapping(Scenario scenario) {
    log.info("creating f2l mapping and write it to a file ...");
    try {
      BufferedWriter bw = IOUtils.getBufferedWriter(basePath + f2lFile);

      // write Header
      bw.write("fid" + "\t" + "lid" + "\n");

      for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) {
        bw.write(facility.getId().toString() + "\t" + facility.getLinkId().toString() + "\n");
      }

      bw.flush();
      bw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    log.info("done.");
  }

  // iterate over all nodes to find all external nodes
  private static final Set<Id<Node>> getExternalNodes(
      Scenario scenario, Map<Integer, SimpleFeature> zonalShapes) {

    Set<Id<Node>> externalNodes = new TreeSet<Id<Node>>();
    for (Node node : scenario.getNetwork().getNodes().values()) {
      Coord pointCoord = toWGS84CoordinateTransformation.transform(node.getCoord());
      Point point =
          geometryFactory.createPoint(new Coordinate(pointCoord.getX(), pointCoord.getY()));

      SimpleFeature pointZone = null;
      for (SimpleFeature zone : zonalShapes.values()) {
        Geometry polygon = (Geometry) zone.getDefaultGeometry();
        if (polygon.contains(point)) {
          pointZone = zone;
          break;
        }
      }

      // if the point is not contained in any Zone it is an external node.
      if (pointZone == null) externalNodes.add(node.getId());
    }

    return externalNodes;
  }

  private static final List<Coord> getRandomCoordinatesInZone(
      int numCoordinates, Geometry zoneGeometry, Random random) {

    /*
     * Get the bounding box of the geometry. The returned geometry is a polygon with the following points:
     * (minx, miny), (maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny).
     */
    Geometry envelope = zoneGeometry.getEnvelope();
    Coordinate[] coords = envelope.getCoordinates();

    double minX = Double.MAX_VALUE;
    double maxX = Double.MIN_VALUE;
    double minY = Double.MAX_VALUE;
    double maxY = Double.MIN_VALUE;

    for (Coordinate coord : coords) {
      if (coord.x < minX) minX = coord.x;
      if (coord.x > maxX) maxX = coord.x;
      if (coord.y < minY) minY = coord.y;
      if (coord.y > maxY) maxY = coord.y;
    }

    List<Coord> list = new ArrayList<Coord>();

    // loop until a valid point was found and is returned
    while (list.size() < numCoordinates) {
      double x = minX + random.nextDouble() * (maxX - minX);
      double y = minY + random.nextDouble() * (maxY - minY);

      Point point = geometryFactory.createPoint(new Coordinate(x, y));
      if (zoneGeometry.contains(point))
        list.add(fromWGS84CoordinateTransformation.transform(new Coord(x, y)));
    }
    return list;
  }
}
  public static void main(String[] args) {
    try {
      if (args.length > 0) {
        String file = args[0];
        Map<String, String> parameterMap = new XMLParameterParser().parseFile(file);
        String value;

        value = parameterMap.remove("basePath");
        if (value != null) basePath = value;

        value = parameterMap.remove("networkFile");
        if (value != null) networkFile = value;

        value = parameterMap.remove("zonalAttributesFile");
        if (value != null) zonalAttributesFile = value;

        value = parameterMap.remove("separator");
        if (value != null) separator = value;

        value = parameterMap.remove("zonalSHPFile");
        if (value != null) zonalSHPFile = value;

        value = parameterMap.remove("facilitiesFile");
        if (value != null) facilitiesFile = value;

        value = parameterMap.remove("facilitiesAttributesFile");
        if (value != null) facilitiesAttributesFile = value;

        value = parameterMap.remove("f2lFile");
        if (value != null) f2lFile = value;

        value = parameterMap.remove("facilitiesPerZone");
        if (value != null) facilitiesPerZone = Integer.parseInt(value);

        value = parameterMap.remove("validLinkTypes");
        if (value != null) validLinkTypes = CollectionUtils.stringToSet(value);

        for (String key : parameterMap.keySet())
          log.warn("Found parameter " + key + " which is not handled!");
      } else {
        log.error("No input config file was given. Therefore cannot proceed. Aborting!");
        return;
      }

      log.info("loading network ...");
      Config config = ConfigUtils.createConfig();
      config.network().setInputFile(basePath + networkFile);
      Scenario scenario = ScenarioUtils.loadScenario(config);
      log.info("done.\n");

      log.info("loading zonal attributes ...");
      boolean skipHeader = true;
      Map<Integer, Emme2Zone> zonalAttributes =
          new Emme2ZonesFileParser(basePath + zonalAttributesFile, separator).readFile(skipHeader);
      log.info("done.\n");

      log.info("loading zonal shp file ...");
      // use a TreeMap to be deterministic
      Map<Integer, SimpleFeature> zonalShapes = new TreeMap<Integer, SimpleFeature>();
      for (SimpleFeature feature : ShapeFileReader.getAllFeatures(basePath + zonalSHPFile)) {
        zonalShapes.put((Integer) feature.getAttribute(3), feature);
      }
      log.info("done.\n");

      log.info("identify nodes outside the model area ...");
      Set<Id<Node>> externalNodes = getExternalNodes(scenario, zonalShapes);
      log.info("\tfound " + externalNodes.size() + " nodes outside the mapped area");
      log.info("done.\n");

      /*
       * We have to create tta activities BEFORE filtering the network. They might also start
       * and end at highways. We do not know their real start and end positions. The coordinate
       * we know might only be the place where the agents enter the modeled area, which will
       * probably be by using a highway.
       */
      log.info("creating external facilities for tta activities ...");
      createExternalFacilities(scenario, externalNodes);
      log.info("done.\n");

      /*
       * Before creating the internal facilities, we can perform the links filtering.
       */
      log.info("removing links from network where no facilities should be attached to ...");
      List<Id<Link>> linksToRemove = new ArrayList<Id<Link>>();
      for (Link link : scenario.getNetwork().getLinks().values()) {
        String type = ((LinkImpl) link).getType();
        if (!validLinkTypes.contains(type)) linksToRemove.add(link.getId());
      }
      log.info("\tfound " + linksToRemove.size() + " links which do not match the criteria");
      for (Id<Link> linkId : linksToRemove)
        ((NetworkImpl) scenario.getNetwork()).removeLink(linkId);
      log.info(
          "\tprocessed network contains "
              + scenario.getNetwork().getLinks().size()
              + " valid links");
      log.info("done.\n");

      log.info("creating internal facilities ...");
      createInternalFacilities(scenario, zonalAttributes, zonalShapes);
      log.info("done.\n");

      log.info("writing facilities to links mapping to a file ...");
      createAndWriteF2LMapping(scenario);
      log.info("done.\n");

      log.info(
          "writing "
              + scenario.getActivityFacilities().getFacilities().size()
              + " facilities to a file ...");
      FacilitiesWriter facilitiesWriter = new FacilitiesWriter(scenario.getActivityFacilities());
      facilitiesWriter.write(basePath + facilitiesFile);
      new ObjectAttributesXmlWriter(scenario.getActivityFacilities().getFacilityAttributes())
          .writeFile(basePath + facilitiesAttributesFile);
      log.info("done.\n");
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }