Search code examples
androidosmdroid

OSMdroid: How best to achieve an offset map centre


My Android app has two flavours, one for the Google Play Store, utilising Google Maps (v2) and another for the Amazon App Store, utilizing the greatness of open source that is osmdroid. Thank you for your continued efforts with the library, I just updated to v5.6.4 from a much earlier version and it was great to see the progress that has been made.

My question

I have been adopting Material design over the last year and a half and recently moved to a card interface when setting up a 'spot' (the main model in my app). The card covers half the map on a phone form factor - see screenshots:

When moving the map...

osmdroid-question-moving

When the map stops...

osmdroid-question

Note the difference between the polyline apex coming from what osmdroid believes to be the center and what would be the centre to the user when the card animates to cover part of the map

Notice that the map centre (denoted by the white place icon when the map stops) is not in the centre, rather it is shifted up by the height of the card and padding. Within Google Maps (v2) this behaviour can be achieved using the setPadding(left, top, right, bottom). How should a similar effect be achieved in osmdroid?

What I have tried

To date, I have taken the approach of overriding getCenter() with code akin to;

 /**
   * @see com.brantapps.polaris.api.Mappable#getCenter()
   */
  @Override
  public GeoPoint getCenter() {
    // Where is the current centre
    final IGeoPoint center = mapView.getMapCenter();

    // Calculate the top-left and bottom-right positions
    IGeoPoint tl = mapView.getProjection().fromPixels(0, 0);
    IGeoPoint br = mapView.getProjection().fromPixels(mapView.getWidth(), mapView.getHeight());

    // reset the centre with the px per degree calc.
    double newLon = leftPxOffset * (br.getLongitude() - tl.getLongitude()) / mapView.getWidth() + center.getLongitude();
    double newLat = bottomPxOffset * (br.getLatitude() - tl.getLatitude()) / mapView.getHeight() + center.getLatitude();

    return GeoPointHelper.fromIGeoPointToPolarisGeoPoint(new org.osmdroid.util.GeoPoint(newLat, newLon));
  }

...where leftPxOffset and bottomPxOffset represent the movement up and slight to the right of centre (the slight right is a side-effect from the Google logo and my maps abstraction library).

It kind of works, but not great. Does the community have a better idea? Am I missing something in the SDK that takes care of these calculations?


Solution

  • ok, so I have a working implementation that looks like this;

    private GeoPoint adjustCentreByPadding(final double latitude, final double longitude, boolean negate) {
        final int newY = negate ? (int) (mapView.getHeight()/2-bottomPxOffset) : (int) (mapView.getHeight()/2+bottomPxOffset);
        mapView.getController().setCenter(new org.osmdroid.util.GeoPoint(latitude, longitude));
        final IGeoPoint offset = mapView.getProjection().fromPixels(mapView.getWidth()/2, newY);
        return KindleGeoPointHelper.fromIGeoPointToRoverGeoPoint(offset);
      }
    

    Snippet 1: new adjust for padding method

    I'm not 100% on why it's working as it is right now but essentially, I call this method from both my getCenter() and setCenter(...) overrides which look like this respectively;

      /**
       * @see com.brantapps.polaris.api.Mappable#getCenter()
       */
      @Override
      public GeoPoint getCenter() {
        return adjustCentreByPadding(mapView.getMapCenter().getLatitude(), mapView.getMapCenter().getLongitude(), true);
      }
    

    Snippet 2: getCenter() implementation

      /**
       * @see com.brantapps.polaris.api.Mappable#setCenter(GeoPoint)
       */
      @Override
      public void setCenter(final GeoPoint geoPoint) {
        final GeoPoint adjustedCenter = adjustCentreByPadding(geoPoint.getLatitude(), geoPoint.getLongitude(), false);
        mapView.getController().setCenter(new org.osmdroid.util.GeoPoint(adjustedCenter.getLatitude(), adjustedCenter.getLongitude()));
      }
    

    Snippet 3: setCenter(...) implementation

    Notice the "negate" term which is the bit I need to figure out. As I observe it, when shifting the map "up" owing to the card overlay (i.e. the setCenter(...) override) then I add pixels to the height projection. When I subsequently call getCenter() I subtract pixels from the height projection.

    The KindleGeoPointHelper class is an artifact of the Polaris abstraction library I have placed over Google and OSMDroid and can be ignored.