/*
* Geopaparazzi - Digital field mapping on Android based devices
* Copyright (C) 2016 HydroloGIS (www.hydrologis.com)
*
* 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 3 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 eu.geopaparazzi.core.ui.activities;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import org.mapsforge.core.model.GeoPoint;
import org.mapsforge.core.model.Tag;
import org.mapsforge.core.model.Tile;
import org.mapsforge.core.util.MercatorProjection;
import org.mapsforge.map.reader.MapDatabase;
import org.mapsforge.map.reader.MapReadResult;
import org.mapsforge.map.reader.PointOfInterest;
import org.mapsforge.map.reader.Way;
import org.mapsforge.map.reader.header.FileOpenResult;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import eu.geopaparazzi.library.database.GPLog;
import eu.geopaparazzi.library.util.GPDialogs;
import eu.geopaparazzi.library.util.LibraryConstants;
import eu.geopaparazzi.library.util.StringAsyncTask;
import eu.geopaparazzi.mapsforge.BaseMapSourcesManager;
import eu.geopaparazzi.mapsforge.databasehandlers.core.MapTable;
import eu.geopaparazzi.spatialite.database.spatial.SpatialiteSourcesManager;
import eu.geopaparazzi.spatialite.database.spatial.core.tables.AbstractSpatialTable;
import eu.geopaparazzi.core.R;
import eu.geopaparazzi.core.database.DaoNotes;
import eu.geopaparazzi.core.ui.activities.mapsforgeextractor.MapsforgeExtractedFormHelper;
import eu.geopaparazzi.core.ui.activities.mapsforgeextractor.MapsforgeExtractorUtilities;
import jsqlite.Database;
/**
* The mapsforge data extraction activity.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
public class ImportMapsforgeActivity extends AppCompatActivity {
private float[] nswe;
private int zoomLevel;
private StringAsyncTask importTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_import_mapsforge);
Toolbar toolbar = (Toolbar) findViewById(eu.geopaparazzi.core.R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Bundle extras = getIntent().getExtras();
nswe = extras.getFloatArray(LibraryConstants.NSWE);
zoomLevel = extras.getInt(LibraryConstants.ZOOMLEVEL);
}
/**
* Start data extraction.
*
* @param view parent.
*/
public void startExtraction(View view) {
Date date = new Date();
final long dateLong = date.getTime();
CheckBox poisCheckbox = (CheckBox) findViewById(R.id.poisCheckbox);
final boolean doPois = poisCheckbox.isChecked();
CheckBox waysCheckbox = (CheckBox) findViewById(R.id.waysCheckbox);
final boolean doWays = waysCheckbox.isChecked();
CheckBox waterCheckbox = (CheckBox) findViewById(R.id.waterCheckbox);
final boolean doWater = waterCheckbox.isChecked();
CheckBox contoursCheckbox = (CheckBox) findViewById(R.id.contoursCheckbox);
final boolean doContours = contoursCheckbox.isChecked();
if (!doPois && !doWays && !doContours && !doWater) {
return;
}
EditText filterEditTExt = (EditText) findViewById(R.id.filterEditText);
final String filter = filterEditTExt.getText().toString().toLowerCase();
CheckBox excludeCheckBox = (CheckBox) findViewById(R.id.excludeFilterCheckbox);
final boolean filterExcludes = excludeCheckBox.isChecked();
AbstractSpatialTable selectedSpatialTable = BaseMapSourcesManager.INSTANCE.getSelectedBaseMapTable();
if (selectedSpatialTable instanceof MapTable) {
MapTable mapTable = (MapTable) selectedSpatialTable;
File databaseFile = mapTable.getDatabaseFile();
final MapDatabase mapFile = new MapDatabase();
FileOpenResult result = mapFile.openFile(databaseFile);
final double w = nswe[2];
final double s = nswe[1];
final double e = nswe[3];
final double n = nswe[0];
final byte z = (byte) zoomLevel;
// prepare count for various levels, we go a few into detail
long count = 0;
long singleZoomCount = 0;
final int zoomLimit = 4;
for (int i = 0; i <= zoomLimit; i++) {
int zoom = z + i;
if (zoom > 22) {
break;
}
long startXTile = MercatorProjection.longitudeToTileX(w, (byte) zoom);
long endXTile = MercatorProjection.longitudeToTileX(e, (byte) zoom);
long startYTile = MercatorProjection.latitudeToTileY(n, (byte) zoom);
long endYTile = MercatorProjection.latitudeToTileY(s, (byte) zoom);
count = count + (endXTile - startXTile) * (endYTile - startYTile);
if (i == 0) {
singleZoomCount = (endXTile - startXTile) * (endYTile - startYTile);
}
}
// get mapsforge db
// ignore external points
// ignore
// check if the complete text contains the thing
// check if it is double
// TODO
// get next available field
// now alpha values
// ignore only the one unable to import
importTask = new StringAsyncTask(this) {
@Override
protected String doBackgroundWork() {
Database database = null;
try {
TreeSet<String> pointsSet = new TreeSet<>();
TreeSet<String> waysSet = new TreeSet<>();
HashMap<String, String> fieldsMap = new HashMap<>();
List<String> fieldNames = new ArrayList<>();
StringBuilder fieldsStringBuilder = new StringBuilder();
for (int j = 1; j <= 20; j++) {
String name = "field" + j;
fieldNames.add(name);
if (j != 1) {
fieldsStringBuilder.append(",");
}
fieldsStringBuilder.append(name);
}
String fieldsString = fieldsStringBuilder.toString();
// get mapsforge db
database = MapsforgeExtractorUtilities.getDatabase(ImportMapsforgeActivity.this);
boolean doFilter = filter.length() > 0;
int index = 0;
for (int i = 0; i <= zoomLimit; i++) {
int zoom = z + i;
if (zoom > 22) {
break;
}
long startXTile = MercatorProjection.longitudeToTileX(w, (byte) zoom);
long endXTile = MercatorProjection.longitudeToTileX(e, (byte) zoom);
long startYTile = MercatorProjection.latitudeToTileY(n, (byte) zoom);
long endYTile = MercatorProjection.latitudeToTileY(s, (byte) zoom);
for (long tileX = startXTile; tileX <= endXTile; tileX++) {
for (long tileY = startYTile; tileY <= endYTile; tileY++) {
index++;
Tile tile = new Tile(tileX, tileY, (byte) zoom);
MapReadResult mapReadResult = mapFile.readMapData(tile);
if (doPois) {
for (PointOfInterest pointOfInterest : mapReadResult.pointOfInterests) {
GeoPoint p = pointOfInterest.position;
double longitude = p.getLongitude();
double latitude = p.getLatitude();
if (longitude < w || longitude > e || latitude < s || latitude > n) {
// ignore external points
continue;
}
MapsforgeExtractedFormHelper mapsforgeHelper = new MapsforgeExtractedFormHelper();
List<Tag> tags = pointOfInterest.tags;
double elev = -1.0;
for (Tag tag : tags) {
String key = tag.key;
String value = tag.value;
if (key.equals(MapsforgeExtractorUtilities.tagPoiElevation)) {
try {
elev = Double.parseDouble(value);
} catch (Exception e1) {
// ignore
}
}
mapsforgeHelper.addTag(key, value);
}
String form = mapsforgeHelper.toForm();
String labelValue = mapsforgeHelper.getLabelValue();
// check if the complete text contains the thing
if (doFilter) {
String formLC = form.toLowerCase();
if (filterExcludes && formLC.contains(filter)) {
continue;
} else if (!filterExcludes && !formLC.contains(filter)) {
continue;
}
}
try {
// check if it is double
String key = longitude + "_" + latitude + "_" + form;
if (pointsSet.add(key)) {
DaoNotes.addNote(longitude, latitude, elev, dateLong, labelValue, "POI", form, null);
}
} catch (IOException ex) {
GPLog.error(this, null, ex);
}
}
}
// TODO
if (i == 0 && (doWays || doContours || doWater)) {
List<Way> ways = mapReadResult.ways;
List<String> fieldNamesTmp = new ArrayList<>();
fieldNamesTmp.addAll(fieldNames);
for (Way way : ways) {
List<Tag> tags = way.tags;
HashMap<String, String> fieldValueMap = new HashMap<>();
boolean isRoad = false;
boolean isWater = false;
boolean isContour = false;
for (Tag tag : tags) {
String key = tag.key;
if (MapsforgeExtractorUtilities.isWay(key) && doWays) {
isRoad = true;
break;
} else if (MapsforgeExtractorUtilities.isContour(key) && doContours) {
isContour = true;
break;
} else if (MapsforgeExtractorUtilities.isWaterline(key) && doWater) {
isWater = true;
break;
}
}
if (!isRoad && !isContour && !isWater) {
continue;
}
for (Tag tag : tags) {
String key = tag.key;
String value = tag.value;
String field = fieldsMap.get(key);
if (field == null) {
// get next available field
field = fieldNamesTmp.remove(0);
fieldsMap.put(key, field);
}
fieldValueMap.put(field, value);
}
float[][] wayNodes = way.wayNodes;
String trackId = wayNodes[0][0] + "_" + wayNodes[0][1];
if (!waysSet.add(trackId)) {
continue;
}
String tableName = MapsforgeExtractorUtilities.TABLENAME_WAYS;
if (isContour) {
tableName = MapsforgeExtractorUtilities.TABLENAME_CONTOURS;
}
if (isWater) {
tableName = MapsforgeExtractorUtilities.TABLENAME_WATERLINES;
}
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("insert into ").append(tableName).append(" (geometry,");
queryBuilder.append(fieldsString);
queryBuilder.append(") values (CastToSingle(CastToXY(CastToLineString(GeomFromText('LINESTRING (");
for (float[] wayNode : wayNodes) {
for (int j = 0; j < wayNode.length - 1; j = j + 2) {
double lon = wayNode[j] / 1000000.0;
double lat = wayNode[j + 1] / 1000000.0;
if (j != 0) {
queryBuilder.append(",");
}
queryBuilder.append(lon).append(" ").append(lat);
}
}
queryBuilder.append(")' , 4326))))");
// now alpha values
for (String fieldName : fieldNames) {
String value = fieldValueMap.get(fieldName);
if (value == null) {
value = "";
}
value = value.replaceAll("'", "''");
queryBuilder.append(",'").append(value).append("'");
}
queryBuilder.append(")");
try {
database.exec(queryBuilder.toString(), null);
} catch (jsqlite.Exception e1) {
// ignore only the one unable to import
}
}
}
publishProgress(index);
}
}
if (!doPois) {
break;
}
}
SpatialiteSourcesManager.INSTANCE.addSpatialiteMapFromFile(MapsforgeExtractorUtilities.getExtractMapsforgeDbFile(ImportMapsforgeActivity.this));
finish();
} catch (Exception e) {
GPLog.error(ImportMapsforgeActivity.this, null, e);
return "ERROR: " + e.getLocalizedMessage();
} finally {
mapFile.closeFile();
if (database != null)
try {
database.close();
} catch (jsqlite.Exception e1) {
GPLog.error(this, null, e1);
}
}
return "";
}
@Override
protected void doUiPostWork(String response) {
dispose();
if (response.length() != 0) {
GPDialogs.warningDialog(ImportMapsforgeActivity.this, response, new Runnable() {
@Override
public void run() {
finish();
}
});
}
}
};
if (!doPois) {
count = singleZoomCount;
}
importTask.setProgressDialog(null, getString(R.string.extract_mapsforge_data), false, (int) count);
importTask.execute();
} else {
GPDialogs.warningDialog(this, getString(R.string.extract_mapsforge_only_when_loaded), null);
}
}
@Override
protected void onDestroy() {
if (importTask!= null) importTask.dispose();
super.onDestroy();
}
}