该指南的悠久历史-我如何编写5年的智能远足径服务

许多人有一个或多个自己的家庭项目。 这些是小型公用事业,实验性作品,新技术样本,Facebook“杀手”等等。 此类项目长期参与的频率明显降低。


在这篇文章中,我将分享我的经验,并告诉您五年来我偶尔为圣彼得堡开发智能指南的方法,如何设法不放弃这项业务,对项目的态度如何变化以及最终发生了什么。



下面描述了许多可疑的技术解决方案和自行车,但请不要感到惊讶-几乎所有这些解决方案都为当时的目标和资源辩护(通常是我的知识和时间)

主意


在任何一个大型旅游城市中,都有数百本很好的和不太好的指南,其中绘出了主要路线,主要要点以及许多其他有用的信息。
但是那里的路线是恒定且有限的,景点通常只是最重要的景点,例如“三天内可以看到的风景”。


因此,构想是建立智能路线。 用户选择点A和B并不会收到经典的最短路线,而是收到靠近它的东西,但会经过附近的景点。


这样的指南的受众不仅是游客,还是想要多样化其典型的到家/公司/商店的路线的本地居民(应用程序中有这样的评论!),或者只是更好地了解这座城市。


TL; DR
最重要的是Wander Android应用程序,该应用程序现在仅适用于圣彼得堡。


第1幕:原型


这个想法是在2014年回到大学的,我什至不确定这个想法最初是我的,在某些课程中,有必要分成小组,提出项目并完成,作为学期论文。 他们当机并产生了该项目的想法,然后开始使用TravelPath这个名字。


它应该是一个Web服务,我对PHP有点了解,所以我选择了PHP。
有必要解决几个问题:


1.景点基地
在这里,我们遇到了OpenStreetMap ,结果证明这是最适合我们的最酷的项目。 OSM是基于社区的非营利性社区地图项目。 像Wikipedia一样,仅关于地图。
以xml格式下载圣彼得堡的地图- 此处有资源列表。
我记得当时当时地图文件的重量超过1 GB,因此编写了一个流解析器,用于选择预选类别的对象-纪念碑,公园,教堂,博物馆。
OSM在此处提供了有关标签,属性和您需要的所有内容的大量文档,这使它很容易弄清楚如何从卡中获取所需的数据。


制图数据是由发烧友创建的,因此并非到处都有,并且并非总是对象具有必要的标签,某些地方存在错误,问题。 但是对于大城市,数据质量很高。


如果我正确发掘了旧代码,则可以使用以下标签来获取对象:


  • 意味着cathedralchapelchurch已成为church一种
  • fountain价值的amenity成为monument
  • 具有artworkviewpoint价值的tourismmonumentzoomuseummuseum ,和theme_parkpark
  • 具有boundary_stone historiccastlememorialmonument值归入monument类别
  • park享有价值的leisure自然会在park结束

我们将所有接收到的对象放入具有坐标和欢乐的数据库中! 圣彼得堡已经释放了1,200点以上。


2.建立路线
当然,这并不意味着要在课程中实施复杂的系统,并且当时的技术知识很匮乏,因此我们决定将路线构造分为两部分:路线中对象的选择和路线构造本身。 在某种程度上,这决定了未来的命运,因为后来我不想改变这一计划。


选定点之间的路线由Google Maps API给出,该路线可以很好地建立汽车路线,但当时至少在俄罗斯还不知道如何走路。


对象的选择必须由您自己实现。 事实证明,该应用程序的以下方案:



该方法的主要缺点是仅基于GPS坐标而在不考虑地图的情况下选择路线中的点。 总体而言,这相当有效,特别是在街道网频繁的地方。 例如,在桥梁稀少的河流上,罕见的问题就很明显。


接下来,我们提出一种用于选择路线中点的算法。 一方面,路线必须不会过多地转移到一侧,但另一方面,它仍会在给定的邻域中选择一些点。


关于问题的陈述不是很清楚,出现了相同的算法,类似于A *( wiki ):相信从每个点到每个点都有过渡。 此外,在A *中,搜索AB路径时到达顶点n的价格通常表示为: f(n) = g(An) + h(nB) ,其中g(x,y)是从x到y的路径长度,而h(x,y)是从x到y的距离的启发式估计。 我的价格是相同的f(n) = h(x,n) + h(n,y) * q ,其中q是小于1的常数。通过更改q的值,您可以影响路线中的点数。


我们实现了算法,并且欢呼雀跃,一切正常,紧固了界面,购买了域名,将其上传到托管服务器,编写了报告和其他官僚主义,并为之欢欣鼓舞! 看起来像这样(之后并没有太大变化):



总计:我有了一个很酷的主意的原型,熟悉OSM,Google API,作为一个团队工作,在大学中获得荣誉,这也很有趣。


第二幕:第一个完整的网络版本


我真的很喜欢这个主意,因此决定进一步发展,但已经有了一个主意。 显而易见,首先需要的是:当时Google Maps尚无法建立步行路线,而仅建造了汽车路线。 应用程序的整体思想主要是关于步行,这意味着您需要能够沿着公园,人行道,桥梁等遮荫小巷建造路线。


那是在2015年,我继续从事TravelPath的工作,纠正了这一致命缺陷的方法-我开始自行建立方法。


我没有更改技术,即PHP + MySQL 。 当然,我立即意识到这不是执行此任务的最佳技术堆栈(用PHP构建路由?认真吗?)。
但是我时间有限,还没有准备好切换到一个更合理的堆栈,其次,直到现在,该项目是“摆在桌面上”编写的-我想在移动设备上使用这样的应用程序,并且在那些年中,这仅意味着本机开发。 也就是说,尽管进行了一些尝试来创建自适应界面,但它以Web版本的形式不适用仍然很明显。


出现了以下任务:


1.获取城市图
在这里,相同的OSM可以拯救我们。 再次,以xml格式下载图形,对其进行解析,仅选择必要的道路类型并惊叹于数据量-那时,出现了超过300万个顶点和500k边。 第一个想法是图形可能不相关。
任何院子,封闭的领土,难以理解的事物对于构建路线都不是很有趣,因此我们选择一个点,从中绕过整个图,标记通过的顶点并获得图的连接部分。
总共出现了超过60万个峰值和15万个边缘。
将收到的内容保存在数据库中,直到更好的时间为止。


2.建立路线
有数据,一切都剩下了:在上面建立路由。 我们无需发明任何新技术-记住算法的过程,仔细阅读有关使用图的文章,采用A *算法,并在PHP中实现它, 欢喜 一切工作的缓慢程度令其感到沮丧。


有很多数据,因此,在构建图形时,我们不会使用完整的数据,而只会读取其中的一部分-落入从路径中所有选定点获得的矩形中的所有内容。



(红色框=〜读取了这样的图以构建路径)


与路线的候选点相同-我们不考虑所有内容,只考虑起点和终点之间的点+一个小值。
选择了这个矩形的大小(增加它的常数)后,我们尝试保证读取的数据最小为最小。
还有一种想法是将城市划分为正方形,首先通过正方形构建路线,然后读取这些正方形内的图形并仅沿其寻找路径。 您可以预先计算所有相邻正方形之间的路径。 甚至更多-计算并保存所有景点之间的路径。 然后,仍然可以仅构建从初始对象到第一个对象以及从最后一个对象到终点的路径。
但是手没有达到这种自行车的实现。


3.在合理的时间内路由
显而易见,瓶颈是存储和处理数据-搜索算法本身并没有特别加速。 膝上的基准测试确认,大部分时间都花在了从数据库和类似操作中的读取上。
在这种情况下应该怎么做? 不要存储该图。 但是我有一种特殊的方式, MySQL ,仅此而已,但是这个问题需要解决。


  1. 首先-数据被规范化,这在解析时很方便,但是读取成千上万条记录甚至从两个表中读取都是很痛苦的。 显而易见的解决方案是只为边创建两个表(用于顶点和边)之一,在每个表中记录两个顶点的坐标及其ID。
  2. 我们会预先计算图表中与景点最近的顶点,而不是每次都动态地进行计算。
  3. 要花费大量时间搜索最接近给定用户的图形点。 为了加快速度,我将整个图形分为多个小扇区,使用坐标公式,您可以立即获取扇区ID,然后仅在扇区内部而不是整个图形中选择顶点,这要快得多。

经过所有操作后,我们在2秒钟内即可在圣彼得堡构建出一条合理的步行路线,这听起来不错-这是用户可以接受的等待时间。
最终方案如下所示:



总计:我更加了解OSM数据,在实践中,我解决了图形问题(图形算法终于有用的情况),写了我的第一个基准测试。


第三幕:第一个普通的网络版本


这是有关该项目作为一种可能对某人有用的产品的最初想法,而不是桌面上的工艺品。
因此,您需要解决两个最重要的问题:


  1. 显而易见,像我在上一段中所做的那样构建路线是错误的。
  2. 对于任何人来说,浏览器中的指南可能都不方便-您想要在城市中漫步,研究一个方便的应用程序,在其中可以阅读周围的特色等等。 简而言之,至少-您需要一个很酷的自适应界面,并且很好地需要一个移动应用程序。

但是在这个阶段,我决定只处理第一点。 我们研究了有关此主题的Internet,在此OSM再次提供了帮助,或者更确切地说,这归功于OSM诞生的项目。
基于OSM的地图数据,开源路由机可以在适当的时候构建各种路线。 是的,还有一个带有良好文档的开源项目,您可以在服务器上安装和使用它。
另外,它还有一个相当方便的API。
antaresm早已在帮助我的时候就撰写了关于OSRM的文章。 没错,大量的水已经流过,文章已经过时了。 但是非常感谢作者! 实际的,很好的文档在这里
成功安装后,我们几乎毫无遗憾地抛弃了所有关于路由的代码,并开始遵循OSRM api中的路由,最终图如下所示:



我们甚至不涉及客户端部分-就其而言,我们服务的API不会发生任何变化。


总计 :我考虑了其他人对应用程序的实际使用,结果证明这是正常的体系结构和良好的路由。 网络版本仍然可用,尽管自那时以来没有更改。 https://travelpath.ru/


行动4:Android应用


尽管已解决了过去的问题,但很明显,该项目对除我以外的所有人而言仍然毫无用处。 因为您需要一个普通的界面,最重要的是需要一个移动应用程序,而不是浏览器版本。 现在,这项技术已经取得了进展,也许已经有可能为移动客户端开发一种功能良好的浏览器应用程序,但是在2015-2016年将无法实现。 这意味着以网络版本的形式使用该项目的可能性非常小-坐在家里,提前选择有趣的地方。 因此,如果我们谈论的是普通产品,则需要具有良好界面的移动应用程序。


然后到2016年,我开始用Java(但不是后端)编写程序,并认为在Android下,他们也用Java编写程序。 为什么不尝试应用程序开发,那应该是一个有趣的经历。
第二个问题是我不知道如何设计,我自己也不会做一个好的界面。 我来参观了我的第一个工作地点(一个小型​​的Le- Dantu工作室),并邀请了我的同事burovk参加该项目。 奇怪的是,他同意了,对此非常感谢:)


第一个布局和原型
首先,我们试图命名一个新名称,因为TravelPath并不是一个非常确切的缩写。 因此,在西里尔的努力下,“ Wander”出现了一个有趣的徽标:



我一直把它和一个大胡子男人联系在一起,但是总的来说,我喜欢它。


然后出现将来的应用程序的第一个布局:



我们还认为,讨论,出现第二个选项,它成为应用程序的第一个版本:



从开发方面,我有意识地决定不使用各种具有不同魔术效果的流行框架-因为在不了解工作的情况下使用它们存在很大风险,并且研究基础知识和基础知识都非常耗时。 Google,我们阅读了官方码头,教育文章,并且克服了困难。


记住的问题和任务:


  • 对于android,有ProGuard一种实用程序,可在汇编过程中压缩源代码,切掉未使用的代码,更改名称等。 它的存在是合理的,但是当您只是自己开发并自己进行开发时-尚不存在重命名字段,这会影响行为,这一点并不明显。 而且一切都只发生在产品装配中。 发布后,一切都变得很酷,但是由于某种原因,没有统计信息。 您开始从后端扣款-客户端发送一些废话,而不是普通的字段名-a a, b, c等。 拐杖使API能够熟练地对此进行解析,然后您发现ProGuard尝试了,您需要在其配置中指定必要的类作为异常。 通常没有什么异常,但是调试时钟就消失了。
  • 一个有趣的任务是更新应用程序数据。 主数据库带有一个apk文件,我在每次发行前都会对其进行更新。 但是,您还需要支持通过网络进行的更新,以便在发生任何更改时,客户端始终可以获取最新的数据。
    在第一个版本中,我决定只关注上一次更新的时间。 该应用程序存储最新鲜对象的时间,在每次启动时,它都会询问后端是否有新的东西出现。 将更新发送给他,客户端再次记住最新接收到的对象的时间(但不记住更新本身的时间,这很重要)。
    原来,我想得很糟-因为一年后出现了第五类-建筑物。 从旧版本升级到新版本时,会出现问题。 我必须稍作更改,客户会为每个类别(而不是一般类别)发送时间。 但是该方案仍然不是完美的-在向对象添加新字段时,会出现无法像这样解决的问题。 有一些关于如何做得更好的想法,但是没有伸出援手,总的来说任务不是很优先。 道德-值得认真对待这些问题并事先考虑一下。

我还想说一下每个人都已经知道的知识-Stack Overflow非常酷,当您以我的方式进行一些无关紧要的开发时,这一点尤其明显-立即编写应用程序,而不是从课程,指南,演示示例中轻松编写。 当没有人问朋友时。 但是除了他之外,关于android开发的聊天也有帮助-在这里您可以提出更多抽象问题,并询问如何最好地做某事的提示。


该应用程序于2016年12月5日登陆Google Play
他只有基本功能-铺设路线,查看有关点的信息。 但这似乎还是成功的! 成功当然只有在自己的眼里。 据了解,该应用程序仍是原始应用程序,需要改进。


第一个完整版和媒体
因此,我认为下一个版本是完整版本,并且是首次发布。
首先,我们同时审查了设计和徽标-现在它变成了这样:



他们还添加了对指南重要的功能-收藏夹,地图上的观察点,带有搜索的景点列表。 更新后的应用程序如下所示:



另一个想法浮出水面-与博物馆之夜联系,并为在圣彼得堡的活动设置单独的模式。
博物馆之夜是一年一度的国际性假期,博物馆经常在夜间开放,并提供特别节目。 一张票是有效的,许多人试图一次游览许多博物馆。 同时,没有用于移动设备的导航器/地图-活动官方网站上只有一些功能。
听起来很符合Wander的想法,并且也很需要。 我联系了活动的组织者,但不幸的是,这个主意没有得到赞赏,直到更美好的时光,它一直铭记在心。


最后,事实证明很酷,最终该应用程序可以被充分使用。 2018年6月2日,发布了一个版本,我们决定尝试向全世界展示-我们在VKontakte和Instagram上购买了一些广告。 但最重要的是,他们写信给几个媒体。 该文件写了关于该应用程序启动的注释 ,之后,彼得堡的许多出版物-The Village,Dog和其他-用风扇被编写或转载。 终于有交通了! 应用程序内部也出现了安装高峰期的活动图。 当然,我们不必为很长的时间而欢欣鼓舞-但是我们得到了第一批用户,一个有用的反馈,总的来说,这非常有启发性。 在同一个夏天,来自Google Play搜索的流量非常引人注目,这非常令人愉快,但后来无法再现结果。
总之,这给了我很大的动力,并有助于继续开发该应用程序。


接下来要做什么? 博物馆之夜,邪恶的Google,循环路线


季节已经过去,应用程序中的活动急剧下降,很明显,锯功能不是很有用-没有人会看到它们,没有用于营销的预算。 有必要提出一些建议,然后我们在博物馆之夜再次回到被推迟的想法。
但是,再次无法达成合作伙伴关系,但是这次我们决定不推迟这个想法,而是自己做所有事情。 事实证明,在单独的导航模式下,许多有用的功能:地图上的博物馆,仅用于参与者的路线,简要信息,收藏夹。


除此模式外,即使从积压的订单中,我们也保留了路由,最重要的是-循环路由的功能。


循环路线始终比AB路线更重要,但被固执地忽略了。 尽管如此,人们还是经常有目的地从A到B漫步在地铁/家庭/工作/酒店周围。最后,他们把手伸到了手,我想到了一个简单的启发式方法:
我们得到给定半径内的所有点。 半径是路线长度的限制器。 我们首先选择一些点(有条件地随机)。 来自它的所有其他元素按照它,中心和给定点之间的角度从小到大的顺序进行排序。 / , .


, .


, , :



, , , , , . — , , .


Kotlin , , Wander. , . , . , java — .


, , , . — The Village. - 1000 , 1400 . , Wander , , , . .


Google . — 2018 , API. — 2018- Places API . API, - — . .


Places API , . , . , — Google Maps, . .


: android Google Play, . burovk , Wander , .



, , , — . , , , — . , — , . , , .


  • git tag v1.0.5 .
  • , , , .
  • - , .
  • — , //etc .

Android . , , , .


— , .



! , . , , , , , .
:


  1. — ? - ?
  2. . , — .
  3. IOS

. , IOS , .


. Google Play .


结论


  • , — . , . . , .
  • — , . backend , , android Kotlin, , , . , .
  • — . - , . - . , — .

. IOS ( ) — !


聚苯乙烯
https://habr.com/post/414433/ . . , ( -!), .

Source: https://habr.com/ru/post/zh-CN471216/


All Articles