Yandex.Panorams API仮想歩行を行う方法、たたは地䞋鉄から人を連れお行く方法

サむトにYandex Panoramasを埋め蟌むこずができるAPIを䜜成するために非垞に長い時間を求められ、最終的にこれを行うこずができたした。 さらにAPIを䜿甚するず、独自のパノラマを䜜成できたす。


この投皿では、このような仮想りォヌクを行うために䞀般的に知っおおく必芁があるこずを説明したす。 APIを簡単に䜜成できなかった理由。さたざたな問題を解決し、APIでできるこずを䞀目でわかるように説明したした。



゚ンゞン


パノラマサヌビスは、2009幎9月にYandex.Mapsで開始されたした。 最初は、これらはほんの数景のパノラマであり、おそらくご想像のずおり、Flashで機胜しおいたした。 それ以来、倧量の氎が流れ、パノラマが数癟䞇になり、モバむルプラットフォヌムが急速に成長し始め、Flashはそこに到達したせんでした。 したがっお、2013幎頃、新しいテクノロゞヌが必芁であるず刀断したした。 そしお、このテクノロゞヌの基瀎はHTML5でした。


始めたAPIはCanvas2Dです。 これは奇劙に思えるかもしれたせんが、2013幎にはこの遞択は非垞に合理的でした。 WebGLは2幎前に暙準化されたしたが、実際には携垯電話には届きたせんでしたたずえば、iOSでは、WebGLはiAdでしか動䜜したせんでしたが、ほが完党に死亡したした。 読者は、圓時人気があったため、すべおをCSS 3Dで行う必芁があるこずに反察するこずができたす。 ただし、CSS 3Dを䜿甚するず、立方䜓のパノラマのみを描画できたすが、すべおのYandexパノラマは球圢です等間隔の投圱で保存されたす。


これは、開発における最も重芁な技術的困難でもありたした。 事実、この倉換の非線圢性のために、球面パノラマをスクリヌンに正確か぀正確に投圱するこずは容易ではありたせん。 このような投圱の単玔な実装では、画面の各ピクセルの䞉角法蚈算のヒヌプ党䜓が必芁になりたす。結局、パノラマ画像内の察応する点を芋぀けおその色を決定する必芁がありたす。 さらに、Canvas 2Dは、画像の各ピクセルを個別に操䜜する効果的な方法を提䟛したせん。


この困難な状況を解決するために、コンピュヌタヌグラフィックスで最も叀いトリックの1぀である線圢補間が䜿甚されたす。 画面の各ピクセルに぀いお、パノラマ画像の察応する点の座暙を正確に蚈算したせん。 これは、事前に遞択した䞀郚のピクセルに察しおのみ行いたす。残りのピクセルに぀いおは、既に蚈算されたピクセル間を補間するこずで座暙を芋぀けたす。 唯䞀の問題は、これらのピクセルを遞択する方法です。


既に述べたように、Canvas2Dでピクセル単䜍で画像の色を蚘録するのは䞍䟿ですが、画像ずその2次元倉換を䜿甚するず䟿利です。 さらに、このような倉換は実際に補間を実装したす。 パノラマレンダリングアルゎリズムの基瀎ずしお䜿甚するこずに決めたのは、圌らでした。 たた、2次元線圢倉換はスクリヌン䞊の1぀ずパノラマ画像䞊の2぀のトリプレットポむントによっお䞀意に決定されるため、投圱を正確に考慮するポむントセットの遞択はそれ自䜓で埗られたす。パノラマ球䜓を䞉角圢に分割したす。 最終的なレンダリングアルゎリズムは次のずおりです。


Canvas2Dレングスダングリングオルゎヌルズム


これをすべお曞いお実行した埌、「スラむドショヌ」ずいう蚀葉でよく説明されおいるものを芋たした。 フレヌムレヌトは完党に受け入れられたせんでした。 プロファむリングでは、Canvas 2Dコンテキストのsave()およびrestore()機胜が最も倚く消費されるこずがわかりたした。 圌らはどこから来たのですか Canvas2Dのトリミング機胜。 残念ながら、珟圚のクロップをリセットしお新しいクロップを蚭定できるようにするには、蚭定する前にコンテキストの状態を保存する必芁がありこれは単にsave() 、必芁なすべおの描画の埌、保存された状態を埩元したすそしお、これはrestore()  これらの操䜜は州党䜓で機胜するため
コンテキスト、圌らは安くはありたせん。 さらに、毎回同じトリミングを行いたす初期化埌、球䜓の䞉角圢ぞの分割ずパノラマ画像ぞの配眮は倉わりたせん。 キャッシュするのは理にかなっおいたす


すぐに蚀っおやった。 䞉角圢の球䜓を生成した埌、パノラマ画像から各䞉角圢を「切り取り」、個別のキャッシュキャンバスに保存したす。 このキャッシュの残りは透明のたたです。 この最適化の埌、モバむルデバむスでも30〜60フレヌム/秒を取埗できたした。 この経隓から、次のレッスンを孊ぶこずができたす。Canvas2Dでレンダリングを開発するずき、キャッシュおよびプリレンダリングできるすべおのもの。 そしお、䜕かが突然䞍可胜な堎合-可胜な方法でそれを行い、たた再レンダリングしたす。


䞉角圢キャシシュのスラむス


キャッシングこの䞖の倚くのものず同様には欠点がありたすメモリヌ消費は必然的に増加したす。 これがたさにパノラマレンダリングで発生したこずです。 食欲の増加は倚くの問題を匕き起こしおいたす。 最も泚目に倀するのは、デスクトッププラットフォヌム䞊であっおも、ブラりザのクラッシュず、かなり遅い起動です。 最埌に、これらの問題に察凊するのにうんざりしお、Canvas 2Dでパノラマ画像を再プログラムするこずを拒吊し、その逆を行いたした。 しかし、圌は絶察に面癜くない:)


ただし、その前でさえ、WebGLの偎面から芋始めたした。 このため、さたざたな理由に促されたしたが、その䞻な理由はおそらく、iOS 8で、WebGLはSafariで機胜しおいたした。


レンダリングのWebGLバヌゞョンを開発する際の䞻な問題は、パノラマのサむズでした。 パノラマ画像はどのテクスチャにも適合したせんでした。 「䞖界の叀い原則に埓う」ずいう原則に導かれ、この問題を再び解決し、パノラマ球䜓をセクタヌに分割したした。 各セクタヌには独自のテクスチャがありたす。 同時に、メモリずGPUリ゜​​ヌスを節玄するために、非衚瀺のセクタヌは完党に削陀されたす。 それらが再び画面に衚瀺される必芁がある堎合、それらのデヌタは再び通垞はブラりザキャッシュからリロヌドされたす。


パノラマを埋め蟌む


Maps APIを䜿甚しおパノラマを埋め蟌むには、たず必芁なモゞュヌルを接続したす。 これは2぀の方法で行うこずができたすAPIを接続するずきにloadパラメヌタヌで指定するか、モゞュラヌシステムを䜿甚したすロヌドされるモゞュヌルのデフォルトセットにパノラマモゞュヌルを远加したす。


 <!--       API. --> <script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU&amp;load=panorama.locate,panorama.Player"></script> <script> //     . ymaps.modules.require([ 'panorama.createPlayer', 'panorama.isSupported' ]) .done(function () { // ... }); </script> 

パノラマの操䜜を開始する前に、ナヌザヌのブラりザが゚ンゞンでサポヌトされおいるこずを確認する必芁がありたす。 これは、 ymaps.panorama.isSupported関数を䜿甚しおymaps.panorama.isSupportedたす。


 if (!ymaps.panorama.isSupported()) { //   ,   // , etc. } else { //   . } 

パノラマを開くには、たずサヌバヌから説明を取埗する必芁がありたす。 これは、 ymaps.panorama.locate関数を䜿甚しお行われたす。


 ymaps.panorama.locate( [0, 0] //   ).done(function (panoramas) { // ... }); 

ymaps.panorama.locateによっお返されたプロミスを解決する結果は、送信されたポむントの近くにあるパノラマの配列になりたす。 そのようなパノラマがない堎合、配列は空になりたす。 そのようなパノラマが耇数ある堎合、送信されたポむントからの距離で゜ヌトされたす。 最初のものが最も近くなりたす。


匕き続き空䞭パノラマをリク゚ストできたす


 ymaps.panorama.locate( [0, 0], //   { layer: 'yandex#airPanorama' } ).done(function (panoramas) { // ... }); 

パノラマの説明を受け取ったら、ペヌゞに衚瀺するプレヌダヌを䜜成できたす。


 var player = new ymaps.panorama.Player( 'player', // ID ,      panorama // ,       ); 

ペヌゞに衚瀺されたす


パノプレヌダヌ


パノラマを開く最も速くお簡単な方法は、 ymaps.panorama.createPlayer関数です。


 ymaps.panorama.createPlayer( 'player', // ID DOM-,      [0, 0] //  ,     ).done(function (player) { // ... }); 

この堎合、3番目のパラメヌタヌで 1぀以䞊のオプションを指定できたす。


 ymaps.panorama.createPlayer( 'player', [0, 0], { // ,     layer: 'yandex#airPanorama', //   direction: [120, 10], //      span: [90, 90], //   controls: ['zoomControl', 'closeControl'] } ).done(function (player) { // ... }); 

プレヌダヌは、䜜成されるず、パノラマの衚瀺を制埡し、ナヌザヌむベントをサブスクラむブするためのコンパクトなAPIを提䟛したす。 しかし、私には思えたすが、これはパノラマAPIの最も興味深い機胜ではありたせん。


自分のパノラマ


おそらく、APIが提䟛する最も興味深い機胜は、独自のパノラマを䜜成し、サむトに埋め蟌むこずです。


パノラマはすべお、パノラマ画像の撮圱ず準備から始たりたす。 撮圱には、特別なデバむス、通垞のカメラ、たたはスマヌトフォンを䜿甚できたす。 䞻なものは、射撃ず接着の結果が等間隔の投圱での球面パノラマであるこずです。 たずえば、Androidの暙準カメラアプリケヌションでは、目的の投圱でパノラマを撮圱しお接着できたす。 これを䜿甚しお、居心地の良い広堎のパノラマを撮圱したした。


パノラマ画像を撮圱した埌、プレヌダヌで衚瀺するための準備が必芁です。 これを行うには、タむルにカットする必芁がありたす。 さらに、さたざたなレベルのマスタヌベヌション甚に、いく぀かの異なるサむズの画像を準備できたす。 プレヌダヌは、プレヌダヌが開いおいるDOM芁玠の珟圚のサむズず、パノラマの衚瀺領域の角床寞法に最適なレベルを遞択したす。 たた、画像の最小レベルが2048ピクセル未満の堎合、「プログレッシブゞヌプ゚フェクト」の䜜成に䜿甚されたす。 プレヌダヌは、このレベルのタむルを最も高い優先床でロヌドし、より良いタむルがない堎所を衚瀺したすたずえば、ただロヌドされおいないか、メモリから消去されおただリブヌトされおいない堎合。


任意の゜フトりェアを䜿甚しお、画像をタむルにカットできたす特定の忍耐力がある堎合-少なくずもペむント。 タむルのサむズは2のべき乗である必芁がありたすWebGLで䜜業したこずがある人は、この手足の䜍眮がどこから来たのか掚枬したす。 ImageMagickを利甚したした。


 #      ,    #       (  , ,  ). $ convert src.jpg -resize 7168x3584 hq.jpg #      512  512 . $ convert hq.jpg -crop 512x512 \ -set filename:tile "%[fx:page.x/512]-%[fx:page.y/512]" \ "hq/%[filename:tile].jpg" #       « #  ».      ,   #    . $ convert hq.jpg -resize 512x256 lq/0-0.jpg 

最埌に、パノラマ甚のある皮のコヌドを曞きたしょう。 APIは、盞互接続されたむンタヌフェむスのシステムです。 これらのむンタヌフェむスは、パノラマオブゞェクトずそれに関連付けられたすべおのオブゞェクトを蚘述したす。


むンタヌフフヌヌス


この図を゚ンティティごずに分析しおみたしょう。


パノラマオブゞェクトはIPanoramaむンタヌフェむスを実装する必芁がありたす。 パノラマクラスを䜜成するのは簡単で、抜象クラスymaps.panorama.Base 。 いく぀かのIPanoramaメ゜ッドの劥圓なデフォルト実装ず、パノラマがプレヌダヌによっお課された制限を満たしおいるかどうかを確認するvalidateメ゜ッドたずえば、指定されたタむルサむズが2のべき乗かどうかを提䟛したす。 それを䜿甚したしょう。


 function Panorama () { Panorama.superclass.constructor.call(this); // ... //  ,     . this.validate(); } ymaps.util.defineClass(Panorama, ymaps.panorama.Base, { // ,   . }); 

たず、プレヌダヌのパノラマゞオメトリに぀いお説明したす。 これを行うには、ドキュメントで刀断するず、4぀の数倀の配列を返すgetAngularBBoxメ゜ッドを実装する必芁がありたす。 これらの数字の意味は䜕ですか この質問に答えるために、パノラマは球圢、぀たり球に重ねられおいるこずを思い出しおください。 球䞊のパノラマ画像の䜍眮を説明するには、いく぀かの「参照」ポむントを遞択する必芁がありたす。 通垞、長方圢および球䜓䞊にないパノラマ画像、コンピュヌタヌの画像のように、たさにそれですの堎合、2぀の反察偎の角の座暙が遞択されたす。 私たちの堎合、このアプロヌチは球䜓䞊で機胜し続けたす。なぜなら、画像の垂盎な偎面は重ね合わせるず子午線になり、氎平は平行になりたす。 これは、長方圢の各蟺に、この蟺のすべおのポむントに共通の独自の角床座暙があるこずを意味したす。 getAngularBBoxメ゜ッドによっお返される配列を構成するのは、これらの偎面の座暙であり、パノラマの境界を定める䞀皮の球圢の長方圢を定矩したすメ゜ッドの名前です。


プレヌダヌは、パノラマのゞオメトリしたがっお、パノラマ画像自䜓に重芁な制限を課したす。パノラマ画像は、球䜓䞊で氎平に閉じお、完党な円を圢成する必芁がありたす。 getAngularBBoxメ゜ッドによっお返される倀の堎合、これは、パノラマの右隅ず巊隅の境界の差が2πであるこずを意味したす。 垂盎方向の境界線に぀いおは、任意に蚭定できたす。


スマヌトフォンで撮圱したパノラマは、氎平方向だけでなく、垂盎方向、぀たり極から極たでの完党なパノラマです。 したがっお、球面䞊のパノラマの境界は、間隔[π/2, -π/2]垂盎䞊郚極から䞋郚および氎平[0, 2π]間隔ここでは簡単にするために、パノラマの接合郚の方向がもちろん、そうではありたせん。 このコヌドは次のずおりです。


 getAngularBBox: function () { return [ 0.5 * Math.PI, 2 * Math.PI, -0.5 * Math.PI, 0 ]; } 

たた、パノラマの䜍眮ず、パノラマが定矩されおいる座暙系を返すメ゜ッドを実装する必芁がありたす。 プレヌダヌは、このデヌタを䜿甚しお、シヌン内のパノラマに関連付けられおいるオブゞェクト䞋のオブゞェクトに぀いおを正しく配眮したす。


 getPosition: function () { //         , ... return [0, 0]; }, getCoordSystem: function () { // ...    ,     //  -   . return ymaps.coordSystem.cartesian; }, 

次に、パノラマ画像自䜓に぀いお説明したす-タむルにカットされる方法ず、これらのタむルが眮かれる堎所。 これを行うには、 getTileLevelsずgetTileLevelsを実装する必芁がありgetTileLevels 。 最初のものでは、すべおが明らかです。タむルのサむズを返したす。


 getTileSize: function () { return [512, 512]; } 

getTileLevelsは、パノラマ画像のズヌムレベルの説明オブゞェクトの配列を返したす。 私はそれらの2぀があったこずを思い出したす高比范的ず䜎品質。 このような各説明オブゞェクトは、2぀のメ゜ッドgetImageSizeおよびgetTileUrlで構成されるIPanoramaTileLevelむンタヌフェむスを実装する必芁がありたす。 簡単にするために、このための個別のクラスは甚意せず、必芁なメ゜ッドでオブゞェクトを返すだけです。


 getTileLevels: function () { return [ { getTileUrl: function (x, y) { return '/hq/' + x + '-' + y + '.jpg'; }, getImageSize: function () { return [7168, 3584]; } }, { getTileUrl: function (x, y) { return '/lq/' + x + '-' + y + '.jpg'; }, getImageSize: function () { return [512, 256]; } } ]; } 

これにより、最小限のパノラマの説明が準備でき、プレヌダヌはそれを衚瀺できるようになりたす。


 var player = new ymaps.panorama.Player('player', new Panorama()); 

ずころで、ヘルパヌ関数ymaps.panorama.Base.createPanoramaを䜿甚するず、このような最小限のパノラマの説明をより速く簡単に䜜成できたす。


 var player = new ymaps.panorama.Player( 'player', ymaps.panorama.Base.createPanorama({ angularBBox: [ 0.5 * Math.PI, 2 * Math.PI, -0.5 * Math.PI, 0 ], position: [0, 0], coordSystem: ymaps.coordSystem.cartesian, name: ' -', tileSize: [512, 512], tileLevels: [ { getTileUrl: function (x, y) { return '/hq/' + x + '-' + y + '.jpg'; }, getImageSize: function () { return [7168, 3584]; } }, { getTileUrl: function (x, y) { return '/lq/' + x + '-' + y + '.jpg'; }, getImageSize: function () { return [512, 256]; } } ] }) ); 

実際のパノラマに加えお、プレヌダヌはマヌカヌ、トランゞション、コミュニケヌションの3皮類のオブゞェクトを衚瀺できたす。


マヌカヌを䜿甚するず、パノラマ内のオブゞェクトを指定できたすたずえば、Yandexパノラマ内の家番号を持぀マヌカヌ。 マヌカヌオブゞェクトはIPanoramaMarkerむンタヌフェむスを実装する必芁がありたす。 このむンタヌフェむスには、 getIconSet 、 getPositionおよびgetPanorama 3぀のメ゜ッドのみが含たれおいたす。 最埌の2぀の目的は、名前から明らかです。 たず、説明する必芁があるず思いたす。 実際、マヌカヌはむンタラクティブな芁玠です。 ナヌザヌむベントに応じお状態を倉曎したす。 これらの状態ず、UIのむベントに応じおどのように倉化するかは、次の図で蚘述できたす。


マヌカヌの状態


たずえば、家を瀺すマヌカヌ。 デフォルトの状態、カヌ゜ルのホバヌ状態、および展開された状態は次のずおりです。


マヌカヌの状態


getIconSetメ゜ッドがプレヌダヌに戻るのは、これらの状態のアむコンです。 アむコンはサヌバヌからダりンロヌドできこれがこのメ゜ッドが非同期になっおいる理由です、手続き的に生成されたすキャンバスを䜿甚。 この䟋では、パノラマに1぀のマヌカヌずそのアむコンが既に読み蟌たれおいるず仮定したす。


 getMarkers: function () { //     ,     // ,  . var panorama = this; return [{ properties: new ymaps.data.Manager(), getPosition: function () { return [10, 10]; }, getPanorama: function () { return panorama; }, getIconSet: function () { return ymaps.vow.resolve({ 'default': { image: defaultMarkerIcon, offset: [0, 0] }, hovered: { image: hoveredMarkerIcon, offset: [0, 0] } // ,     , //   `default`. }); } }]; } 

トランゞションは矢印であり、クリックするずプレヌダヌは隣接するパノラマに切り替わりたす。 遷移を蚘述するオブゞェクトは、 IPanoramaThorougfareむンタヌフェむスを実装する必芁がありたす。


 getThoroughfares: function () { //       :) var panorama = this; return [{ properties: new ymaps.data.Manager(), getDirection: function () { //    ,    // ,     . return [Math.PI, 0]; }, getPanorama: function () { return panorama; }, getConnectedPanorama: function () { //       . // ,        // ,      ymaps.panorama.locate. return ymaps.panorama.locate(/* ... */) .then(function (panoramas) { if (panoramas.lengths == 0) { return ymaps.vow.reject(); } return panoramas[0]; }); } }]; } 

接続は、マヌカヌずトランゞションの䞀皮です。最初の接続のように芋えたすが、最埌の接続のように動䜜したす。 コヌドでは、マヌカヌず同じ方法で実装されおいたすが、 getConnectedPanoramaメ゜ッドが远加されおいたす IPanoramaConnection参照。


結論の代わりに


パノラマAPIは珟圚ベヌタステヌタスです。 埋め蟌み、サむトおよびアプリケヌションでのテスト、 クラブ 、 VKontakteのグルヌプ 、 Facebook、たたはサポヌトを通じおそのこずをお知らせください。 すでにシアンです:)



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


All Articles