Android WMS map library

1.9k views Asked by At

Currently I am working with maps in Geoserver. I want to display maps in WMS format in android. I have seen openscales, mapsforge, osm-droid. What android map libraries best for working with WMS, WFS layers? What are their pros and cons?

1

There are 1 answers

7
N Dorigatti On BEST ANSWER

I don't use specific libraries for WMS because i didn't like them (too complex for a specific and in my case non standard case), so I've created my class to manage it. For example, showing a WMS on the map is simply overriding URLTileProvider as such:

/*
 * Copyright [2015] Nicola Dorigatti
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package it.dorigatti.android.editing.wms;

import com.google.android.gms.maps.model.UrlTileProvider;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;

public abstract class WMSTileProvider extends UrlTileProvider {

    // Web Mercator n/w corner of the map.
    private static final double[] TILE_ORIGIN = { -20037508.34789244, 20037508.34789244 };
    // array indexes for that data
    private static final int ORIG_X = 0;
    private static final int ORIG_Y = 1; // "

    // Size of square world map in meters, using WebMerc projection.
    private static final double MAP_SIZE = 20037508.34789244 * 2;

    // array indexes for array to hold bounding boxes.
    protected static final int MINX = 0;
    protected static final int MAXX = 1;
    protected static final int MINY = 2;
    protected static final int MAXY = 3;

    // cql filters
    private String cqlString = "";

    // Construct with tile size in pixels, normally 256, see parent class.
    public WMSTileProvider(int x, int y) {
        super(x, y);
    }

    @SuppressWarnings("deprecation")
    protected String getCql() {
        try {
            return URLEncoder.encode(cqlString, Charset.defaultCharset().name());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return URLEncoder.encode(cqlString);
        }
    }

    public void setCql(String c) {
        cqlString = c;
    }

    // Return a web Mercator bounding box given tile x/y indexes and a zoom
    // level.
    protected double[] getBoundingBox(int x, int y, int zoom) {
        double tileSize = MAP_SIZE / Math.pow(2, zoom);
        double minx = TILE_ORIGIN[ORIG_X] + x * tileSize;
        double maxx = TILE_ORIGIN[ORIG_X] + (x + 1) * tileSize;
        double miny = TILE_ORIGIN[ORIG_Y] - (y + 1) * tileSize;
        double maxy = TILE_ORIGIN[ORIG_Y] - y * tileSize;

        double[] bbox = new double[4];
        bbox[MINX] = minx;
        bbox[MINY] = miny;
        bbox[MAXX] = maxx;
        bbox[MAXY] = maxy;

        return bbox;
    }

}

And them manage the layers as:

/*
 * Copyright [2015] Nicola Dorigatti
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package it.dorigatti.android.editing.wms;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;

import it.dorigatti.android.editing.dto.WMSLayerInfo;

public class TileProviderFactory {

    public static WMSTileProvider getWMSTileProviderByName(String layerName) {
        final String OSGEO_WMS = "YOURGEOSERVER/gwc/service/wms/?"
                + "LAYERS=" + layerName
                + "&FORMAT=image/png8&"
                + "PROJECTION=EPSG:3857&"
                + "TILEORIGIN=lon=-20037508.34,lat=-20037508.34&"
                + "TILESIZE=w=256,h=256"
                + "&MAXEXTENT=-20037508.34,-20037508.34,20037508.34,20037508.34&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG:3857"
                + "&BBOX=%f,%f,%f,%f&WIDTH=256&HEIGHT=256";

        return new WMSTileProvider(256, 256) {

            @Override
            public synchronized URL getTileUrl(int x, int y, int zoom) {
                final double[] bbox = getBoundingBox(x, y, zoom);
                String s = String.format(Locale.US, OSGEO_WMS, bbox[MINX], bbox[MINY], bbox[MAXX], bbox[MAXY]);
                try {
                    return new URL(s);
                } catch (MalformedURLException e) {
                    throw new AssertionError(e);
                }
            }
        };
    }

}

For the WFS service, I've developed a small library which uses GeoJSON for the data download/upload. XML is not suitable in android and WFS is too wide to support everything, so I've just implemented it on my needs. Be aware that geoserver has some issues with WFS v2 in some cases, I had to switch to WFS 1.1.