Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Offline tiles are not being rendered if they don't include CJK glyphs #14271

Closed
LukasPaczos opened this issue Mar 29, 2019 · 6 comments · Fixed by #15253
Closed

Offline tiles are not being rendered if they don't include CJK glyphs #14271

LukasPaczos opened this issue Mar 29, 2019 · 6 comments · Fixed by #15253
Assignees
Labels
Android Mapbox Maps SDK for Android high priority
Milestone

Comments

@LukasPaczos
Copy link
Member

LukasPaczos commented Mar 29, 2019

If the offline region definition excludes CJK glyphs, the downloaded tiles are not being rendered.
Example region:

  • Bounds: [LatLng(26.644858, 118.17), LatLng(26.63, 118.181632)]
  • MinZoom: 14
  • MaxZoom: 17
  • Style: MAPBOX_STREETS
  • Include ideographs: false

The issue is present even if the client-site ideographs generation is enabled.
There are no warning/error logs emitted from core.

Full reproducible example:

public class SimpleMapActivity extends AppCompatActivity {

  private MapView mapView;
  private OfflineRegion offlineRegion;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_map_simple);
    mapView = findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);

    LatLngBounds bounds = new LatLngBounds.Builder()
      .include(new LatLng(26.644858, 118.17))
      .include(new LatLng(26.63, 118.181632))
      .build();
    double minZoom = 14;
    double maxZoom = 17;
    float pixelRatio = this.getResources().getDisplayMetrics().density;
    OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
      Style.MAPBOX_STREETS, bounds, minZoom, maxZoom, pixelRatio, false);

    OfflineManager offlineManager = OfflineManager.getInstance(this);
    byte[] metadata = OfflineUtils.convertRegionName("test_region");
    offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
      @Override
      public void onCreate(OfflineRegion offlineRegion) {
        SimpleMapActivity.this.offlineRegion = offlineRegion;
        launchDownload();
      }

      @Override
      public void onError(String error) {
      }
    });
  }

  private void launchDownload() {
    offlineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
      @Override
      public void onStatusChanged(OfflineRegionStatus status) {
        if (status.isComplete()) {
          offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
          offlineRegion.setObserver(null);
          Mapbox.setConnected(false);
          Toast.makeText(SimpleMapActivity.this, "Region downloaded", Toast.LENGTH_SHORT).show();
          mapView.getMapAsync(mapboxMap -> mapboxMap.setOfflineRegionDefinition(offlineRegion.getDefinition()));
        }
      }

      @Override
      public void onError(OfflineRegionError error) {
      }

      @Override
      public void mapboxTileCountLimitExceeded(long limit) {
      }
    });

    offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);
  }


  @Override
  protected void onStart() {
    super.onStart();
    mapView.onStart();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mapView.onResume();
  }

  @Override
  protected void onPause() {
    super.onPause();
    mapView.onPause();
  }

  @Override
  protected void onStop() {
    super.onStop();
    mapView.onStop();
  }

  @Override
  public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
    Mapbox.setConnected(true);
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mapView.onSaveInstanceState(outState);
  }
}
@LukasPaczos LukasPaczos added the Core The cross-platform C++ core, aka mbgl label Mar 29, 2019
@LukasPaczos LukasPaczos changed the title Offline tiles are not being rendered render if they don't include CJK glyphs Offline tiles are not being rendered if they don't include CJK glyphs Mar 29, 2019
@tmpsantos tmpsantos self-assigned this Jun 18, 2019
@LukasPaczos
Copy link
Member Author

Looks like this is not an issue on iOS/macOS - #14952, so I retested and here are the results.

Without CJK glyphs

OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
  Style.MAPBOX_STREETS, bounds, minZoom, maxZoom, pixelRatio, false);

ezgif com-video-to-gif (55)
Only the tiles that do not contain the glyphs seem to be downloaded and rendered correctly. The downloaded region weighs around 8 MB, but it obviously misses a lot of stuff.

Including CJK glyphs

OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
  Style.MAPBOX_STREETS, bounds, minZoom, maxZoom, pixelRatio, true);

ezgif com-video-to-gif (56)

The download takes a whole lot longer and weighs 60 MB.

Our default font family for local CJK glyphs generation is "sans-serif".

Updated example code:

public class SimpleMapActivity extends AppCompatActivity {

  private MapView mapView;
  private OfflineRegion offlineRegion;
  private TextView tv;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    Mapbox.setConnected(false);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_map_simple);
    mapView = findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);

    tv = new TextView(this);
    ((RelativeLayout) mapView.getParent()).addView(tv);

    OfflineManager offlineManager = OfflineManager.getInstance(this);
    offlineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() {
      @Override
      public void onList(OfflineRegion[] offlineRegions) {
        if (offlineRegions.length > 0) {
          tv.setText("region present");
          Logger.d("OfflineTest", "region present");
          mapView.getMapAsync(mapboxMap -> mapboxMap.setOfflineRegionDefinition(offlineRegions[0].getDefinition()));
        } else {
          LatLngBounds bounds = new LatLngBounds.Builder()
            .include(new LatLng(26.644858, 118.17))
            .include(new LatLng(26.63, 118.181632))
            .build();
          double minZoom = 14;
          double maxZoom = 17;

          float pixelRatio = SimpleMapActivity.this.getResources().getDisplayMetrics().density;
          OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
            Style.MAPBOX_STREETS, bounds, minZoom, maxZoom, pixelRatio, true);

          byte[] metadata = OfflineUtils.convertRegionName("test_region");
          offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
            @Override
            public void onCreate(OfflineRegion offlineRegion) {
              Logger.d("OfflineTest", "starting download");
              SimpleMapActivity.this.offlineRegion = offlineRegion;
              Mapbox.setConnected(null);
              launchDownload();
            }

            @Override
            public void onError(String error) {
            }
          });
        }
      }

      @Override
      public void onError(String error) {
      }
    });
  }

  private void launchDownload() {
    offlineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
      @Override
      public void onStatusChanged(OfflineRegionStatus status) {
        tv.setText(status.getCompletedResourceCount() + "/" + status.getRequiredResourceCount());
        Logger.d("OfflineTest", status.getCompletedResourceCount() + "/" + status.getRequiredResourceCount());
        if (status.isComplete()) {
          offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
          offlineRegion.setObserver(null);
          Mapbox.setConnected(false);
          Toast.makeText(SimpleMapActivity.this, "Region downloaded", Toast.LENGTH_SHORT).show();
          mapView.getMapAsync(mapboxMap -> mapboxMap.setOfflineRegionDefinition(offlineRegion.getDefinition()));
        }
      }

      @Override
      public void onError(OfflineRegionError error) {

      }

      @Override
      public void mapboxTileCountLimitExceeded(long limit) {
      }
    });

    offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);
  }


  @Override
  protected void onStart() {
    super.onStart();
    mapView.onStart();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mapView.onResume();
  }

  @Override
  protected void onPause() {
    super.onPause();
    mapView.onPause();
  }

  @Override
  protected void onStop() {
    super.onStop();
    mapView.onStop();
  }

  @Override
  public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
    Mapbox.setConnected(true);
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mapView.onSaveInstanceState(outState);
  }
}

/cc @chloekraw @tmpsantos

@tmpsantos
Copy link
Contributor

tmpsantos commented Jul 10, 2019

@LukasPaczos do we have system fonts enabled on this test?

@LukasPaczos
Copy link
Member Author

Our default font family for local CJK glyphs generation is "sans-serif".

@tmpsantos is this the answer you're looking for? Otherwise, I'm not sure how to verify that.

@pozdnyakov
Copy link
Contributor

This issue is caused by the fact that map renderer on Android is not given a non-empty local font family. This branch contains the repro example and the naive fix on top.

@pozdnyakov pozdnyakov added Android Mapbox Maps SDK for Android and removed Core The cross-platform C++ core, aka mbgl labels Jul 30, 2019
@pozdnyakov
Copy link
Contributor

Assigning to @LukasPaczos for the appropriate fix in java.

@LukasPaczos
Copy link
Member Author

Great find, thanks for debugging this @pozdnyakov!

@LukasPaczos LukasPaczos added this to the release-queso milestone Jul 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android high priority
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants