
在上一篇文章中,我讨论了如何快速制作Web拨号器。 但是,如果您设定了更雄心勃勃的任务-用卡片组装自己的应用程序,而没有广告和二十一点呢? 如果仅仅几天呢?
开始吧! 我要猫。
首先,让我们弄清楚我们必须做什么。 在输出中,我们希望获得一个带有参考数据和地图的应用程序。 并离线工作。 作为开发人员,我主要只对地图感兴趣,因为我们已经知道如何显示参考数据。 在这种情况下,脱机是一个很大的限制,因为没有很多支持脱机的好的库。 因此,在本文中,我们将专注于地图,但让我们来谈谈通行指南。
选择地图引擎
首先要做的是获取应用程序的数据。 市场上有很多货源,都是免费的,不是很免费。 首先, OpenStreetMap非常适合作为开放的地图数据源。 在那里,您可以为我们的目录获取一定数量的POI 。
下一步是选择一个卡片引擎。 在Internet上有很多,免费的甚至更少,并且具有离线支持,通常只有少数。 我建议使用一个很酷的选项-mapsforge / vtm 。 这是一个矢量OpenGL引擎,非常快,支持离线,Android,iOS,各种数据源,自定义样式,叠加层,标记,3D甚至对象的3D模型! 非常非常酷
该存储库中有许多用于快速入门的示例,有现成的卡,还有一个插件,可让您使用OSM格式的数据组装自己的卡。 所以,让我们开始吧!
MapView mapView = findViewById(R.id.map_view); this.map = mapView.map(); File baseMapFile = getMapFile("cyprus.map"); MapFileTileSource tileSource = new MapFileTileSource(); tileSource.setMapFile(baseMapFile.getAbsolutePath()); VectorTileLayer layer = this.map.setBaseMap(tileSource); MapInfo info = tileSource.getMapInfo(); if (info != null) { MapPosition pos = new MapPosition(); pos.setByBoundingBox(info.boundingBox, Tile.SIZE * 4, Tile.SIZE * 4); this.map.setMapPosition(pos); } this.map.setTheme(VtmThemes.DEFAULT); this.map.layers().add(new BuildingLayer(this.map, layer)); this.map.layers().add(new LabelLayer(this.map, layer));
创建一个MapFileTileSource数据源,指定地图文件的位置。 此外,我们将自己放置在我们感兴趣的边界框的中央,以便在应用程序启动时不会位于所选位置之外。 安装默认主题。 添加一层房屋和一层签名。 仅此而已。 我们推出-奇迹!
似乎更快,更容易,而且不可能。
我们进行地理编码
下一个重要步骤是实施地理编码。 卡本身已经很好,但是需要交互性。 我们想进入地图并查看我们击中的物体的信息。 而且有一些困难。 总的来说,我们的库中没有完整的地理编码。 这也许是它最大的缺点。 如果未发明任何东西,那么我们可以利用现有功能。
原来比较冗长。 您需要找到一个图块,获取方法(在OSM术语中,这是一个线性对象),然后可以从中提取一些属性。 除了方法外,还可以获取POI,仅此而已。 您将不得不自己整理其余逻辑:从单击击中的整个对象集中选择“正确”,并按缩放级别进行过滤。 还有一件事。 实际上,我们丢失了有关原始几何图形的信息,并且仅对一组线进行了搜索。 如果您还想制作地理编辑器,那么显然这是不够的。
但是要证明这种方法,一切都适合我们。
进阶地理编码
一般来说,有一个更高级的选项。 为此,我们需要自己的基地。 特别是,您可以使用SQLite。 的确,标准的SQLite对我们来说还不够,我们必须通过连接用于地理搜索的RTree插件来构建自己的SQLite。 我已经在文章的“做好搜索”一节中告诉过如何做到这一点。
在这种情况下,我们可以完全控制数据,可以以正确的格式保存所需的所有内容。 我们还可以固定全文搜索,并按名称,地址和其他属性搜索我们的地理对象和公司。
方向是:
- 我们制作表格:
- 我们用数据填充一切。
- 当您点击地图时,我们将获得GeoPoint并执行请求:
SELECT id FROM geo_index WHERE minX>=-81.08 AND maxX<=-80.58 AND minY>=35.00 AND maxY<=35.44
- 最后一步:过滤并选择适当的对象。
可以在存储库中查看实现选项之一。
结果,我们已经知道如何显示地图并处理点击。 还不错
添加重要的小东西。
让我们添加几个重要功能。
让我们从当前位置开始。 在mapsforge / vtm中有一个特殊的功能。 LocationLayer层。 使用非常简单。
LocationLayer locationLayer = new LocationLayer(this.map); locationLayer.setEnabled(true);
唯一的缺点是-当当前位置在地图外部时,屏幕边框上的“蓝点”会不断波动。 在使用过程中,最有可能的情况是,您很少会遇到这种情况,但这会导致不断的重新渲染,因此会给处理器带来一些负担。 要摆脱这一点要困难一些,您需要进入着色器并进行修复。 但这已经为完美主义者所用。 怎么做-您可以在这里看到。
所以,位置是。 现在是时候将导航按钮添加到当前位置了,就像在所有自尊地图应用程序中一样。
View vLocation = findViewById(R.id.am_location); vLocation.setOnClickListener(v -> this.map.animator().animateTo(initialGeoPoint));
我们还需要缩放按钮。
View vZoomIn = findViewById(R.id.am_zoom_in); vZoomIn.setOnClickListener(v -> this.map.animator().animateZoom(500, 2, 0, 0)); View vZoomOut = findViewById(R.id.am_zoom_out); vZoomOut.setOnClickListener(v -> this.map.animator().animateZoom(500, 0.5, 0, 0));
蛋糕上的樱桃是指南针。
View vCompass = findViewById(R.id.am_compass); vCompass.setVisibility(View.GONE); vCompass.setOnClickListener(v -> { MapPosition mapPosition = this.map.getMapPosition(); mapPosition.setBearing(0); this.map.animator().animateTo(500, mapPosition); vCompass.animate().setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { vCompass.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }).setDuration(500).rotation(0).start(); }); this.map.events.bind((e, mapPosition) -> { if (e == Map.ROTATE_EVENT) { vCompass.setRotation(mapPosition.getBearing()); vCompass.setVisibility(View.VISIBLE); } });
捕捉世界
朋友,我们已经到了终点。 它仍然可以添加最终的触感。 我们计划捕获世界,这意味着我们需要以某种方式将其塞入我们的应用程序。
而且情况如此,我们的引擎比听起来容易得多。
我们需要通过向其中添加MultyMapTileSource稍微修改地图加载方法。 本质上,这是任何其他图块源的包装,使您可以立即在地图上显示添加到它的所有内容。 只是一个杀手级功能。 结果,剩下的事情就是我们要准备一张最少细节的世界地图,将其首先添加到包装中,然后将其余部分绘制在顶部。 此外,我们可以立即将目录中拥有的所有卡与应用程序地图一起添加! 华丽,只是华丽。 并且不要忘记它是脱机的:)
也许我们已经准备好发布了。 我们收集构建,将其投放市场,并获得当之无愧的明星:)
在一大桶蜂蜜中的几匙焦油
开源引擎正在积极开发中,但是坦率地说,他的团队相当谦虚。 总的来说,这是一个名为devemux86的人 。 还有不时的几个家伙。
有时,渲染中会出现伪影,有些眨眼和抽搐。 但是我从未遇到过任何严重的问题,而且崩溃更多,这让我感到高兴。
还有一点细微差别可能并不令人愉快。 这是圆角和圆形的图形。 屏幕快照中的示例:
如果在初始几何图形中有很多点(舍入是平滑的),则在地图上可以看到一个相当“成角度的”圆,上面有许多小的凸起和凹面。 显然,这样做是为了提高性能和映射文件的大小,但它看起来不是很好。
也许这就是今天的弊病。 您决定是否可以与他们同住。 同时,我们使用此库已有1.5年以上的时间了,至少在Android平台上,飞行效果非常好。
总结
在本文中,我证明了即使是这样一个非同寻常的问题也可以相对迅速地解决。 您已经获得了现成的骨架,您可以用它来原型化任何涉及在最短时间内使用离线地图的项目。
如果有兴趣,在下一篇文章中,我将展示如何将地板制作为la 2GIS。 这实际上比看起来简单得多:)