MyTracks 読み (7)

My Location で現在地な測位をする場合の制御について気になったので中身確認。
MyTracksMap のオプションメニュー選択なあたりの処理が以下。

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case MyTracksConstants.MENU_MY_LOCATION: {
        Location loc = MyTracks.getInstance().getCurrentLocation();
        if (loc != null) {
          currentLocation = loc;
          setVariation(currentLocation);
          mapOverlay.setMyLocation(loc);
          mapView.invalidate();
          GeoPoint geoPoint = MyTracksUtils.getGeoPoint(loc);
          MapController controller = mapView.getController();
          controller.animateTo(geoPoint);
          if (mapView.getZoomLevel() < 18) {
            controller.setZoom(18);
          }
          keepMyLocationVisible = true;
        }
        return true;
      }
      case MyTracksConstants.MENU_TOGGLE_LAYERS: {
        toggleLayer();
        return true;
      }
    }
    return super.onOptionsItemSelected(item);
  }

getInstance って何だよ、と言いつつ MyTracks の中身を見てみたら以下な記述が

  /**
   * Singleton instance
   */
  private static MyTracks instance = null;

onCreate にて

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    Log.d(MyTracksConstants.TAG, "MyTracks.onCreate");
    super.onCreate(savedInstanceState);
    instance = this;

みたいな事をしてる模様。getCurrentLocation というメソドも参考になります。

  public Location getCurrentLocation() {
    // TODO: Let's look at more advanced algorithms to determine the best
    // current location.
    LocationManager locationManager =
        (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    if (locationManager == null) {
      return null;
    }
    final long maxAgeMilliSeconds = 1000 * 60 * 1;  // 1 minute
    final long maxAgeNetworkMilliSeconds = 1000 * 60 * 10;  // 10 minutes
    final long now = System.currentTimeMillis();
    Location loc = locationManager.getLastKnownLocation(
        MyTracksConstants.GPS_PROVIDER);
    if (loc == null || loc.getTime() < now - maxAgeMilliSeconds) {
      // We don't have a recent GPS fix, just use cell towers if available
      loc = locationManager.getLastKnownLocation(
          LocationManager.NETWORK_PROVIDER);
      if (loc == null || loc.getTime() < now - maxAgeNetworkMilliSeconds) {
        // We don't have a recent cell tower location, let the user know:
        Toast.makeText(this, getString(R.string.status_no_location),
            Toast.LENGTH_LONG).show();
        return null;
      } else {
       // Let the user know we have only an approximate location:
       Toast.makeText(this, getString(R.string.status_approximate_location),
           Toast.LENGTH_LONG).show();
      }
    }
    return loc;
  }

LocationManager なソレを取得して測位して時刻で判定しております。これも GPS 扱うソレ的には定型的に使える記述だと思います。
で、オプションメニューな処理の記述に戻るんですが、keepMyLocationVisible というスイッチが微妙に気になったので確認してみました。上から順にソースをナメてみます。まず定義。

  /**
   * True if the map should be scrolled so that the pointer is always in the
   * visible area.
   */
  private boolean keepMyLocationVisible;

これが true ならマーカは見える範囲に常にある、と。次に出てくるのは onSaveInstanceState と onRestoreInstanceState になります。これ、Activity のライフサイクルという部分でおさらいが必要だな。てか、これって notepad tutorial に出てきてたような気がするけど違うかな。
別途保存しているデータの確認をしておくという事にしておいて次。

  /**
   * Moves the location pointer to the current location and center the map if
   * the current location is outside the visible area.
   */
  private void showCurrentLocation() {
    if (currentLocation == null || mapOverlay == null || mapView == null) {
      return;
    }
    mapOverlay.setMyLocation(currentLocation);
    mapView.invalidate();
    if (keepMyLocationVisible && !locationIsVisible(currentLocation)) {
      MapController controller = mapView.getController();
      GeoPoint geoPoint = MyTracksUtils.getGeoPoint(currentLocation);
      controller.animateTo(geoPoint);
    }
  }

これ、マーカの位置だけ変えて表示領域の外に出た判定がダウトだった (かつ keepMyLocationVisible) 場合にセンター表示みたいな形を取っているのかな。この方が色々な意味で楽 (機械てきには)。
Overlay 的には描画はお任せなんですが、WebKit でナニする場合どうしたものやら。つっても基本的に javascript:reloadFunction() するしかないのかどうなのか。
次。

  /**
   * Zooms and pans the map so that the given track is visible.
   *
   * @param track a given track
   */
  public void showTrack(Track track) {
    if (track == null || mapView == null || track.getNumberOfPoints() < 2) {
      return;
    }
    int latSpanE6 = track.getTop() - track.getBottom();
    int lonSpanE6 = track.getRight() - track.getLeft();
    if (latSpanE6 > 0 && latSpanE6 < 180E6 && lonSpanE6 > 0
        && lonSpanE6 < 180E6) {
      keepMyLocationVisible = false;
      GeoPoint center = new GeoPoint(
          track.getBottom() + latSpanE6 / 2,
          track.getLeft() + lonSpanE6 / 2);
      if (MyTracksUtils.isValidGeoPoint(center)) {
        mapView.getController().setCenter(center);
        mapView.getController().zoomToSpan(latSpanE6, lonSpanE6);
      }
    }
  }

あ、ここは保存されている情報をナニするので keepMyLocationVisible を false にしているのか。次の showWaypoint メソドも同様。で、最後に本題のオプションメニューな処理で true にしてる処理が出てくる模様。
で、何度か出てくる

   if (keepMyLocationVisible && !locationIsVisible(currentLocation)) {

な条件式なんですが、locationIsVisible についてもなかなか参考になる、と思いつつ GeoRect なクラスがおそらく使えないはずなので、このあたりの作戦を練っておく必要あり。