package net.osmand; import gnu.trove.list.array.TIntArrayList; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion; import net.osmand.binary.RouteDataObject; import net.osmand.data.MapObject; import net.osmand.data.QuadRect; import net.osmand.render.RenderingRuleSearchRequest; import net.osmand.render.RenderingRulesStorage; import net.osmand.router.PrecalculatedRouteDirection; import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RouteSegmentResult; import net.osmand.router.RoutingConfiguration; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; public class NativeLibrary { public NativeLibrary() { } public static class RenderingGenerationResult { public RenderingGenerationResult(ByteBuffer bitmap) { bitmapBuffer = bitmap; } public final ByteBuffer bitmapBuffer; } public static class NativeSearchResult { public long nativeHandler; private NativeSearchResult(long nativeHandler) { this.nativeHandler = nativeHandler; } @Override protected void finalize() throws Throwable { deleteNativeResult(); super.finalize(); } public void deleteNativeResult() { if (nativeHandler != 0) { deleteSearchResult(nativeHandler); nativeHandler = 0; } } } public static class NativeRouteSearchResult { public long nativeHandler; public RouteDataObject[] objects; public RouteSubregion region; public NativeRouteSearchResult(long nativeHandler, RouteDataObject[] objects) { this.nativeHandler = nativeHandler; this.objects = objects; } @Override protected void finalize() throws Throwable { deleteNativeResult(); super.finalize(); } public void deleteNativeResult() { if (nativeHandler != 0) { deleteRouteSearchResult(nativeHandler); nativeHandler = 0; } } } /** * @param * - must be null if there is no need to append to previous results returns native handle to results */ public NativeSearchResult searchObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, RenderingRuleSearchRequest request, boolean skipDuplicates, Object objectWithInterruptedField, String msgIfNothingFound) { int renderRouteDataFile = 0; if (request.searchRenderingAttribute("showRoadMapsAttribute")) { renderRouteDataFile = request.getIntPropertyValue(request.ALL.R_ATTR_INT_VALUE); } return new NativeSearchResult(searchNativeObjectsForRendering(sleft, sright, stop, sbottom, zoom, request, skipDuplicates, renderRouteDataFile, objectWithInterruptedField, msgIfNothingFound)); } public RouteDataObject[] getDataObjects(NativeRouteSearchResult rs, int x31, int y31) { if (rs.nativeHandler == 0) { // do not throw exception because it is expected situation return new RouteDataObject[0]; } return getRouteDataObjects(rs.region.routeReg, rs.nativeHandler, x31, y31); } public boolean initMapFile(String filePath) { return initBinaryMapFile(filePath); } public boolean initCacheMapFile(String filePath) { return initCacheMapFiles(filePath); } public boolean closeMapFile(String filePath) { return closeBinaryMapFile(filePath); } public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config, RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap) { // config.router.printRules(System.out); return nativeRouting(new int[] { sx31, sy31, ex31, ey31 }, config, config.initialDirection == null ? -360 : config.initialDirection.floatValue(), regions, progress, precalculatedRouteDirection, basemap); } public NativeRouteSearchResult loadRouteRegion(RouteSubregion sub, boolean loadObjects) { NativeRouteSearchResult lr = loadRoutingData(sub.routeReg, sub.routeReg.getName(), sub.routeReg.getFilePointer(), sub, loadObjects); if (lr != null && lr.nativeHandler != 0) { lr.region = sub; } return lr; } /**/ protected static native NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int regfp, RouteSubregion subreg, boolean loadObjects); protected static native void deleteRenderingContextHandle(long handle); protected static native void deleteRouteSearchResult(long searchResultHandle); protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31); protected static native RouteSegmentResult[] nativeRouting(int[] coordinates, RoutingConfiguration r, float initDirection, RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap); protected static native void deleteSearchResult(long searchResultHandle); protected static native boolean initBinaryMapFile(String filePath); protected static native boolean initCacheMapFiles(String filePath); protected static native boolean closeBinaryMapFile(String filePath); protected static native void initRenderingRulesStorage(RenderingRulesStorage storage); protected static native RenderingGenerationResult generateRenderingIndirect(RenderingContext rc, long searchResultHandler, boolean isTransparent, RenderingRuleSearchRequest render, boolean encodePng); protected static native long searchNativeObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, RenderingRuleSearchRequest request, boolean skipDuplicates, int renderRouteDataFile, Object objectWithInterruptedField, String msgIfNothingFound); protected static native boolean initFontType(byte[] byteData, String name, boolean bold, boolean italic); protected static native RenderedObject[] searchRenderedObjects(RenderingContext context, int x, int y, boolean notvisible); public RenderedObject[] searchRenderedObjectsFromContext(RenderingContext context, int x, int y) { return searchRenderedObjects(context, x, y, false); } public RenderedObject[] searchRenderedObjectsFromContext(RenderingContext context, int x, int y, boolean notvisible) { return searchRenderedObjects(context, x, y, notvisible); } /**/ // Empty native impl /* * protected static NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int regfp,RouteSubregion subreg, boolean * loadObjects) { return null;} * * protected static void deleteRouteSearchResult(long searchResultHandle) {} * * protected static RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31){return null;} * * protected static RouteSegmentResult[] nativeRouting(int[] coordinates, int[] state, String[] keyConfig, String[] valueConfig, float * initDirection, RouteRegion[] regions, RouteCalculationProgress progress) {return null;} * * protected static void deleteSearchResult(long searchResultHandle) {} * * protected static boolean initBinaryMapFile(String filePath) {return false;} * * protected static boolean initCacheMapFiles(String filePath) {return false;} * * protected static boolean closeBinaryMapFile(String filePath) {return false;} * * protected static void initRenderingRulesStorage(RenderingRulesStorage storage) {} * * * protected static RenderingGenerationResult generateRenderingIndirect(RenderingContext rc, long searchResultHandler, boolean * isTransparent, RenderingRuleSearchRequest render, boolean encodePng) { return null; } * * protected static long searchNativeObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom, * RenderingRuleSearchRequest request, boolean skipDuplicates, int renderRouteDataFile, Object objectWithInterruptedField, String * msgIfNothingFound) { return 0; } * * public static void testRoutingPing() {} * * public static int testNativeRouting(String obfPath, double sLat, double sLon, double eLat, double eLon) {return 0;} /* */ private static final Log log = PlatformUtil.getLog(NativeLibrary.class); public static boolean loadNewLib(String path) { return load("OsmAndJNI", path); } public static boolean loadOldLib(String path) { boolean b = true; b &= load("osmand", path); return b; } public static boolean load(String libBaseName, String path) { // look for a pre-installed library if (path != null) { try { System.load(path + "/" + System.mapLibraryName(libBaseName)); return true; } catch (UnsatisfiedLinkError e) { log.error(e); } // fall through } // guess what a bundled library would be called String osname = System.getProperty("os.name").toLowerCase(); String osarch = System.getProperty("os.arch"); if (osname.startsWith("mac os")) { osname = "mac"; osarch = "universal"; } if (osname.startsWith("windows")) osname = "win"; if (osname.startsWith("sunos")) osname = "solaris"; if (osarch.startsWith("i") && osarch.endsWith("86")) osarch = "x86"; String libname = libBaseName + "-" + osname + '-' + osarch + ".lib"; // try a bundled library try { ClassLoader cl = NativeLibrary.class.getClassLoader(); InputStream in = cl.getResourceAsStream( libname); if (in == null) { log.error("libname: " + libname + " not found"); return false; } File tmplib = File.createTempFile(libBaseName + "-", ".lib"); tmplib.deleteOnExit(); OutputStream out = new FileOutputStream(tmplib); byte[] buf = new byte[1024]; for (int len; (len = in.read(buf)) != -1;) out.write(buf, 0, len); in.close(); out.close(); System.load(tmplib.getAbsolutePath()); return true; } catch (Exception e) { e.printStackTrace(); log.error(e.getMessage(), e); } catch (UnsatisfiedLinkError e) { log.error(e.getMessage(), e); } // fall through return false; } /** * Compares two {@code int} values. * @return 0 if lhs = rhs, less than 0 if lhs < rhs, and greater than 0 if lhs > rhs. * @since 1.7 */ public static int ccmp(int lhs, int rhs) { return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1); } public void loadFontData(File dr) { File[] lf = dr.listFiles(); if (lf == null) { System.err.println("No fonts loaded from " + dr.getAbsolutePath()); return; } ArrayList lst = new ArrayList(Arrays.asList(lf)); Collections.sort(lst, new Comparator() { @Override public int compare(File arg0, File arg1) { return ccmp(order(arg0), order(arg1)); } private int order(File a) { final String nm = a.getName().toLowerCase(); if(nm.contains("OpenSans".toLowerCase())) { if(nm.contains("Regular".toLowerCase())) { return 0; } return 1; } if(nm.contains("Fallback".toLowerCase())) { return 3; } if(nm.contains("MTLmr3m".toLowerCase())) { return 5; } return 2; } }); for(File f : lst) { final String name = f.getName(); if(!name.endsWith(".ttf") && !name.endsWith(".otf")) { continue; } try { ByteArrayOutputStream ous = new ByteArrayOutputStream(); FileInputStream fis = new FileInputStream(f); Algorithms.streamCopy(fis, ous); fis.close(); initFontType(ous.toByteArray(), name.substring(0, name.length() - 4), name.toLowerCase().contains("bold"), name.toLowerCase().contains("italic")); } catch (IOException e) { e.printStackTrace(); } } } public static class RenderedObject extends MapObject { private Map tags = new LinkedHashMap<>(); private QuadRect bbox = new QuadRect(); private TIntArrayList x = new TIntArrayList(); private TIntArrayList y = new TIntArrayList(); private String iconRes; private int order; private boolean visible; public Map getTags() { return tags; } public boolean isText() { return !getName().isEmpty(); } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public void addLocation(int x, int y) { this.x.add(x); this.y.add(y); } public TIntArrayList getX() { return x; } public String getIconRes() { return iconRes; } public void setIconRes(String iconRes) { this.iconRes = iconRes; } public void setVisible(boolean visible) { this.visible = visible; } public boolean isVisible() { return visible; } public TIntArrayList getY() { return y; } public void setBbox(int left, int top, int right, int bottom) { bbox = new QuadRect(left, top, right, bottom); } public QuadRect getBbox() { return bbox; } public void setNativeId(long id) { setId(id); } public void putTag(String t, String v) { tags.put(t, v); } } }