Menggunakan Peta QML untuk Membangun Airways - Bagian 1

Untuk beberapa waktu sekarang saya telah menggunakan QML untuk membangun antarmuka grafis, tetapi sejauh ini belum ada kesempatan untuk bekerja dalam proyek nyata dengan Qt Location API dan QML Map.
Karena itu, menjadi menarik untuk mencoba komponen ini untuk membangun saluran udara.
Di bawah pemotong adalah deskripsi implementasi editor, untuk membuat jalur seperti itu di peta:

gambar

Untuk menyederhanakan implementasi, pesawat kami terbang di pesawat 2D pada ketinggian yang sama. Kecepatan dan kelebihan beban diizinkan - 920 km / jam dan 3g, yang memberikan radius belokan

R= fracv2G=21770m


Lintasan terdiri dari segmen-segmen berikut:
gambar
di mana S adalah awal dari manuver (itu adalah titik keluar dari yang sebelumnya), M adalah awal dari belokan, E adalah jalan keluar dari itu, dan F adalah titik akhir (M untuk yang berikutnya).

Untuk menghitung titik masuk dan keluar dari lintasan, saya menggunakan persamaan tangen ke lingkaran, perhitungan ternyata agak rumit, saya yakin itu bisa dibuat lebih sederhana.

void Manoeuvre::calculate() { // General equation of line between first and middle points auto A = mStart.y() - mMiddle.y(); auto B = mMiddle.x() - mStart.x(); // Check cross product sign whether final point lies on left side auto crossProduct = (B*(mFinal.y() - mStart.y()) + A*(mFinal.x() - mStart.x())); // All three points lie on the same line if (isEqualToZero(crossProduct)) { mIsValid = true; mCircle = mExit = mMiddle; return; } mIsLeftTurn = crossProduct > 0; auto lineNorm = A*A + B*B; auto exitSign = mIsLeftTurn ? 1 : -1; auto projection = exitSign*mRadius * qSqrt(lineNorm); // Center lies on perpendicular to middle point if (!isEqualToZero(A) && !isEqualToZero(B)) { auto C = -B*mStart.y() - A*mStart.x(); auto right = (projection - C)/A - (mMiddle.x()*lineNorm + A*C) / (B*B); mCircle.ry() = right / (A/B + B/A); mCircle.rx() = (projection - B*mCircle.y() - C) / A; } else { // Entering line is perpendicular to either x- or y-axis auto deltaY = isEqualToZero(A) ? 0 : exitSign*mRadius; auto deltaX = isEqualToZero(B) ? 0 : exitSign*mRadius; mCircle.ry() = mMiddle.y() + deltaY; mCircle.rx() = mMiddle.x() + deltaX; } // Check if final point is outside manouevre circle auto circleDiffX = mFinal.x() - mCircle.x(); auto circleDiffY = mFinal.y() - mCircle.y(); auto distance = qSqrt(circleDiffX*circleDiffX + circleDiffY*circleDiffY); mIsValid = distance > mRadius; // Does not make sence to calculate futher if (!mIsValid) return; // Length of hypotenuse from final point to exit point auto beta = qAtan2(mCircle.y() - mFinal.y(), mCircle.x() - mFinal.x()); auto alpha = qAsin(mRadius / distance); auto length = qSqrt(distance*distance - mRadius*mRadius); // Depends on position of final point find exit point mExit.rx() = mFinal.x() + length*qCos(beta + exitSign*alpha); mExit.ry() = mFinal.y() + length*qSin(beta + exitSign*alpha); // Finally calculate start/span angles auto startAngle = qAtan2(mCircle.y() - mMiddle.y(), mMiddle.x() - mCircle.x()); auto endAngle = qAtan2(mCircle.y() - mExit.y(), mExit.x() - mCircle.x()); mStartAngle = startAngle < 0 ? startAngle + 2*M_PI : startAngle; endAngle = endAngle < 0 ? endAngle + 2*M_PI : endAngle; auto smallSpan = qFabs(endAngle - mStartAngle); auto bigSpan = 2*M_PI - qFabs(mStartAngle - endAngle); bool isZeroCrossed = mStartAngle > endAngle; if (!mIsLeftTurn) { mSpanAngle = isZeroCrossed ? bigSpan : smallSpan; } else { mSpanAngle = isZeroCrossed ? smallSpan : bigSpan; } } 

Setelah menyelesaikan kesalahan perhitungan model matematika lintasan kami, kami melanjutkan untuk bekerja langsung dengan peta. Pilihan alami untuk membuat polyline pada peta QML adalah menambahkan MapPolyline langsung ke peta.

 Map { id: map plugin: Plugin { name: "osm" } MapPolyline { path: [ { latitude: -27, longitude: 153.0 }, ... ] } } 

Awalnya, saya ingin memberi pengguna kesempatan untuk mensimulasikan setiap bagian selanjutnya dari rute β€œon the fly” - untuk membuat efek lintasan di belakang kursor.

gambar

Mengubah jalur saat memindahkan kursor adalah operasi yang agak mahal, jadi saya mencoba menggunakan jalur "pixel" pendahuluan yang ditampilkan hingga pengguna akhirnya menyimpan rute.

 Repeater { id: trajectoryView model: flightRegistry.hasActiveFlight ? flightRegistry.flightModel : [] FlightItem { anchors.fill: parent startPoint: start endPoint: end manoeuvreRect: rect manoeuvreStartAngle: startAngle manoeuvreSpanAngle: spanAngle isVirtualLink: isVirtual } } 

FlightItem adalah QQuickItem , dan flightModel QAbstractListModel memungkinkan Anda untuk memperbarui bagian yang diperlukan dari lintasan ketika mengubah data untuk manuver.

 QVariant FlightModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } switch (role) { case FlightRoles::StartPoint: return mFlight->flightSegment(index.row()).line().p1(); case FlightRoles::EndPoint: return mFlight->flightSegment(index.row()).line().p2(); ... } 

Pembaruan langsung semacam itu memungkinkan Anda untuk memperingatkan pengguna tentang manuver yang tidak dapat direalisasi.

gambar

Hanya setelah selesainya pembuatan jalan napas (misalnya, dengan klik kanan mouse), rute tersebut akhirnya akan ditambahkan ke Peta QML sebagai GeoPath dengan kemungkinan georeferensi (sampai saat ini peta tidak dapat dipindahkan dan diperbesar, piksel tidak tahu apa-apa tentang garis bujur dan garis lintang).
Untuk menghitung ulang segmen piksel ke dalam koordinat geografis, untuk permulaan kita perlu menggunakan untuk setiap manuver sistem koordinat lokal ke titik masuk manuver (titik S kami).

 QPointF FlightGeoRoute::toPlaneCoordinate(const QGeoCoordinate &origin, const QGeoCoordinate &point) { auto distance = origin.distanceTo(point); auto azimuth = origin.azimuthTo(point); auto x = qSin(qDegreesToRadians(azimuth)) * distance; auto y = qCos(qDegreesToRadians(azimuth)) * distance; return QPointF(x, y); } 

Setelah kami menghitung ulang manuver yang sudah meter, Anda perlu melakukan operasi terbalik dan mengetahui geolokasi titik S untuk menerjemahkan meter dalam lintang-bujur.

 QGeoCoordinate FlightGeoRoute::toGeoCoordinate(const QGeoCoordinate &origin, const QPointF &point) { auto distance = qSqrt(point.x()*point.x() + point.y()*point.y()); auto radianAngle = qAtan2(point.x(), point.y()); auto azimuth = qRadiansToDegrees(radianAngle < 0 ? radianAngle + 2*M_PI : radianAngle); return origin.atDistanceAndAzimuth(distance, azimuth); } 


Dari sudut pandang formal, tentu saja tidak mungkin untuk mempertimbangkan lintasan "piksel" dan "dalam meter" kami identik, tetapi tampaknya sangat lezat bagi saya untuk melihat ke masa depan dan menunjukkan kepada pengguna apa yang akan terjadi (atau tidak akan terjadi jika pesawat tidak terbang seperti itu) ketika dia akan mengklik waktu berikutnya. Setelah menyelesaikan lintasan (sedikit berbeda dari lintasan pixel dalam warna dan transparansi, karena bahkan garis putus statis tidak terlihat sangat mulus pada peta).

gambar

Sumber tersedia di sini , untuk kompilasi saya menggunakan Qt 5.11.2.

Pada bagian selanjutnya, kami akan mengajar editor kami untuk memindahkan titik referensi lintasan, serta menyimpan / membuka rute yang ada untuk simulasi selanjutnya dari pergerakan pesawat.

Source: https://habr.com/ru/post/id433828/


All Articles