-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
sgelb
committed
Nov 15, 2019
1 parent
be3e5a3
commit dc8a57f
Showing
2 changed files
with
248 additions
and
0 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
app/src/main/java/fi/bitrite/android/ws/model/MapsForgeTheme.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package fi.bitrite.android.ws.model; | ||
|
||
import java.io.Serializable; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class MapsForgeTheme implements Serializable { | ||
private String name; | ||
private String id; | ||
private Map<String, String> localizedNames; | ||
private String filePath; | ||
|
||
public MapsForgeTheme(String name, String id, String filePath) { | ||
this.name = name; | ||
this.id = id; | ||
this.filePath = filePath; | ||
localizedNames = new HashMap<>(); | ||
} | ||
|
||
public String getId() { | ||
return id; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public String getFilePath() { | ||
return filePath; | ||
} | ||
|
||
public String getLocalizedDisplayName(String lang) { | ||
if (!localizedNames.containsKey(lang)) { | ||
lang = "en"; | ||
} | ||
if (localizedNames.get(lang) != null) { | ||
return localizedNames.get(lang) + " (" + name + ")"; | ||
} else { | ||
return name; | ||
} | ||
} | ||
|
||
public void addLocalizedName(String lang, String name) { | ||
localizedNames.put(lang, name); | ||
} | ||
} |
202 changes: 202 additions & 0 deletions
202
app/src/main/java/fi/bitrite/android/ws/ui/util/OfflineMapHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package fi.bitrite.android.ws.ui.util; | ||
|
||
import android.content.Context; | ||
import android.os.Environment; | ||
import android.util.Xml; | ||
|
||
import org.mapsforge.map.rendertheme.XmlRenderTheme; | ||
import org.mapsforge.map.rendertheme.XmlRenderThemeStyleLayer; | ||
import org.xmlpull.v1.XmlPullParser; | ||
import org.xmlpull.v1.XmlPullParserException; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.FileReader; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import androidx.fragment.app.FragmentActivity; | ||
import fi.bitrite.android.ws.R; | ||
import fi.bitrite.android.ws.model.MapsForgeTheme; | ||
import fi.bitrite.android.ws.repository.SettingsRepository; | ||
import io.reactivex.disposables.Disposable; | ||
|
||
public class OfflineMapHelper { | ||
|
||
public static final String TAG = "OfflineMapHelper"; | ||
|
||
public static String defaultMapDataDirectory(Context context) { | ||
return context.getExternalFilesDir(null).toString() + "/"; | ||
} | ||
|
||
public static boolean containsExistingFile(File[] files) { | ||
for (File f : files) { | ||
if (f.exists()) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
public static void searchOfflineMapData(SettingsRepository settingsRepository, boolean extendedSearchScope, FragmentActivity activity) { | ||
File[] startFolders; | ||
if (extendedSearchScope) { | ||
startFolders = new File[]{ | ||
new File(defaultMapDataDirectory(activity)), | ||
Environment.getExternalStorageDirectory()}; | ||
} else { | ||
startFolders = new File[]{ | ||
new File(defaultMapDataDirectory(activity))}; | ||
} | ||
|
||
Disposable progressDisposable = ProgressDialog.create(R.string.map_search_in_progress) | ||
.showDelayed(activity, 100, TimeUnit.MILLISECONDS); | ||
|
||
List<String> mapfiles = OfflineMapHelper.findMapsforgeMapFiles(startFolders); | ||
settingsRepository.setAvailableOfflineMapSources(new HashSet<>(mapfiles)); | ||
|
||
List<MapsForgeTheme> themes = new ArrayList<>(); | ||
for (File themefile : OfflineMapHelper.findMapsforgeThemeFiles(startFolders)) { | ||
themes.addAll(OfflineMapHelper.getThemeStyles(themefile)); | ||
} | ||
settingsRepository.setAvailableOfflineThemeSources(themes); | ||
|
||
progressDisposable.dispose(); | ||
} | ||
|
||
private static List<String> findMapsforgeMapFiles(File[] rootFolders) { | ||
List<String> mapFiles = new ArrayList<>(); | ||
for (File file : findFilesWithExtension(new ArrayList<>(), rootFolders, ".map")) { | ||
if (isMapsforgeBinaryOSM(file)) { | ||
mapFiles.add(file.getAbsolutePath()); | ||
} | ||
} | ||
return mapFiles; | ||
} | ||
|
||
|
||
private static List<File> findMapsforgeThemeFiles(File[] rootFolders) { | ||
List<File> mapFiles = new ArrayList<>(); | ||
for (File file : findFilesWithExtension(new ArrayList<>(), rootFolders, ".xml")) { | ||
if (isMapsforgeThemeXML(file)) { | ||
mapFiles.add(file); | ||
} | ||
} | ||
return mapFiles; | ||
} | ||
|
||
private static List<MapsForgeTheme> getThemeStyles(File theme) { | ||
XmlPullParser parser = Xml.newPullParser(); | ||
try (InputStream in_s = new FileInputStream(theme)) { | ||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); | ||
parser.setInput(in_s, null); | ||
return extractThemes(parser, theme.getAbsolutePath()); | ||
} catch (XmlPullParserException|IOException ignored) {} | ||
|
||
return new ArrayList<>(); | ||
} | ||
|
||
private static List<MapsForgeTheme> extractThemes(XmlPullParser parser, final String filePath) | ||
throws XmlPullParserException, IOException { | ||
|
||
List<MapsForgeTheme> themes = new ArrayList<>(); | ||
|
||
int eventType = parser.getEventType(); | ||
String elementName; | ||
MapsForgeTheme theme = null; | ||
while (eventType != XmlPullParser.END_DOCUMENT) { | ||
switch (eventType) { | ||
case XmlPullParser.START_TAG: | ||
elementName = parser.getName(); | ||
if ("layer".equalsIgnoreCase(elementName)) { | ||
if ("true".equalsIgnoreCase(parser.getAttributeValue(null, "visible"))) { | ||
theme = new MapsForgeTheme(new File(filePath).getName(), parser.getAttributeValue(null, "id"), filePath); | ||
} | ||
} else if (theme != null && "name".equalsIgnoreCase(elementName)) { | ||
String lang = parser.getAttributeValue(null,"lang"); | ||
String localizedThemeName = parser.getAttributeValue(null,"value"); | ||
theme.addLocalizedName(lang, localizedThemeName); | ||
} | ||
break; | ||
case XmlPullParser.END_TAG: | ||
elementName = parser.getName(); | ||
if ("layer".equalsIgnoreCase(elementName) && theme != null ) { | ||
themes.add(theme); | ||
theme = null; | ||
} | ||
} | ||
eventType = parser.next(); | ||
} | ||
|
||
return themes; | ||
} | ||
|
||
public static void setThemeStyle(XmlRenderTheme theme, String id) { | ||
theme.setMenuCallback(themestyle -> { | ||
String themeId = id.isEmpty() ? themestyle.getDefaultValue() : id; | ||
XmlRenderThemeStyleLayer baseLayer = themestyle.getLayer(themeId); | ||
if (baseLayer == null) { | ||
return null; | ||
} | ||
|
||
// add the categories from overlays | ||
Set<String> result = baseLayer.getCategories(); | ||
for (XmlRenderThemeStyleLayer overlay : baseLayer.getOverlays()) { | ||
result.addAll(overlay.getCategories()); | ||
} | ||
|
||
return result; | ||
}); | ||
} | ||
|
||
|
||
private static List<File> findFilesWithExtension(List<File> foundFiles, File[] filesArray, String ext) { | ||
if (filesArray == null) { | ||
return foundFiles; | ||
} | ||
|
||
for (File file : filesArray) { | ||
if (file.isDirectory()) { | ||
findFilesWithExtension(foundFiles,file.listFiles(path -> | ||
(path.isDirectory() || path.getName().toLowerCase().endsWith(ext))), ext); | ||
} else if (file.getName().toLowerCase().endsWith(ext)) { | ||
foundFiles.add(file); | ||
} | ||
} | ||
return foundFiles; | ||
} | ||
|
||
|
||
private static boolean isMapsforgeBinaryOSM(File file) { | ||
// https://github.com/mapsforge/mapsforge/blob/master/docs/Specification-Binary-Map-File.md | ||
int magicByteSize = 20; | ||
String magicByteString = "mapsforge binary OSM"; | ||
|
||
byte[] buffer = new byte[magicByteSize]; | ||
try (InputStream is = new FileInputStream(file.getAbsolutePath())) { | ||
is.read(buffer); | ||
} catch (IOException ignored) { } | ||
return new String(buffer).contentEquals(magicByteString); | ||
} | ||
|
||
private static boolean isMapsforgeThemeXML(File file) { | ||
String identifier = "http://mapsforge.org/renderTheme"; | ||
try (BufferedReader br = new BufferedReader(new FileReader(file))) { | ||
String line; | ||
while ((line = br.readLine()) != null) { | ||
if (line.contains(identifier)) { | ||
return true; | ||
} | ||
} | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
return false; | ||
} | ||
} |