diff --git a/settings.gradle b/settings.gradle
index d4201927..5426f4a3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,6 +15,7 @@ include ':vtm-json'
include ':vtm-jts'
include ':vtm-playground'
include ':vtm-tests'
+include ':vtm-theme-comparator'
include ':vtm-themes'
include ':vtm-web'
include ':vtm-web-app'
diff --git a/vtm-theme-comparator/build.gradle b/vtm-theme-comparator/build.gradle
new file mode 100644
index 00000000..10f42092
--- /dev/null
+++ b/vtm-theme-comparator/build.gradle
@@ -0,0 +1,34 @@
+apply plugin: 'java'
+
+dependencies {
+ compile project(':vtm-desktop')
+ compile project(':vtm-extras')
+ compile project(':vtm-http')
+ compile project(':vtm-json')
+ compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
+
+
+ compile group: 'com.jtattoo', name: 'JTattoo', version: '1.6.11'
+ compile group: 'com.fifesoft', name: 'rsyntaxtextarea', version: '2.6.1'
+
+
+ compile 'org.mapsforge:mapsforge-core:0.8.0'
+ compile 'org.mapsforge:mapsforge-themes:0.8.0'
+ compile 'org.mapsforge:mapsforge-map:0.8.0'
+ compile 'org.mapsforge:mapsforge-map-reader:0.8.0'
+ compile("org.mapsforge:mapsforge-map-awt:0.8.0")
+ compile 'com.kitfox.svg:svg-salamander:1.0'
+ compile 'net.sf.kxml:kxml2:2.3.0'
+
+ //include libs
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+
+
+}
+
+sourceSets {
+ main {
+ java.srcDirs = ['src']
+ resources.srcDirs = ['resources']
+ }
+}
diff --git a/vtm-theme-comparator/resources/debug_dark.png b/vtm-theme-comparator/resources/debug_dark.png
new file mode 100644
index 00000000..37008808
Binary files /dev/null and b/vtm-theme-comparator/resources/debug_dark.png differ
diff --git a/vtm-theme-comparator/resources/editSource_dark.png b/vtm-theme-comparator/resources/editSource_dark.png
new file mode 100644
index 00000000..3605b6fc
Binary files /dev/null and b/vtm-theme-comparator/resources/editSource_dark.png differ
diff --git a/vtm-theme-comparator/resources/exit_dark.png b/vtm-theme-comparator/resources/exit_dark.png
new file mode 100644
index 00000000..7f107515
Binary files /dev/null and b/vtm-theme-comparator/resources/exit_dark.png differ
diff --git a/vtm-theme-comparator/resources/gc_dark.png b/vtm-theme-comparator/resources/gc_dark.png
new file mode 100644
index 00000000..d442900f
Binary files /dev/null and b/vtm-theme-comparator/resources/gc_dark.png differ
diff --git a/vtm-theme-comparator/resources/locate_dark.png b/vtm-theme-comparator/resources/locate_dark.png
new file mode 100644
index 00000000..6f3f64d2
Binary files /dev/null and b/vtm-theme-comparator/resources/locate_dark.png differ
diff --git a/vtm-theme-comparator/resources/menu-open.png b/vtm-theme-comparator/resources/menu-open.png
new file mode 100644
index 00000000..b8b43ec9
Binary files /dev/null and b/vtm-theme-comparator/resources/menu-open.png differ
diff --git a/vtm-theme-comparator/resources/menu-saveall.png b/vtm-theme-comparator/resources/menu-saveall.png
new file mode 100644
index 00000000..40edbf15
Binary files /dev/null and b/vtm-theme-comparator/resources/menu-saveall.png differ
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/BothMapPositionHandler.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/BothMapPositionHandler.java
new file mode 100644
index 00000000..9042c61c
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/BothMapPositionHandler.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import com.badlogic.gdx.Gdx;
+
+import org.mapsforge.core.model.MapPosition;
+import org.oscim.theme.comparator.location.LocationData;
+import org.oscim.theme.comparator.location.LocationDataListener;
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.util.prefs.BackingStoreException;
+
+public class BothMapPositionHandler implements LocationDataListener {
+
+ private final MapsforgeMapPanel mapsforgeMapPanel;
+ private final VtmPanel vtmPanel;
+ private MainMenu menu;
+
+ public BothMapPositionHandler(MapsforgeMapPanel mapsforgeMapPanel, VtmPanel vtmPanel) {
+ this.mapsforgeMapPanel = mapsforgeMapPanel;
+ this.vtmPanel = vtmPanel;
+ LocationData.addChangeListener(this);
+ }
+
+ void setCoordinate(final double latitude, final double longitude, final byte zoomLevel) {
+ Gdx.app.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mapsforgeMapPanel.setCoordinate(latitude, longitude, zoomLevel);
+ vtmPanel.setCoordinate(latitude, longitude, (byte) (zoomLevel - 1));
+ storePositionOnPrefs(latitude, longitude, zoomLevel);
+ }
+ });
+
+ }
+
+ public void mapPositionChangedFromMapPanel(final MapPosition mapPosition) {
+ Gdx.app.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ vtmPanel.setCoordinate(mapPosition.latLong.latitude, mapPosition.latLong.longitude, (byte) (mapPosition.zoomLevel - 1));
+ //TODO wy is zoom level on vtm x-1?
+ storePositionOnPrefs(mapPosition.latLong.latitude, mapPosition.latLong.longitude, mapPosition.zoomLevel);
+ }
+ });
+ }
+
+ public void mapPositionChangedFromVtmMap(org.oscim.core.MapPosition mapPosition) {
+ mapsforgeMapPanel.setCoordinate(mapPosition.getLatitude(), mapPosition.getLongitude(), (byte) (mapPosition.zoomLevel + 1));
+ storePositionOnPrefs(mapPosition.getLatitude(), mapPosition.getLongitude(), (byte) (mapPosition.zoomLevel));
+ }
+
+ private void storePositionOnPrefs(double latitude, double longitude, byte zoomLevel) {
+ Main.prefs.putDouble("latitude", latitude);
+ Main.prefs.putDouble("longitude", longitude);
+ Main.prefs.putInt("zoomLevel", zoomLevel);
+ try {
+ Main.prefs.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+
+ // change position label on MenuBar
+ if (this.menu != null) this.menu.setPos(latitude, longitude, zoomLevel);
+
+ // change values on Location panel
+ LocationData.set(latitude, longitude, zoomLevel);
+ }
+
+ void loadPrefsPosition() {
+ double latitude = Main.prefs.getDouble("latitude", 300);
+ double longitude = Main.prefs.getDouble("longitude", 300);
+ byte zoomLevel = (byte) Main.prefs.getInt("zoomLevel", -1);
+
+ if (latitude < 300) {
+ setCoordinate(latitude, longitude, zoomLevel);
+ }
+ }
+
+ void setCallBack(MainMenu menu) {
+ this.menu = menu;
+ }
+
+ @Override
+ public void valueChanged() {
+
+ double latitude = LocationData.getLatitude();
+ double longitude = LocationData.getLongitude();
+ int zoomLevel = LocationData.getZoom();
+
+ if (LocationData.getNS() == LocationData.Orientation.SOUTH) {
+ latitude *= -1;
+ }
+
+ if (LocationData.getEW() == LocationData.Orientation.WEST) {
+ longitude *= -1;
+ }
+
+ setCoordinate(latitude, longitude, (byte) zoomLevel);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/InfoPanel.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/InfoPanel.java
new file mode 100644
index 00000000..7e97e684
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/InfoPanel.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import org.oscim.theme.comparator.editor.EditorPane;
+import org.oscim.theme.comparator.location.LocationPane;
+import org.oscim.theme.comparator.logging.AllLoggingPane;
+import org.oscim.theme.comparator.logging.MapsforgeLoggingPane;
+import org.oscim.theme.comparator.logging.VtmLoggingPane;
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.awt.GridLayout;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.SwingUtilities;
+
+class InfoPanel extends JPanel {
+
+ InfoPanel(final VtmPanel vtmPanel, final MapsforgeMapPanel mapsforgeMapPanel,
+ final MainMenu mainMenu) {
+ super(new GridLayout(1, 1));
+
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ JTabbedPane tabbedPane = new JTabbedPane();
+ tabbedPane.addTab("Theme Editor", Main.ICON_EDIT, new EditorPane(vtmPanel, mapsforgeMapPanel, mainMenu), "");
+ tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
+
+ tabbedPane.addTab("Log's ALL", Main.ICON_DEBUG, new AllLoggingPane(), "");
+ tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);
+
+ tabbedPane.addTab("Log's VTM", Main.ICON_DEBUG, new VtmLoggingPane(), "");
+ tabbedPane.setMnemonicAt(1, KeyEvent.VK_3);
+
+ tabbedPane.addTab("Log's MAPSFORGE", Main.ICON_DEBUG, new MapsforgeLoggingPane(), "");
+ tabbedPane.setMnemonicAt(1, KeyEvent.VK_4);
+
+ tabbedPane.addTab("Set Map position", Main.ICON_LOCATE, new LocationPane(), "");
+ tabbedPane.setMnemonicAt(1, KeyEvent.VK_4);
+
+ tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
+ add(tabbedPane);
+ }
+ });
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/Main.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/Main.java
new file mode 100644
index 00000000..89270033
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/Main.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import com.badlogic.gdx.utils.SharedLibraryLoader;
+import com.jtattoo.plaf.DecorationHelper;
+import com.jtattoo.plaf.hifi.HiFiLookAndFeel;
+
+import org.oscim.awt.AwtGraphics;
+import org.oscim.backend.GLAdapter;
+import org.oscim.gdx.GdxAssets;
+import org.oscim.gdx.LwjglGL20;
+import org.oscim.theme.comparator.logging.AllAppender;
+import org.oscim.theme.comparator.logging.BaseAppender;
+import org.oscim.theme.comparator.logging.MapsforgeAppender;
+import org.oscim.theme.comparator.logging.VtmAppender;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JRootPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import ch.qos.logback.classic.LoggerContext;
+
+public class Main {
+
+ public static Preferences prefs = Preferences.userNodeForPackage(Main.class);
+ public static boolean useDarkTheme = true; // set black look and feel as default for unimpaired color impression
+ public final static BaseAppender ALL_APPENDER = new AllAppender();
+ public final static BaseAppender VTM_APPENDER = new VtmAppender();
+ public final static BaseAppender MAPSFORGE_APPENDER = new MapsforgeAppender();
+
+ public final static int MAX_ZOOM_LEVEL = 21;
+ public final static int MIN_ZOOM_LEVEL = 0;
+
+
+ public final static ImageIcon ICON_OPEN = new ImageIcon(Main.class.getClassLoader().getResource("menu-open.png"));
+ public final static ImageIcon ICON_SAVE = new ImageIcon(Main.class.getClassLoader().getResource("menu-saveall.png"));
+ public static final ImageIcon ICON_EDIT = new ImageIcon(Main.class.getClassLoader().getResource("editSource_dark.png"));
+ public static final ImageIcon ICON_DEBUG = new ImageIcon(Main.class.getClassLoader().getResource("debug_dark.png"));
+ public static final ImageIcon ICON_EXIT = new ImageIcon(Main.class.getClassLoader().getResource("exit_dark.png"));
+ public static final ImageIcon ICON_LOCATE = new ImageIcon(Main.class.getClassLoader().getResource("locate_dark.png"));
+
+ private static JFrame window;
+
+ // add TextArea logging appender
+ static {
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ ch.qos.logback.classic.Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
+ rootLogger.addAppender(ALL_APPENDER);
+ ALL_APPENDER.start();
+ rootLogger.addAppender(VTM_APPENDER);
+ VTM_APPENDER.start();
+ rootLogger.addAppender(MAPSFORGE_APPENDER);
+ MAPSFORGE_APPENDER.start();
+ }
+
+
+ public static void main(String[] args) throws Exception {
+
+ useDarkTheme = prefs.getBoolean("DARK", true);
+
+ setTheme();
+
+ System.setProperty("org.lwjgl.opengl.Display.allowSoftwareOpenGL", "true");
+
+ new SharedLibraryLoader().load("vtm-jni");
+ AwtGraphics.init();
+ GdxAssets.init("");
+ GLAdapter.init(new LwjglGL20());
+ GLAdapter.GDX_DESKTOP_QUIRKS = true;
+ window = new MainWindow();
+ window.setSize(800, 600);
+ window.setLocationRelativeTo(null);
+ window.setVisible(true);
+ }
+
+ private static void setTheme() {
+ if (useDarkTheme) {
+ try {
+ UIManager.setLookAndFeel("com.jtattoo.plaf.hifi.HiFiLookAndFeel");
+ Properties properties = HiFiLookAndFeel.getThemeProperties("Default");
+ properties.setProperty("textAntiAliasing", "on");
+ properties.setProperty("backgroundPattern", "off");
+ properties.put("logoString", "");
+ HiFiLookAndFeel.setTheme(properties);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ } else {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ static void switchTheme() {
+
+ useDarkTheme = !useDarkTheme;
+
+ prefs.putBoolean("DARK", useDarkTheme);
+ try {
+ prefs.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+
+ setTheme();
+
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ SwingUtilities.updateComponentTreeUI(window);
+ if (!useDarkTheme) {
+ DecorationHelper.decorateWindows(false);
+ window.dispose();
+ window.setUndecorated(false);
+ window.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+ window.setVisible(true);
+ } else {
+ DecorationHelper.decorateWindows(true);
+ window.dispose();
+ window.setUndecorated(true);
+ window.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+ window.setVisible(true);
+ }
+
+ }
+ });
+
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/MainMenu.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/MainMenu.java
new file mode 100644
index 00000000..64ab1e32
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/MainMenu.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import com.badlogic.gdx.utils.StringBuilder;
+
+import org.mapsforge.core.util.MercatorProjection;
+import org.oscim.theme.comparator.editor.ThemeLoader;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.prefs.BackingStoreException;
+
+import javax.swing.Box;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+
+public class MainMenu extends JMenuBar {
+
+ private final MapLoader mapLoader;
+ private final JMenu fileMenu = new JMenu("File");
+ private final JMenu viewMenu = new JMenu("View");
+ private final JMenu posMenu = new JMenu("Pos");
+ private ThemeLoader themeLoader;
+
+ MainMenu(MapLoader mapLoader, BothMapPositionHandler mapPosition) {
+ this.mapLoader = mapLoader;
+ mapPosition.setCallBack(this);
+ addFileEntrys();
+ addViewEntrys();
+ addPosEntrys();
+ }
+
+ private void addFileEntrys() {
+ fileMenu.getPopupMenu().setLightWeightPopupEnabled(false);
+ fileMenu.setMnemonic(KeyEvent.VK_F);
+ fileMenu.add(itemLoadMap());
+ fileMenu.add(itemLoadTheme());
+ fileMenu.addSeparator();
+ fileMenu.add(itemExit());
+ this.add(fileMenu);
+ }
+
+ private void addViewEntrys() {
+ JCheckBoxMenuItem checkBoxItem = new JCheckBoxMenuItem("Dark Theme");
+ checkBoxItem.setState(Main.useDarkTheme);
+ checkBoxItem.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Main.switchTheme();
+ }
+ });
+ viewMenu.getPopupMenu().setLightWeightPopupEnabled(false);
+ viewMenu.add(checkBoxItem);
+ this.add(viewMenu);
+ }
+
+ private void addPosEntrys() {
+ this.add(Box.createHorizontalGlue());
+ posMenu.getPopupMenu().setLightWeightPopupEnabled(false);
+ this.add(posMenu);
+ }
+
+ private JMenuItem itemExit() {
+ JMenuItem item = new JMenuItem("Exit", Main.ICON_EXIT);
+ item.setMnemonic(KeyEvent.VK_E);
+ item.setToolTipText("Exit application");
+ item.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ System.exit(0);
+ }
+ });
+ return item;
+ }
+
+ private JMenuItem itemLoadMap() {
+ JMenuItem item = new JMenuItem("Load Map file", Main.ICON_OPEN);
+ item.setMnemonic(KeyEvent.VK_M);
+ item.setToolTipText("Load Mapsforge map file (*.map)");
+ item.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // load Map
+ String mapPath = Utils.getFile(Main.prefs.get("lastMapDir", ""), "map", "Load Map");
+ if (mapPath != null) {
+
+ //store last selected folder
+ File file = new File(mapPath);
+ if (!file.exists() || !file.canRead()) return;
+ File dir = file.getParentFile();
+ Main.prefs.put("lastMapDir", dir.getAbsolutePath());
+ try {
+ Main.prefs.flush();
+ } catch (BackingStoreException ex) {
+ ex.printStackTrace();
+ }
+
+ mapLoader.loadMap(new File(mapPath), true);
+ }
+ }
+ });
+ return item;
+ }
+
+ private JMenuItem itemLoadTheme() {
+ JMenuItem item = new JMenuItem("Load Theme file", Main.ICON_OPEN);
+ item.setMnemonic(KeyEvent.VK_M);
+ item.setToolTipText("Load Theme file (*.xml)");
+ item.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ MainMenu.this.themeLoader.selectThemeFile();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ });
+ return item;
+ }
+
+
+ void setPos(double latitude, double longitude, byte zoomLevel) {
+
+ //clear string builder
+ stringBuilder.length = 0;
+ Arrays.fill(stringBuilder.chars, Character.MIN_VALUE);
+
+ stringBuilder.append("LAT: ");
+ FormatDM(stringBuilder, latitude, "N", "S");
+ stringBuilder.append(" LON: ");
+ FormatDM(stringBuilder, longitude, "E", "W");
+ stringBuilder.append(" ZOOM: ");
+ stringBuilder.append(zoomLevel);
+ stringBuilder.append(" Center Tile: X=");
+ stringBuilder.append(MercatorProjection.longitudeToTileX(longitude, zoomLevel));
+ stringBuilder.append(" Y= ");
+ stringBuilder.append(MercatorProjection.latitudeToTileY(latitude, zoomLevel));
+ stringBuilder.append(" Z= ");
+ stringBuilder.append(zoomLevel);
+ posMenu.setText(stringBuilder.toString());
+
+ }
+
+ private final StringBuilder stringBuilder = new StringBuilder(13);
+
+ private void FormatDM(StringBuilder stringBuilder, double coord, String positiveDirection, String negativeDirection) {
+
+ stringBuilder.append(Math.abs(((int) coord)));
+ stringBuilder.append("\u00B0 ");
+ stringBuilder.append(String.format("%.3f", Math.abs((coord - ((int) coord)) * 60)));
+ if (coord < 0)
+ stringBuilder.append(negativeDirection);
+ else
+ stringBuilder.append(positiveDirection);
+ }
+
+ public void setThemeLoader(ThemeLoader themeLoader) {
+ this.themeLoader = themeLoader;
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/MainWindow.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/MainWindow.java
new file mode 100644
index 00000000..0408437b
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/MainWindow.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import com.badlogic.gdx.Gdx;
+
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.MapApplicationAdapter;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+import javax.swing.JFrame;
+import javax.swing.JSplitPane;
+
+class MainWindow extends JFrame {
+
+ private final VtmPanel vtmPanel;
+ private final MapLoader mapLoader;
+ private final BothMapPositionHandler bothMapPositionHandler;
+ private final MapsforgeMapPanel mapsforgeMapPanel;
+ private final InfoPanel infoPanel;
+ private final MapsPanel mapsPanel;
+
+ MainWindow() {
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ this.setLayout(new BorderLayout());
+ MapApplicationAdapter.MapReadyCallback callback = new MapApplicationAdapter.MapReadyCallback() {
+ @Override
+ public void ready() {
+ mapLoader.loadPrefsMapFile();
+ Gdx.app.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ bothMapPositionHandler.loadPrefsPosition();
+ }
+ });
+ }
+ };
+
+ vtmPanel = new VtmPanel(callback);
+ mapsforgeMapPanel = new MapsforgeMapPanel();
+
+ mapsPanel = new MapsPanel(vtmPanel, mapsforgeMapPanel);
+
+ bothMapPositionHandler = new BothMapPositionHandler(mapsforgeMapPanel, vtmPanel);
+ mapsforgeMapPanel.setMapPositionHandler(bothMapPositionHandler);
+ vtmPanel.setMapPositionHandler(bothMapPositionHandler);
+
+ mapLoader = new MapLoader(mapsforgeMapPanel, vtmPanel, bothMapPositionHandler);
+
+ MainMenu mainMenu = new MainMenu(mapLoader, bothMapPositionHandler);
+ setJMenuBar(mainMenu);
+
+ infoPanel = new InfoPanel(vtmPanel, mapsforgeMapPanel, mainMenu);
+
+ this.setMinimumSize(new Dimension(600, 400));
+ mapsPanel.setMinimumSize(new Dimension(200, 100));
+ infoPanel.setMinimumSize(new Dimension(200, 100));
+
+ JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+ splitPane.setTopComponent(mapsPanel);
+ splitPane.setBottomComponent(infoPanel);
+
+ splitPane.setResizeWeight(0.5);
+
+ this.add(splitPane);
+ }
+
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/MapLoader.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/MapLoader.java
new file mode 100644
index 00000000..d391ae78
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/MapLoader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import com.badlogic.gdx.Gdx;
+
+import org.mapsforge.core.model.LatLong;
+import org.mapsforge.map.reader.MapFile;
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.io.File;
+import java.util.prefs.BackingStoreException;
+
+class MapLoader {
+
+ private final MapsforgeMapPanel mapsforgeMapPanel;
+ private final VtmPanel vtmPanel;
+ private final BothMapPositionHandler mapPosition;
+ private File mapFile;
+
+ MapLoader(MapsforgeMapPanel mapsforgeMapPanel, VtmPanel vtmPanel, BothMapPositionHandler mapPosition) {
+
+ this.vtmPanel = vtmPanel;
+ this.mapsforgeMapPanel = mapsforgeMapPanel;
+ this.mapPosition = mapPosition;
+ }
+
+ void loadPrefsMapFile() {
+ String path = Main.prefs.get("loadedMap", "");
+
+ if (!path.isEmpty()) {
+ loadMap(new File(path), false);
+ }
+ }
+
+
+ void loadMap(final File file, final boolean setCenter) {
+ Gdx.app.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mapFile = file;
+
+ //store prefs
+ Main.prefs.put("loadedMap", file.getAbsolutePath());
+ try {
+ Main.prefs.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+
+ mapsforgeMapPanel.loadMap(mapFile, null);
+ vtmPanel.loadMap(mapFile, null);
+ if (setCenter) {
+ MapFile mapsforgeFile = new MapFile(mapFile);
+ LatLong centerPoint = mapsforgeFile.boundingBox().getCenterPoint();
+ mapPosition.setCoordinate(centerPoint.latitude, centerPoint.longitude, (byte) 10);
+ }
+ }
+ });
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/MapsPanel.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/MapsPanel.java
new file mode 100644
index 00000000..89186cb7
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/MapsPanel.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+
+import javax.swing.JPanel;
+
+class MapsPanel extends JPanel {
+
+ private final VtmPanel vtmPanel;
+ private final MapsforgeMapPanel mapsforgeMapPanel;
+
+ MapsPanel(VtmPanel vtm, MapsforgeMapPanel map) {
+ this.setLayout(null);
+ vtmPanel = vtm;
+ mapsforgeMapPanel = map;
+ this.add(vtmPanel);
+ this.add(mapsforgeMapPanel);
+ this.addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ int halfWidth = getWidth() / 2;
+ vtmPanel.setBounds(0, 0, halfWidth, getHeight());
+ mapsforgeMapPanel.setBounds(halfWidth, 0, halfWidth, getHeight());
+ }
+ });
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/Utils.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/Utils.java
new file mode 100644
index 00000000..68ec9685
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/Utils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator;
+
+import java.io.File;
+
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+public class Utils {
+
+ public static String getFile(String initialPath, final String extension, String TitleText) {
+
+ final String ext = extension.replace("*", "");
+
+ JFileChooser chooser = new JFileChooser();
+
+ chooser.setCurrentDirectory(new java.io.File(initialPath));
+ chooser.setDialogTitle(TitleText);
+
+ FileFilter filter = new FileFilter() {
+
+ @Override
+ public String getDescription() {
+ return extension;
+ }
+
+ @Override
+ public boolean accept(File f) {
+ return f.isDirectory() || f.getAbsolutePath().endsWith(ext);
+ }
+ };
+
+ chooser.setFileFilter(filter);
+
+ int returnVal = chooser.showOpenDialog(null);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ return chooser.getSelectedFile().getAbsolutePath();
+ }
+ return null;
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/EditorPane.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/EditorPane.java
new file mode 100644
index 00000000..8cad7dfb
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/EditorPane.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.editor;
+
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
+import org.fife.ui.rsyntaxtextarea.Theme;
+import org.fife.ui.rtextarea.RTextScrollPane;
+import org.oscim.theme.comparator.Main;
+import org.oscim.theme.comparator.MainMenu;
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.awt.BorderLayout;
+import java.io.IOException;
+
+import javax.swing.JPanel;
+
+public class EditorPane extends JPanel {
+
+ private final RSyntaxTextArea textArea;
+
+
+ /**
+ * It's used the RSyntaxTextArea. Show https://github.com/bobbylight/RSyntaxTextArea/wiki
+ */
+ public EditorPane(VtmPanel vtmPanel, MapsforgeMapPanel mapsforgeMapPanel, MainMenu mainMenu) {
+ super(new BorderLayout());
+ textArea = new RSyntaxTextArea(5, 60);
+ textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
+ textArea.setCodeFoldingEnabled(true);
+ RTextScrollPane sp = new RTextScrollPane(textArea);
+ this.add(sp);
+ setTheme(Main.useDarkTheme);
+ add(new Menu(vtmPanel, mapsforgeMapPanel, textArea, mainMenu), BorderLayout.NORTH);
+ }
+
+
+ private void setTheme(boolean dark) {
+ try {
+ Theme theme;
+ if (dark) {
+ theme = Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/dark.xml"));
+ } else {
+ theme = Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/default.xml"));
+ }
+ theme.apply(textArea);
+ } catch (IOException ioe) { // Never happens
+ ioe.printStackTrace();
+ }
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/Menu.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/Menu.java
new file mode 100644
index 00000000..14287971
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/Menu.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.editor;
+
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.oscim.theme.comparator.MainMenu;
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+
+import javax.swing.JButton;
+import javax.swing.JToolBar;
+
+import static org.oscim.theme.comparator.Main.ICON_OPEN;
+import static org.oscim.theme.comparator.Main.ICON_SAVE;
+
+class Menu extends JToolBar {
+
+ private final ThemeLoader themeLoader;
+
+ Menu(VtmPanel vtmPanel, MapsforgeMapPanel mapsforgeMapPanel, RSyntaxTextArea textArea, MainMenu mainMenu) {
+ this.setRollover(false);
+
+
+ JButton openButton = new JButton(ICON_OPEN);
+ openButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ try {
+ themeLoader.selectThemeFile();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ });
+ this.add(openButton);
+ this.addSeparator();
+
+
+ JButton saveButton = new JButton(ICON_SAVE);
+ saveButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // Save xml File and reload Theme
+ try {
+ themeLoader.saveAndReload();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ });
+ this.add(saveButton);
+
+
+ themeLoader = new ThemeLoader(vtmPanel, mapsforgeMapPanel, textArea, mainMenu);
+ }
+
+
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/ThemeLoader.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/ThemeLoader.java
new file mode 100644
index 00000000..81bbfca1
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/editor/ThemeLoader.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.editor;
+
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.oscim.theme.comparator.Main;
+import org.oscim.theme.comparator.MainMenu;
+import org.oscim.theme.comparator.Utils;
+import org.oscim.theme.comparator.mapsforge.MapsforgeMapPanel;
+import org.oscim.theme.comparator.vtm.VtmPanel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.prefs.BackingStoreException;
+
+public class ThemeLoader {
+
+ final private Logger vtmLog = LoggerFactory.getLogger("org.oscim.ThemeLoader");
+ final private Logger mapsforgeLog = LoggerFactory.getLogger("org.mapsforge.ThemeLoader");
+
+
+ final private VtmPanel vtmPanel;
+ final private MapsforgeMapPanel mapsforgeMapPanel;
+ final private RSyntaxTextArea syntaxTextArea;
+
+ private String themePath;
+ private String editorText;
+
+ ThemeLoader(VtmPanel vtmPanel, MapsforgeMapPanel mapsforgeMapPanel, RSyntaxTextArea editorPane, MainMenu mainMenu) {
+ this.vtmPanel = vtmPanel;
+ this.mapsforgeMapPanel = mapsforgeMapPanel;
+ this.syntaxTextArea = editorPane;
+ mainMenu.setThemeLoader(this);
+ }
+
+ public void selectThemeFile() throws IOException {
+ String themePath = Utils.getFile(Main.prefs.get("lastTheme", ""), "*.xml", "Load Theme File");
+
+ if (themePath == null) return;
+
+ //store last selected folder
+ File file = new File(themePath);
+ if (!file.exists() || !file.canRead()) return;
+ File dir = file.getParentFile();
+ Main.prefs.put("lastTheme", dir.getAbsolutePath());
+ try {
+ Main.prefs.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+
+ setTheme(themePath);
+ }
+
+ private void setTheme(String themePath) throws IOException {
+ if (themePath != null) {
+ this.themePath = themePath;
+ BufferedReader br = new BufferedReader(new FileReader(this.themePath));
+ try {
+ StringBuilder sb = new StringBuilder();
+ String line = br.readLine();
+
+ while (line != null) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ line = br.readLine();
+ }
+ editorText = sb.toString();
+
+ } finally {
+ br.close();
+ }
+ syntaxTextArea.setText(editorText);
+
+ syntaxTextArea.setCaretPosition(0);
+
+
+ try {
+ this.mapsforgeMapPanel.setTheme(this.themePath);
+ } catch (Exception e) {
+ mapsforgeLog.error("LoadTheme", e);
+ }
+
+ try {
+ this.vtmPanel.setTheme(this.themePath);
+ } catch (Exception e) {
+ vtmLog.error("LoadTheme", e);
+ }
+ }
+ }
+
+ void saveAndReload() throws IOException {
+ int lastCaretPosition = syntaxTextArea.getCaretPosition();
+
+ editorText = syntaxTextArea.getText();
+
+ Writer out = new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(this.themePath), "UTF-8"));
+ try {
+ out.write(editorText);
+ } finally {
+ out.close();
+ }
+
+ setTheme(themePath);
+ syntaxTextArea.setCaretPosition(lastCaretPosition);
+
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationData.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationData.java
new file mode 100644
index 00000000..24464dec
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationData.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+import org.oscim.theme.comparator.BothMapPositionHandler;
+
+import java.util.Vector;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class LocationData {
+
+
+ public enum Orientation {
+ EAST, WEST, NORTH, SOUTH;
+
+ @Override
+ public String toString() {
+ return this.name().substring(0, 1) + this.name().substring(1).toLowerCase();
+ }
+ }
+
+ private static double latitude;
+ private static double longitude;
+ private static int zoom;
+ private static Orientation ns;
+ private static Orientation ew;
+
+ static {
+ latitude = 47.48135;
+ longitude = 8.20797;
+ ns = Orientation.NORTH;
+ ew = Orientation.EAST;
+ }
+
+ private static Vector listeners = new Vector<>();
+
+ public static void addChangeListener(LocationDataListener listener) {
+ listeners.add(listener);
+ }
+
+
+ private static void notifyChange() {
+ if (blockCircleEvent.get()) return;
+ notifyChange(true);
+ }
+
+ private static AtomicBoolean blockCircleEvent = new AtomicBoolean(false);
+
+ private static void notifyChange(boolean all) {
+
+ if (!all) blockCircleEvent.set(true);
+
+ for (LocationDataListener l : listeners) {
+ if (all) {
+ l.valueChanged();
+ } else {
+ if (!(l instanceof BothMapPositionHandler))
+ l.valueChanged();
+ }
+ }
+ blockCircleEvent.set(false);
+ }
+
+ private LocationData() {
+ }
+
+ public static int getZoom() {
+ return zoom;
+ }
+
+
+ public synchronized static double getLatitude() {
+ return latitude;
+ }
+
+ synchronized static void setLatitude(double latitude) {
+ if (LocationData.latitude != latitude) {
+ LocationData.latitude = latitude;
+ notifyChange();
+ }
+ }
+
+ public synchronized static double getLongitude() {
+ return longitude;
+ }
+
+ public synchronized static void setLongitude(double longitude) {
+ if (LocationData.longitude != longitude) {
+ LocationData.longitude = longitude;
+ notifyChange();
+ }
+ }
+
+ public static Orientation getEW() {
+ return ew;
+ }
+
+ static void setEW(Orientation ew) {
+ if (ew == Orientation.NORTH || ew == Orientation.SOUTH)
+ throw new IllegalArgumentException();
+ if (LocationData.ew != ew) {
+ LocationData.ew = ew;
+ notifyChange();
+ }
+ }
+
+ public static Orientation getNS() {
+ return ns;
+ }
+
+ static void setNS(Orientation ns) {
+ if (ns == Orientation.EAST || ns == Orientation.WEST)
+ throw new IllegalArgumentException();
+ if (LocationData.ns != ns) {
+ LocationData.ns = ns;
+ notifyChange();
+ }
+ }
+
+ public static void setZoom(int zoomLevel) {
+ if (LocationData.zoom != zoomLevel) {
+ LocationData.zoom = zoomLevel;
+ notifyChange();
+ }
+ }
+
+ public static void set(double latitude, double longitude, byte zoomLevel) {
+
+ boolean change = false;
+
+ //set orientation
+ Orientation ns = Orientation.NORTH;
+ Orientation ew = Orientation.EAST;
+
+ if (latitude < 0) {
+ ns = Orientation.SOUTH;
+ latitude *= -1;
+ }
+
+ if (longitude < 0) {
+ ew = Orientation.WEST;
+ longitude *= -1;
+ }
+
+ if (LocationData.ns != ns) {
+ LocationData.ns = ns;
+ change = true;
+ }
+
+ if (LocationData.ew != ew) {
+ LocationData.ew = ew;
+ change = true;
+ }
+
+ if (LocationData.latitude != latitude) {
+ LocationData.latitude = latitude;
+ change = true;
+ }
+
+ if (LocationData.longitude != longitude) {
+ LocationData.longitude = longitude;
+ change = true;
+ }
+
+ if (LocationData.zoom != zoomLevel) {
+ LocationData.zoom = zoomLevel;
+ change = true;
+ }
+
+ if (change) {
+ notifyChange(false);
+ }
+
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationDataListener.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationDataListener.java
new file mode 100644
index 00000000..d40db25a
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationDataListener.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+public interface LocationDataListener {
+ void valueChanged();
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationGPS.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationGPS.java
new file mode 100644
index 00000000..8371fb6b
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationGPS.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+import org.oscim.theme.comparator.Main;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class LocationGPS extends JPanel implements LocationDataListener {
+
+ private final JSpinner latitude = new JSpinner();
+ private final JSpinner longitude = new JSpinner();
+ private final JSpinner zoom = new JSpinner();
+ private final JComboBox ew = new JComboBox<>();
+ private final JComboBox ns = new JComboBox<>();
+
+ LocationGPS() {
+ LocationData.addChangeListener(this);
+
+ JPanel labels = new JPanel(new GridLayout(3, 1));
+ labels.add(new JLabel("Latitude: [Deg]", JLabel.RIGHT));
+ labels.add(new JLabel("Longitude: [Deg]", JLabel.RIGHT));
+ labels.add(new JLabel("Zoom:", JLabel.RIGHT));
+
+ JPanel p1 = new JPanel();
+ p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
+
+ p1.add(latitude);
+ latitude.setPreferredSize(new Dimension(100, 20));
+ p1.add(ns);
+ ns.setPreferredSize(new Dimension(80, 20));
+ {
+ JLabel spacer = new JLabel("");
+ spacer.setPreferredSize(new Dimension(50, 20));
+ p1.add(spacer);
+ }
+
+ JPanel p2 = new JPanel();
+ p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
+
+ p2.add(longitude);
+ longitude.setPreferredSize(new Dimension(100, 20));
+ p2.add(ew);
+ ew.setPreferredSize(new Dimension(80, 20));
+ {
+ JLabel spacer = new JLabel("");
+ spacer.setPreferredSize(new Dimension(50, 20));
+ p2.add(spacer);
+ }
+
+ ChangeListener latitudeChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setLatitude((Double) latitude.getValue());
+ }
+ };
+
+ latitude.setModel(new SpinnerNumberModel(LocationData.getLatitude(), 0, 180, 0.001));
+ latitude.addChangeListener(latitudeChangeListener);
+
+ ns.addItem(LocationData.Orientation.NORTH);
+ ns.addItem(LocationData.Orientation.SOUTH);
+ ns.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LocationData.setNS((LocationData.Orientation) ns.getSelectedItem());
+ }
+ });
+
+ ChangeListener longitudeChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setLongitude((Double) longitude.getValue());
+ }
+ };
+
+ longitude.setModel(new SpinnerNumberModel(LocationData.getLongitude(), 0, 180, 0.001));
+ longitude.addChangeListener(longitudeChangeListener);
+
+ ew.addItem(LocationData.Orientation.EAST);
+ ew.addItem(LocationData.Orientation.WEST);
+ ew.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LocationData.setEW((LocationData.Orientation) ew.getSelectedItem());
+ }
+ });
+
+ JPanel p3 = new JPanel();
+ p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS));
+
+ p3.add(zoom);
+ zoom.setPreferredSize(new Dimension(50, 20));
+ zoom.setModel(new SpinnerNumberModel(LocationData.getZoom(), Main.MIN_ZOOM_LEVEL, Main.MAX_ZOOM_LEVEL, 1));
+ zoom.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setZoom((int) zoom.getValue());
+ }
+ });
+
+
+ this.add(labels);
+ this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ JPanel aroundThis = new JPanel();
+ aroundThis.setLayout(new BoxLayout(aroundThis, BoxLayout.Y_AXIS));
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p1);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p2);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p3);
+ aroundThis.add(jPanel);
+ }
+ this.add(aroundThis);
+ }
+
+ @Override
+ public void valueChanged() {
+ latitude.setValue(LocationData.getLatitude());
+ longitude.setValue(LocationData.getLongitude());
+
+ ns.setSelectedItem(LocationData.getNS());
+ ew.setSelectedItem(LocationData.getEW());
+ zoom.setValue(LocationData.getZoom());
+
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationNMEA.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationNMEA.java
new file mode 100644
index 00000000..53455a50
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationNMEA.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+import org.oscim.theme.comparator.Main;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class LocationNMEA extends JPanel implements LocationDataListener {
+
+
+ private final JSpinner latitude = new JSpinner();
+ private final JSpinner longitude = new JSpinner();
+ private final JSpinner zoom = new JSpinner();
+ private final JComboBox ew = new JComboBox<>();
+ private final JComboBox ns = new JComboBox<>();
+
+ LocationNMEA() {
+ LocationData.addChangeListener(this);
+
+ JPanel labels = new JPanel(new GridLayout(3, 1));
+ labels.add(new JLabel("Latitude:", JLabel.RIGHT));
+ labels.add(new JLabel("Longitude:", JLabel.RIGHT));
+ labels.add(new JLabel("Zoom:", JLabel.RIGHT));
+
+ JPanel p1 = new JPanel();
+ p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
+
+ p1.add(latitude);
+ latitude.setPreferredSize(new Dimension(100, 20));
+ p1.add(ns);
+ ns.setPreferredSize(new Dimension(80, 20));
+ {
+ JLabel spacer = new JLabel("");
+ spacer.setPreferredSize(new Dimension(50, 20));
+ p1.add(spacer);
+ }
+
+ JPanel p2 = new JPanel();
+ p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
+
+ p2.add(longitude);
+ longitude.setPreferredSize(new Dimension(100, 20));
+ p2.add(ew);
+ ew.setPreferredSize(new Dimension(80, 20));
+ {
+ JLabel spacer = new JLabel("");
+ spacer.setPreferredSize(new Dimension(50, 20));
+ p2.add(spacer);
+ }
+
+ ChangeListener latitudeChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setLatitude(nmea2degree((Double) latitude.getValue()));
+ }
+ };
+
+ latitude.setModel(new SpinnerNumberModel(degree2nmea(LocationData.getLatitude()), 0, 9000, 0.002));
+ latitude.addChangeListener(latitudeChangeListener);
+
+ ns.addItem(LocationData.Orientation.NORTH);
+ ns.addItem(LocationData.Orientation.SOUTH);
+ ns.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LocationData.setNS((LocationData.Orientation) ns.getSelectedItem());
+ }
+ });
+
+ ChangeListener longitudeChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setLongitude(nmea2degree((Double) longitude.getValue()));
+ }
+ };
+
+ longitude.setModel(new SpinnerNumberModel(degree2nmea(LocationData.getLongitude()), 0, 18000, 0.002));
+ longitude.addChangeListener(longitudeChangeListener);
+
+ ew.addItem(LocationData.Orientation.EAST);
+ ew.addItem(LocationData.Orientation.WEST);
+ ew.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LocationData.setEW((LocationData.Orientation) ew.getSelectedItem());
+ }
+ });
+
+
+ JPanel p3 = new JPanel();
+ p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS));
+
+ p3.add(zoom);
+ zoom.setPreferredSize(new Dimension(50, 20));
+ zoom.setModel(new SpinnerNumberModel(LocationData.getZoom(), Main.MIN_ZOOM_LEVEL, Main.MAX_ZOOM_LEVEL, 1));
+ zoom.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setZoom((int) zoom.getValue());
+ }
+ });
+
+ this.add(labels);
+ this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ JPanel aroundThis = new JPanel();
+ aroundThis.setLayout(new BoxLayout(aroundThis, BoxLayout.Y_AXIS));
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p1);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p2);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p3);
+ aroundThis.add(jPanel);
+ }
+
+ this.add(aroundThis);
+ }
+
+ @Override
+ public void valueChanged() {
+ latitude.setValue(degree2nmea(LocationData.getLatitude()));
+ longitude.setValue(degree2nmea(LocationData.getLongitude()));
+
+ ns.setSelectedItem(LocationData.getNS());
+ ew.setSelectedItem(LocationData.getEW());
+
+ zoom.setValue(LocationData.getZoom());
+
+ }
+
+ private double degree2nmea(double value) {
+ double degree = (int) value;
+ double minute = (int) (value * 60 - degree * 60);
+ double second = value * 60 - (int) (value * 60);
+
+ return (double) Math.round((degree * 100 + minute + second) * 1000) / 1000;
+ }
+
+ private double nmea2degree(double value) {
+ int degree = (int) value / 100;
+ int minute = (int) (value - degree * 100);
+ double second = ((value - (int) value) * 60);
+
+ return degree + ((double) minute / 60) + second / 60 / 60;
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationNormal.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationNormal.java
new file mode 100644
index 00000000..ffd77817
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationNormal.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+import org.oscim.theme.comparator.Main;
+import org.oscim.theme.comparator.location.LocationData.Orientation;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class LocationNormal extends JPanel implements LocationDataListener {
+
+ private final JSpinner latitudeDegree = new JSpinner();
+ private final JSpinner longitudeDegree = new JSpinner();
+ private final JSpinner latitudeMinute = new JSpinner();
+ private final JSpinner longitudeMinute = new JSpinner();
+ private final JSpinner latitudeSecond = new JSpinner();
+ private final JSpinner longitudeSecond = new JSpinner();
+ private final JSpinner zoom = new JSpinner();
+ private final JComboBox ew = new JComboBox<>();
+ private final JComboBox ns = new JComboBox<>();
+ private final AtomicBoolean blockChangeListener = new AtomicBoolean(false);
+
+ LocationNormal() {
+ LocationData.addChangeListener(this);
+
+ JPanel labels = new JPanel(new GridLayout(3, 1));
+ labels.add(new JLabel("Latitude:", JLabel.RIGHT));
+ labels.add(new JLabel("Longitude:", JLabel.RIGHT));
+ labels.add(new JLabel("Zoom:", JLabel.RIGHT));
+
+ JPanel p1 = new JPanel();
+ p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
+
+ latitudeDegree.setPreferredSize(new Dimension(50, 20));
+ p1.add(latitudeDegree);
+ latitudeDegree.setPreferredSize(new Dimension(50, 20));
+ p1.add(latitudeMinute);
+ latitudeMinute.setPreferredSize(new Dimension(50, 20));
+ p1.add(latitudeSecond);
+ latitudeSecond.setPreferredSize(new Dimension(50, 20));
+ p1.add(ns);
+ ns.setPreferredSize(new Dimension(80, 20));
+
+ JPanel p2 = new JPanel();
+ p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
+
+ p2.add(longitudeDegree);
+ longitudeDegree.setPreferredSize(new Dimension(50, 20));
+ p2.add(longitudeMinute);
+ longitudeMinute.setPreferredSize(new Dimension(50, 20));
+ p2.add(longitudeSecond);
+ longitudeSecond.setPreferredSize(new Dimension(50, 20));
+ p2.add(ew);
+ ew.setPreferredSize(new Dimension(80, 20));
+
+ ChangeListener latitudeChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (blockChangeListener.get()) {
+ double latitude = LocationData.getLatitude();
+ assert latitude >= 0;
+ int lat = (int) Math.round(latitude * 3600);
+ latitudeSecond.setValue(lat % 60);
+ lat = lat / 60;
+ latitudeMinute.setValue(lat % 60);
+ lat = lat / 60;
+ latitudeDegree.setValue(lat);
+ } else {
+ Integer degree = (Integer) latitudeDegree.getValue();
+ Integer minute = (Integer) latitudeMinute.getValue();
+ Integer second = (Integer) latitudeSecond.getValue();
+
+ if (degree == 90) {
+ if (minute == -1 && second == 0) {
+ latitudeSecond.setValue(59);
+ } else if (minute == 0 && second == -1) {
+ latitudeMinute.setValue(59);
+ } else if (minute == -1 && second == 59) {
+ latitudeMinute.setValue(59);
+ } else if (minute == 59 && second == -1) {
+ latitudeSecond.setValue(59);
+ } else if (minute == 59 && second == 59) {
+ latitudeDegree.setValue(89);
+ } else if (minute == 0 && second == 0) {
+ Double tmp = allInOne(degree, minute, second);
+ LocationData.setLatitude(tmp);
+ } else {
+ latitudeSecond.setValue(0);
+ latitudeMinute.setValue(0);
+ }
+ } else if (second == 60) {
+ latitudeSecond.setValue(0);
+ latitudeMinute.setValue((Integer) latitudeMinute.getValue() + 1);
+ } else if (second == -1) {
+ if (minute == 0) {
+ latitudeSecond.setValue(0);
+ } else {
+ latitudeSecond.setValue(59);
+ latitudeMinute.setValue((Integer) latitudeMinute.getValue() - 1);
+ }
+ } else if (minute == 60) {
+ latitudeMinute.setValue(0);
+ latitudeDegree.setValue((Integer) latitudeDegree.getValue() + 1);
+ } else if (minute == -1) {
+ if (degree == 0) {
+ latitudeMinute.setValue(0);
+ } else {
+ latitudeMinute.setValue(59);
+ latitudeDegree.setValue((Integer) latitudeDegree.getValue() - 1);
+ }
+ } else {
+ LocationData.setLatitude(allInOne(degree, minute, second));
+ }
+ }
+ }
+ };
+
+ latitudeDegree.setModel(new SpinnerNumberModel(degree(LocationData.getLatitude()), 0, 90, 1));
+ latitudeDegree.addChangeListener(latitudeChangeListener);
+
+ latitudeMinute.setModel(new SpinnerNumberModel(minute(LocationData.getLatitude()), -1, 60, 1));
+ latitudeMinute.addChangeListener(latitudeChangeListener);
+
+ latitudeSecond.setModel(new SpinnerNumberModel(second(LocationData.getLatitude()), -1, 60, 1));
+ latitudeSecond.addChangeListener(latitudeChangeListener);
+
+ ns.addItem(Orientation.NORTH);
+ ns.addItem(Orientation.SOUTH);
+ ns.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LocationData.setNS((Orientation) ns.getSelectedItem());
+ }
+ });
+
+ ChangeListener longitudeChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (blockChangeListener.get()) {
+ double longitude = LocationData.getLongitude();
+ assert longitude >= 0;
+ int lng = (int) Math.round(longitude * 3600);
+ longitudeSecond.setValue(lng % 60);
+ lng = lng / 60;
+ longitudeMinute.setValue(lng % 60);
+ lng = lng / 60;
+ longitudeDegree.setValue(lng);
+ } else {
+ Integer degree = (Integer) longitudeDegree.getValue();
+ Integer minute = (Integer) longitudeMinute.getValue();
+ Integer second = (Integer) longitudeSecond.getValue();
+
+ if (degree == 180) {
+ if (minute == -1 && second == 0) {
+ longitudeSecond.setValue(59);
+ } else if (minute == 0 && second == -1) {
+ longitudeMinute.setValue(59);
+ } else if (minute == -1 && second == 59) {
+ longitudeMinute.setValue(59);
+ } else if (minute == 59 && second == -1) {
+ longitudeSecond.setValue(59);
+ } else if (minute == 59 && second == 59) {
+ longitudeDegree.setValue(179);
+ } else if (minute == 0 && second == 0) {
+ Double tmp = allInOne(degree, minute, second);
+ LocationData.setLongitude(tmp);
+ } else {
+ longitudeSecond.setValue(0);
+ longitudeMinute.setValue(0);
+ }
+ } else if (second == 60) {
+ longitudeSecond.setValue(0);
+ longitudeMinute.setValue((Integer) longitudeMinute.getValue() + 1);
+ } else if (second == -1) {
+ if (minute == 0) {
+ longitudeSecond.setValue(0);
+ } else {
+ longitudeSecond.setValue(59);
+ longitudeMinute.setValue((Integer) longitudeMinute.getValue() - 1);
+ }
+ } else if (minute == 60) {
+ longitudeMinute.setValue(0);
+ longitudeDegree.setValue((Integer) longitudeDegree.getValue() + 1);
+ } else if (minute == -1) {
+ if (degree == 0) {
+ longitudeMinute.setValue(0);
+ } else {
+ longitudeMinute.setValue(59);
+ longitudeDegree.setValue((Integer) longitudeDegree.getValue() - 1);
+ }
+ } else {
+ LocationData.setLongitude(allInOne(degree, minute, second));
+ }
+ }
+ }
+ };
+
+ longitudeDegree.setModel(new SpinnerNumberModel(degree(LocationData.getLongitude()), 0, 180, 1));
+ longitudeDegree.addChangeListener(longitudeChangeListener);
+
+ longitudeMinute.setModel(new SpinnerNumberModel(minute(LocationData.getLongitude()), -1, 60, 1));
+ longitudeMinute.addChangeListener(longitudeChangeListener);
+
+ longitudeSecond.setModel(new SpinnerNumberModel(second(LocationData.getLongitude()), -1, 60, 1));
+ longitudeSecond.addChangeListener(longitudeChangeListener);
+
+ ew.addItem(Orientation.EAST);
+ ew.addItem(Orientation.WEST);
+ ew.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ LocationData.setEW((Orientation) ew.getSelectedItem());
+ }
+ });
+
+ JPanel p3 = new JPanel();
+ p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS));
+
+ p3.add(zoom);
+ zoom.setPreferredSize(new Dimension(50, 20));
+ zoom.setModel(new SpinnerNumberModel(LocationData.getZoom(), Main.MIN_ZOOM_LEVEL, Main.MAX_ZOOM_LEVEL, 1));
+ zoom.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (blockChangeListener.get()) return;
+ LocationData.setZoom((int) zoom.getValue());
+ }
+ });
+
+
+ this.add(labels);
+ this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ JPanel aroundThis = new JPanel();
+ aroundThis.setLayout(new BoxLayout(aroundThis, BoxLayout.Y_AXIS));
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p1);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p2);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p3);
+ aroundThis.add(jPanel);
+ }
+ this.add(aroundThis);
+ }
+
+
+ @Override
+ public void valueChanged() {
+ blockChangeListener.set(true);
+ latitudeDegree.setValue(degree(LocationData.getLatitude()));
+ longitudeDegree.setValue(degree(LocationData.getLongitude()));
+
+ latitudeMinute.setValue(minute(LocationData.getLatitude()));
+ longitudeMinute.setValue(minute(LocationData.getLongitude()));
+
+ latitudeSecond.setValue(second(LocationData.getLatitude()));
+ longitudeSecond.setValue(second(LocationData.getLongitude()));
+
+ ns.setSelectedItem(LocationData.getNS());
+ ew.setSelectedItem(LocationData.getEW());
+
+ zoom.setValue(LocationData.getZoom());
+
+ blockChangeListener.set(false);
+ }
+
+ private double allInOne(int degree, int minute, int second) {
+ return degree + ((double) minute / 60) + (double) second / 60 / 60;
+ }
+
+ private int degree(double value) {
+ return (int) (Math.round(value * 3600) / 3600);
+ }
+
+ private int minute(double value) {
+ return (int) (Math.round(value * 3600) / 60 % 60);
+ }
+
+ private int second(double value) {
+ return (int) (Math.round(value * 3600) % 60);
+ }
+
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationPane.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationPane.java
new file mode 100644
index 00000000..2aaedbd7
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationPane.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+
+public class LocationPane extends JPanel {
+
+ public LocationPane() {
+
+ this.setBorder(BorderFactory.createTitledBorder("Location"));
+
+ JTabbedPane tabs = new JTabbedPane();
+
+ tabs.addTab("Normal", new LocationNormal());
+ tabs.addTab("NMEA Format", new LocationNMEA());
+ tabs.addTab("GPS Format", new LocationGPS());
+ tabs.addTab("Tile Format", new LocationTile());
+
+ this.add(tabs);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationTile.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationTile.java
new file mode 100644
index 00000000..e92df2c6
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/location/LocationTile.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.location;
+
+import org.mapsforge.core.model.LatLong;
+import org.mapsforge.core.model.Tile;
+import org.mapsforge.core.util.MercatorProjection;
+import org.oscim.theme.comparator.Main;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class LocationTile extends JPanel implements LocationDataListener {
+
+ private final JSpinner tileX = new JSpinner();
+ private final JSpinner tileY = new JSpinner();
+ private final JSpinner tileZ = new JSpinner();
+ private final AtomicBoolean blockChangeListener = new AtomicBoolean(false);
+
+ LocationTile() {
+ LocationData.addChangeListener(this);
+
+ JPanel labels = new JPanel(new GridLayout(3, 1));
+ labels.add(new JLabel("Tile X:", JLabel.RIGHT));
+ labels.add(new JLabel("Tile Y:", JLabel.RIGHT));
+ labels.add(new JLabel("Tile Z:", JLabel.RIGHT));
+
+ JPanel p1 = new JPanel();
+ p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
+
+ p1.add(tileX);
+ tileX.setPreferredSize(new Dimension(100, 20));
+ {
+ JLabel spacer = new JLabel("");
+ spacer.setPreferredSize(new Dimension(50, 20));
+ p1.add(spacer);
+ }
+
+ JPanel p2 = new JPanel();
+ p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
+
+ p2.add(tileY);
+ tileY.setPreferredSize(new Dimension(100, 20));
+ {
+ JLabel spacer = new JLabel("");
+ spacer.setPreferredSize(new Dimension(50, 20));
+ p2.add(spacer);
+ }
+
+ JPanel p3 = new JPanel();
+ p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS));
+
+ p3.add(tileZ);
+ tileZ.setPreferredSize(new Dimension(50, 20));
+
+
+ ChangeListener tileNumberChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (blockChangeListener.get()) return;
+ try {
+ Tile tile = new Tile((int) tileX.getValue(), (int) tileY.getValue(), ((Integer) tileZ.getValue()).byteValue(), 256);
+ LatLong latLon = tile.getBoundingBox().getCenterPoint();
+
+ LocationData.setLatitude(latLon.latitude);
+ LocationData.setLongitude(latLon.longitude);
+ LocationData.setZoom((Integer) tileZ.getValue());
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ }
+ };
+
+ ChangeListener zoomChangeListener = new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ LocationData.setZoom((Integer) tileZ.getValue());
+ }
+ };
+
+ int maxTileNumber = Tile.getMaxTileNumber((byte) Main.MAX_ZOOM_LEVEL);
+
+ tileX.setModel(new SpinnerNumberModel(0, 0, maxTileNumber, 1));
+ tileY.setModel(new SpinnerNumberModel(0, 0, maxTileNumber, 1));
+ tileZ.setModel(new SpinnerNumberModel(0, Main.MIN_ZOOM_LEVEL, Main.MAX_ZOOM_LEVEL, 1));
+
+
+ tileX.addChangeListener(tileNumberChangeListener);
+ tileY.addChangeListener(tileNumberChangeListener);
+ tileZ.addChangeListener(zoomChangeListener);
+
+
+ this.add(labels);
+ this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ JPanel aroundThis = new JPanel();
+ aroundThis.setLayout(new BoxLayout(aroundThis, BoxLayout.Y_AXIS));
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p1);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p2);
+ aroundThis.add(jPanel);
+ }
+ {
+ JPanel jPanel = new JPanel();
+ jPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ jPanel.add(p3);
+ aroundThis.add(jPanel);
+ }
+ this.add(aroundThis);
+ }
+
+ @Override
+ public void valueChanged() {
+ blockChangeListener.set(true);
+ tileX.setValue(MercatorProjection.longitudeToTileX(LocationData.getLongitude(), (byte) LocationData.getZoom()));
+ tileY.setValue(MercatorProjection.latitudeToTileY(LocationData.getLatitude(), (byte) LocationData.getZoom()));
+ tileZ.setValue(LocationData.getZoom());
+ blockChangeListener.set(false);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/AllAppender.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/AllAppender.java
new file mode 100644
index 00000000..ae16e006
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/AllAppender.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+public class AllAppender extends BaseAppender {
+
+ @Override
+ boolean canLogClass(String className) {
+ return true;
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/AllLoggingPane.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/AllLoggingPane.java
new file mode 100644
index 00000000..9b81084b
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/AllLoggingPane.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+import org.oscim.theme.comparator.Main;
+
+public class AllLoggingPane extends BaseLoggingPane {
+ public AllLoggingPane() {
+ super(Main.ALL_APPENDER);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/BaseAppender.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/BaseAppender.java
new file mode 100644
index 00000000..262d4ff1
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/BaseAppender.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+import com.badlogic.gdx.utils.StringBuilder;
+
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.core.AppenderBase;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.util.CachingDateFormatter;
+
+public abstract class BaseAppender extends AppenderBase {
+ private RSyntaxTextArea textArea;
+ private StringBuilder stringBuilder = new StringBuilder();
+ private final CachingDateFormatter cachingDateFormatter = new CachingDateFormatter("HH:mm:ss.SSS");
+ private final ThrowableProxyConverter tpc = new ThrowableProxyConverter();
+
+
+ //Layout settings
+ private final boolean writeTime = true;
+ private final boolean writeThread = false;
+ private final boolean writeLevel = true;
+ private final boolean writeShortLoggerName = true;
+
+ BaseAppender() {
+
+ //set stackTrace count to 10
+ List optionList = new ArrayList<>();
+ optionList.add("10");
+ tpc.setOptionList(optionList);
+ tpc.start();
+ }
+
+ @Override
+ protected void append(ILoggingEvent eventObject) {
+ if (eventObject != null && canLogClass(eventObject.getLoggerName())) {
+ stringBuilder.append(doLayout(eventObject));
+ String areaText = stringBuilder.toString();
+ this.textArea.setText(areaText);
+ this.textArea.setCaretPosition(areaText.length());
+ }
+//TODO set Highlight for LogLevel [WARN], [ERROR]
+ }
+
+ String doLayout(ILoggingEvent event) {
+ if (!isStarted()) {
+ return CoreConstants.EMPTY_STRING;
+ }
+ java.lang.StringBuilder sb = new java.lang.StringBuilder();
+
+ if (writeTime) {
+ sb.append(cachingDateFormatter.format(event.getTimeStamp()));
+ sb.append(" ");
+ }
+
+ if (writeThread) {
+ sb.append("[");
+ sb.append(event.getThreadName());
+ sb.append("] ");
+ }
+
+ if (writeLevel) {
+ sb.append(event.getLevel().toString());
+ sb.append(" ");
+ }
+
+ if (writeShortLoggerName) {
+ String name = event.getLoggerName();
+ int pos = name.lastIndexOf(".") + 1;
+ sb.append(name.substring(pos));
+ } else {
+ sb.append(event.getLoggerName());
+ }
+
+
+ sb.append(" - ");
+ sb.append(event.getFormattedMessage());
+ sb.append(CoreConstants.LINE_SEPARATOR);
+ IThrowableProxy tp = event.getThrowableProxy();
+ if (tp != null) {
+ String stackTrace = tpc.convert(event);
+ sb.append(stackTrace);
+ }
+ return sb.toString();
+ }
+
+ void setTextArea(RSyntaxTextArea textArea) {
+ this.textArea = textArea;
+ }
+
+ abstract boolean canLogClass(String className);
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/BaseLoggingPane.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/BaseLoggingPane.java
new file mode 100644
index 00000000..3a18b243
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/BaseLoggingPane.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.fife.ui.rsyntaxtextarea.Theme;
+import org.fife.ui.rtextarea.RTextScrollPane;
+import org.oscim.theme.comparator.Main;
+
+import java.awt.BorderLayout;
+import java.io.IOException;
+
+import javax.swing.JPanel;
+
+abstract class BaseLoggingPane extends JPanel {
+
+ private final RSyntaxTextArea textArea;
+
+ BaseLoggingPane(BaseAppender appender) {
+ super(new BorderLayout());
+ textArea = new RSyntaxTextArea(5, 60);
+ textArea.enableInputMethods(false);
+ RTextScrollPane sp = new RTextScrollPane(textArea);
+ sp.setLineNumbersEnabled(false);
+ this.add(sp);
+ setTheme(Main.useDarkTheme);
+ appender.setTextArea(textArea);
+ }
+
+ private void setTheme(boolean dark) {
+ try {
+ Theme theme;
+ if (dark) {
+ theme = Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/dark.xml"));
+ } else {
+ theme = Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/default.xml"));
+ }
+ theme.apply(textArea);
+ } catch (IOException ioe) { // Never happens
+ ioe.printStackTrace();
+ }
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/MapsforgeAppender.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/MapsforgeAppender.java
new file mode 100644
index 00000000..50f49586
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/MapsforgeAppender.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+public class MapsforgeAppender extends BaseAppender {
+
+ @Override
+ boolean canLogClass(String className) {
+ return className.startsWith("org.mapsforge.");
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/MapsforgeLoggingPane.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/MapsforgeLoggingPane.java
new file mode 100644
index 00000000..b3a8e570
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/MapsforgeLoggingPane.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+import org.oscim.theme.comparator.Main;
+
+public class MapsforgeLoggingPane extends BaseLoggingPane {
+ public MapsforgeLoggingPane() {
+ super(Main.MAPSFORGE_APPENDER);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/VtmAppender.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/VtmAppender.java
new file mode 100644
index 00000000..58856007
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/VtmAppender.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+public class VtmAppender extends BaseAppender {
+
+ @Override
+ boolean canLogClass(String className) {
+ return className.startsWith("org.oscim.") && !className.startsWith("org.oscim.theme.comparator.");
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/VtmLoggingPane.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/VtmLoggingPane.java
new file mode 100644
index 00000000..31c0f73a
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/logging/VtmLoggingPane.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.logging;
+
+import org.oscim.theme.comparator.Main;
+
+public class VtmLoggingPane extends BaseLoggingPane {
+ public VtmLoggingPane() {
+ super(Main.VTM_APPENDER);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/AwtMapView.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/AwtMapView.java
new file mode 100644
index 00000000..b0bb0bdd
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/AwtMapView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.mapsforge;
+
+import org.mapsforge.map.awt.input.MapViewComponentListener;
+import org.mapsforge.map.awt.view.MapView;
+import org.oscim.theme.comparator.BothMapPositionHandler;
+
+import java.awt.Color;
+import java.awt.Graphics;
+
+public class AwtMapView extends MapView {
+
+ private static final long serialVersionUID = 1L;
+ private final MapPanelMouseListener mouseEventListener = new MapPanelMouseListener(this);
+
+ AwtMapView() {
+ super();
+ addMyListeners();
+ }
+
+ @Override
+ public void paint(Graphics graphics) {
+ super.paint(graphics);
+ int xc = this.getWidth() / 2;
+ int yc = this.getHeight() / 2;
+
+ graphics.setColor(Color.RED);
+ graphics.drawLine(xc - 50, yc - 50, xc + 50, yc + 50);
+ graphics.drawLine(xc - 50, yc + 50, xc + 50, yc - 50);
+ graphics.drawOval(xc - 25, yc - 25, 50, 50);
+ }
+
+
+ @Override
+ public void addListeners() {
+ //do nothing
+ }
+
+ void addMyListeners() {
+ addComponentListener(new MapViewComponentListener(this));
+ addMouseListener(mouseEventListener);
+ addMouseMotionListener(mouseEventListener);
+ addMouseWheelListener(mouseEventListener);
+ }
+
+ void setMapPositionHandler(BothMapPositionHandler mapPositionHandler) {
+ mouseEventListener.setMapPositionHandler(mapPositionHandler);
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/MapPanelMouseListener.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/MapPanelMouseListener.java
new file mode 100644
index 00000000..e76d19ef
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/MapPanelMouseListener.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.mapsforge;
+
+import org.mapsforge.map.awt.input.MouseEventListener;
+import org.mapsforge.map.view.MapView;
+import org.oscim.theme.comparator.BothMapPositionHandler;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+
+public class MapPanelMouseListener extends MouseEventListener {
+
+ private BothMapPositionHandler mapPositionHandler;
+ private final MapView mapView;
+
+ MapPanelMouseListener(MapView mapView) {
+ super(mapView);
+ this.mapView = mapView;
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ super.mouseDragged(e);
+ if (this.mapPositionHandler != null) {
+ this.mapPositionHandler.mapPositionChangedFromMapPanel(this.mapView.getModel().mapViewPosition.getMapPosition());
+ }
+ }
+
+ @Override
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ super.mouseWheelMoved(e);
+ if (this.mapPositionHandler != null) {
+ this.mapPositionHandler.mapPositionChangedFromMapPanel(this.mapView.getModel().mapViewPosition.getMapPosition());
+ }
+ }
+
+ void setMapPositionHandler(BothMapPositionHandler mapPositionHandler) {
+ this.mapPositionHandler = mapPositionHandler;
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/MapsforgeMapPanel.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/MapsforgeMapPanel.java
new file mode 100644
index 00000000..a651cc83
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/mapsforge/MapsforgeMapPanel.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.mapsforge;
+
+import org.mapsforge.core.graphics.GraphicFactory;
+import org.mapsforge.core.model.LatLong;
+import org.mapsforge.map.awt.graphics.AwtGraphicFactory;
+import org.mapsforge.map.awt.input.MapViewComponentListener;
+import org.mapsforge.map.awt.util.JavaPreferences;
+import org.mapsforge.map.awt.view.MapView;
+import org.mapsforge.map.layer.Layer;
+import org.mapsforge.map.layer.LayerManager;
+import org.mapsforge.map.layer.Layers;
+import org.mapsforge.map.layer.cache.FileSystemTileCache;
+import org.mapsforge.map.layer.cache.InMemoryTileCache;
+import org.mapsforge.map.layer.cache.TileCache;
+import org.mapsforge.map.layer.cache.TwoLevelTileCache;
+import org.mapsforge.map.layer.renderer.TileRendererLayer;
+import org.mapsforge.map.model.MapViewPosition;
+import org.mapsforge.map.model.Model;
+import org.mapsforge.map.model.common.PreferencesFacade;
+import org.mapsforge.map.reader.MapFile;
+import org.mapsforge.map.rendertheme.ExternalRenderTheme;
+import org.mapsforge.map.rendertheme.InternalRenderTheme;
+import org.mapsforge.map.rendertheme.XmlRenderTheme;
+import org.oscim.theme.comparator.BothMapPositionHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.prefs.Preferences;
+
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+
+public class MapsforgeMapPanel extends JPanel {
+ private final static Logger log = LoggerFactory.getLogger(MapsforgeMapPanel.class);
+ private final GraphicFactory GRAPHIC_FACTORY = AwtGraphicFactory.INSTANCE;
+ private AwtMapView mapView;
+
+ private File mapFile, themeFile;
+
+ public MapsforgeMapPanel() {
+ this.setLayout(null);
+ mapView = createMapView();
+
+ PreferencesFacade preferencesFacade = new JavaPreferences(Preferences.userNodeForPackage(MapView.class));
+ final Model model = mapView.getModel();
+ model.init(preferencesFacade);
+
+ this.setBorder(BorderFactory.createTitledBorder("MAPSFORGE-Map"));
+ this.add(mapView);
+ mapView.setVisible(true);
+
+ this.addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ super.componentResized(e);
+ mapView.setBounds(10, 20, getWidth() - 20, getHeight() - 30);
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ super.componentMoved(e);
+ mapView.setBounds(10, 20, getWidth() - 20, getHeight() - 30);
+ }
+ });
+
+ }
+
+ private void addLayers(MapView mapView, File mapPath, File themePath) {
+ LayerManager layerManager = mapView.getLayerManager();
+ Layers layers = layerManager.getLayers();
+ TileCache tileCache = createTileCache();
+ layers.clear();
+ layers.add(createTileRendererLayer(tileCache, mapView.getModel().mapViewPosition, mapPath, themePath));
+ }
+
+ private Layer createTileRendererLayer(TileCache tileCache, MapViewPosition mapViewPosition, File mapFile, File themeFile) {
+
+ TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, new MapFile(mapFile), mapViewPosition, false, true, true, GRAPHIC_FACTORY);
+
+ if (themeFile != null) {
+
+ XmlRenderTheme renderTheme;
+ try {
+ renderTheme = new ExternalRenderTheme(themeFile);
+ } catch (FileNotFoundException e) {
+ renderTheme = InternalRenderTheme.OSMARENDER;
+ }
+ tileRendererLayer.setXmlRenderTheme(renderTheme);
+
+ } else {
+ tileRendererLayer.setXmlRenderTheme(InternalRenderTheme.OSMARENDER);
+ }
+
+ return tileRendererLayer;
+ }
+
+ private AwtMapView createMapView() {
+ AwtMapView mapView = new AwtMapView();
+ mapView.getFpsCounter().setVisible(false);
+ mapView.addComponentListener(new MapViewComponentListener(mapView));
+ return mapView;
+ }
+
+ private TileCache createTileCache() {
+ TileCache firstLevelTileCache = new InMemoryTileCache(64);
+ File cacheDirectory = new File(System.getProperty("java.io.tmpdir"), "mapsforge");
+ TileCache secondLevelTileCache = new FileSystemTileCache(1024, cacheDirectory, GRAPHIC_FACTORY);
+ return new TwoLevelTileCache(firstLevelTileCache, secondLevelTileCache);
+ }
+
+ public void loadMap(File mapFile, File themeFile) {
+ log.debug("reload MAP:{} THEME:{}", mapFile, themeFile);
+ this.mapFile = mapFile;
+ this.themeFile = themeFile;
+ addLayers(mapView, this.mapFile, this.themeFile);
+ }
+
+ public void setCoordinate(double latidude, double longitude, byte zoomLevel) {
+ mapView.setCenter(new LatLong(latidude, longitude));
+ mapView.setZoomLevel(zoomLevel);
+ }
+
+ public void setMapPositionHandler(BothMapPositionHandler mapPositionHandler) {
+ mapView.setMapPositionHandler(mapPositionHandler);
+ }
+
+ public void setTheme(String themePath) {
+ loadMap(this.mapFile, new File(themePath));
+ }
+}
\ No newline at end of file
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/CenterCrossLayer.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/CenterCrossLayer.java
new file mode 100644
index 00000000..b5573962
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/CenterCrossLayer.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.vtm;
+
+import com.badlogic.gdx.math.MathUtils;
+import com.badlogic.gdx.utils.Disposable;
+
+import org.oscim.backend.canvas.Color;
+import org.oscim.backend.canvas.Paint;
+import org.oscim.core.GeometryBuffer;
+import org.oscim.layers.GenericLayer;
+import org.oscim.map.Map;
+import org.oscim.renderer.BucketRenderer;
+import org.oscim.renderer.GLViewport;
+import org.oscim.renderer.bucket.LineBucket;
+import org.oscim.theme.styles.LineStyle;
+
+public class CenterCrossLayer extends GenericLayer implements Disposable {
+
+ private static final float crossLength = 50;
+ private static final float crossWidth = 1.7f;
+ private static final float circleRadius = 25;
+ private static final int drawingColor = Color.RED;
+
+ CenterCrossLayer(Map map) {
+ super(map, new Renderer());
+ ((Renderer) this.mRenderer).setLayer(this);
+ }
+
+ @Override
+ public void dispose() {
+ ((Renderer) this.mRenderer).dispose();
+ }
+
+ private static class Renderer extends BucketRenderer {
+
+ final LineBucket ll = buckets.addLineBucket(0,
+ new LineStyle(drawingColor, crossWidth, Paint.Cap.ROUND));
+
+
+ final GeometryBuffer g1 = new GeometryBuffer(2, 1);
+ final GeometryBuffer g2 = new GeometryBuffer(2, 1);
+ final GeometryBuffer g3 = new GeometryBuffer(2, 1);
+
+ private CenterCrossLayer centerCrossLayer;
+
+ @Override
+ public void update(GLViewport v) {
+ buckets.clear();
+ if (!centerCrossLayer.isEnabled()) return;
+ mMapPosition.copy(v.pos);
+ buckets.set(ll);
+
+ g1.clear();
+ g1.startLine();
+ g1.addPoint(-crossLength, -crossLength);
+ g1.addPoint(crossLength, crossLength);
+ ll.addLine(g1);
+
+ g2.clear();
+ g2.startLine();
+ g2.addPoint(-crossLength, crossLength);
+ g2.addPoint(crossLength, -crossLength);
+ ll.addLine(g2);
+
+ g3.clear();
+ // calculate segment count
+ float alpha = (360 * 5) / (MathUtils.PI2 * circleRadius);
+ int segmente = Math.max(16, (int) (360 / alpha));
+
+ // calculate theta step
+ float thetaStep = (MathUtils.PI2 / segmente);
+
+ g3.startLine();
+ for (float i = 0, n = MathUtils.PI2 + thetaStep; i < n; i += thetaStep) {
+ g3.addPoint(circleRadius * MathUtils.cos(i), circleRadius * MathUtils.sin(i));
+ }
+
+ ll.addLine(g3);
+
+ compile();
+ }
+
+ void setLayer(CenterCrossLayer centerCrossLayer) {
+ this.centerCrossLayer = centerCrossLayer;
+ }
+
+ void dispose() {
+ centerCrossLayer = null;
+ }
+ }
+
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapAdapter.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapAdapter.java
new file mode 100644
index 00000000..7aa874f4
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapAdapter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.vtm;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.utils.Timer;
+
+import org.oscim.core.MapPosition;
+import org.oscim.event.Event;
+import org.oscim.event.Gesture;
+import org.oscim.event.MotionEvent;
+import org.oscim.map.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MapAdapter extends Map implements Map.UpdateListener {
+
+ private final static Logger log = LoggerFactory.getLogger(MapAdapter.class);
+
+ MapAdapter() {
+ super();
+ events.bind(this); //register Update listener
+ this.viewport().setMaxTilt(65f);
+ }
+
+
+ private boolean mRenderWait;
+ private boolean mRenderRequest;
+ private int width = Gdx.graphics.getWidth(), height = Gdx.graphics.getHeight();
+
+
+ @Override
+ public int getWidth() {
+ return width;
+ }
+
+ @Override
+ public int getHeight() {
+ return height;
+ }
+
+ private final Runnable mRedrawCb = new Runnable() {
+ @Override
+ public void run() {
+ prepareFrame();
+ Gdx.graphics.requestRendering();
+ }
+ };
+
+ @Override
+ public void updateMap(boolean forceRender) {
+ synchronized (mRedrawCb) {
+ if (!mRenderRequest) {
+ mRenderRequest = true;
+ Gdx.app.postRunnable(mRedrawCb);
+ } else {
+ mRenderWait = true;
+ }
+ }
+ }
+
+
+ @Override
+ public void render() {
+ synchronized (mRedrawCb) {
+ mRenderRequest = true;
+ if (mClearMap)
+ updateMap(false);
+ }
+ }
+
+ @Override
+ public boolean post(Runnable runnable) {
+ Gdx.app.postRunnable(runnable);
+ return true;
+ }
+
+ @Override
+ public boolean postDelayed(final Runnable action, long delay) {
+ Timer.schedule(new Timer.Task() {
+ @Override
+ public void run() {
+ action.run();
+ }
+ }, delay / 1000f);
+ return true;
+ }
+
+ @Override
+ public void beginFrame() {
+ }
+
+ @Override
+ public void doneFrame(boolean animate) {
+ synchronized (mRedrawCb) {
+ mRenderRequest = false;
+ if (animate || mRenderWait) {
+ mRenderWait = false;
+ updateMap(true);
+ }
+ }
+ }
+
+
+ public boolean handleGesture(Gesture g, MotionEvent e) {
+ this.updateMap(true);
+ return super.handleGesture(g, e);
+ }
+
+ @Override
+ public void onMapEvent(Event e, MapPosition mapPosition) {
+
+ }
+
+
+}
+
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapApplicationAdapter.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapApplicationAdapter.java
new file mode 100644
index 00000000..d2e1a96e
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapApplicationAdapter.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.vtm;
+
+import com.badlogic.gdx.Application;
+import com.badlogic.gdx.ApplicationAdapter;
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.InputMultiplexer;
+import com.badlogic.gdx.files.FileHandle;
+import com.badlogic.gdx.input.GestureDetector;
+
+import org.oscim.core.MapPosition;
+import org.oscim.event.Event;
+import org.oscim.gdx.GestureHandlerImpl;
+import org.oscim.gdx.MotionHandler;
+import org.oscim.layers.AbstractMapEventLayer;
+import org.oscim.layers.GroupLayer;
+import org.oscim.layers.tile.buildings.BuildingLayer;
+import org.oscim.layers.tile.vector.VectorTileLayer;
+import org.oscim.layers.tile.vector.labeling.LabelLayer;
+import org.oscim.map.Map;
+import org.oscim.renderer.GLViewport;
+import org.oscim.renderer.MapRenderer;
+import org.oscim.scalebar.DefaultMapScaleBar;
+import org.oscim.scalebar.ImperialUnitAdapter;
+import org.oscim.scalebar.MapScaleBar;
+import org.oscim.scalebar.MapScaleBarLayer;
+import org.oscim.scalebar.MetricUnitAdapter;
+import org.oscim.theme.ExternalRenderTheme;
+import org.oscim.theme.VtmThemes;
+import org.oscim.theme.comparator.BothMapPositionHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.oscim.backend.GLAdapter.gl;
+
+public class MapApplicationAdapter extends ApplicationAdapter {
+
+ Logger log = LoggerFactory.getLogger(MapApplicationAdapter.class);
+
+ private int x, y, width, height;
+ private MapScaleBarLayer mapScaleBarLayer;
+ private CenterCrossLayer centerCrossLayer;
+ private DefaultMapScaleBar mapScaleBar;
+ private BothMapPositionHandler bothMapPositionHandler;
+ private AtomicInteger iniCount = new AtomicInteger();
+ private AtomicBoolean drawMap = new AtomicBoolean(false);
+
+ MapApplicationAdapter(MapReadyCallback callback) {
+ this.callback = callback;
+ }
+
+ void setBounds(int x, int y, int width, int height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ if (map != null && mapRenderer != null) {
+ Gdx.app.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ map.viewport().setScreenSize(MapApplicationAdapter.this.width, MapApplicationAdapter.this.height);
+ mapRenderer.onSurfaceChanged(MapApplicationAdapter.this.width, MapApplicationAdapter.this.height);
+ }
+ });
+ }
+ }
+
+ void setMapPositionHandler(BothMapPositionHandler bothMapPositionHandler) {
+ this.bothMapPositionHandler = bothMapPositionHandler;
+ }
+
+ void setTheme(String themePath) {
+ ExternalRenderTheme externalRenderTheme = new ExternalRenderTheme(themePath);
+ try {
+ map.setTheme(externalRenderTheme, true);
+ } catch (Exception e) {
+ log.error("SetTheme", e);
+ }
+ }
+
+ public interface MapReadyCallback {
+ void ready();
+ }
+
+ private MapRenderer mapRenderer;
+ private MapAdapter map;
+ private VectorTileLayer vectorTileLayer;
+ private final MapReadyCallback callback;
+
+ @Override
+ public void create() {
+ map = new MapAdapter() {
+
+ @Override
+ public void beginFrame() {
+ super.beginFrame();
+ }
+
+ @Override
+ public void onMapEvent(Event e, final MapPosition mapPosition) {
+ super.onMapEvent(e, mapPosition);
+ if (e == Map.MOVE_EVENT || e == Map.SCALE_EVENT) {
+ bothMapPositionHandler.mapPositionChangedFromVtmMap(mapPosition);
+ }
+ }
+ };
+ mapRenderer = new MapRenderer(map);
+ Gdx.graphics.setContinuousRendering(true);
+ Gdx.app.setLogLevel(Application.LOG_DEBUG);
+
+ int w = Gdx.graphics.getWidth();
+ int h = Gdx.graphics.getHeight();
+
+ map.viewport().setScreenSize(w, h);
+ mapRenderer.onSurfaceCreated();
+ mapRenderer.onSurfaceChanged(w, h);
+
+ InputMultiplexer mux = new InputMultiplexer();
+ mux.addProcessor(new MotionHandler(map) {
+ @Override
+ public boolean scrolled(int amount) {
+ super.scrolled(amount);
+ MapPosition mapPosition = map.getMapPosition();
+ int zoomLevel = mapPosition.getZoomLevel() - amount;
+ mapPosition.setZoomLevel(zoomLevel);
+ map.setMapPosition(mapPosition);
+ bothMapPositionHandler.mapPositionChangedFromVtmMap(mapPosition);
+ return true;
+ }
+ });
+ mux.addProcessor(new GestureDetector(new GestureHandlerImpl(map)));
+
+
+ Gdx.input.setInputProcessor(mux);
+
+ mapScaleBar = new DefaultMapScaleBar(map);
+ mapScaleBar.setScaleBarMode(DefaultMapScaleBar.ScaleBarMode.BOTH);
+ mapScaleBar.setDistanceUnitAdapter(MetricUnitAdapter.INSTANCE);
+ mapScaleBar.setSecondaryDistanceUnitAdapter(ImperialUnitAdapter.INSTANCE);
+ mapScaleBarLayer = new MapScaleBarLayer(map, mapScaleBar);
+ mapScaleBarLayer.getRenderer().setPosition(GLViewport.Position.BOTTOM_LEFT);
+
+ centerCrossLayer = new CenterCrossLayer(map);
+ }
+
+
+ @Override
+ public void render() {
+ gl.viewport(0, 0, width, height);
+
+ try {
+ mapRenderer.onDrawFrame();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (iniCount.get() == 10) {
+ mapRenderer.onSurfaceCreated();
+
+ Gdx.app.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ drawMap.set(true);
+ }
+ });
+ callback.ready();
+ iniCount.incrementAndGet();
+ } else {
+ iniCount.incrementAndGet();
+ }
+ }
+
+ @Override
+ public void dispose() {
+ System.out.print("Dispose");
+ }
+
+ void loadMap(File mapFile, File themeFile) {
+ FileHandle fileHandle = Gdx.files.absolute(mapFile.getAbsolutePath());
+ MapsforgeVectorSingleMap mapLayer = new MapsforgeVectorSingleMap(fileHandle);
+ if (vectorTileLayer == null) {
+ vectorTileLayer = (VectorTileLayer) mapLayer.getTileLayer(map);
+ } else {
+ vectorTileLayer.setTileSource(mapLayer.getVectorTileSource());
+ }
+
+ AbstractMapEventLayer eventLayer = null;
+ if (map.layers().size() > 1) {
+ map.layers().clear();
+ eventLayer = map.getEventLayer();
+ }
+
+ map.layers().add(vectorTileLayer);
+ map.setTheme(VtmThemes.OSMARENDER);
+ if (eventLayer != null) map.layers().add(eventLayer);
+ map.layers().add(new BuildingLabelLayer(map, vectorTileLayer));
+ map.layers().add(centerCrossLayer);
+ map.layers().add(mapScaleBarLayer);
+ }
+
+ void setCoordinate(double latitude, double longitude, byte zoomLevel) {
+ map.setMapPosition(latitude, longitude, 1 << zoomLevel);
+ mapScaleBar.setScaleBarPosition(MapScaleBar.ScaleBarPosition.BOTTOM_CENTER);
+ }
+
+ public static final class BuildingLabelLayer extends GroupLayer {
+ final BuildingLayer buildingLayer;
+
+ BuildingLabelLayer(Map map, VectorTileLayer vectorTileLayer) {
+ super(map);
+ this.buildingLayer = new BuildingLayer(map, vectorTileLayer);
+ this.layers.add(this.buildingLayer);
+ this.layers.add(new LabelLayer(map, vectorTileLayer));
+ }
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapsforgeVectorSingleMap.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapsforgeVectorSingleMap.java
new file mode 100644
index 00000000..637b3538
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/MapsforgeVectorSingleMap.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.vtm;
+
+import com.badlogic.gdx.files.FileHandle;
+
+import org.oscim.layers.tile.TileLayer;
+import org.oscim.layers.tile.vector.OsmTileLayer;
+import org.oscim.layers.tile.vector.VectorTileLayer;
+import org.oscim.map.Map;
+import org.oscim.tiling.TileSource;
+import org.oscim.tiling.source.mapfile.MapFileTileSource;
+
+class MapsforgeVectorSingleMap {
+ private VectorTileLayer vectorTileLayer;
+ private final FileHandle mapFile;
+ private MapFileTileSource tileSource;
+
+ MapsforgeVectorSingleMap(FileHandle mapFile) {
+ this.mapFile = mapFile;
+ }
+
+ TileSource getVectorTileSource() {
+ if (tileSource == null) {
+ tileSource = new MapFileTileSource();
+ tileSource.setMapFile(mapFile.path());
+ tileSource.setPreferredLanguage("en");
+ }
+ return tileSource;
+ }
+
+ TileLayer getTileLayer(Map map) {
+ if (vectorTileLayer == null) {
+ if (tileSource == null) {
+ tileSource = new MapFileTileSource();
+ tileSource.setMapFile(mapFile.path());
+ tileSource.setPreferredLanguage("en");
+ }
+ vectorTileLayer = new OsmTileLayer(map);
+ vectorTileLayer.setTileSource(tileSource);
+ }
+ return vectorTileLayer;
+ }
+}
diff --git a/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/VtmPanel.java b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/VtmPanel.java
new file mode 100644
index 00000000..f22af2af
--- /dev/null
+++ b/vtm-theme-comparator/src/org/oscim/theme/comparator/vtm/VtmPanel.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 Longri
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.theme.comparator.vtm;
+
+import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
+import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
+
+import org.oscim.theme.comparator.BothMapPositionHandler;
+
+import java.awt.Canvas;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.io.File;
+
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+
+public class VtmPanel extends JPanel {
+
+ private final MapApplicationAdapter appListener;
+ private final Canvas vtmCanvas = new Canvas();
+
+ public VtmPanel(MapApplicationAdapter.MapReadyCallback callback) {
+ this.setLayout(null);
+ LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
+ config.width = 300;
+ config.height = 300;
+
+ appListener = new MapApplicationAdapter(callback);
+
+ new LwjglApplication(appListener, config, vtmCanvas);
+
+ this.add(vtmCanvas);
+ this.setSize(350, 350);
+ this.setBorder(BorderFactory.createTitledBorder("VTM-Map"));
+
+ this.addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(ComponentEvent e) {
+ super.componentResized(e);
+ boundsChanged();
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ super.componentMoved(e);
+ boundsChanged();
+ }
+
+ });
+
+ boundsChanged();
+
+ }
+
+ private void boundsChanged() {
+ vtmCanvas.setBounds(getX() + 10, getY() + 20, getWidth() - 20, getHeight() - 30);
+ appListener.setBounds(getX() + 10, getY() + 20, getWidth() - 20, getHeight() - 30);
+ }
+
+ public void loadMap(File mapFile, File themeFile) {
+ appListener.loadMap(mapFile, themeFile);
+ }
+
+ public void setCoordinate(double latidude, double longitude, byte zoomLevel) {
+ appListener.setCoordinate(latidude, longitude, zoomLevel);
+ }
+
+ public void setMapPositionHandler(BothMapPositionHandler bothMapPositionHandler) {
+ appListener.setMapPositionHandler(bothMapPositionHandler);
+ }
+
+ public void setTheme(String themePath) {
+ appListener.setTheme(themePath);
+ }
+}