
Baru-baru ini, kami menggabungkan teknologi lama dengan teknologi modern, yang hasilnya terbaca di bawah ini.
Augmented Reality
Aplikasi augmented reality sebagai panduan kota adalah tema terkenal dan diimplementasikan oleh banyak pengembang. Arah penggunaan AR ini adalah yang pertama, karena ini memungkinkan Anda untuk menggunakan semua kemungkinan yang nyata dari augmented reality: menunjukkan informasi kepada pengguna tentang bangunan, memberikan informasi tentang pekerjaan institusi dan mengenal pemandangan. Pada hackathon terakhir, yang diadakan di dalam perusahaan, beberapa proyek menggunakan augmented reality disajikan, dan kami datang dengan ide untuk membuat aplikasi AR yang akan menunjukkan bagaimana tengara atau tempat bersejarah terlihat di masa lalu. Untuk melakukan ini, gabungkan teknologi augmented reality modern dengan foto-foto lama. Misalnya, menghadap Katedral St. Isaac, Anda dapat mengarahkan kamera smartphone kepadanya dan melihat bangunan kayu pertamanya, yang dihancurkan pada 1715.
Mekanisme kerja adalah sebagai berikut: aplikasi menampilkan tempat-tempat bersejarah yang diberikan dan pemandangan kota pada peta, menampilkan informasi singkat tentang mereka, dengan bantuan pemberitahuan memberi tahu pengguna bahwa ia tidak jauh dari titik yang menarik. Ketika seseorang mendekati monumen bersejarah pada jarak 40 meter, mode AR menjadi tersedia. Pada saat yang sama, kamera terbuka, dan informasi singkat tentang objek ditampilkan langsung di ruang di sekitar pengguna. Yang terakhir memiliki kemampuan untuk berinteraksi dengan objek virtual: dengan menyentuh kartu tempat bersejarah, Anda dapat melanjutkan untuk melihat album dengan gambar.
Tampaknya aplikasi ini sangat sederhana, tetapi bahkan di sini ada beberapa jebakan. Saya tidak akan membuat Anda bosan dengan cerita tentang implementasi hal-hal sepele seperti mengunduh data dari server atau menampilkan poin pada peta, saya akan langsung menuju ke fungsi yang menyebabkan masalah.
Masalah 1. Poin Mengambang
Jadi, hal pertama yang harus dilakukan adalah menempatkan titik penanda di ruang sesuai dengan lokasi aktual tempat bersejarah relatif terhadap lokasi saat ini dan arah pandangan pengguna.
Untuk memulai, kami memutuskan untuk menggunakan pustaka yang sudah disiapkan untuk iOS:
ARKit-CoreLocation . Proyek ini terletak pada GitHub dalam domain publik, berisi, di samping kode kelas utama, contoh integrasi dan memungkinkan kami untuk menyelesaikan tugas yang kami minati dalam beberapa jam. Hanya perlu memberi makan perpustakaan koordinat titik dan gambar yang digunakan sebagai penanda.
Tidak mengherankan, kemudahan ini harus dibayar. Titik penanda terus melayang di angkasa: mereka naik ke langit-langit, atau ditarik ke suatu tempat di bawah kaki. Tidak setiap pengguna akan setuju untuk menangkap objek-AR dalam fokus selama beberapa menit untuk berkenalan dengan informasi yang menarik baginya.
Ternyata, banyak yang menghadapi bug perpustakaan ini, tetapi solusi belum ditemukan. Kode pada GitHub, sayangnya, belum diperbarui selama lebih dari enam bulan, jadi saya harus mem-bypassnya.
Kami mencoba menggunakan ketinggian alih-alih ketinggian tetap dalam koordinat, yang dikembalikan LocationManager untuk posisi pengguna saat ini. Namun, ini tidak sepenuhnya menghilangkan masalah. Data yang berasal dari Manajer Lokasi mulai melompat dengan penyebaran hingga 60 meter, segera setelah perangkat diputar. Akibatnya, gambar itu tidak stabil, yang, tentu saja, tidak cocok untuk kita lagi.
Akibatnya, diputuskan untuk meninggalkan perpustakaan ARKit-CoreLocation dan menempatkan poin di ruang mereka sendiri. Artikel ARKit dan CoreLocation, ditulis oleh Christopher Web-Orenstein, banyak membantu dalam hal ini. Saya harus menghabiskan sedikit lebih banyak waktu dan menyegarkan beberapa aspek matematika dalam ingatan saya, tetapi hasilnya sepadan: objek-AR akhirnya ada di tempat mereka. Setelah itu, tinggal menyebarkannya di sepanjang sumbu Y sehingga label dan titik lebih mudah dibaca, dan untuk membangun korespondensi antara jarak dari posisi saat ini ke titik dan koordinat Z dari objek AR, sehingga informasi tentang tempat bersejarah terdekat ada di latar depan.
Itu perlu untuk menghitung posisi SCNNode baru di ruang angkasa, dengan fokus pada koordinat:
let place = PlaceNode() let locationTransform = MatrixHelper.transformMatrix(for: matrix_identity_float4x4, originLocation: curUserLocation, location: nodeLocation, yPosition: pin.yPos, shouldScaleByDistance: false) let nodeAnchor = ARAnchor(transform: locationTransform) scene.session.add(anchor: nodeAnchor) scene.scene.rootNode.addChildNode(place)
Fungsi-fungsi berikut ditambahkan ke kelas MatrixHelper:
class MatrixHelper { static func transformMatrix(for matrix: simd_float4x4, originLocation: CLLocation, location: CLLocation, yPosition: Float) -> simd_float4x4 { let distanceToPoint = Float(location.distance(from: originLocation)) let distanceToNode = (10 + distanceToPoint/1000.0) let bearing = GLKMathDegreesToRadians(Float(originLocation.coordinate.direction(to: location.coordinate))) let position = vector_float4(0.0, yPosition, -distanceToNode, 0.0) let translationMatrix = MatrixHelper.translationMatrix(with: matrix_identity_float4x4, for: position) let rotationMatrix = MatrixHelper.rotateAroundY(with: matrix_identity_float4x4, for: bearing) let transformMatrix = simd_mul(rotationMatrix, translationMatrix) return simd_mul(matrix, transformMatrix) } static func translationMatrix(with matrix: matrix_float4x4, for translation : vector_float4) -> matrix_float4x4 { var matrix = matrix matrix.columns.3 = translation return matrix } static func rotateAroundY(with matrix: matrix_float4x4, for degrees: Float) -> matrix_float4x4 { var matrix : matrix_float4x4 = matrix matrix.columns.0.x = cos(degrees) matrix.columns.0.z = -sin(degrees) matrix.columns.2.x = sin(degrees) matrix.columns.2.z = cos(degrees) return matrix.inverse } }
Untuk menghitung azimuth ditambahkan ekstensi
CLLocationCoordinate2D extension CLLocationCoordinate2D { func calculateBearing(to coordinate: CLLocationCoordinate2D) -> Double { let a = sin(coordinate.longitude.toRadians() - longitude.toRadians()) * cos(coordinate.latitude.toRadians()) let b = cos(latitude.toRadians()) * sin(coordinate.latitude.toRadians()) - sin(latitude.toRadians()) * cos(coordinate.latitude.toRadians()) * cos(coordinate.longitude.toRadians() - longitude.toRadians()) return atan2(a, b) } func direction(to coordinate: CLLocationCoordinate2D) -> CLLocationDirection { return self.calculateBearing(to: coordinate).toDegrees() } }
Masalah 2. Kelebihan objek AR
Masalah berikutnya yang kami temui adalah sejumlah besar objek AR. Ada banyak tempat bersejarah dan pemandangan di kota kami, jadi dadu dengan informasi digabungkan dan merangkak satu ke yang lain. Akan sangat sulit bagi pengguna untuk membuat bagian dari prasasti, dan ini bisa membuat kesan menjijikkan. Setelah berunding, kami memutuskan untuk membatasi jumlah objek AR yang ditampilkan secara bersamaan, hanya menyisakan titik dalam radius 500 meter dari lokasi saat ini.
Namun, di beberapa daerah, konsentrasi titik masih terlalu tinggi. Oleh karena itu, untuk meningkatkan visibilitas, mereka memutuskan untuk menggunakan pengelompokan. Pada layar peta, fitur ini tersedia secara default karena logika yang tertanam dalam MapKit, tetapi dalam mode AR itu perlu untuk mengimplementasikannya secara manual.
Clustering didasarkan pada jarak dari posisi saat ini ke target. Jadi, jika titik jatuh ke zona dengan jari-jari sama dengan setengah jarak antara pengguna dan objek wisata sebelumnya dari daftar, itu hanya bersembunyi dan merupakan bagian dari cluster. Saat pengguna mendekatinya, jarak menurun, dan jari-jari zona cluster menurun, sehingga pemandangan yang berada di dekatnya tidak bergabung menjadi kelompok. Untuk membedakan cluster secara visual dari satu titik, kami memutuskan untuk mengubah warna marker dan menampilkan jumlah objek dalam AR alih-alih nama tempat.

Untuk memastikan interaktivitas objek AR, UITapGestureRecognizer digantung di ARSCNView dan di handler menggunakan metode hitTest yang memeriksa objek SCNNode mana yang diklik pengguna. Jika ternyata itu adalah foto objek wisata terdekat, aplikasi membuka album yang sesuai dalam mode layar penuh.
Masalah 3. Radar
Selama implementasi aplikasi, perlu untuk menunjukkan titik-titik pada radar kecil. Secara teori, seharusnya tidak ada kesalahpahaman, karena kami sudah menghitung azimuth dan jarak ke titik, bahkan berhasil mengubahnya menjadi koordinat 3D. Hanya tinggal menempatkan titik-titik dalam ruang dua dimensi di layar.

Agar tidak menemukan kembali roda, mereka beralih ke perpustakaan
Radar , yang kode sumber terbukanya diterbitkan di GitHub. Pratinjau yang jelas dan pengaturan yang fleksibel dari contoh ini menggembirakan, tetapi dalam kenyataannya poin-poin tersebut bergeser relatif terhadap lokasi sebenarnya di ruang angkasa. Setelah menghabiskan beberapa waktu mencoba memperbaiki rumus, kami beralih ke opsi yang kurang indah, tetapi lebih andal yang dijelaskan dalam
iPhone Augmented Reality Toolkit :
func place(dot: Dot) { var y: CGFloat = 0.0 var x: CGFloat = 0.0 if degree < 0 { degree += 360 } let bearing = dot.bearing.toRadians() let radius: CGFloat = 60.0 // radius of the radar view if (bearing > 0 && bearing < .pi / 2) { //the 1 quadrant of the radar x = radius + CGFloat(cosf(Float((.pi / 2) - bearing)) * Float(dot.distance)) y = radius - CGFloat(sinf(Float((.pi / 2) - bearing)) * Float(dot.distance)) } else if (bearing > .pi / 2.0 && bearing < .pi) { //the 2 quadrant of the radar x = radius + CGFloat(cosf(Float(bearing - (.pi / 2))) * Float(dot.distance)) y = radius + CGFloat(sinf(Float(bearing - (.pi / 2))) * Float(dot.distance)) } else if (bearing > .pi && bearing < (3 * .pi / 2)) { //the 3 quadrant of the radar x = radius - CGFloat(cosf(Float((3 * .pi / 2) - bearing)) * Float(dot.distance)) y = radius + CGFloat(sinf(Float((3 * .pi / 2) - bearing)) * Float(dot.distance)) } else if (bearing > (3 * .pi / 2.0) && bearing < (2 * .pi)) { //the 4 quadrant of the radar x = radius - CGFloat(cosf(Float(bearing - (3 * .pi / 2))) * Float(dot.distance)) y = radius - CGFloat(sinf(Float(bearing - (3 * .pi / 2))) * Float(dot.distance)) } else if (bearing == 0) { x = radius y = radius - CGFloat(dot.distance) } else if (bearing == .pi / 2) { x = radius + CGFloat(dot.distance) y = radius } else if (bearing == .pi) { x = radius y = radius + CGFloat(dot.distance) } else if (bearing == 3 * .pi / 2) { x = radius - CGFloat(dot.distance) y = radius } else { x = radius y = radius - CGFloat(dot.distance) } let newPosition = CGPoint(x: x, y: y) dot.layer.position = newPosition
Backend
Tetap untuk menyelesaikan masalah menyimpan titik dan foto. Untuk tujuan ini, diputuskan untuk menggunakan Contentful, dan dalam implementasi proyek saat ini ia sangat cocok untuk kami.


Pada saat pengembangan aplikasi mobile, semua bekender terlibat dalam proyek komersial, dan puas diizinkan untuk menyediakan selama beberapa jam:
- pengembang seluler - backend yang nyaman
- pengelola konten - area admin yang nyaman untuk mengisi data
Implementasi yang sama dari backend pada awalnya digunakan oleh tim yang berpartisipasi dalam hackathon (disebutkan di awal artikel), yang sekali lagi membuktikan bahwa hal-hal seperti hackathon memungkinkan Anda untuk melarikan diri dari menyelesaikan masalah mendesak Anda pada proyek, memungkinkan untuk menciptakan kembali dan mencoba sesuatu baru.
Kesimpulan
Sangat menarik untuk mengembangkan
aplikasi AR , dalam prosesnya kami mencoba beberapa perpustakaan yang sudah jadi, tetapi kami juga harus mengingat matematika dan menulis banyak hal sendiri.
Sekilas, proyek ini membutuhkan banyak jam kerja untuk mengimplementasikan dan memperbaiki algoritme, meskipun kami menggunakan SDK standar dari Apple.
Kami baru-baru ini memposting aplikasi di
AppStore . Begini tampilannya di tempat kerja.
Sejauh ini, dalam basis data kami hanya ada poin untuk Taganrog, namun, semua orang dapat berpartisipasi dalam perluasan "area cakupan".