/**
   * Finds the position value in the image series map that is closest to the POI. This partially
   * populates an Image object but does NOT include the image id.
   *
   * @param imageSeries
   * @param poi
   * @return
   * @throws IOException
   */
  private void getClosestPosition(ImageSeries imageSeries, Point3d poi100, Image image)
      throws IOException {
    URL u = new URL(assembleAtlasMapURI(imageSeries.imageSeriesId));

    LOG.debug("Atlas map URI: {}", u.toString());

    BufferedReader in = new BufferedReader(new InputStreamReader(u.openStream()));
    String line = null;

    // discard first 2 lines
    in.readLine();
    in.readLine();
    double leastDistance = Double.POSITIVE_INFINITY;
    String[] bestLineSegs = null;
    int count = 2;
    while ((line = in.readLine()) != null) {
      String[] lineSegs = line.split(",");
      Point3d abaCoordinates =
          new Point3d(
              Double.parseDouble(lineSegs[0]),
              Double.parseDouble(lineSegs[1]),
              Double.parseDouble(lineSegs[2]));

      LOG.debug("lineSegs X: {}", lineSegs[0]);
      LOG.debug("lineSegs Y: {}", lineSegs[1]);
      LOG.debug("lineSegs Z: {}", lineSegs[2]);

      if (poi100.distanceSquared(abaCoordinates) < leastDistance) {
        leastDistance = poi100.distanceSquared(abaCoordinates);
        bestLineSegs = lineSegs;
      }

      // debug
      if ((++count % 1000) == 0) {
        System.out.printf("%d%n", count);
      }
    }
    in.close();

    image.imagesCheckedForProximity = count;
    image.abaCoordinates =
        new Point3d(
            Double.parseDouble(bestLineSegs[0]),
            Double.parseDouble(bestLineSegs[1]),
            Double.parseDouble(bestLineSegs[2]));
    image.abaXPixelPosition = Integer.parseInt(bestLineSegs[3]);
    image.abaYPixelPosition = Integer.parseInt(bestLineSegs[4]);
    image.abaImagePosition = bestLineSegs[5];

    LOG.debug("Points examined: {}", count);
  }
  private double calcSingleAsa(int i) {
    Atom atom_i = atoms[i];
    ArrayList<Integer> neighbor_indices = findNeighborIndices(i);
    int n_neighbor = neighbor_indices.size();
    int j_closest_neighbor = 0;
    double radius = probe + radii[i];

    int n_accessible_point = 0;

    for (Point3d point : spherePoints) {
      boolean is_accessible = true;
      Point3d test_point =
          new Point3d(
              point.x * radius + atom_i.getX(),
              point.y * radius + atom_i.getY(),
              point.z * radius + atom_i.getZ());

      int[] cycled_indices = new int[n_neighbor];
      int arind = 0;
      for (int ind = j_closest_neighbor; ind < n_neighbor; ind++) {
        cycled_indices[arind] = ind;
        arind++;
      }
      for (int ind = 0; ind < j_closest_neighbor; ind++) {
        cycled_indices[arind] = ind;
        arind++;
      }

      for (int j : cycled_indices) {
        Atom atom_j = atoms[neighbor_indices.get(j)];
        double r = radii[neighbor_indices.get(j)] + probe;
        double diff_sq = test_point.distanceSquared(new Point3d(atom_j.getCoords()));
        if (diff_sq < r * r) {
          j_closest_neighbor = j;
          is_accessible = false;
          break;
        }
      }
      if (is_accessible) {
        n_accessible_point++;
      }
    }
    return cons * n_accessible_point * radius * radius;
  }