Codename One ships a modern mapping API in the com.codename1.maps package built around two components:

  • MapView — a pure-vector map drawn entirely through the Codename One Graphics pipeline. It never embeds a native peer, so it composes cleanly with the rest of your UI (dialogs, lists and overlays draw over it) and behaves identically on every platform, including the simulator and the web.

  • NativeMap — a map backed by a native provider (Apple MapKit, Google Maps, …​) when one is wired into the build, and which transparently falls back to a MapView when no provider is available (the simulator, devices without the selected provider, or builds that didn’t opt in).

Both components implement the same MapSurface interface, so application code is identical regardless of which one — or which provider — is backing the map.

The legacy tile-based MapComponent and the external codenameone-google-maps cn1lib are deprecated in favor of this API.

A first map

The pure-vector MapView renders real maps with zero configuration and no API key — by default it shows the free, keyless OpenFreeMap vector basemap (real OpenStreetMap data):

MapView map = new MapView();
map.moveCamera(new LatLng(37.7749, -122.4194), 12);
form.add(BorderLayout.CENTER, map);

LatLng is the immutable WGS84 coordinate value type used throughout the API. The camera is described by a center LatLng and a fractional zoom level (the standard slippy-map scale where each whole increment doubles the scale).

The pure-vector MapView rendering OpenStreetMap data

The MapSurface API

Every map — vector or native — exposes the same operations through MapSurface:

// Camera
map.setCameraPosition(new CameraPosition(new LatLng(48.8566, 2.3522), 11));
map.moveCamera(new LatLng(48.8566, 2.3522), 11);
map.setZoom(13);
map.fitBounds(new MapBounds(new LatLng(48.8, 2.2), new LatLng(48.9, 2.4)), 24);

// Markers
Marker m = map.addMarker(new MarkerOptions(new LatLng(48.8584, 2.2945))
        .icon(pinImage)
        .title("Eiffel Tower")
        .anchor(0.5f, 1.0f)
        .onClick(e -> showDetails()));
map.removeMarker(m);

// Shapes
map.addPolyline(new Polyline(routePoints).setStrokeColor(0xff5722).setStrokeWidth(6));
map.addPolygon(new Polygon(areaPoints).setFillColor(0x803f51b5).setStrokeColor(0x3f51b5));
map.addCircle(new Circle(new LatLng(48.85, 2.35), 500).setFillColor(0x804caf50));
map.clearMapObjects();

// Coordinate conversion and bounds
Point pixel = map.latLngToScreen(new LatLng(48.85, 2.35));
LatLng coord = map.screenToLatLng(120, 240);
MapBounds visible = map.getVisibleRegion();

// Events
map.addTapListener((surface, location, x, y) -> placeMarker(location));
map.addLongPressListener((surface, location, x, y) -> contextMenu(location));
map.addCameraChangeListener((surface, camera) -> persist(camera));

Marker icons are supplied as EncodedImage and anchored in normalized (u, v) image space (0.5, 1.0 puts the pin tip on the location). When no icon is given, a marker draws the standard Material Design map pin. Polygon and circle fills accept an 0xAARRGGBB color so you can make them translucent.

Markers drawn with the default Material map pin

Routes that follow the road

A polyline joins the coordinates you hand it with straight lines — it knows nothing about roads. Passing two endpoints to addPolyline therefore draws a single straight segment across the map, which is seldom what a navigation screen wants. Working out the road geometry between two points is a separate job, done by a routing service, and that’s what the com.codename1.maps.routing package is for:

MapView map = new MapView();
form.add(BorderLayout.CENTER, map);

// Draws the driving route along the actual roads and frames it.
Routing.showRoute(map, origin, destination);

Routing.showRoute(…​) fetches the route, draws it and moves the camera to frame it. Like the keyless OpenFreeMap basemap, it works with no API key and no signup: routing runs over OpenStreetMap data through the built-in OsrmRouteService.

Routing is asynchronous — the call returns immediately and the line appears when the service answers. Use Routing.findRoute(…​) when you want the result itself, for example to style the line or show the distance and the estimated time:

RouteRequest request = new RouteRequest(origin, destination)
        .addWaypoint(new LatLng(38.8899, -77.0091))
        .setTravelMode(TravelMode.DRIVING);

Routing.findRoute(request, new RouteCallback() {
    @Override
    public void routesFound(java.util.List routes) {
        Route best = (Route)routes.get(0);
        map.addPolyline(best.toPolyline().setStrokeColor(0xff5722).setStrokeWidth(6));
        map.fitBounds(best.getBounds(), CN.convertToPixels(4));
        label.setText((int)(best.getDistanceMeters() / 1000) + " km, "
                + (int)(best.getDurationSeconds() / 60) + " min");
    }

    @Override
    public void routeFailed(String message, Throwable error) {
        label.setText(message);
    }
});

A Route carries the drawable geometry (toPolyline()), the bounds to frame it with, the total distance and duration, and the RouteLeg/RouteStep breakdown behind a turn-by-turn list. A RouteRequest adds waypoints and a TravelMode (DRIVING, WALKING, CYCLING).

The default endpoint is OSRM’s public demo server. It exists for development and demos: no SLA, rate limited, and it hosts the car profile, so walking and cycling requests are routed over driving data there. Before shipping, point the service at your own OSRM instance (or any OSRM-compatible endpoint) — nothing else in your code changes:
// Your own OSRM instance, or any OSRM-compatible endpoint.
Routing.setService(new OsrmRouteService("https://osrm.example.com"));

To route through a different provider entirely — Google Directions, Mapbox Directions, GraphHopper, your own server — implement RouteService and install it the same way. Every provider of that kind returns the route as an encoded polyline, which Polyline.fromEncoded(geometry) turns straight into something drawable (PolylineCodec also handles the polyline6 variant used by Valhalla).

Tile sources and styles (MapView)

MapView pulls its tiles from a pluggable com.codename1.maps.vector.TileSource:

  • MvtTileSource — networked Mapbox Vector Tiles (.pbf/.mvt). MvtTileSource.openFreeMap() is the free, keyless OpenStreetMap-based default (its tile URLs are versioned, so it resolves them from OpenFreeMap’s TileJSON automatically). Most other hosted vector basemaps require an API key supplied via setApiKey(…​) and referenced as {key} in the URL template; a TileJSON endpoint (a URL with no {z} token) is resolved automatically.

  • RasterTileSource — networked XYZ image tiles. RasterTileSource.openStreetMap() is a keyless raster alternative.

  • BundledTileSource — tiles bundled into the app as resources, for fully offline maps. The screenshot tests in this repo use this against a small fixture set so the renders are deterministic and network-free; it’s not how you ship a real map.

  • DemoTileSource — a self-contained synthetic tile set (no network), handy for demos and deterministic tests.

Connecting to real map data

For a real app you point MapView at a hosted tile service. The simplest keyless option is OpenFreeMap (OpenStreetMap-based vector tiles); for branded styles or higher quotas use a keyed provider such as MapTiler or your own self-hosted Protomaps/TileServer GL endpoint:

// Keyless, zero-config vector basemap (OpenStreetMap data via OpenFreeMap):
MapView vector = new MapView(MvtTileSource.openFreeMap(), MapStyle.light());

// A keyed hosted provider (e.g. MapTiler). {z}/{x}/{y} are the XYZ tile
// coordinates and {key} is substituted from setApiKey(...):
MapView branded = new MapView(
        new MvtTileSource("https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key={key}", 0, 14)
                .setApiKey(apiKey),
        MapStyle.dark());

// A keyless raster (image-tile) basemap, if you don't need vector styling:
MapView raster = new MapView(RasterTileSource.openStreetMap());

If the URL has no {z} token it’s treated as a TileJSON document and the real tile-URL template (and min/max zoom) are resolved from it automatically, so you can paste a provider’s TileJSON URL directly.

To bundle your own offline tiles instead, drop .mvt/.pbf files into the app’s resources under a {z}/{x}/{y} layout and point BundledTileSource at them — that’s exactly what the test fixtures do.

Vector tiles are painted according to a MapStyle. The built-in MapStyle.light() and MapStyle.dark() cover a usable basemap; MapStyle.fromJson(json) parses a subset of the MapLibre GL style specification (background/fill/line/symbol layers, zoom-stop interpolation, simple ["==", key, value] filters).

The same vector data rendered with the built-in dark style

Native maps and providers

NativeMap renders through a native map SDK when the build wires one in:

NativeMap map = new NativeMap(new LatLng(37.7749, -122.4194), 12);
map.addMarker(new MarkerOptions(new LatLng(37.7749, -122.4194)).title("San Francisco"));
form.add(BorderLayout.CENTER, map);

if (!map.isNativeMap()) {
    // Running on the simulator (or a build without a provider) -> vector fallback.
}
NativeMap backed by Apple MapKit (ios.maps.provider=apple)

Which provider (if any) backs the map is decided entirely by build hints — the public API never names a provider, so unused providers add nothing to your app’s size. Select one with the maps.provider build hint (or the per-platform ios.maps.provider / android.maps.provider):

# iOS uses Apple MapKit (a free system framework, no API key, no pod):
codename1.arg.ios.maps.provider=apple

# Android uses Google Maps (pulls Google Play Services Maps):
codename1.arg.android.maps.provider=google

# ...or Huawei Map Kit, for devices without Google Play Services (HMS Core).
# The build pulls com.huawei.hms:maps from Huawei's Maven repo automatically;
# your app still needs the usual HMS onboarding (agconnect-services.json / key):
codename1.arg.android.maps.provider=huawei

The built-in providers are Apple MapKit (iOS), Google Maps (Android) and Huawei Map Kit (Android). Huawei Map Kit runs on any Android device with HMS Core installed — not only Huawei hardware — so it’s the natural choice for the Android market without Google Play Services. Each reports isAvailable() at runtime (Google checks Play Services, Huawei checks HMS Core), and when the selected provider is unavailable NativeMap falls back to the pure-vector MapView.

When a provider is selected the build server injects that provider’s implementation into your app and wires it in; with no provider selected (or when the provider is unavailable at runtime, for example when Google Play Services is missing) NativeMap simply renders the vector MapView fallback. You can configure the fallback basemap explicitly:

NativeMap map = new NativeMap(new LatLng(0, 0), 4, fallbackTileSource, MapStyle.light());

Provider API keys

Apple MapKit needs no key. Google Maps requires the usual keys, supplied as build hints:

codename1.arg.android.xapplication=<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR_ANDROID_API_KEY"/>
codename1.arg.ios.afterFinishLaunching=[GMSServices provideAPIKey:@"YOUR_IOS_API_KEY"];

Cross-platform web maps

A native provider only renders on the platforms that ship its SDK. To show a provider’s map everywhere — including platforms with no native SDK for it — register a WebMapProvider, which hosts the provider’s JavaScript SDK inside a BrowserComponent. It draws the basemap and not much else, though: the camera getters report zero and markers, shapes, coordinate conversion and gestures are no-ops, so prefer it only where a static map is what you want:

// Render Google Maps through its JavaScript SDK on any platform with a browser:
MapProviderRegistry.register(WebMapProvider.google("YOUR_MAPS_JS_API_KEY"));

// Registering only appends to the registry, and a build-injected native
// provider already there wins -- Apple Maps on iOS, for one. Asking for
// the web provider by name is what makes this one render everywhere.
//
// Ask for it only when a basemap is all you need. WebMapProvider draws
// the tiles and little else: the camera getters answer zero, and
// markers, shapes, coordinate conversion and the gesture callbacks are
// no-ops. setZoom() through it reads that zero position and recenters on
// (0, 0). For the operations the MapSurface section shows, leave the
// native provider in front.
MapProviderRegistry.setPreferredProvider("web");

NativeMap map = new NativeMap(new LatLng(41.0, 13.0), 5);

Because it needs only a web view, the web provider (id web) is the natural last step before the pure-vector fallback in a provider chain. The order is fully customizable from code, so you can express per-platform preferences — for example "try the native Google SDK, then the web map, then the vector `MapView`":

MapProviderRegistry.setProviderOrder(new String[]{"google", "web", "vector"});
Google Maps rendered cross-platform through WebMapProvider

Choosing between MapView and NativeMap

Use MapView when you want a lightweight, dependency-free map that looks the same everywhere and composes with CN1 components. Use NativeMap when you want the platform’s native map look, gestures and performance, and are willing to opt a provider in through build hints — with the guarantee that it still works (as a vector map) where the provider is unavailable.