Java tutorial
/****************************************************************************** * Project: NextGIS mobile * Purpose: Mobile GIS for Android. * Author: Dmitry Baryshnikov (aka Bishop), polimax@mail.ru ****************************************************************************** * Copyright (C) 2014 NextGIS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ****************************************************************************/ package com.nextgis.mobile.map; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Message; import android.util.Log; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import static com.nextgis.mobile.util.Constants.*; import static com.nextgis.mobile.util.GeoConstants.*; import com.nextgis.mobile.R; import com.nextgis.mobile.datasource.Feature; import com.nextgis.mobile.datasource.Field; import com.nextgis.mobile.datasource.Geo; import com.nextgis.mobile.datasource.GeoEnvelope; import com.nextgis.mobile.datasource.GeoGeometry; import com.nextgis.mobile.util.FileUtil; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; public class LocalGeoJsonLayer extends GeoJsonLayer { public LocalGeoJsonLayer(MapBase map, File path, JSONObject config) { super(map, path, config); } @Override public Drawable getIcon() { return getContext().getResources().getDrawable(R.drawable.ic_local_json); } @Override public int getType() { return LAYERTYPE_LOCAL_GEOJSON; } public static void create(final MapBase map, Uri uri) { String sName = getFileNameByUri(map.getContext(), uri, "new layer.geojson"); sName = (String) sName.subSequence(0, sName.length() - 8); showPropertiesDialog(map, true, sName, uri, null); } @Override public void changeProperties() { showPropertiesDialog(mMap, false, mName, null, this); } protected static void showPropertiesDialog(final MapBase map, final boolean bCreate, String layerName, final Uri uri, final LocalGeoJsonLayer layer) { final LinearLayout linearLayout = new LinearLayout(map.getContext()); final EditText input = new EditText(map.getContext()); input.setText(layerName); final TextView stLayerName = new TextView(map.getContext()); stLayerName.setText(map.getContext().getString(R.string.layer_name) + ":"); linearLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.addView(stLayerName); linearLayout.addView(input); if (!bCreate) { //TODO: style for drawing } new AlertDialog.Builder(map.getContext()) .setTitle(bCreate ? R.string.input_layer_properties : R.string.change_layer_properties) // .setMessage(message) .setView(linearLayout).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (bCreate) { create(map, input.getText().toString(), uri); } else { layer.setName(input.getText().toString()); map.onLayerChanged(layer); } } }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Do nothing. Toast.makeText(map.getContext(), R.string.error_cancel_by_user, Toast.LENGTH_SHORT).show(); } }).show(); } /** * Create a LocalGeoJsonLayer from the GeoJson data submitted by uri. */ protected static void create(final MapBase map, String layerName, Uri uri) { String sErr = map.getContext().getString(R.string.error_occurred); ProgressDialog progressDialog = new ProgressDialog(map.getContext()); try { InputStream inputStream = map.getContext().getContentResolver().openInputStream(uri); if (inputStream != null) { progressDialog.setMessage(map.getContext().getString(R.string.message_loading_progress)); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setCancelable(true); progressDialog.show(); int nSize = inputStream.available(); int nIncrement = 0; progressDialog.setMax(nSize); //read all geojson BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); StringBuilder responseStrBuilder = new StringBuilder(); String inputStr; while ((inputStr = streamReader.readLine()) != null) { nIncrement += inputStr.length(); progressDialog.setProgress(nIncrement); responseStrBuilder.append(inputStr); } progressDialog.setMessage(map.getContext().getString(R.string.message_opening_progress)); JSONObject geoJSONObject = new JSONObject(responseStrBuilder.toString()); if (!geoJSONObject.has(GEOJSON_TYPE)) { sErr += ": " + map.getContext().getString(R.string.error_geojson_unsupported); Toast.makeText(map.getContext(), sErr, Toast.LENGTH_SHORT).show(); progressDialog.hide(); return; } //check crs boolean isWGS84 = true; //if no crs tag - WGS84 CRS if (geoJSONObject.has(GEOJSON_CRS)) { JSONObject crsJSONObject = geoJSONObject.getJSONObject(GEOJSON_CRS); //the link is unsupported yet. if (!crsJSONObject.getString(GEOJSON_TYPE).equals(GEOJSON_NAME)) { sErr += ": " + map.getContext().getString(R.string.error_geojson_crs_unsupported); Toast.makeText(map.getContext(), sErr, Toast.LENGTH_SHORT).show(); progressDialog.hide(); return; } JSONObject crsPropertiesJSONObject = crsJSONObject.getJSONObject(GEOJSON_PROPERTIES); String crsName = crsPropertiesJSONObject.getString(GEOJSON_NAME); if (crsName.equals("urn:ogc:def:crs:OGC:1.3:CRS84")) { // WGS84 isWGS84 = true; } else if (crsName.equals("urn:ogc:def:crs:EPSG::3857")) { //Web Mercator isWGS84 = false; } else { sErr += ": " + map.getContext().getString(R.string.error_geojson_crs_unsupported); Toast.makeText(map.getContext(), sErr, Toast.LENGTH_SHORT).show(); progressDialog.hide(); return; } } //load contents to memory and reproject if needed JSONArray geoJSONFeatures = geoJSONObject.getJSONArray(GEOJSON_TYPE_FEATURES); if (0 == geoJSONFeatures.length()) { sErr += ": " + map.getContext().getString(R.string.error_geojson_crs_unsupported); Toast.makeText(map.getContext(), sErr, Toast.LENGTH_SHORT).show(); progressDialog.hide(); return; } List<Feature> features = geoJSONFeaturesToFeatures(geoJSONFeatures, isWGS84, map.getContext(), progressDialog); create(map, layerName, features); } } catch (UnsupportedEncodingException e) { Log.d(TAG, "Exception: " + e.getLocalizedMessage()); sErr += ": " + e.getLocalizedMessage(); } catch (FileNotFoundException e) { Log.d(TAG, "Exception: " + e.getLocalizedMessage()); sErr += ": " + e.getLocalizedMessage(); } catch (IOException e) { Log.d(TAG, "Exception: " + e.getLocalizedMessage()); sErr += ": " + e.getLocalizedMessage(); } catch (JSONException e) { Log.d(TAG, "Exception: " + e.getLocalizedMessage()); sErr += ": " + e.getLocalizedMessage(); } progressDialog.hide(); //if we here something wrong occurred Toast.makeText(map.getContext(), sErr, Toast.LENGTH_SHORT).show(); } /** * Create a LocalGeoJsonLayer from the GeoJson data submitted by features. */ public static void create(final MapBase map, String layerName, List<Feature> features) throws JSONException, IOException { create(map, layerName, features, LAYERTYPE_LOCAL_GEOJSON); } protected static void create(final MapBase map, String layerName, List<Feature> features, int layerType) throws JSONException, IOException { GeoEnvelope extents = new GeoEnvelope(); for (Feature feature : features) { //update bbox extents.merge(feature.getGeometry().getEnvelope()); } Feature feature = features.get(0); int geometryType = feature.getGeometry().getType(); List<Field> fields = feature.getFields(); //create layer description file JSONObject oJSONRoot = new JSONObject(); oJSONRoot.put(JSON_NAME_KEY, layerName); oJSONRoot.put(JSON_VISIBILITY_KEY, true); oJSONRoot.put(JSON_TYPE_KEY, layerType); oJSONRoot.put(JSON_MAXLEVEL_KEY, 50); oJSONRoot.put(JSON_MINLEVEL_KEY, 0); //add geometry type oJSONRoot.put(JSON_GEOMETRY_TYPE_KEY, geometryType); //add bbox JSONObject oJSONBBox = extents.toJSON(); oJSONRoot.put(JSON_BBOX_KEY, oJSONBBox); //add fields description JSONArray oJSONFields = new JSONArray(); for (Field field : fields) { oJSONFields.put(field.toJSON()); } oJSONRoot.put(JSON_FIELDS_KEY, oJSONFields); // store layer description to file File outputPath = map.cretateLayerStorage(); File file = new File(outputPath, LAYER_CONFIG); FileUtil.createDir(outputPath); FileUtil.writeToFile(file, oJSONRoot.toString()); //store GeoJson to file store(features, outputPath); if (map.getMapEventsHandler() != null) { Bundle bundle = new Bundle(); bundle.putBoolean(BUNDLE_HASERROR_KEY, false); bundle.putString(BUNDLE_MSG_KEY, map.getContext().getString(R.string.message_layer_added)); bundle.putInt(BUNDLE_TYPE_KEY, MSGTYPE_LAYER_ADDED); bundle.putSerializable(BUNDLE_PATH_KEY, outputPath); Message msg = new Message(); msg.setData(bundle); map.getMapEventsHandler().sendMessage(msg); } } private static List<Feature> geoJSONFeaturesToFeatures(JSONArray geoJSONFeatures, boolean isWGS84, Context context, ProgressDialog progressDialog) throws JSONException { List<Feature> features = new ArrayList<Feature>(); List<Field> fields = new ArrayList<Field>(); int geometryType = GTNone; progressDialog.setMessage(context.getString(R.string.message_loading_progress)); progressDialog.setMax(geoJSONFeatures.length()); for (int i = 0; i < geoJSONFeatures.length(); i++) { progressDialog.setProgress(i); JSONObject jsonFeature = geoJSONFeatures.getJSONObject(i); //get geometry JSONObject jsonGeometry = jsonFeature.getJSONObject(GEOJSON_GEOMETRY); GeoGeometry geometry = GeoGeometry.fromJson(jsonGeometry); if (geometryType == GTNone) { geometryType = geometry.getType(); } else if (!Geo.isGeometryTypeSame(geometryType, geometry.getType())) { //skip different geometry type continue; } //reproject if needed if (isWGS84) { geometry.setCRS(CRS_WGS84); geometry.project(CRS_WEB_MERCATOR); } else { geometry.setCRS(CRS_WEB_MERCATOR); } Feature feature = new Feature(fields); feature.setGeometry(geometry); //TODO: add to RTree for fast spatial queries //normalize attributes JSONObject jsonAttributes = jsonFeature.getJSONObject(GEOJSON_PROPERTIES); Iterator<String> iter = jsonAttributes.keys(); while (iter.hasNext()) { String key = iter.next(); Object value = jsonAttributes.get(key); int nType = -1; //check type if (value instanceof Integer || value instanceof Long) { nType = FTInteger; } else if (value instanceof Double || value instanceof Float) { nType = FTReal; } else if (value instanceof Date) { nType = FTDateTime; } else if (value instanceof String) { nType = FTString; } else if (value instanceof JSONObject) { nType = -1; //the some list - need to check it type FTIntegerList, FTRealList, FTStringList } int nField = -1; for (int j = 0; j < fields.size(); j++) { if (fields.get(j).getFieldName().equals(key)) { nField = j; } } if (nField == -1) { //add new field Field field = new Field(key, key, nType); nField = fields.size(); fields.add(field); } feature.setField(nField, value); } features.add(feature); } return features; } }