diff --git a/core/res/assets/geoid_height_map/README.md b/core/res/assets/geoid_height_map/README.md new file mode 100644 index 0000000000000..849d32ec34aac --- /dev/null +++ b/core/res/assets/geoid_height_map/README.md @@ -0,0 +1,2 @@ +These binary protos are generated at runtime from the text protos in ../../geoid_height_map_assets +and using aprotoc. \ No newline at end of file diff --git a/core/res/assets/geoid_height_map/map-params.pb b/core/res/assets/geoid_height_map/map-params.pb new file mode 100644 index 0000000000000..8ca032c5317fd Binary files /dev/null and b/core/res/assets/geoid_height_map/map-params.pb differ diff --git a/core/res/assets/geoid_height_map/tile-1.pb b/core/res/assets/geoid_height_map/tile-1.pb new file mode 100644 index 0000000000000..93a2fa09ea36b Binary files /dev/null and b/core/res/assets/geoid_height_map/tile-1.pb differ diff --git a/core/res/assets/geoid_height_map/tile-3.pb b/core/res/assets/geoid_height_map/tile-3.pb new file mode 100644 index 0000000000000..4e22ca1dad672 Binary files /dev/null and b/core/res/assets/geoid_height_map/tile-3.pb differ diff --git a/core/res/assets/geoid_height_map/tile-5.pb b/core/res/assets/geoid_height_map/tile-5.pb new file mode 100644 index 0000000000000..c5f512765ece7 Binary files /dev/null and b/core/res/assets/geoid_height_map/tile-5.pb differ diff --git a/core/res/assets/geoid_height_map/tile-7.pb b/core/res/assets/geoid_height_map/tile-7.pb new file mode 100644 index 0000000000000..0928a6a9c7150 Binary files /dev/null and b/core/res/assets/geoid_height_map/tile-7.pb differ diff --git a/core/res/assets/geoid_height_map/tile-9.pb b/core/res/assets/geoid_height_map/tile-9.pb new file mode 100644 index 0000000000000..6a2210af518ed Binary files /dev/null and b/core/res/assets/geoid_height_map/tile-9.pb differ diff --git a/core/res/assets/geoid_height_map/tile-b.pb b/core/res/assets/geoid_height_map/tile-b.pb new file mode 100644 index 0000000000000..5fce996dfa89d Binary files /dev/null and b/core/res/assets/geoid_height_map/tile-b.pb differ diff --git a/core/res/geoid_height_map_assets/README.md b/core/res/geoid_height_map_assets/README.md new file mode 100644 index 0000000000000..800b3e50795ad --- /dev/null +++ b/core/res/geoid_height_map_assets/README.md @@ -0,0 +1,8 @@ +These text protos contain composite JPEG/PNG images representing the EGM2008 Earth Gravitational +Model[^1] published by the National Geospatial-Intelligence Agency.[^2] + +[^1]: Pavlis, Nikolaos K., et al. "The development and evaluation of the Earth Gravitational Model +2008 (EGM2008)." Journal of geophysical research: solid earth 117.B4 (2012). + +[^2]: National Geospatial-Intelligence Agency. “Office of Geomatics.” 2022. +URL: https://earth-info.nga.mil. \ No newline at end of file diff --git a/core/res/geoid_height_map_assets/map-params.textpb b/core/res/geoid_height_map_assets/map-params.textpb new file mode 100644 index 0000000000000..5ca6e4e1bf4fb --- /dev/null +++ b/core/res/geoid_height_map_assets/map-params.textpb @@ -0,0 +1,6 @@ +map_s2_level: 9 +cache_tile_s2_level: 5 +disk_tile_s2_level: 0 +model_a_meters: 193.0 +model_b_meters: -107.0 +model_rmse_meters: 0.29 diff --git a/core/res/geoid_height_map_assets/tile-1.textpb b/core/res/geoid_height_map_assets/tile-1.textpb new file mode 100644 index 0000000000000..7edba5babdb52 --- /dev/null +++ b/core/res/geoid_height_map_assets/tile-1.textpb @@ -0,0 +1,3 @@ +tile_key: "1" +byte_jpeg: "\377\330\377\340\000\020JFIF\000\001\002\000\000\001\000\001\000\000\377\333\000C\000\004\003\003\003\003\002\004\003\003\003\004\004\004\004\005\t\006\005\005\005\005\013\010\010\007\t\r\014\016\016\r\014\r\r\017\020\025\022\017\020\024\020\r\r\022\031\022\024\026\026\027\030\027\016\022\032\034\032\027\033\025\027\027\027\377\300\000\013\010\002\000\002\000\001\001\021\000\377\304\000\037\000\000\001\005\001\001\001\001\001\001\000\000\000\000\000\000\000\000\001\002\003\004\005\006\007\010\t\n\013\377\304\000\265\020\000\002\001\003\003\002\004\003\005\005\004\004\000\000\001}\001\002\003\000\004\021\005\022!1A\006\023Qa\007\"q\0242\201\221\241\010#B\261\301\025R\321\360$3br\202\t\n\026\027\030\031\032%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\203\204\205\206\207\210\211\212\222\223\224\225\226\227\230\231\232\242\243\244\245\246\247\250\251\252\262\263\264\265\266\267\270\271\272\302\303\304\305\306\307\310\311\312\322\323\324\325\326\327\330\331\332\341\342\343\344\345\346\347\350\351\352\361\362\363\364\365\366\367\370\371\372\377\332\000\010\001\001\000\000?\000\262zRf\212\007Zu(\251\005H\2652\324\311V\022\247Z\235*e\251\222\247Z\231jU\251V\244Zu\024f\220\232M\324\231\244-I\232B\307\322\232Z\223i#\"\233\234\034\0328\244\243&\237\237Zr\346\244\000\323\205H\0058\n\\R\3654\361\322\224\032p4\340i\300\324\212\325\"\340\323\2155\272Tg\255 <\322\226\246\226\2463S\013\363F\372]\374SK\322\007\346\245F\342\244\025\342\346\233E\024\341J:\323\326\245Z\231jt\251\323\255XZ\231je\0252\212\231ML\246\246Z\220\032\221i\331\2434\224\204\322S{\322\036\224\224\205\211\\SW\2574bEl\2575\031fc\222)A\347\232w\031\340\361G\000\365\247\003O\rO\014i\331\315H\254E<\034\323\207\265.9\247t\242\2274\240\322\346\236\255S\241\342\236MF\315\357L&\233\232\t\240\221\212\211\232\242-\315\033\275\351\273\351\013P\036\247\215\376Z\234\034/5\343\004\322\023H\r-\024\361\326\236\265*\232\231jt\251\322\247J\235je\251\224\324\313R\251\251T\324\202\244\007\212vh\315\031\244\2434\204\323I\246\223M\310\246\232h\225\220piRp\017\314)\306H\335\260\026\214\247\360\3233\3158\032p5\"\232\221MH)\343\332\234\033\035i\343\004qF}h\006\226\212Pi\301\260je~)\305\3522\336\364\322\334sM\335\2323\315-D\346\240f\346\232Z\232^\215\364n\253\020\277\025+\310\000\257\035\311\240ry\240\364\240\032Zx\251\007Z\221EL\2252\232\235*u\251\320\324\313S)\251\224\324\253R\n\220\032\220\032vh\315\'z3A\225(\007\373\325*\023\353V\020\361S+T\252jU5 5\"\232~h\335\357F\352\003\016\364\233\271\353F\341I\272\202i\244\323\016i\234\236\224\234\203I\232u8S\201\247\216\224\360)\340S\300\245\002\2368\247\016\264\352Q\326\2342jE\\\232\230\014\014S_\221\305@Cf\233\363\003N\003\"\232\300\212a\244\3158>;\323\204\224<\200\255Q\235\252\243?4\302\364\322\364\236g\2754\313\317Z_7\216\265\300?\006\232\t\024\271\006\224T\212jU<\325\230\300\253\n\243\034\212pL\373R\355\307J\221jU5 <\324\252\336\3652\265L\257R\253\324\201\252@\324\355\307\024\273\250\335HZ\223u\033\2517P[\320\322n\242\233\310<\032NM\000R\323\200\247\001O\024\360jE4\341\214\323\307Zp\245\243\245\225\t\\\032QO\002\236\265*\232\225Z\244\rO\017R+\323\303T\212\325\"\265L\255R+T\201\252@\325 j\2205<\032vi3Fh\024\264\341O\024\341OZ\220s\326\234\020\366\247(\303zT\214\271]\324\301\310\245\002\234\005<\nv)G\025\"\324\350\271\247\037Jz\216i\345\305D\317\317\024\240\320\302\243\3174n\247\243\205\006\243v\004U\031\334\n\253+\374\237\205g\273\363U\344j\201\244\302\325fl\232\211\2150\266)\214\374S\021\263%X\335Y\244f\243*)\207\203\203P\315\037q\322\240\034\032\222\234\r<5<5H\032\234\032\236\257R+T\252\325*\265J\255R\253T\212\325\"\232\221MH\246\244\006\235\2323K@\353N\247v\247\216\264\361\322\234:\324\202\236\017j\221@\245l\343\255 \245\247\016\224\240\323\201\247\016\265\"\364\253\021\364\247\001JA\315#\003\212\21384\273\360i\032aL,)\245\361M2\212C(\332y\346\263.f\303\021\232\255$\277\'Z\246\317\223QH\337-Uw\310\353P\027\250\331\3522\365\0337\024\261\260\034\323\332Z\246\030\021Q\273sL\311\2458)U\230\r\324\242\224S\3058qN\006\224\036i\341\251\352\325*\275L\255R\253T\252\325\"\265J\255R\251\251\024\324\252i\331\245\245\024\264\352p\247\016\264\361OZx4\3655 5 \346\220\251\006\224\n\\R\343\024R\216\2652U\205\030\025*\200z\322K\305Dd\355Q\226\025\02374\306jn\376:\324O6\017Z\215\245\'\241\250ZfPsT&\230\273\342\241\226L\014f\253\0319\250\344\227\212\246\362u\346\253\264\2304\322\371\031\250\332Za\222\2177\024\246L\214\325E\223\035\351\036NsR+\202\264\021\201\327\255V|\206\240\003\214\322\203O\006\236)\302\226\224\032p4\365j\225^\246W\025*\265J\255S+T\252\325*\232\225O\275?4\240\323\201\245\007\326\234\r8\032x4\361N\024\340j@i\340\324\210qR\251\007\255?\313R3Q\036\030\342\214\322f\224\036jx\3175eO5:\021Q\314sUX\363L-\305D\355Q\027\250\236Oz\254\354}i\242LTo #\255Sb7f\252\315\'5\\\275A,\274UG\227\336\242i\006:\324M)\035\352?2\233\346d\320\315\315;\177\000T\000\363Crz\320\254T\342\245\r\221\212\0317\034\212v6\'_\302\221Dl\331\'\025 H\313\3434\245\027\034\032\0008\245\246\321N\006\236\rJ\255S+T\252\3252\265L\255S+T\312j@iA\247f\2274\240\323\203S\303S\301\247\202)\340\323\305<\032x5*T\352p\265\024\204d\324Y\2434\345\353V#\367\251\325\252d<\323d\252\256y\250Y\261P\274\225\013\275@\317Q3f\240w\252\354\347\326\242y\224\n\255#\206\351Udb*\243\266MT\225\231MW\363\2114\326zo\231\332\200\3376sJ_-O\r\315@\254jL\323\360\010\3159x5*\021I8\314y\035\215B\265 \251\026\244RGJJ01M\242\234\rH\246\245SS)\251\224\324\312je52\237z\224\032x\351KK\232Pi\300\323\201\247\212x\247\003\212x5\"\232\221ML\255R\206\342\243\2238\315F\304`b\2234\3655b3\362\324\240\346\245V\305\016\331\025Y\201\316j\254\274UV|\032\215\237\212\201\232\243-\305D\3075^CT\344\004\363U\367`\234\232\255$\234\021U\332@\rE#\007Z\252\300)\250\030\363L/\212E\2239\247\253\367\247\t~j\007\002\234\016jD\315J\007\024\240\363@\2339\\f\232\230\363>a\200i\347\001\270\364g\336\214\373\322\344\321\232\001\315(\251\026\254\306\374`\324\312A S\330dqU\335MB\300\212\201\311\252\322UGnj\0265\0137\025\0136*\274\217U\334d\032\245(*I\355P\253\203L\232LGU\321\2629\252\362Hw\034TE\351\273\252\tOz\204\340\214\323\343tT9\034\324/&{\327B\0004\323\303T\221\223\272\247\006\221\211\333O\207&>iJ\234SFsR\251\251\001\002\215\324S\201\247\003O\025\"\324\253S\245N\242\246QR\250\251@4\352p\351N\006\234\016i\302\235E\024f\223\"\214\321\2323FiA\247\003N\247/\0252\232\260\277w4\365n)\030dT\022\014\n\251%V\220\360j\213\236M@\315P\263\32426EUs\203Q\263\325i~l\212\244AI)\263\214\307\232\256\271T\252\362\017\232\2424s\212\206f\001\rW\017\362Tm\'\275F^\272d\223\006\244\300nE9A\006\245SO#\345\247D0*P8\366\250\317\r\322\212viA\247\216\224\341\322\224t\251\026\245Z\231\005N\202\247U\251\224\032\225EJ:R\201KJ:\323\205\364g\336\2274\240\323\201\247\212\220S\324\325\210\337\214T\203\031\247\026\025Vw\002\250H\375j\254\217U\034\325gnj6\344T\016\330\025^S\221\305Vf\346\230\306\252\312\t4\222\200\260c\271\252\255\220\2435\004\225\rG#\200\247\332\250M1n*2\377\000-FZ\232Z\272Ez\2367\307z\262\2445J\026\236\027#\025\"\256*`\277-1\343\371O\265C\216h\247\003O\024\341O\002\244Z\225\005XAV\021ju\025*\212\225EH\242\237\262\215\270\243\024\240S\200\245\034\036\224\224\206\222\220\201\214\346\222\214\212L\322\346\2245<585H\032\236\24752\034t\247\211>j\032L)\252\023\312I\252\216\365ZG\252\316\325RY>n\265\027\235\216\365\014\222g\245FN\027&\252\310\3377\024\3259\240\200MD\350\031\306O\002\253\334\340/\025M\216V\240c\212\2551%H\252.1Q\026\246\026\246\226\256\221O525Z\211\252\354|\212\220qR\246\rL\005+/\025NA\363`Spi\303\232\220\np\353OZ\225EL\225f\325\374\3526c\236\264\307 \214\032\247\"\340\222*\263\236j\t8\346\250\315\326\2531\346\230M0\232\351\325\252U5j#W\243<\n\224\036je\004U\204\351J\307\212\250\313\363\023M\332i@\247\201O\003\232xZ\225V\245E\253\010\265a\001\253\010*u\251\026\247QO\306h\333F\332LR\342\223\024\270\243m!Zc-FS9\305FGjm74\271\243w\322\215\324\340\364\360MJ\257\201\315D\322e\372\320\357\362u\252R?=j\007~*\244\262{\325W\226\240iy\353Lw\371j\224\214wR\t\t\342\245\004\0049\250\027\345\311\365\244<\032c\021\214\325Y\030\226\246\220JUiGz\247)\340\325)\016j\263\036j2i\205\253\246CS\251\2531\032\273\033t\253+\311\253\013\322\244SJ\307\010j\034R`R\205\247\205\247\005\251\024T\252\265*\255N\202\254 \253\010*e\025*\250\305J\253R\000i\330\"\212L\nLRb\226\227\024b\202\265\013!\007\336\241e \221Q\221\3150\212CM\315\0314\252y\253)\214S%|US)\337Oi>^\265U\337\232\2554\230\025FI3U\245~8\252\215!\317Zo\230}i\216\331\246.wT\305\3601L<\324r1\034T,\334b\242#\346\247\201\362\325i\2078\254\351\007\316T\3259\0075U\3705\003\032c\032\351\34395i\007\031\2531U\2658\305[\214\346\246\007\212ps\234\016hi\0161\212@\t\245\002\244QO\013N\013R*\324\212*U\0252\212\235\005XAV\024T\252*E\025(\024\374qI\217jiQM+A\007\322\223\221\332\224\032wZ\010\250\310\367\250]y\315B\302\243 \342\230i\206\212\013\005\024\345\233\013P\311)c\305E\273\236i\036O\226\252\311.*\234\222\363U^Z\254\362Uvl\232izM\331\247\240\346\236\352w\001I\322\241\227\223P7Z\\\0023K\3749\252\3569\315P\270\0309\252.2j\254\334f\252\265D\306\272\270\2075eH\305O\031\346\256\2475f>\225(`x\315\n\3309\025&\355\303\232r\021\267\004S\306\010\351NQR\001\3058\n\225EH\005J\242\246QS \253\010*d\025:\212\225EH\243\212v)(\"\223\036\364\230\244\305\030\024\270\244\"\232W\332\242e\250Y0j2\274Tej2)\216v\364\250\276f4\216p0)\231\310\300\246\266@\305Wv+\326\251\315\'5NG\367\252\316\347\326\241g\305@\317\223\232n\354\232z\363V\241\\\265X\362\376f\315Wu\305V\222\240 \232pS\214R\200X`Tr.\334\203Y\367\0309\252\014\270\315Q\233\255@zTO]dq\232\260\221\346\246E\000\360j\322\034\016\265\'\233\306\005<1\305=Z\247S\232\221jQR\n\221EH\253R\252\324\201MH\240\372T\252\017\245L\202\246Pj\302\n\231EJ\242\245Q\3058\322R\021IHE%\024\240\322\322c\212c(=\252&OJ\215\222\242d\250\231*\023\031-Mq\261p*\"7P#\3052A\305Q\236\263\346\357T\244lUs\'5^I2j=\324\003\315XJ\267o\367\305]+\3015Ra\326\251\275G\336\235\203\267#\255\021r3\336\241\270\357Y\263UV\350Ef\3160\306\253\032\215\353\264\034\016)\305\2601J\214sS\253f\246B3\315XF\\T\2523S*\324\312\206\245X\315L\261\032\225acR\254,*A\031\025*\217j\220/\265H\240T\312\005L\2121S*T\252\010\352*E\346\245\002\226\223\024\224\204SM%\024\240\322\322f\220\323H\246\225\250\33103Q\024\250\332,\034\325yP\356\246\254}\315\016\000Z\253)\300\254\371\233\346\254\373\206\306k>F\316j\253\232\204\365\244\3159z\325\210\375*\354#\004\032\272H+\326\250\314y5U\2075\0369\251TR\005\333\237z\255q\315gL9\252\222U9\323p\315Qq\203\212\211\253\262\'\271\246\007\311\353R\206\311\342\247CS\241\251\322\254!\253\010jt5a\005N\200\325\204\025:\212\220-H\020c\245;o\265(@}\252EC\354EH\247i\301\006\254&\017z\234\001\212\000\346\2348\245\315.E%%!\024\322)(\244\315\031\2434\224\207\255&3Q\262\342\243c\306*\264\202\231\310\024\307\351T\2478\252\022u\315g\\r\325JA\216\005Uq\315FV\243\"\244J\262\235j\344<\232\232L\256\010\351U\344\344\324.\225\001\004\032\2263\232\220\250e5Fq\265\2105BaTe\025]\271\2527\t\203\232\252\334WZd\3341\332\204\305N\203\232\260\265:/5a\026\247E9\253(\207\322\254F\206\254\242\032\262\210jtCS(\305J)\343\351N\030\247\355\006\224\002:\032pb\01752:\036\243\006\246\016\244p\324\340\376\242\227z\323\201\007\275;\212m\024\231\244\310\246\320Fi\264SsK\232J\003`\322HK\235\330\250\010\346\241)\227\244 \n\202\\sY\363\214\325)\001\301\254\371\207&\251\272\325w\034\324l8\250\312\323\220U\230\305Z\210\342\2452\014r8\250\030\203\322\233\326\241e\347\002\236\027j\032\003|\231\252s\034\223\232\317\233\031\252\222\n\254\313\203U\346M\303\245P\225\0105\322)\251\320U\204\253\010\005XLU\230\352\312U\230\352\312\n\262\202\254\240\251@\346\244\002\236\005\235iKdb\243q\301\250\302\360(\333\301\342\240\220s\212\2472V|\243\346\250\372TrT\310y\315XCV\022\254\307VR\255\307V\343\253q\325\310\352\302\324\313R\251\251W\221R(\346\245\003\212\231\005J\007\031\310\251\027\2458\npZxZ\225#,p\005\\\212\330\377\000\025X\010\007\013R,t\245\016zS\030sQ\260\250\217Z)\r0\344\322\n\010\006\230c\036\224\323\036i\206!I\267\024\323\326\230[\024\326 \365\353P8\007\245W\221\0063T\345\305S\224dU)P\223\322\2534\\\323Lx\024\306J\202E\315E\345\363N\330\0051\2074\323\234Tei\241y\2472dSD\177/\343HS\202*\274\251\212\2457B*\204\211\363T,\270\250\034f\244J\260\225e\rY\216\255F*\334B\256G\305Z\215\200\253(\342\247G\0252\276jecR\253\032\231X\324\252ML\225:\032\220\014\366\247\216\264\365\034\324\310\271\355W\355\242\001rEN\370U\342\210\372T\352)\314\006*\263\236j\"j&4\235\0050\2657w\2757w4\340x\2434\231\3074\306zo\033y\250\3109\342\243j\214\363Q7\326\253\271\355T\345\3435U\310\315W|T\004\014\323\030\003Q\262\361Q\030\375i\205EF\302\242aM<\323H\244\013\315=W=\2516b\232G\315\232\212Xw\216+>X\212\223\221Te\\\036\225U\224\324L\224\211S\241\2531\325\250\301=\005[\211I\253\221\200\rYCV\025\252tj\231\rN\206\247F\251\320\324\311S\255J\265:\021\221\236\225 ~\010\035\351\350y\353V\341@y\253\221\306\243\034U\225\340qNe\310\024\252\273E;v)\013\361P9\250\311\3151\263L=)\215L5\031\340\323\325\270\245&\232M 6M8\220\335(\030\025\004\243=*\273\n\211\201\250\034Ui\024UGJ\255\"T\014\244S(=:Tdf\242u\301\250\212\324L2x\240&\0055\2050/52.\006\010\241\2075\033.W\216\324\3022\236\365Ja\234\326|\221\363\322\253\264Y5\003\307\216\325Y*\302.j\334J*\344C\247\025i8\351V\022\247J\235\rN\206\247CS\247\326\247J\260\225:T\312*u\024\376\264\365\0252\212\277m\234U\261\326\245CS!\005\262M=\260G\025\031\024\303Q7Z@\0055\2526\024\302)\204S\010\246\034\203C7\025\031s\232p3\212\204\307\232\004x\355LaQ2\324\014\264\300\234\324\321G\226\346\254\210\323n1U\257!\0333X\322\'5\036\334\032LsK\267\"\233\214Tl9\250]y\246\025\246\221Q\260\250\312\323Z<\212\213n\001\315B\313\315F\313\305BW&\223n\r6Rv\364\256M\026\246E\251\321j\302-YE\2531\255[\215j\344KW\342Z\275\022\325\264\025(\351J\r=MH\246\245\024\372P\246\244U9\253\360\003\266\256\240\310\247\010\371\351O*\000\346\242a\351P\262\361P0\250\231y\250\331*2\224\230\3058\034\324\211S)\247g\232\225H\247\000\t\251B)\\\032\211\341\002\243d\342\240d\347\245E \305Wa\3154(\315N\253\307\024\356\202\243\224\227\\\032\315\232,\032\247 \250\361\315H\213\236\264\216\270\250\030TdTdPS#5\023%3m#\014-V~\265\031Z\211\206i\241\t8\002\217,\214\223U\2468\004W.\213S\242\324\350\225a\026\254\"\325\250\322\255D\265v$\253\261-]\217\212\260\246\237\232QR(52\212\225EH\242\245T\251\322*\261\031\nqV\025\360sV\222@\313C\014\323\n\324l\225\003.\r@\303\232aZB\224\302\224\3020h\r\212p\223\024\242Z\2327\315YCR\206\024\204\202)\244`T\0220\351U\230d\324.\265\027CSFi\355Q61U&\031\315Q\225j\014sR\245\0168\250Yj\026\\S\010\024\336\224\322)\214\275\351\256>Z\250\313\3151\2050\246)\321\'$\232d\247\002\263\247<\232\347\321*t_j\235\026\254\"\325\224J\263\032\325\270\327\245[\211j\344ue*u\251\000\251\025jE\025*\212\225EL\213VcJ\262\023\002\230\334\032z\260\"\247\214\221\315Y\007\"\2341H\3121U\332<\324\017\036*=\234\322\354\342\230R\240\221y\250[\212o4d\324\210\344T\3511\251\226Q\267\255\006nz\322\031r)\204\346\243\250\334TEy\247\247\024\366\037-B\365ZJ\251\"\346\253\262\340\323\223\216\2643s\212\214\372\324n*-\264\205i\002\363Lp1Q>6\325v\024\2018\245\362{\232G\340qT\345=j\204\375\353\025\026\254\"\324\350\265a\022\254\242\325\204Z\265\030\253Q\212\262\200\325\204\025e\005L\253S*T\202:xLT\212*t\025f>*\302\3621H\321\223\332\221m\244#p\034\n\2364u\352*\302\216)G\006\203M#\212\211\227=\251\276]\006\023Q\274~\325]\343>\225\003E\203\322\231\266\232W\232r\245;\030\243w\024\201\262is\357J)\330\244+\232iJM\274\323\361\362T\016\005Wu\025\001J\255\"\363Q7\024\302i\273\275i\t\3153\034\323Xb\223\024\307^3U\330S\030qH\024\346\236zT2\016*\234\253Tf\025\224\211\305N\211\355V\021=\252\312%XD\253\010\225f8\352\312\'\265YD\253(\225f4\253\n\2252\245H\026\227\024\241Njd\006\247AV\341\031aW\320B\215\373\305\004\373S\036\340.V!\362\237j\222\031\004\213\265\200\311\350iY6\022\010\246\000\244\324\251\010jV\265n\302\233\366V\356)~\314})\0148\035*\027\207\332\253\274|t\252\317\017\265Wx\210\355Q\354 \321\320SX\323I\342\220\034R\206\346\244\0074\361\322\234\005\004z\323X\001M\'\214T\017P0\250\230TN\274UI\006*#\322\230F)\0014\354\342\232\3447A@^*)=*\022\2714\323\031\243f)\030T.*\264\213\236\225Nh\353%\024\021VQ*\302\'\265YD\2531\307VR:\262\221\325\204\216\254\307\035Y\216:\262\221T\341)\341i\341i\301i\312\2252\245J\253S&GJ\230\022\335jA\030\"\247H\260r*|e0y\367\250\0323\273\212\265o\021=MhG\0261\226\006\2440\257aQ\2748\031\252\357\025A$B\253\264#\322\240xEVx\375\252\026\213\'\245@\361\021\332\253\272\324d\032L\032P\2475\"\255L\026\234\006\r5\21534\323Q\270\250XT$sMe\342\252\312\231\252\345qL<\2321M\"\200\224\347\033V\253\260\346\231\263\276)\344q\214S\030s\322\243aP8\250\035y\252\322\245d\306\225j4\253)\035YH\375\252\314iV\243\216\254\307\035Z\216?j\262\221U\230\343\253)\035J\261\023\320S\214%z\322\204\245\333J\242\247U\251UjUJ\220-H\240\212\231\037\024\362\336\207\255.\340\274\232\226\031\001\351\326\255+g\275J\222:\236:U\225uu\301\034\324o\032z\325w\210T\r\020\3061U\344\204\325W\214\347\245Dc\366\250^.*\244\261\363\322\253\262sM\333OU\0252\240\305-\033sQ\262\342\242n)\273\251\244\346\232\313\305@\312EF}\3526\000\324\022G\305W*\001\240\257\313M\003\232\220\000\006i\216\245\201#\265E\260\346\235\345\340f\232\312)\214\265\023\n\256\342\253\271\252\362\034\326tiV\243J\267\032U\224\216\254\307\035Z\216:\265\034uj8\275\252\312GVR:\231S\332\245@C\014\016i\322ng\313\nLzR\0244\345\\T\312*x\327&\255$9\2531\333\307\217\230\323^\020\033\212\210\251ZP\334S\035\216*%\231\243\223\'\245]\212\344\021\326\254\255\310\035\352d\274@z\324\313q\034\202\220\220x\246\025\250\2313P\264c\322\241x\207\245@\361{U)\243\307j\245\"\340\324X\346\234\240\372T\352\016)H\246\236;S\0335\013f\243\"\220u\251\225C\n\212X\275\252\234\213\203P\263\001\326\230\314\245z\324\014E3p\244\030\315?\256\006)\357\030\021\361L\211\006\303\221\310\246>G\025\013S\017J\215\252\t\005S\224sU\\\340\3241\'j\264\211V\343J\265\034uj8\375\252\334q{U\250\342\366\253I\035XH\352\302%L\261\324\210\230l\342\246h\325\227$\363Q\254>\324\246*n\312z\245X\210b\255+`T\250\031\217\024\374\034sMd\310\344\322}\230\323\036\r\275\301\250Z\000{SB\025\355F\342)\3502y5n\027U8\002\254\357\315/\231\317ZB\331\357L$S\030\002:Uw\036\325ZT\317j\254m7\236x\245\026*)\r\250\034\201L1c\265\'\227\355M1\342\242)Q\274|f\253\270\301\250\330\342\232\322\343\220i\032\360m\303V}\315\310\347\006\263d\273ny\250\276\322\347\253R\371\304\236\265\271\024\321\345\267JB\027\2650\250\250\2360i\233*6J\217\312\315V\237\344\342\251\261\3115\024\234\n\2434\273sY\323\\\221\221\232\250\316\362\034\014\324m\023\355\316*\234\216\310\324Gq\223W\241\233\241\315_\216\\\212ql\232\001\346\245\003\346\006\211\000\252\316*\007S\212\254\365\003\265U\220\325I\0075j\025\351W\242Z\273\024y\253\221\307V\343\216\255\307\035Z\216:\262\221\325\224\217\332\254,u*E\236\265*\307\315I\345qI\344\346\224A\355G\225\201M1\023I\345\036\302\201\023T\311\031\025:pj\306T\247AUef\317\024\304\231\324\343<\032\263\037\314A\315\\P\010\351JcS\326\242{u5\027\221\203\3059c9\347\212\031{c5\033 \364\250Y=)\214\206\243+Q\224\246\025\244\301\035\351W9\346\235\212M\276\324\306\217#\201LD\344\356\025\235x?zj\213u\250\345\031J\311\271V\347\025\234\320<\215\3005~\322\303\241e\316j\343i\350W\356\363X\267\372n\030\225\037Z\307{w\215\372qR\306\344U\330%\347\031\253j\331\247\003\223W#\031AJ\361df\240h\361U\345^*\214\242\252?\025]\372\325i*\364KW\341Z\277\n\364\253\321\'\025r8\352\334q\325\250\343\2531\307\355VR\235(\335\317Zz\311\212\231d\2517\347\2751\316qP\261\250\330\324L\324\302\324\302\324\233\251\271\2434f\214\322\023M&\232Z\230^\220\2650\2650\2654\265F\315Q\261\250\213Tl\325\0235D\315Q3TL\325\0235D\315P\263T\014\325\013\275Wv\250\035\352\007z\201\236\240w\250]\352\006z\205\232\241f\250Y\2522\324\302\324\322\324\231\244-I\2323J\r.i\300\322\203O\006\236\r<\032\2324-W`\207=\005j[\300\027\006\275\334\2750\275(z\221d\251VOzs8\332>\264\307<\324Lj&5\tjijB\324\233\251s\357I\2327Q\232a4\302\324\302\324\315\364\205\251\205\251\205\251\205\251\214\325\013\032\211\236\243f\250\231\252&j\211\232\242f\250Y\352\026j\201\236\240w\250\035\352\273\275@\357U\331\352\026z\205\336\240w\250Y\370\250Y\2526j\214\2650\265!jB\324\233\275\350\335\357F}\351sK\272\234\r8\032x4\360jh\320\261\366\253\360\307\320b\264`\214\001\315^\213\257\025\354\276g\024\323%\001\352E\222\244\022{\323\304\231\025!`?*\215\273\324,y\250\030\363M&\2234\231\245\335Fi3HZ\232Z\243f\250\313S\013Ro\246\226\2463Tl\325\033=F\317Q3\324E\3526j\205\236\242g\250Y\352\026z\201\336\241w\252\356\365\003\275Ww\250\035\352\007z\205\236\241g\250\035\352\026j\215\233\232\214\2650\2650\265&\352Bi7P\032\215\324\273\251CS\201\247\203R\256jx\243,\325z(\272\014V\2041m\031\"\255\'Z\275\002\2168\257U\022{\321\346R\357\247\t)\342Ozz\311\357S,\231\247n\004~\025\023\324\014j<\321\2322)i3HZ\230Z\230Z\243f\250\313S\013R\026\244\335\221Lf\250\231\352&z\211\236\242g\250\231\352&\222\242i*&z\205\236\241w\250\035\352\007z\201\236\240w\250\035\352\273=@\357P\263\324,\365\013=D\315L-L&\230Z\230M&\354SK\323w\322n4\340I\251\002\223R\254f\244X\215L\220\237J\260\220\037J\271\014\030\034\212\273\014`\021V\366p1SE\0375\241\nb\275\024I\2327\347\275\'\231\216\246\234%\247\211=\351\313%L\222\373\324\353 8\311\244f\343\336\241cQ1\346\215\324\271\2434\204\323\013S\013S\013Tl\325\031jajn\352\003sLsP3\324,\365\023=D\317Q3\324L\365\013IQ4\225\013=B\317P<\225\013\275@\357P;\325wz\201\336\240w\250Y\352\026z\211\236\243-M/Q4\224\303\'\2753~{\320\r8)jx\205\261\322\224D{\212\225!>\225a!\035\352\302D\276\225a!^\302\247HG\245J\261\201\332\245A\315Y\211rE_\216<\212\265\0245r8\253\256YjA\'\275!z\003\323\204\224\361%J\262qS\244\276\365/\230\n\363Q\226\342\243c\3153u(zv\372ijajaj\214\265FZ\230Z\230Z\232Z\223~\r5\236\253\310\3309\252\354\365\023=D\317Q3\324,\365\013=B\317Q3\324,\365\003=B\357P;\324\016\365\003=@\317P\263\324,\365\013=D\316\005F\322\014u\250\213\222z\323K\344\360)\2474\000I\305Y\216\036\346\255\244\\p*_+\326\223f;S\302\n\225\02352&:\325\224Q\212\231G\024w\251\220U\310S\232\321\205*\354iV\343J\332V\367\247\253\340\323\367\323K\340\321\346S\326L\324\312\365*\311R\tx\247\007\033i\254x\250\313a\261@z7\322\027\246\227\246\026\250\313S\013Tl\324\302\324\322\324\302\364\326~*\027z\254\355\203P\263\324L\365\013=D\317P\263\324,\365\013\275B\317P\263\324\016\365\003\275@\357P\263\324,\365\0135B\357\201P4\237\205@\\\036\246\232Z\214T\213\036FM)@\005*(\335V\324\000\225n!\362\212\227\034R\004\317jp\217\332\236\213\265\272qS\205\335\322\246U\307\025*\255\001~j\261\032U\330W\025\241\0168\253\321(5m\026\256\206 sN\363)\302\\w\246\264\264\321\'5\"\311\357S,\225 \226\234%\251R^\324\375\371\034\364\250\334\374\324\315\364o\244/HZ\230Z\230^\230Z\243-L-L-L/Q\263\324,\365\003\276j\026z\205\236\242g\250Y\352\007z\205\236\242g\250Y\352\006z\205\336\240w\250\031\352\026z\211\232\241w\300\252\356\304\362j\027l\364\250\371\3158\nr\34358a\267\2550\276M*\036j\310<\001W`9\214T\340d\324\201i\341i\3733RD\270n\225g\313\357N\013\355OT9\351S\306\225n8\311\350*\334Q\277\241\253\360#\367\025~4=\352q\351Q\267\025\031\223\236\264\236e\002Jp\223\336\245YjA\'\2758KOY\275\352u\224\025\306h-\317ZilSw\321\272\220\2754\2750\275F^\230Z\243-M/Q\263\324,\365\013IP\263\324.\374T-%D\317Q\263\325wz\201\236\241g\250Y\352\026z\205\336\240f\250Y\352\"\325\0335Wrwgw\341Q9\'\251\250\215.\0062i)E<\036(\035jT\034\325\265L\246j\354\t\373\261VUjP\234S\202\232\235\"\251V,v\251BT\253\02052\302OAV\340\264f=+F+eA\310\251\324 \350*\304]zU\330\2235\022\237Jc\037\326\2531\3014\335\364n\245\335NW\251VJ~\372p\223\336\244Yy\251\004\231#\007\2458\276x\246\261\305&\372B\364\322\364\302\365\033=0\275F^\230^\243g\250\036J\205\244\250ZJ\205\344\250^J\205\244\301\353M2f\241\221\252\273\275@\317P\263\324,\365\01351\201#\223\212\205\266z\232\211\266\212\215\231GAQ\2221P7&\232E\030#\212LR\216\224\341\326\244QS\305\031f\000U\365\213\345\n*\344q\355\000U\204J\224-J\221\367\305N\211S,y\251\004^\325b\030\t=*\364V\3309\"\257\307\032*qC\002\306\237\034^\265n(\371\351W\341\210\2228\254\264l\216\264;\201\305V\224\363\326\241\337@pi\341\251CS\303S\303\322\357\367\245\022c\275H\262\343\232\224I\232w\231\3074\335\334\365\246\226\301\246\227\246\027\250\331\3522\365\031z\215\244\250\232J\201\344\250\032Oz\205\244\367\250ZOz\211\344\250\232Oz\217\314\346\230\362\002*\273\267\275B\355\357P3\014\375\352\214\225\317,M1\235{T.\344\367\250Y\251\205\252&4\322s\3054\323H\315\030\346\202))\312*tRN+B\332\036kE!\300\034U\205\216\244\013\212\225\0235a#\251\202T\311\037\025:\307\223V\342P\200U\225\313T\300ai\312\244\232\267\014E\210\300\255(,\316\001j\322\206\331Gj\343b\224\355\247\261\312\3475ZV\371A\252\345\350\017\316\rH\036\236\032\234\032\234\032\227}.\352]\370\247\254\230\251\004\264\246@E4\271\034\365\024\306zaz\214\275F\317Q\264\225\013IP\274\236\365\003I\357P<\236\365\013K\357P\264\225\023I\357Q4\236\364\303\'\275F\363\n\256\363sP\274\265\013I\351L\363)\205\3526z\210\2674\247\356\324$\363I\232\\PzSh\240-H\243\264\264\340h\353K\2126\320\027&\254E\0375\243o\027\025u\027\025j%\343\232\260\027\003\245K\034lM^\206\016*\322\307\212\220GR\244\'=*\322G\201V\"\213\236\225u!\366\2531\303\355Vc\200\347\245]\212\000\0075r4Q\300\025n$\357^I\t\357W\021\262*9N\t\036\265\235+a\351\201\351\341\352E\222\246R[\245)b\247\006\223}(zpzv\3727\321\276\220\311M2S\014\225\023I\357Q<\265\013KP<\236\365\003\313\357P4\274\324-%D\322{\324M%B\322{\324fJM\376\365\014\257\232\256\317P\263\363Q\227\246\027\246\027\246\027\244\337K\272\236\270=\351s\3159i\342\235\364\037\215\001K\032\231c\300\251\242\034\326\225\270\351WUrj\324Q\364\253\211\026{U\210\342\031\253*\010\342\254\306\271\025j8A\031\251\204c\260\251\222.j\3240\367\305]\216?j\267\024G\322\255$x\025*\251&\255\305\025\\D\257\027\212@\006*\322II#du\254\373\203\363f\240\335N\017N\017\203Wm.\021\037/\310\245\273\2366|\247\025]d=\352E\223\212v\356x\243}/\231\357H^\232d\246\231)\215\'\025\013KP\264\265\003\313\357P\274\276\365]\345\367\250^_z\205\245\250\232_z\211\244\367\250\232Z\210\311J$\250\344\222\253;\324E\371\353L/L/M-M\335I\272\200\324\360\325 4\3655*\344\364\247\201\306\007J\236%\343\221R\225\342\204\030Fx\246,\322\2111\223W\241\235\370\316kB)\233\002\257C6\352\321\210\202\265n5\257\316\326\"\230%d\340P$gq\232\262\255\205\245\335\232\034\341(-\200\007\240\246\357\342\232\317\301\250Y\351\273\352)e\317z\253$\234UY$\252\315/5\023KU\336Z\256\322\363\326\240y=\352\244\322\325V\227\336\242i}\3523/\2757\3164\341/\275E<=\014\371\000{\323K\374\344\032ilS7\361\223\353Q\226$\346\243\222L\n\252\362Ug\223\336\253I%Vy*\026\227\336\240\222N:\3257\227\236\265\033I\225\316j\234\322\036y\252\215-D\322\373\324fJo\233OY\252Q-^*\324L\003\344-XP\307\247\031\251\222\023\216j\302\333\344c\232p\265m\336\325r;4\013\223\315L\"\033~Q\212z\307\315L\261\361\234S\274\241\216\224\236^:\nF\214\025\344V-\3434Rc\265Od\306\\V\314!\341 \366\255\313V\337\0305\371\323\232L\322\026\245\rN\rJ[\212s\2601\206=j\"\331<\323@%\010\250\331\2609\252\262I\315U\221\352\263\275U\222J\254\362{\324\017%B\362qT\345\223\236\265\020\227*Fj\274\357\212\240\362{\324&Zi\223\336\220\311J\262\017Z\235d\004u\251D\200T\210\3435a\032\254\306\325n.H\253\212\274T\321\214\032\271\020\346\264!^\005]\210b\264!L\340\326\244\013\225\025\251n\274\n\322\2161\264U\204\343\212\263\032g\223W`\265i\233\n8\256\233L\323\342\210\253\021\315tq0U\300\0252d\232\264\212[\255Y\216?j\267\034~\325r(\352\312GV\222\364\022)2*D5j\"*\354=\210\255(\017J\322\203\222+V\334t\255[s\310\255XG\312*\354}*\314m\310\002\264\255\366\246\t\353Z\266\363\344akR\322&\221\263\330V\335\263\204@\243\255_\211\263\315hBzV\204MWbz\275\013\362+B&\351W\342~\225~\027\351Z\020\311\322\264\355\3378\346\264\340\223\003\025r6\315HX\343\212b\246O5b45f8\363W\"\214\n\262St$W?r\031n\370\365\255k;\221\264#\325\304!\234\232\260\264\341A\3252T\273\260\264\350n\006Z\263\256\256\202\263\002z\324p\334\371\244\"\236+^\016\024U\215\330\245V\3474\245\273TD\363_\235N\265]\326\253\3102*\204\310A\'\025JNy\250M38z\220\323XqQ0\250\035j\274\202\251MY\363w\254\371\217Z\2435R\222\252I\324\325w\024\323\302\325v\353L\"\221\242%sQ\252s\212\321\267Q\201\232\225\325H8\034TH\234\325\250\227\221Z\020\016+J\016\202\264\355\307J\325\267\343\025\245\023`S\244l\212l,|\320\265\275h\000\003\035+j\335\276Z\321\201\271\255(_\245hB\376\365r9=\352\334Rt\253\260\275h\301\'\003\232\320\206N\225\241\014\235\352\3642qZ0=_Bx\"\264\255f\316\024\326\315\264\230\030\253\221\237\2375m&\332x\255(]eJyR\215\355OH\232s\261i\322Z\371\0216\016N+\234\325\213!\\w\251\364\230\311@\306\267\323\205\247n\241[\232y~*2\334\327\347\244\213U\331*\273\247\265U\232<\251\254\313\204\307J\252EF\302\204b~SR\021\305F\313Q:\361Ue\351Y\363w\254\371\273\326|\335j\224\2435JAUd\025]\2050\216*\006\034\320\250X\324\214\277/\322\243\362\376l\342\254F6\361Nw\300\300\241\016j\334C5z.\325\245o\320V\225\277Z\323\207\265\\V\300\251\003df\235\003\005\230\023\336\267m[*+V\332N\331\2558\032\264!nj\364r`u\253Q\311\357W\"~\225~\031:U\370d\255\010_8\346\264a~+B\006\034V\224\r\300\255(\033\245]C\206\004\032\323\265\230\226\025\250\257\214\037Z\261\033sV\341\220\306\331\006\265\243a4 \324\326\200\3071\357\232\226\340\026\213\031\357\\\366\263lJ\207\307N*M4m\205kU[\212B\324\006\346\236[\345\376U\003HK`\365\257\200\331*\026\212\241x\270\252\322E\362\326U\314?1\315g\272`\324ei\2730sSm\312\212a^*\t\006\005R\233\275g\315\336\250J3\232\243*\363T\245Z\247*\325I\026\253\262\363Le\342\241e\346\237\032\340sO)\224\316)\270\246\222A\246\002X\346\254F*\344C\025r.\265\243\007j\322\267\355Zp\236*\306\356)\352\337\'\024D\314g\003\322\267\255\033\n+J\007\303\n\326\267z\277\024\234\325\270\344\367\253q\311Waz\277\024\225z\027\351Z0I\322\264\241\223\"\257\301\'J\323\267\220qZPIZ0\276j\374\r\265\201\255h\217\231\020\301\346\254\302\177\204\365\025h\034\n\263iz\310\302>\2435\257\034\352\0109\353V%u)\307z\311\325\231D\000T\026g\021\212\274\033\2127\363K\272\224?4\223(\362\374\320y\035k\340\307\216\241d\305D\313\236\325\004\221\326m\324\031\311\254\231b \342\240)L+\315=}\r\014\274t\252\262\214U\t\273\325\tFj\224\213T\345Z\245*\3259\026\252H\274\324%*7^*-\231j\223\313\317\312\005+\302Tz\323V>*9\227\013\305E\032\036*\334kV\343\025f!\315hC\332\264\240\343\025\241\021\253=\251\321\236\325,\n\005\306Ml[\2368\253\3617J\325\201\370\025z\027\253\221\275Z\211\352\364/W\243~\225~\007\351Z0\022qZP1\030\253\321=h\301\'NkN\ty\025\251\014\230 \326\204M\220+J\326L\034V\212\000He<\325\245;\223\007\255X\262E$\226\352*\356\335\304\020zU\230$,\370n\202\262uysr\020S\355\316\020U\235\374R\027\247\007\367\245\335N\335\272&\\\360k\341\246\216\241h\271\346\243h\300\252\322\2475Fx\301\025\223s\017\315UZZ\312\235\017\236O\2557g\025\023\247\265Wt\317AU\332\016rEV\2311\332\243K~7\021Mx\316zS\032/\223\245\'\223\305\'\223G\223\355G\224iV\034\036\2250Zr\214\032\234(`8\240\304)\236X\317J\2368T\212\220B\271\251\2225\0252\"\372T\3012q\212\010*\341E\\\204t\006\257(\343\031\251T\366\251\227\202*\3127\002\255F\325e\033\26152\260\315O\033\340\326\215\264\240V\224R\002j\364,3W\341>\225~\027#\025\245o/J\324\206N:\325\373y\260\343\232\350 \224\375\233\000\3247S\017-S\024\353y1\201\232\272\034\036\224\340\364\360\364\360\331\247\006\374i\341\251\341\252En+\345\311\007<\325g Ui$\025U\345\0035\003\315PI6\005Ty\2115\023MQn\335L\2212\2475E\343\301\252W1\2563\216EV\300\305F\351\305V~\rD\343#\245Vh\213\267J\224A\204\306*&\200Tf!\214Ry9\035(\362@\024\010A\245\3629\244\3629\351Hc \342\200\234\364\251\321)\3738\346\232c\031\340S\320b\244\024\360\265*\014\032\262\230\353Qn\r6M[\211\276aWU\375jU5:\032\231\rX\215\215YC\201\272\245V$\325\270\317\025j#\310\255(\037\245hB\375+F\027\253\360\234\343\232\321\204\364\255\010d#\002\256\306\344r+R\322\365\266\354-\364\253\022\313\271@=E>)\010\357V\322CR\207\251\025\375\352@\324\360\364\340\364\365j\221\036\276c\225\272\325\031Z\250\312\375j\224\216sU\336CP<\204\236\265\00350\234\323\220sR2\356^*\264\261|\244\325\tb\316A\252\262@P\203\330\323\032\034\255S\222\002\rFb$t\240E\264S\266\361\322\242t=j\026\214\322\010\370\2441\n\014D\nn\n\323\270#\232\215\216N1B\21475:\205\305;\002\200\240\322\005\301\247\201R(\247\214T\312F:\325F\312\334u\3435\241\017@j\352\020W\336\246^\225:\324\311V#\342\246R{\324\351V#j\271\023U\350X\325\370_\232\320\205\353F\007\344V\234\017\322\257\304\365md\371j\305\274\204J+FI1\201R\304\371\025i\037\336\247W\251U\352@\364\340\325 jz\265H\255\315|\3131\315P\224\360j\204\247\255S\220\363U\234\324,j2)6\323\227\255XQ\362\342\225\242\005\t\254\311T\0068\252\223\222Wo\2450H6\035\313\323\275@J\276qM1\000\271\246\030\275i\276VzSZ\037ja\203=\251\255\016\321\322\242+\212c\016*\0278\355Q\226\246\026\002\232e\002\220\\c\245H\267$\324\211?52\035\334\232\225W\322\246\0203t\251\005\234\230\247\010vpXTR@I\310\251\340;W\rVQ\271\030\253\211\322\247AS\250\251\324T\243\212\221X\324\350j\324M\357W\241n\225~\026\255\010^\264!n\225\245\003\364\255\030[ U\325\031\025f!\202\017\275Y\022n?J\261\023\374\242\255#T\352\3252\265H\036\244\rO\rOV\251U\253\346\251\253>~\365B^\365NJ\254\365\021\024\200S\266qB\247\315W#\207\271\351Q]\312\021<\265\357Yn3\315U\2226=\252\026\215\300\306*5\211\267\344\255K\262\243u\246\250\031\346\234\311\305FW\024\326P\313\357T\345R\r@\304\201\322\253H\325]\237\025\013\313P\231\t4\201\232\246\215]\217\000\325\350\255\'l|\246\257Eg8\034\241\253q\331\312W;\rJ!\224tSO+2\257\3355RW+\235\331\250#\271\303\340\364\253@\357\371\224\325\210\367\005\346\257\302r\2435eEL\265:\221R\016\224\253\311\251\323\336\254\307\332\256\304\334\201W\341j\275\013t\255\030_\245h\302\331\305h\333\2675\245\023dU\370\202\233r{\212\256n65O\r\320 \014\325\370\245\014:\325\244qV\024\323\301\251\025\251\341\252Ej\221Z\276s\225+>\341q\232\316\225z\325)\005WaQ\221@\024\265<\021\006\033\217AN\222C\214-Sx]\333-J-\0169\024\033U\364\246\233U\307J\202[eQ\322\251\274x\'\212\256\313\223Q\221\203N\004\032k`\364\250\360A\250\245@j\224\213\214\212\245(\252\216\244\232#\264y[\000\023W\342\320\345l\022\r\\\207\303\262\226\037!\307\322\267l\2746\212\0032\201Z\277\3316\311\036\000\031\247\255\235\272\250\033zw\251\014\021\036\000\002\243k(\363\221\326\201e\0360{\326]\356\2302Lc \326,\332k#gn)\320Z\313\274\000\rk\245\231h\307j\222;vC\203V\2218\351S\252qHx\346\225e\003\214\324\213 \365\251\321\363V\3425r3\305Z\211\261W\340|\365\253\360\276+J\007\351Z0\267CZ\020\275h[\310\000 \367\025\\\300\355!\347\214\324\253l\3523\326\255B$S\305^\212^\307\255\\\215\352`sO\006\244SR)\251\001\257\237\344A\212\317\270\217\255e\314\230\315P\225y5]\227\232\210\216i\246\232z\325\373r\r\271\003\255H\220n\346\236`\003\265#E\307J\256\351\212\210\236i\031\003\2575FxxU\305Tq\315DE4\2550\306Oj\232\022\3126\326\245\250\314d\232s)\'\000S|\246\352j\274\361\340{\325_(\223\322\232P\216\364\313\210\301\204\340sY+\0212\221\212e\335\261\021\022+\034\214=N\243\212q\034Tdd\324\260\331<\307\201\305l\331h\245\261\362\217\251\255h\364\350!\003(2*|*\016\000\342\230\322\212\211\244\364\250\211$\323zSY\2522i\215Q\223\212n\352B\300\365\241d\301\351O\363s\322\200\331\251\224\324\252x5N\352\027\306\345\351T9\007\232\221^\256\301!\030\007\245iC\222\001\255\010\211\003\232\260\217\315[\205\253B\026\255\010[\221\315iB\331\305h\304\334\n\273\013U\330\232\256\304\325iGqM\222\"N\345\353Q\200\310wU\333y\201\002\256\307\363T\230\002\234\010\024\365a\351^\037 \2523\200A\254\273\201\326\263f\025M\227\232\217nMM\025\266\343\222*\317\331F\334b\237\025\206\366\351W\322\321\243\033q\201Lt\np((\002f\251\310\027<\363P\266\320\274\n\254\351\223K\345\3450zT\006\3324$\201T\357\024\010\017\322\271\326Be\0063Te89\250\214\200rM0\334\002x\300\250\332u\365\252\362^(<\032\315\274\275\000\034\036j(/\224\367\346\256,\276g\275J\031\261\214\3242)\335\232E\234\307\317aT\256\365\226BT\032\306\271\325&\220\237\230\325\026\232G9$\323\014\204u5\033M\203\326\201vW\2759u\'O\342\253\t\252D\303\347\353S\245\3242/\rI#(\344\020j\244\267\014\007\312*\224\263\273\036\265\007\314MO\014d\236EhG\020\002\245X\2239\305N\252\005H\024z\324\200\250\352i\014\311\234u\245Y\001>\325f99\034\325\224`M[\216\255\307\206\"\257\300\017J\266\261\215\271\025<#\014+F\036\325\241\rhE\322\256E\305^\204\325\310\352\324}\252\324f\255!\310\251W\221L\2252\204b\253\307\303}+B#\305N\rx|\306\250JI5\020RM_\262\266gpq[\"\r\253HP/-PH\302\253H\343\025Q\333\232\201\311\316j\026RNMS\270e\000\232\315\226nO5U\347\307z\251%\313d\340\3259n$\355Tf/ $\223N\265\014$\002\267\240\341\005X\310\025\024\207\035\3532\356\361#B\240\363XSHdri\202<\3221U\025Ri@5U\245\367\250Zoz\211\246\347\2553\315\367\245[\211\020\345X\212\235u)G\004\346\2365\014\365\024\357\265#v\025$sC\374UX\200x\254\351\036\252H\304\324\'\2558\"\3106\232i\262\344\363\305*\333l\344u\025a$)\301\025\'\332x\351Q\311#:\341k*{7\221\211\344\324\037be<\203QK\031\215N\005e\\\273\202EPvcP\263\032\205\230\323@$\323\3262M?\310cA\2674\303\036\017\"\234\022\234\020\216\365\"\203\330\324\350\362\001\324\324\333\344\307Z\001c\324\323\325\210\342\246F\311\353Vc\000\325\204\025j6*q\332\255\306A\305_\204f\257D:V\244#**\334y\013\322\245N\265r\032\277\t5\241\t\365\253\321t\253q\325\310\215\\\217\232\265\031\253)S\241\251\205Wx\201\223\"\245X\316\337zz\022\016\rx\337\226Y\261\212\267\r\240\000\02295v+p9\305X\330\025x\252\363\002O\265W\362I\344\360*\t\004`\341NMT\223\216r*\234\204\223U\344p\240\222\177\032\307\274\272\334J\251\342\263$bj\263d\324,*&\024\200\355\325a\007=*\344B\255\306*\364\006\257GV\3435m\017\025f3V\3435>~ZT|\232\260\204\036\rL\231Z\225\260\303\322\274\272\336\002\010\342\264R<-5\2075Vi\025~\361\342\261\265\013\360F\3058\002\261d\233q\353T\345\227\025_v\343H\344\343\002\252I\336\253=Wz\205\205B\330\250_\247\025RC\315@\331\317\02549\034\346\244i\317L\361Q=\303\021\200q\364\250\303\022rM#\310@\3008\250\274\307\354\306\232\306S\3375\013\254\347\326\241te\\\271\252\336|jNEW\227Pp~A\201Tf\275\231\273\232\250\322\310\307\222jx\245\224\014\002j\302\227=sR\250&\237\222)3\'\360\203H\"y\033\234\324\353m\264sO\021\324\213\037\240\251\0262\265*\223\236\225:c\275N\230\355S(\315XN*\344MW\242n\225~\027\255\010_5z\026\253\211\216*\344X\342\256 \300\2531\034\021W\3425n:\267\031\253IVc\251\213\014b\232\247\232\2367\344U\304~9\251F\010\3105\377\331" +byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\002\000\000\000\002\000\010\000\000\000\000\321\023\213&\000\000\003JIDATx^\355\334Yn\3430\014\000\320 \275\377\221k\014\246\235\026\250\006\256W1\224\370\336_\2354\213$S\244\344\370\361\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000`RK{\200Z\014\000\000\000\000\000\000\000\000\000\000\000\000\000\000\006\365l\017\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\0000-\367\232\000\000\000\000\000\000\000\000\000\000\000\000\340\005\374\254\245\256\245=\000\000\000\000\244g-\007\000\230\226\215\213-RA\000\3160\177\000\000\000\000\000\000\000\000\314\3075!\000\000\005\371\351\r\000\014\345i\001\007\000n\245,\006\000\250\340\255=\000\024ey\025\000\000\200\241(d\263\212\352\231\263\333\331Q\237o\030g\033\222\201\030\365\014\353\275=\000\000\034%\027\334C+Q[\2273\300j\003L\241K|\000\000 \231^Y_\257\327\005x\0101\247\365m\270\357%Ak\203Y\365\035\000$\246\353+X\273\361\342\312\341UB8\014l\337\t|4,0\032=\\\315\235=\276/\212\000\000@\200;\023\335\271\254\255\002\302\313\250%\001\240\013i\037\000\024\245\316&\001\303\020 \216\372\277\254$]\357\026\257P\310j\232\237$ \321\270\332/W\377\037\000\000\200K\276\3122\345\031\220\202`\304\303\246Py\253\233\004\000L\316\014\020.(\371\326\263\300\016A\021\tHD\212P\234\001\000@CM\000\000\231\231\251\001\200M\233\253\276\233O` \207\362\303CO\006\000\000\310GY\003\000\000\300>*\310\342j\r\200Z\337\026\000\000\000\356\347\266\313\000\000\000\000\014\312e#\000\000\300\005nF\367\032M\273\377\275h\341\253\272s\001Ca\316G\000\000\200\001]\330\257U\007\002T!\342\003\017\241\000\240\224\013\213\005\244wlJ7\026\000\n96E\000@g\252\221`~\001\000\000\0000\033\245uC\203\000\000\300Ord\000H\312\325\234\000\000\000\300\026\353\007\000\000@N\252\225\237\\\242\005\000\000\000\3005o\355\001\000\000\210f\033\270\032\033\335\000\000\261d\334H\302\001\352\222\007\000\000\324c\031\000\200\341X\300\270\211,\000\000\000\200\\\346(\371\325\333\000\000\000\003S\324\365\322\266l\373\367\213,I>\007\020\252\327\231?\307\362f1\275\006\003P\224\231`,\037\223\300\221N\333z\356\326\343\3743\354\364\373\313\007\327\371\005\3752\036\000\200\214L\336\000\325\324\216\374K\301\357_\357\033\003\237\"\327f\227\3107c\277\250~\211z\037&%W\201\016\336\333\003\014N\250d\027\247>\214@H\007\200\211\005.\224\313\376+\213\033g\244\245\254\000\000\000\200y=\255\377\000\000\300\334l\366\325\246\342\343\233`\320\3370m,2\324\243\317\0010\033@=\303\024(\264N\004\354\247\356\006(\313\024\000\220\321\275\321\371\336W\243?=\006\3779\261\326\001\000\0000\263\271\312\244\271\276\315\005\032\002\000\000\000\000\000\010b[\002\362q\r9\221\314\003\221>[;\366\034\277\275\207c?>p\320\3069\277\361\3606!\000\000\340\210\313\351\0270\034U\023\000\224#\355\007\230\333\037\020\217U\035\335\242\351\375\000\000\000\000IEND\256B`\202" diff --git a/core/res/geoid_height_map_assets/tile-5.textpb b/core/res/geoid_height_map_assets/tile-5.textpb new file mode 100644 index 0000000000000..ac2a9bad14024 --- /dev/null +++ b/core/res/geoid_height_map_assets/tile-5.textpb @@ -0,0 +1,3 @@ +tile_key: "5" +byte_jpeg: "\377\330\377\340\000\020JFIF\000\001\002\000\000\001\000\001\000\000\377\333\000C\000\004\003\003\003\003\002\004\003\003\003\004\004\004\004\005\t\006\005\005\005\005\013\010\010\007\t\r\014\016\016\r\014\r\r\017\020\025\022\017\020\024\020\r\r\022\031\022\024\026\026\027\030\027\016\022\032\034\032\027\033\025\027\027\027\377\300\000\013\010\002\000\002\000\001\001\021\000\377\304\000\037\000\000\001\005\001\001\001\001\001\001\000\000\000\000\000\000\000\000\001\002\003\004\005\006\007\010\t\n\013\377\304\000\265\020\000\002\001\003\003\002\004\003\005\005\004\004\000\000\001}\001\002\003\000\004\021\005\022!1A\006\023Qa\007\"q\0242\201\221\241\010#B\261\301\025R\321\360$3br\202\t\n\026\027\030\031\032%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\203\204\205\206\207\210\211\212\222\223\224\225\226\227\230\231\232\242\243\244\245\246\247\250\251\252\262\263\264\265\266\267\270\271\272\302\303\304\305\306\307\310\311\312\322\323\324\325\326\327\330\331\332\341\342\343\344\345\346\347\350\351\352\361\362\363\364\365\366\367\370\371\372\377\332\000\010\001\001\000\000?\000\355\240^@\305^\013\307J\216D\364\254\353\333c$$\250\344W){\033\243\220Ee\274\233N\r@\347q\247\305\037\034\212{E\362\325)\243\366\252n\265\003.j\'LT,\202\253\3128\252\216\224\325\213-Ne\3300*\027\250\271\3154\266\005B\314sQ\371\204S\322\343\007\232\237\355q\221\214b\241\231cu\'uQ\026\310\344\232\206k4\301\305f\313m\206\351Qy\030=*T\213\007\245[\2163\351V\0211\332\234\"\311\351R,>\324\343\032\251\353\212P\201\272\232pD\035\251\331_JQ\317AOU\377\000f\236\0015*)\253(*\314k\236\325j5\301\253\321-^\205j\374+\322\264!\216\257\305\035\\\215qV\343\025f5\346\257B\231\253\221\245[\212>j\322\200)\341\373\nx\315J\242\247\215FsS\203\307\0252\034W\017\034D6E[\t\224\2462\014sPI\030\333\310\256wW\262\004\026Q\\\205\344L\214qU\"$\310\001\255\025N\001\245#\212\247:\216j\204\213\315@W\006\241p*\273\325i\0275\003-4\014Tl3P\277&\230W\212\214\255F\313P:\324{M;i\244*\304w\244\021\277jw\222q\363S\r\242\021\234\212\257%\262\251\342\230\260\200zU\210\340\'\265YX\000\\\232v\305Q\3154\221\351Q\225\014zT\212\240p)\342<\365\247yB\236\261\014p)\333\0058%J\213S\242\325\230\326\255\306\271\355W\242J\277\014}+B\030\353F\004\253\261\216j\324kV\243Z\271\022V\204)\305\\D\346\256G\036\0274\214\010\247 \251S\255L\271&\247\034\n\2259\002\255\302\231<\327\036\251\206\351V\226!\266\242\222,s\212\257\"\022\207\025\221y\021\332w\n\3455\033#\274\2208\254\'\210\3077N\365z\026\312\ns\217J\2470\311\252\217\037z\255\"sU\234T\016\271\250]8\250\031j&\025\036\332\214\307\363S]qQ\025\2462\324f<\320!\366\245\362}\251\2736\236E5\363\216\006*\003\274\232M\222\021\306iD\016\335jT\265\\\344\324\3425Q\300\244a\336\240`\305\263\212M\2714\241\005H\261\343\232\220\001N\243\236\324\000jE\0254jI\253(\225f4\253q%^\2059\351Z0\250\035\252\374J8\253\360\255[E\253Q\255\\\211j\354I\322\257\302\265v5\351V\321N9\250\237\031\241jd\0315a\027\002\244\0315j\024\030\315[\207\357\375:W*c\300\251P|\234\320W#\221UZ\370\244\300\317J@\311\273mj\027Z\201\226\243+M\"\215\200\323J{P\023\024\365\000\324\2332\265\023)\007\212L\032x_Zz\306\rH\020/z^\005!q\332\232y\246\264Y\0359\251a\004\214\036\325n5\307j\267\022\325\264^j\324IW\241J\275\nsZ0\'J\277\032\364\253q\255Z\215*\314kV\321p*\312t\251\226\246_j{p\264\325\353S(\2531\246E\\\213\n*Y.c\212<\2223XW\332\226\342@Z\257o)I\352\335\314\341\227\255eNCf\263\245A\232\2472b\250L\274\032\317tl\364\250\2323Q\355\3074\326\344qQ\030\211\355RD\2305+\304\010\315V\222,\366\252\357\027\245B\321\221Q\262\032\210\247\034\324L\225\003\245BR\230V\233\266\223\024\204RS]\261\322\242,M\031\244-\351M>\244\322\026 qM\3114\365\031\251\220T\3523R\201\3058\251$\n\267\014d?>\225z4\342\255F\265n%\253\320\255_\205zU\370\226\256D1V\323\000T\351\326\254\307\232\234T\3103V\021y\253 \355J\213\253T\310\274\325\204;W\245W\271Y$S\316\005a]\220\231\033\263T\025\232Y1\236+N\004\n\242\273\006\002\240\227\001I\254[\2627qY\316x\"\251\225\"\\\321+\035\330\252\3563U\2359\252\263G\221Y\363FA\351Ud@*\006@{T\r\037\265 \200\372T\202\334c\2455\241\332sJ\027\326\232\360\2022\005Vx}\252\273\307U\3351P\262\324N\265]\327\232\214\255D\311Q\225\246\221L\"\223\024\205sQ\225\3054\216iBf\230\350s\3050\202(\003\232\231\026\245\013R\n\225~f\002\256,9\301\035\252\314c\236G5n%\342\255F\231\253\221\245\\\211j\354C\245^\212\255\307V\223\232\263\032\325\270\326\245\003\232\225\0075j0:\323\235\2060)\261\365\253\np\000\251\227\2474\311\220\272\234\032\346\265\004\362\344`j\255\240\033\363Z\212p+\252\226P\243\232\316\270\271b0+6f,MRpsM\332B\356\305VpY\363M)Q\264~\325ZD\346\252K\016{UI-s\332\252\275\271\007\2450[\367\305?\310\366\245\362\206:TRG\236\325Y\243*iW8\344S^ FES\226#\232\252\351\353P:b\240t\250Y*\026^j2\265\033/5\031ZiJo\227AJ\215\224\322,y\352*Q\027\035)\031\000\250\314c\322\233\345\217Jr\255N\023\345\243a\251#R\032\264b\344U\205NEZ\2123\332\257\302\203\035*\312\256*\314KWbL\325\310\324\212\267\030\253q\212\267\030\300\253H8\024\363\326\244\214U\220p*6ni\350x\251U\261O\022c\275L\016V\271\215^P\'j\255fr3Z\000\361]=\316zU\007L\325Yb\246G\030$\356\\\323g\215v\340\014U6\204g\212i\207\035\252\'L\n\244\313\226\250\335\001\025]\222\241\222!\351P\371X\2441\373SvS\035\006*\254\211\315F\023\332\224\256*\274\321\036\265JH\371\252\357\035Wh\352\027LUvJ\215\222\243+\232aZn\312]\236\324\236]0\305\226\351N\021\201\332\224\214\016\005B\352z\324x4\355\264\340\225 S\212pZ\221W\025\224u\251\342\341\263\336\2615\3713\0063\326\261\364\350q\363\036\365\256\016\005F\357\305z\254\203\002\252\310\274U_\'{\023Q\274\033y\002\242+\332\230\311\305V\222:\252\351\236*\264\221\340\363U\244QP4t\337,b\230\311\355Q2S6R\025\250\310\2460\315A\"\342\253\025\313Tn\230\2462\007Z\253$>\325U\342\305B\350j\007Z\200\2574l\244d\346\220%&\314\324f/jo\227\315\036_\265\'\225\232C\035(\217\035\251\342<\323\304C\024\377\000+\332\243d\035)\004X9\251\025A<\325\270\260\005Z\211\271\253\321\034\325\350\205]\210\340\325\310\316j\324t\362y\240f\245\\c\232\013\372R\014\232x\025\"\324\270\342\201\326\247P\0262Msz\273y\267\001\007\255%\274B8\305H\315\212\206F\257_q\220EV\221\017LP\261m\213=\315B\312\017\006\252J\233[\332\242 \032\202U\342\251Jv\266j\274\256\254*\233\365\250\310\244#\212\215\210\250\331M7e5\200\025\031\003\322\243+\212\205\343$Uf\217\006\241qQ\021\264\344S\035\327o\"\240\"78\250\244\203\216*\244\221c\265V)\316)6\373PV\233\266\215\264\214\244SB\022zS\374\260GJo\226=)\014C4\024\244\nE=G5&\336)\206<\236\224\322\204Sy\006\247\2175a\016\0105\241n\300\342\264b\305YL\223\305_\210`\n\262\247\002\22795\"\212~\323\212\002\323\324S\300\342\234\274T\203\030\247\306\273\2152\366\341m\355\311>\234\n\347\243\314\363\231[\271\253\'\000T\016\330\252\356\365\355,*2}i\254s\326\241\221Fr*\234\343*MT\315F\374\325;\204\342\2502\235\325\033%FW\024\322*\026Ni\n\234Tnq\305G\3114\204S\031y\246\260\312\325vNMV\225y\250\031x\250]\005@\321\200sM\335\203\212\202^j\253&M&\312B\224\302)\n\363\232P\240\360i\341\024\016i\010\002\243`{SB\234\323\274\262\324\276Q\245\010}*EZ]\225\033(\246\010\213\267\002\257Am\201\310\251\036\330\237\273O\2127\214\362+B\007$\201Z\220 \332\r[Q\212\220S\227\255N\20350\240\n\\sN\002\236\253R\005\251\031\322\010K\023\\\305\375\343]\335\354S\300\251\241@\250(\221\270\252\22275]\332\275\300\217\226\242\"\243j\211\263P:\361T$R\030\342\240b{\324\017\310\252\357\0379\250]*\007Z\205\201\024\303\327\232R\006\332\257*\234\346\230\006E!\024\025\342\240~\016\005B\334\036j)\023#5]\227\031\250\030T.8\250\031p3P5FE&(*1Q2\203L)I\202(\301=\315\000{\324\212\027\034\322aI\342\245D\006\236\312\000\342\243\"\200)[\201U\231\362\330\025z\326/\227$U\261\2000*h\3075dF\254:T\321\333\250l\212\275\032\2201V\024T\201x\346\234\027\0252\034\n\225i\364S\224sS*\324\221\250\316\343\320V>\263x\0262\212k\032\321\tb\355\336\257\347\002\241\221\252\253\265Wv\257u\030+La\336\230\302\243+Q\272|\271\2522\'\314j\244\321\367\025X\255F\313PH*\273\212\205\307\025X\3474\274\342\232F\356)\230\000\321\267\232F\034TE\006rj)P\036\202\241\333\301\252\322\016MWu\250\030d\325yW\236*\002\246\220\255&\321A\034T{i\254\242\231\266\202\231\246\354*iH\371r)\213\220\325aM.I4`S\325\0052D\310\250\243\200\2313Z(\241\020\001OQVcQ\212\263\030\305YC\201S\2415a\rJ\005H\242\234\006)\301\261O\rR/5:-L\253\315%\304\253\014\004\347\265r\027\222\233\213\242\001\34354K\265\000\2473b\253\310\365Y\332\253\273W\274#eqN<\212f)\207\2555\271\030\252\262\256\006j\214\300sU\031y\250\234b\253?5\003\017Z\211\300\"\253\270\301\246g&\235\266\230\303&\200\264\256\237-FS\345\250]qU\334aj\254\213\315Wa\315Fc\357P<|\364\250\032>i\205)\n\323H\246c\232\nf\231\345\322m\"\202\271\246c\007\245!Nr)Fi\3034\361\322\244JW\031\024D0sS\257&\245U\315Y\2163\334\325\244\003\0252\214\364\025*}*u\315XOz\224\nv>Z@*H\324U\230\343-\320U\205\214(\250\347\235`\214\223\332\271\275CSiIU\245\177\030VBy\305s\250\014\323\356\355\232\276\200(\024;qU\235\252\273\265WsQ\023^\363\023\032\271\021\334qR\260\001j\034\344\032c\364\252\362\216+>\341j\223\2475\013\241\252\3161U\244\316\352\204\234\032C\203M \003E4\321\301\244\301\247\001\362\325yW\232\255\"\325vZ\205\326\240d\346\233\202)\303\004r)\215\022\236\365\013@\247\241\252\317\0378\246y=\3151\227\034b\231\267\332\224\'\265;ei\245@\355MaQ\260\342\2532\363@L\323Z,\366\246\030\271\245\021\323\204\\\363N\021\322\025\3057\214\320R\223e!JP\224\241)\301qNQS(\251\226\244\034\324\213S%YN\224\254iTqV\020qR\027\001}\252\255\304\247\313f\007\240\256vi\345\222B\245\2163V-\242P3\216j\346\000\034TN\330\252\2621\252\257!\252\317-@\322\023Q\0279\257\241`\005n1\357Z\352\006\320qP\315\311\250@\305.2*\t\005T\221rj\263\246*\"\271\250dL\325Y#\305Vx\262j#\026;To\037\030\002\232\260\023\332\217 \346\236\">\224\361\027\265)\213\"\240\222.\rUd\250\331\006j&\217\332\240x\352\"\236\325\023%4\245F\321\361P4DR*\034\323\214d\016\225\036\337QO\021\361K\345\361\322\232@\025\033\014\232f\312B9\247\201\305 \\\265<\247\024\230\346\214{R\201\212\220T\202\244Z\225z\324\311\326\254)\342\224r\334\323\267\000i\341\317\2552Iq\225\007\232\251.\346\3435VK\022\351\272#\363\016\265\n\273A&\3118\"\254\254\301\207ZF \214\325i\031q\315@\301J\014\n\204\307\023\003\203\315C\373\224\310\3175\032\307\026\342Y\270\257\241b\205\214\275*\371R\261\363P`\263R\355\246\221QH8\252\2169\342\240e\250\212\340\323\0361\212\253\"sP\230\306y\243\311\004t\246\033q\236\224\206 \005 \210zS\274\241G\225\307J\211\320\366\025\003\241\3475Y\343\346\2421\323Z>*\027\212\240h\3523\036{S\014G\322\232c>\225\031\204\236\324\317#\236\224\276W\035)\206\036i6\001H\330\013\212\201\201&\232W\024\3023MaJ\006V\236\006\005\007\232B(\242\234\rH\265*\324\313S(\251A\342\214\220x\240\034\236i\373\360*\256\377\000\230\234\322\226\315\"\311\264\361T\357\224I\363\257Z\247\034\254\255\264\325\245\223\"\231 \014\rU\221[\261\252\305Xg\232\201\220\346\242`\330\353_R,h\234\201QJs\300\250\200\300\351M9>\324\230\250e\342\251\277Z\217\031\246\262Tex\250\214y=)\215\007\265\013\027\035)\216\235\361Qyd\232_/\024\334\200i\330\310\340Tm\031\034\324\017\021\'\245@\360\221\332\2431\037J\215\242\343\245@\361\361\322\2411\323<\256zQ\345f\232\320\373Tf`\250\255\333)R\265B\3759\252\356*&\257\246\237\247Z\256\325\013\032ny\244&\230\325\023S@\357O\307\024c\232\220t\251\024\343\255+\032\211\360\006\rE\263\'4\354`{\323v\026\353N\t\201AZP\234S\0352*\"\n\212\257$a\273sU\274\262\037\030\250\245\213\346\340UwM\240\3256\004\232\211\226\242)\3150\306*6P\007^j\007\340qU\336\240j\214\203M)\216MB\352I\250\231j3\301\247#\343\203N\'=)\246\222\201\315;\"\200i\353R\255;w\024\240\340S\267TRI\216*\204\257\271\350SRg\212c\036)\320\241\332d4\363\326\222\361\317\331\016\006N+2\316_\230\251\253\347\221\232\211\352\007\025\013\n\372T\267\025\013\032\205\271\246\342\222\214dS\031i\240T\212\274P\027\232v\332P\246\244\013\232\206D$\322\204\342\217/&\237\345\340Rl\243fi\3730*7\003\246*\026_J\205\327\332\253\272\214\362*\027\030\311\252r\215\306\252\310\230\342\242)Q\225\000\032\205\224c\232\201\300\354*\263\365\252\362\n\210\306@\311\244\340\016\325\023\234\232\204\203Q\260\250\330TD`\323\220\323\262)\t\244\024\3409\245P\245aP8\250Z\276\214\002\235\203J\007\024\025\240GR$f\244\331\353N\330\r4\307\3155\223\240\247\254|R8\343\002\221c\346\227\0304\355\274R\025\246\3554\001\203R\250\371>\225\024\202\252\270\252\354\271\252\263\0208\025BI\260z\324M)aQ\227\300\346\242r\030d\032\256\334\234\032\2570\317N\325Y\207\025\033.MF\303\265@\353\315FF*\026\034\323\017J\211\352\263\032\214\232i4\274\232c\n#\03148\301\246\203N\355IN\006\236\r8S\200\247\021\306)6\342\224\256T\325}\270\245\034\322\355\246\021\212z\361\023\034v\254\031>k\354\373\326\274C\367c\351C\212\201\205B\342\276\213\214\202\264\360)\312)\341sOT\025\"\256\005;\031\355J\027\024m\243`\353K\267\232i\217-NT\366\244h\271\351K\345\340Ry~\264\206>(\362\300<\212q\\\'\002\253\3108\252\3169\252\322\215\243\216\265\231u.\320Tu\254\311\030\223\232El\234S\23521P\210\310lSd\030\037J\251!\252\315\313`S\\aj\003Lj\211\205D\370\252\354j\0075]\215D\315M\007&\245\310\305FNO4\241\202\232\034\202\271\250\327\255?\265%\003\232\225G\024\3608\247\216\224\360)v\203F\334\016\225]\303g\247\024\325\353O#\212i\024\262\376\356\305\233\326\260#\033\256\363\357[\0100\224\214*\026\025\003\327\320\250p\265\365L:S\327\255H\005<\np\024\374Pc\014\270\305T)\266B)J\322\005\313b\242\324\330Ge\264V5\222n\233uk\205\302\323\030T/P=}\010\237v\236\231V\253\2502\271\251\025jM\264\345\024\340)\364\034\nM\340Td\206oJX\3075:\212\231E<-\005\001\352*#\036\r&\332\211\370\252\356\275\315V\226\251\311\030`I\254\351-D\222{TRZ*\360\005@b\nzPH\013\212\256\354\002\232\312\272r\030\340U?8\347\255\014\341\2075\023t\342\240bA\346\241f\250\236N*\263\275@\355P1\250X\342\243-Q\347\234\203K\270\343\232C&)\276`\025$d\260\346\244\343\024\231\346\236\001\357J\007\315S\252\361O\333\212\220t\247\001\315J\242\235\216i\300UYS\022\223I\266\234\211\316k;X?\273\013T\354\023\0038\255 8\250\334T\016*\006\025\364\"\014\n\221E[\204\360\001\253\010\265(ZpZ\\P~Q\232\210\266\016i\243\0079\243\0254c\212\224S\301\311\305[\21523J\313\307J\205\226\242~*\007\300\025^CT\344\334OC\305V\22398\250w*\365\252\362\260&\251J\3305Y\233\212\257+qY\327<\346\262\246r\215R\307\"\262\212s\343\025ZF\035\352\263\363\336\253H\304\032\254\356j\026\222\240ij\026|\324l\324\320\364\355\374R\026\2461\251\"|\032\230\266E*\014\265[\013\204\243n:T\250\274S\310\247(\251TT\201i\330\245\002\242\222<\266{Sv\361OT\302\023X\032\213\231.\n\203\221\232\232\322-\261\216*\321\034T/U\336\240j\372\031W\212zu\305M\037\016\r_\214n\031\251\200\002\227\0034\340\006*)\272\201P\3435$Q\344\344\364\247\030\300l\366\247\001\201N\035jE\034\325\330\370ZV\306*\006\252\322\036j\']\340\3253\220\331&\240\221\267\0021\212\252\374\003\315R\221\360\335j\264\217\236\365RF\311\252\356j\274\206\250\334t5\213t\334\232[Y\003\246\t\346\245\222L\034\032\254\362\202j\007\222\240f\334j\'L\212\255\"\221U\0379\250\211\300\250\231\251\273\351\301\351wS\031\252H[\236j\312\363SF\274\325\240\016\005<.jUZv\316j@\270\247\001\212\220\nv)v\321\267\"\220G\226\305C}\"\301\t\003\251\025\317\252\031\256\013\037Z\321\2156\255)\250^\253\270\252\357_GF\200\241\372S6\235\3254c\234\342\256\304H\024\362\3314\364\346\244\002\242\224e\251\250\2718\251\210\300\300\246\362E.>Zp\025*\216*\312\034(\024\254j\t\017z\252\334\234\321\301CT\2350\344\365\252\322\232\253.6\363Y\227\r\363qU]\252\263\236j\002q\326\243r\rS\235r\ra^FA&\250\307#\306\374T\322\\\026NG5XHKu\245\222E\333\214\325G\220\347\203Q\231\330w\246\231\367\016j\007`j2\271\025\003\241\315B\331SH\032\235\272\221\216jH\201\334*\344}j\354)\222*\340Q\216\224\005\305H\243\332\2368\355N\034\323\302\373S\302\323\361F\005(\\\364\247\235\261FX\327?\177pg\230\2504[\301\265rEY\306\005F\325\003\346\253\275Wz\372L)\t\305\t\026j@\230\251\001\"\236\t\251\321\224\nq\223\216)\230\'\232z(\316i\314F)\024qGSNQ\315L\243\212\224t\246\226\250\035\263\305@\346\242g\307\031\340\324\016\371\031\025Jg\2523I\3335JS\221U\\\325Y\016\016j\254\262\017Z\252\323d\365\250d\220\342\263\256$R\010\"\263\300_4\361I0]\243\034U\031$\021\236\rWy\263\320\324-6\017Z\215\246\3175\031\230SL\271\246\2311\336\221d\014y4J\237.EV\344\032p\346\236\006M\\\211@\213=\352\324\020\226\347\025\243\024AG\"\245\306\005(Zp\004\236\224\340\005=G5*\343\024\341\326\227\212\007&\245A\203\223T5;\200\020\242\237\255dA\016\3717\032\320T\300\244j\201\352\273\232\201\352\006\257\246\000\036]\n\2705 \024\355\264m\245\301\245\004\324\313\215\264\361\200(<\232\017\024\224\365\0252\322\223\201P\273\361P\027\031\344\324NsQ\034\223Ue}\271\025FY9\353Te|\325g5^R\000\254\371\245\352\001\254\371d9\353U\032S\236)\246V+\315S\230\026=\352\253(S\234\325yd\031\301j\245q\317C\232\250r\rA!9\342\242.GZ\211\236\2247\035j\'\223\234f\221\031\263\232\264\262\345v\2654\246NE*\241\024\360\234\325\270Wq\000V\254*\252\200T\333\261J\244\261\251Gj\221T\236\225\"\307\223\315.\314\036\224\240b\226\223\222x\251Qq\311\246\334>\310\211\2549KM9\006\254\305\026\325\034T\207\201Q9\250\034\325w5\003\232\201\253\351\264\033\233\025\"\257\315N\013O\013F\303K\263\332\215\224\340)NH\247F\0079\241\272\320:\324\252*A\300\250\335\252\254\217U\213\374\324\222I\264Tm8\331\214\325\031_$\325\t\237\006\251\311 \252\3176;\325if\005q\336\263f$\223\212\247\266I\037\030\246\264a\007<\232\214\270\3060*\tYpk.\341\362\373A\252r\3061\235\374\32578\352j\t\037\266j\263\277\275V\222_z\201\246\347\255\'\235\357B\266\346\346\247\\\nvOj\222)\212\234\032\264\256\206\224\272\366\251\355\233\347\255(\332\245\000\232\236%\305N\213\226\253 m\034\014\221Ud\235\222_\230qO[\210\334u\346\236\034\023N,\000\246\371\252\017\024\241\311\250.\245\312\355\252\261E\203\223\326\254`\005\250\334\324\014j\0275\003\232\201\315@\306\276\241\215v\362jLqN\013O\002\227m.=\250\332qQ\3644\274\232r\360\r%9jU\245f\300\252\322=T\221\352\020\3375U\270\234\324BM\313\357L~\225\235p\330\315e\3176\334\325\006\237{\340f\227\2675\013\2202j\234\263\355\310^*\234\223\022z\324&N:\325;\211\361\3005\225q<\212I\004U\031.\244\317&\253\274\356z\324FBj\007\223\236j\254\262b\253\231i\276a\315M\034\225ad\036\265 \224b\205l\234\325\204j\235Wq\253P\215\254+F2x5i9\025b1\212\261\020\313\375*\302\221\232\257w\002\261\311\357U\205\272\250\316iw`\342\245Q\221\232O/\234\322I E\300\250\000.\3315&0)\t\342\241sP1\250X\324,j\0075\003\032\372\224\360qOZ\227\256)\300S\200\243\024\275\25229\244\307\024\023IOZx8\025\034\217T\345z\252\355Qo\301\252\223e\345\300\245\t\265j\031d\254\333\231\0075\217tI\316*\224jD\231\251\035\360:\325I\246\033N\rfO8\007\255Q\222\351W\275V\226\364\343\212\247-\300\332N\356k6I\333\177\'5^Yr:\325f\230\201Q\375\243\265G$\274\365\252\322\313U|\317\232\227\314\031\251\342pj]\370\247\243d\340\325\310\243\316\017AS\252\340\325\210\370\253*\325z\007\312b\257B8\251\301\033\270\251\342?=[\0108#\255Ar\334\212\200\234\216h\n\247\236)\306DE\252\315p\316\330^\224l$e\251\352\000\024\214j\026j\215\332\240f\250Y\252\026j\205\332\240s_T\205\311\251U{S\300 \323\2058R\221IL4\334\361IH\r=x\245f\300\252\262\311T\344z\201\236\242v\001ri\261\000\331j%\220\0055\227s6\001\254\271%\017\236j\224\254NEV\334\007SU\247\234m\3005\235,\303\004\223X\363\334\356\224\200x\252\262L\000\344\325)\256\200\035k>k\337CT\336\357=\352\273\\\373\324/u\357Q\371\371n\264I0\307Z\254\363g\275@d\301\2442\361\326\246\202nz\325\325\220b\247\210\214\346\257$\213\267\203R\207\025:IR\306K8\025\257n\200\'5l8\003\002\236\215\315Z\211\276aW\200\371s\350+>\342_\336\022{UG\231\211\343\245 \226Lb\234r\303\223O\211@5+\035\274S7Tl\325\0135D\315P;TL\325\0135D\315P\261\257\254UMJ\253\212x\\\323\202\214Q\267\006\214SH\246\221\221Q7\006\220\320\r(5\024\217\306*\244\257U$z\204\265FX3\373\n\014\312\203\212\247s8\307\006\261\256g$\360j\231c\234\223U\346\230c\000\325)$\371z\326u\305\301\307\025\2354\216\312rqYsH\251\336\263n.\361\336\262\347\273\'<\325\027\270$\365\250L\331=j)%\003\275C\346g\275\'\233\212\215\356\017\255Df\367\246\231s\336\232$5,r\2259\253\221\316\307\025v)\t^j\3129\003\255L\222\234\3435f7\367\255\0136\005\362kZ7\371i\341\271\251\321\252\314rU\227\270\"\002\027\322\263\335\362\204\223Q\251\024\361\212q\"\225\037\rN\221\352\"\374Tl\365\013=F\317P3TL\325\0235D\315Q3W\327\n\243\025\"\347\275J\005;\024\021M8\2461\246\026\366\250\234\212fx\2434\326|Uid\252rI\326\253<\225\013\310\000\252\223N\300`UG\273!pMT\232\345\210\344\340U\tn\027>\265RK\203\3175RI\262z\325\033\273\235\211\200k1\356\200RI\254\313\273\376\300\361X\367\027\204\347\232\313\232\344\223\326\250\313>{\325V\224\347\255 \177SQ\311&j0\344\320\315\305Ww\346\242g>\264\335\347\326\236\255\232\235\016M]\213\240\253\2616*\300n*X\316M[\210\372\326\205\273a\270\2558\337\345\353R\006\346\247G\340\n\261\034\2035.\360\313\267=j\233\276#\306{\323U\352@\374Q\346P\036\203&i\254\374T,\374\324e\352&z\211\236\242g\250\231\2522\325\0335}x\265*\324\200\323\201\246\263\001Q\263\372TM%F\322Te\363Q\371\2304\246Q\212\201\345\367\252\322I\357U$\222\252\311\'\275V\226^:\3259\3468\353Td\227$\363U%\227\336\251K \346\251K7\275V3ry\351Yw\327\031;Ee\312\314\300\362k>\340\343\275eN\374\236k:W\344\325I\036\252\264\230\246y\324\323&h\363\000\2464\271\250\331\370\250K\363@|\323\325\252\3126*\324RU\264\222\247Y*x\344\253\220\277\025v\031p\302\264\243\227\345\034\324\302J\225\0375:I\212y\223\234\203PL\307#\236)\201\352A\'\035i<\312Q\'\024\027\367\246\031*6z\214\275F\317P\263\324e\3526jajal\327\330\013R\203N\0243\342\242g\250\231\352\026\222\2422S\014\225\033=Df\307z\211\345\252\357/\275U\222J\251,\275j\234\223q\326\251\3137\275R\222lg\232\245,\376\365JY\375\352\234\222\344\365\250%\224\"\365\346\262n\246\031\316k>K\2360+>i\207$\234\326U\304\343\'\025\235$\371&\252\313/\025U\234\223I\300\034\232a|SK\322\027\250\335\352\022\374\320$\251\222J\262\214\010\253\021\266*\302\275L\262U\230\244\253\320\313\212\267\033\022r*\3443`\341\215\\Y2:\325\210\333\212\260\255\353J\314;S\035\267!\035\352\272\311\316\r?\314\2442P$\245\363=\351\206OzcIQ\231*6\222\243g\250\313\323\013S\013\023HZ\276\304\024\360h/\212\215\236\241i*\026\223\336\241g\250\313\323\031\352&\223\336\240y*\006\227\261\250\036_z\255$\265Ni}\352\224\263U\031f\347\255R\226c\353Te\233\2575JI\275\352\263\312z\346\251O?^k\036\356\357\031\254\231n\230\236\rT\222vn\365RP\304d\3259\001\355Udoj\256\316j#!\007\232a\226\2433R\031\270\353L3qL/\236\224\002jtlU\230\333\236\265iZ\246W\251U\252\304lsW\241$\342\256\306\370\253*\371\031\253v\355\317Z\274\215\317Z\230I\357A\223\214\323|\317z\255#m\227>\264\276g\024\323\'\275\036e\036e#I\3150\311Q\231)\205\351\205\3522\364\322\364\233\2517W\331#\245\005\200\250\231\352&\222\241g\250Y\352&\222\241y*#/\275B\362\325w\227\336\253\274\330=j\007\233\336\253I?\275S\226oz\2434\336\365FI\275\352\234\263{\325\031e\346\252\274\252\243,j\214\327@\236\rg\334\\`\036k&y\201\'&\250;\214\232\201\210\353P\311&*\224\262\202x5Q\334\032\254\357\315Ww\250\213\324L\306\243.i\273\351\312\334\324\310je\253\021\346\255#qS\255L\265b.\265\241\020\302\365\251\325\275\352\302\023\212\267\013`\216j\342\2775\'\231G\230qM2b\243\225\262\231\035EF$\312\322y\224\236e\036g\2754\311\357M2S\014\224\322\364\322\364\322\324\335\364\233\250\335_d\227\305F\322TM%B\317P\264\234\365\250^J\205\245\250\036J\210\311\357P\274\236\365]\345\367\252\262KU\236~1\232\253$\336\365NYz\325)$,z\3259\337\000\363T\036ny5N\342\351\020u\311\254\251\356\214\204\363U\332L\014\346\250\\K\327\232\314\236^j\241\227&\242iq\336\253\311.{\325\031\344\367\252fc\232cI\232\201\332\242\335\315\007\245D\324\334\363OZ\260\202\247QS\2409\253(*e\310\251\220\232\263\026sW\020\260\253\t\353V\343n*tl\032\260\262qO\337\317Zpn84\326~y\246\357\312\221\353U\303\320^\233\346R\031)\246Ozi\222\223\314\244\337H^\232^\233\272\215\324\273\253\354fz\205\244\367\250ZOz\215\244\367\250^J\256\362{\324-\'\275@\362\373\324&Rj\'s\212\251,\270\357U%\233\320\325ff<\324\022>:\232\251,\243\034U\031f\300\'5\233qpq\301\254\271\256X\023\315P\226m\304\344\325v`;\324\022\313\307Z\317\270\224\016\365\233+\3565\003\270\002\253<\236\365ZIj\244\262\023\232\252MD_\232k6j<\321\272\232ri6\234\324\210\rYE5e\026\254F\230\253\n\271\251\225*eQV#\035\305ZC\221S\247\0252\032\231Z\247\007\013K\272\244V\244f\346\242g\301\315D_\232izizazizizO2\227}\033\3517Rn\244\335J\032\276\302y}\352\006\223\232\211\244\250\232J\205\345\367\252\357&j\006\226\241g\317z@x\315E+\200+:y0MT,Y\251\256\333S\255g\3157\315T\345\227\216\265Fi\307J\241<\240\326M\324\230\'\025\236\362\234\346\242i}\352\t$\033k>bI\252r6*\254\217Udz\252\356j\2731&\242s\212\2074\322sHzR\001N\002\234\007\265N\211S\242\325\230\326\254\242\324\312\2652\216)\340sSG\232\262\206\254!\251\327\025*.MM\216\202\223\245=Z\234\330\252\357\334\325p\371\'\353HZ\230Z\243/M/M/I\276\227}.\356(\335I\272\215\324\273\361_]<\265\013I\357Q\231=\352\'\227\336\253\274\276\365\003K\212\205\345\311\3115\013INY\206\314T\022\313\226\306j\244\305H\3115X\262\2579\252w7\000\214\003Y\362K\234\363Tn\'\300\"\262\344\270 \236j\244\263\346\251L\340\203Td\034qU\237 Uvz\255+U)MU\220\325I\rT\221\2105\016\354\323\030\023Q\2254\334Q\212P\016jEZ\225PT\312\240T\350\265a\024T\352\270\251@\251T\032\225V\245AR\250\251\220\325\2049\253Q)<\323\317\007\232i4\344>\264;\324r\034G\232\243\273\014i\013f\232Z\243f\250\313SwQ\272\215\324\273\250\337I\272\227u\005\373W\326\317%B\322{\324M-Wy\252\006\227\336\241y}\352\026\226\2432S\014\330R*\264\263\221\3005NY\333\326\252\311p}j\243\315\234\344\325g\227\216\265B\342N\t\315c\3157&\252I>;\325v\227=\352&~*\274\207 \3257l\032\255#UI\rU\221\252\244\215U\334\346\242\305!\3054\323\010\346\224-=R\245T\251U*EZ\225\027\025a\006j\302\255H\253S\306\276\3252\255J\251\305H\022\234\027\232\232>\r_\207\033)\254z\232\210\223\234S\201\305F\362sI#\342\036j\223\037\232\220\2654\232\215\215FZ\230Z\223u\033\251wQ\272\215\364\027\244V\347&\276\260ij\026\227\336\241y}\352\273\315\357U\336oz\211\246\343\255Be\367\2464\330\357Q\231\262z\325ye\025JY\272\3257\227$\344\325g\232\252\311>;\325\033\251\276^\265\2174\274\232\246\356sP\264\204SL\271\024\326|\325I\271\252N\3308\250\035\252\254\246\2529\252\354Ni\2314\224\020i6\232z\255J\251R*T\252\265*\247\265J\022\244U\305XCV\021r2*tJ\231V\236\000\006\244\030\247`S\224U\250O\312E)\351L4\235F*\274\334sLg-\025V-M\335\232B\324\3265\013\036i\204\323wQ\272\215\324\273\2517SY\351U\253\352\271$\307z\256\322\325w\232\253\274\276\365\003\313\357P\264\276\365\031\227\'\2555\234\343\212\214\270U\353U\345\233\212\241$\274\232\251$\270\315T\226n\274\325\031g\353\315Q\270\270$b\250\271\'\232\201\263\232a\217#\255WrP\342\230_\212\202G\343\255S\224\325v5]\352\263\n\204\2574l\366\244\333N\t\232\014t\241*EJ\231PT\252\2652\255I\266\224-J\252j\324+\212\262\253\315J\027\212]\274\324\252\231\024\340\224\340\2475*\002)\335\3151\215D\317\3155\260\352A\250v\262\361\332\240\225\010\344w\2503\203Aji5\033\034\324d\323I\244\315\031\243u1\236\232\016i\341\253\352Y%\343\255T\222_z\256\362\373\324\017/\275Wi}\352\027\230\016\365\021\270\305/\332GL\3242\3161\326\250\315t\000\353T\245\273\003\275U{\215\3035Ri\275\352\233\271\'\255W\220\214f\2531\250\231\271\246\226\"\253Lr*\266\343\320\324\022\276:\032\252\314MD\306\240rMBFi\002\nv\312iOj\000\247\000\r9TT\312\203\035)\341EH\252*UZ\220-=R\247D\036\225aV\246E\251\200\247\005\315<\014S\351\312*U\034\323\010 \232c\n\201\352,\220i\333\276^j2FpG\025Vh\312\234\216\206\240\335\316\r!4\302j3M&\232M4\2654\27579\245\006\234\032\276\235\222^*\234\222\373\325W\233\336\240ij\027\227\336\253\274\265\003\315\212\203\355\030bI\250&\274\367\252\022\334\226\357U\332L\216\265\027\236\001\306j\031d\356*\263\313\317Z\211\237#\255B\306\242cLf\030\252\3625A\301c\232\212U\034\325fQQ2\324\014\265\031Z6\322\320E&\332P)\300b\244Zx\251\024T\252*e\025\"\216jt\305N\270\"\245A\232\233\024\341N\002\236\242\244\013\223\315?a\0074\2143Q\262\361Q\024\317j\205\242\347\212aLSH\317\007\255F\303+\264\325\031\220\253f\243\007\"\232i\206\230\306\243f\250\313\323rO4\354\321\234\322\203_I\274\276\365RI}\352\254\222\363\326\253\274\325]\345\367\250^n*\264\223q\326\252<\307\035j\263\312OSP4\225\004\223\200:\325V\237\346\353O\363\003\'\275Vs\315Dd#\2750\311\232c\277\025\tcLn\225\003g\250\250\035\352\0265\0315\033sQ\355\346\227m\033ph\"\223\024\001\315<\014\214T\212\230=jt\210\021\326\235\345\221R(\251\005IR\'\275L\246\246C\212\260\274\212\220\016)GZ\220b\245SR\021\221HV\243e\250\310\305D\335j6\250\233\255D\302\240\221C-Qa\265\2150\232i5\013\270\002\240f&\2234g\002\214\346\2274\003\315}\022\362\373\325Y%\252\317/\275Wy}\352\273\313U\336_z\253$\276\365U\345\367\252\3575Vy\375\352\264\223g\275ViNiV\340\203\214\323\313\356\250\236\231\237zBj&<\3237Tly\252\3562\325\023\000*6\250\3174\334\021\326\202i2(\316iv\322\2055\"\241\316sS\205\004sN\000\203\201S\205%y\240%<\nu=jd5*\324\350j\300?-\000S\300\251\200\332\2715e0c\030\240\342\242`\rF\343\212\252\343\232\215\205FED\365\021\372U+\205\301\316*\241$\034TO%BI\'&\232i\271\2434\003K\232Q^\374\362\325i%\367\252\257-Wyj\273\313\357U\336_z\253$\265VI}\352\254\222\325w\222\240y*\026zn\376i\353!\365\247\207\334)\271\346\232Z\243\'4\302y\246\223\336\230~\355V\220\363Q\022)\244\323I\246\021F\017zr\216i\304\220)\273\216jE\222\246Y\001\251A\025\"\310A\366\2513\236{S\201\247\365\247\001OS\212\224\032\225\033\232\262\207\021\000\240c\327\262\241\352\0366\n\037{e\342\016\000\000\000\334\311\023\204;\252\003\034\365\276\000\000@\n\005\t\000\000\000\000\260\017+\370\000\261,\267\000\000\000\000@\010\311\3000\345\t\237\237Z\2061\354\216\001\000\000p$\341$]\320Q\001\000\000^\332\347\226\330}\336\345\024\342\305l\177]WG\000\000\000\332\320q\204\r\000\000\000\000\000\300G\251{\003\036\271*\204H?\321\n+\000\000\000`Qz\372\000\000\000\000\200\321\3342^\262^\214G\257\376\254\346\216ws_hp\2167-Q\013\013Y\2121\310\220\364I\343\\p\307\331\023\000\310c\034Of\366\017\220\314(\000\000\000\000\217\304\312\000\300\030T\001\000\3002Y\200\r\3328h\357~\013S\245\316\275\333\001\000\000\200\332\254\272\326\316\316\304\245\000\000\000\034\241\022\330\337\233+/W\333\241Iyy\225\325?\321\325\033\366\"\357\\\003\000\000\300!\346\361\262\006\000\000@\377\004*\000\000\000\260\262VV\020\235neG\001h\215\001\014\000\2003\010\243\341,\242@\250H\371q\244\354\'\025\327\262\201(\346\340\220\3155 \335l\036\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\374\360\357\347\000\000\000\000\354a*\033\000\000\000\240[\375VS\210\317\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\340\246\337g\t\001\000\000\000\000@[b\236\202\377\005\355\265M\274\251\013\246j\000\000\000\000IEND\256B`\202" diff --git a/core/res/geoid_height_map_assets/tile-7.textpb b/core/res/geoid_height_map_assets/tile-7.textpb new file mode 100644 index 0000000000000..d00efe84093be --- /dev/null +++ b/core/res/geoid_height_map_assets/tile-7.textpb @@ -0,0 +1,3 @@ +tile_key: "7" +byte_jpeg: "\377\330\377\340\000\020JFIF\000\001\002\000\000\001\000\001\000\000\377\333\000C\000\004\003\003\003\003\002\004\003\003\003\004\004\004\004\005\t\006\005\005\005\005\013\010\010\007\t\r\014\016\016\r\014\r\r\017\020\025\022\017\020\024\020\r\r\022\031\022\024\026\026\027\030\027\016\022\032\034\032\027\033\025\027\027\027\377\300\000\013\010\002\000\002\000\001\001\021\000\377\304\000\037\000\000\001\005\001\001\001\001\001\001\000\000\000\000\000\000\000\000\001\002\003\004\005\006\007\010\t\n\013\377\304\000\265\020\000\002\001\003\003\002\004\003\005\005\004\004\000\000\001}\001\002\003\000\004\021\005\022!1A\006\023Qa\007\"q\0242\201\221\241\010#B\261\301\025R\321\360$3br\202\t\n\026\027\030\031\032%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\203\204\205\206\207\210\211\212\222\223\224\225\226\227\230\231\232\242\243\244\245\246\247\250\251\252\262\263\264\265\266\267\270\271\272\302\303\304\305\306\307\310\311\312\322\323\324\325\326\327\330\331\332\341\342\343\344\345\346\347\350\351\352\361\362\363\364\365\366\367\370\371\372\377\332\000\010\001\001\000\000?\000\367\325\3014\371\"R\231\350j\233.\036\244A\305?\024\273i\301iq\322\237\263&\223i\364\247\000A\247\201\232xRMH\252qR\252\324\253R\255J\265*\324\253S\245J\277xT\303>\264\342\271\003\214\323\343#\247z\260\242\245\024\360i\300\323\301\240\324M\301\315Z\267\225Yv\265H\313\316\005T\224\020qK\0023?z\261s\022,@\216\265\234s\232LPi\247\024\302\005D\3521P\2203PI\212\251 \252\262-V\221j\254\203\002\252J*\253\212\254\331\252\362-T\231x\254\371\220U\031V\251\310\2475\023-B\302\253:\344\324E*\027J\204\307PH\203\025\357\215\021\316E1\303l\301\250\n\372\323\324S\300\247\001N\013N\013J\026\234\027\212v\316)\352\224\360\265 Zx\024\365\025 \251V\244Z\231jT5*\324\313\232\260\200w\250\201\006rW\245ZS\305H\r8\032x4\360ii\214)\261\261W\253\350r\271\250\235C\311\315]\265\214q\201F\241\016\330\201\025\223\267\232M\274\322\025\250\331qQ\034\324MQ0\252\356*\273\212\257 \025NA\317\025Y\306j\254\252MUu\252\316*\274\202\251\312*\214\300sY\362\216j\263\212\256\302\241e\315B\313Q\025\250\230\n\201\326\240u\342\276\200\003\326\241\230\002x\252\373rsJ\020\323\366\032pCJ\0058\nx\024\360\271\247\005\247\205\247\005\247\005\247\205\247\201O\002\244QR\n\225M=H\365\251<\330\323\031aR\255\304{~\\\223@\363enX\201\350*\314h\024T\300\323\301\346\236)\340\323\3058t\245#\"\242a\206\315[\200\3459\241\201\r\221Wme b\245\277\005\255\301\035+\030\214\032J\010\315F\313Q2\324L\265\013\200\005V\222\252\310j\263\325wZ\256\353\212\252\340\232\254\350j\263\245U\224`U)GZ\317\230rj\234\213U\034sQ\025\250\314d\366\250^3\351Q4F\240t\305WqU\334W\2767\003\"\252\3111\r\203J\256\255\300\353S*qK\201\232\\sF)\300R\342\236\007\024\360=\251\340S\200\247b\234\001\247\014\366\247\000i\342\2363N\024\244\344c5\022.\331\366\036A\365\2558\342P\271\310\253\010GJ\220\032p4\3654\360i\340\323\301\251\005*\244\250\000\2523-P\230\001\232\316\227\255S\220Uw\034\324Ey\2461\307\002\242<\324NqUd9\252\262\014\325y\026\275\335\345UZ\314\272\270_^k5\365/&\\\356\255\013}b9\024|\302\247k\325<\206\024\364\274\004\365\247\033\300\rK\035\310aS\254\253\364\247\254\253\237\274*u\301\024\375\246\227\276)A\247\003O\024\341\326\234)\302\226\242\225\312\014\324\006\360\t\327\'\232\330\266\270\363\024dU\241R\203\221N\024\361R\nx\251\005H)\353O\003\212F\034T\226\303$\214T\340a\263\332\247\21418\035\352\t\227ksU\317Zi\024\302*\'\007\265V|\367\252\322\016j\273\212\255\"\232\256\302\242~\225]\352\026j\201\315U\220\325\031\210\031\254\331\316j\204\243\232\252\353P\024\346\230\311P\264u\013\214\n\251)\364\252\315\222j&Z\201\326\275vk\261\264\363X\267wyc\315e\314^F\342\243\215.c9V5i\'\272\003\251\251\222\356\345G9\253Q\352\017\374`\326\276\237\177\004\214\003\234\032\350\022+Y\"\0048\006\253\315\004jr\257M\212]\235^\254\255\3169\316j\302H\255\315?\000\364\247`\322\342\234)\343\2558S\202\223Q\317\036R\271\3717\256\242\003\036\365\321Y\311\205\025\253\033\202\265(8\251\027\223\221O\002\244\006\236\r<\021R\003R-H)B\356n*\322\016\001\251\203qRg\2758\032p4\360jUj\2205K\037/W\342l\214T\300qQI\322\252\031\032)w\036\225\245\005\302I\027\0075\014\304\347\232\256O4\307\220(\250\231\362*\264\231=\352\273d\032p9L\032\215\205@\351\223P\262\n\257 \025Bt\2522%R\225=\252\224\251T\3359\250\0351U\335j\274\213\305S\221\016MWt\250\031j\026Z\205\205F\343\013\305T\220z\327l1\212\t\2405\033\271\247\254\230\251\004\224\355\365\"I\212\235d\253\013.@\036\2252JGzr\271\r\212I\000u\254\371-\312>\345\253V\267m\037\312\306\257\255\360\340f\254%\341\007\255[\212\364g\223V\016\242\2128\305S\271\324x\'uc\3114\267sc\355\016\334\212m\263\262\307\221\324u\025\241\023y\311\367j)\241\220\002v\342\263\344\334\257\315\000\344sQ\271\031\250\033\257\025\023\023Q\2279\346\232\317\307\025\013\023P=U\225sT\344J\247,}x\2522\307\355U$J\253 \002\252\272\324\016\231\252\322&*\263\256j\273\255@\353Q\371f\253\315\264qT\2449\351]\216\352B\364\335\376\364\273\3517\363\326\236$\347\031\251\003\232z\311R\254\225b9x\251\204\235\352\306\362@#\222G4\365\177Z~\320\303\2450\333+v\244{b\2540i\271\225s\355H%\237=\rYF\231\327\245\323\214qR\226L\345{\325\210\266\354\334O\"\207\224\0312\016i\333\370\245\337\315(zpl\323\343r\254\rh\255\323\265(|\323\362}kB\326l\240\004\326\224R\002\243\025a^\246F5\235\252\334\241\002 A5\237\n\000FGz\331P\242\025\003\245&y\340\361T/\007$\326y\353P\275*\0361J\325^SP\036\225\023\032\257!\250\031\252\t\000\252\222\2405FD\353UY9\351Q\262\014t\252\322)\002\252\273`UYe\000\034\232\316\270\272\003\214\326t\267LI\252SL\315T\2379\250^\272\306j\214\275FZ\233\276\215\364\241\352E\222\244\016E=^\246I*x\345\303sVc\220g\255XV\315N\2161\201\326\247I*P\374\346\254,\233\227\030\344T\210\375\273\324\245\243\\ps\336\236Y\n\r\275i\241\215<5<5J\204f\264\240h\304G=jx\345]\273v\365\357HH\316(\310\247\003R.jE5\"\2675*\266{\324\360Hc|\036\365\251\024\300\r\302\247[\245\0377j\255s\254$jU3\322\263\340\221\256%.\3479\255\r\277\'\025v\335\213\303\203\332\202p\325Z\353;+9\2175\033S\001\301\245,*\t\016MF\304c\025\013\342\253HqU\335\252\007j\201\310\305U\223\0075U\360\rA#\200j\234\322u\254\313\213\205N3\223YS\312\357\236p*\234\200\325W\025]\305@\342\253\270\256\221\236\242f\246\027\246\227\244\337J\036\236\255\315L\257O\017R\254\203o\275J\222\014`\365\251\243\222\255\244\274U\230\245(Cf\247\363\2030*1S\tr\243\216\225\265]\262\016qU&\31318\250\031\366\202\000\035*\233\325g\252\3626F1U$\252\317\326\253=Wz\276\315Q\226\2463Te\251\312\324\362F)U\371\251\225\252M\324\233\371\247\254\225*\2775:=XG=\252\322\3121\315M\033\215\303\'\0258\223\022gvj_0\023\221\305L\217R\206\315=X\253f\245V\311\311\251A\307\025\265\"\276)\342Jz\275H\262{\324\253%XI)\354\344\212\2127\304\325t\316W\006\234f\004dT\213.!\372\323\004\231\357MbMF\300\001P\261\250\037$qQ\230\230\236\265\023\243\003\216\325V\\\347\025Q\363U\344\351U$\252\317\357U\330\016\265ZNx\002\252\310\010\353U$\034\032\251!\252\262\032\253!\252\357U\234\325g\252\357VY\2522\324\302\324\302\324\241\251\333\351U\271\251D\224\361%858\032\225[\025:\275O\034\225hH\030\016*\304R/\000\212\225~\376\007\"\247\306\334f\247F\342\245C\270\3435(\334\006H\340w\247\253T\241\301\307\255N\273\210\315J\256A\346\254\307\'\025ad\007\275J\257\3163S+{\323\374\314S\204\231\247\007\247\254\224\365\226\247I*\304rU\200\340\256*\031\t\007p\2530\310\255\016\032\246E\017\031\001\272\014\212lr\0026\267j\224\306G(i\271b9\342\230\330\305B\355P\266{\032\217\220:\324lX\036MA$\203?tUY1\234\342\240`\233y\353T\337\000\236*\244\234\236\225\003\223\214\n\256\303\234\325i\201j\245,|U)S\025R@y\252\256*\264\225]\305Wz\254\365#5FZ\230Z\233\2323F\352P\364\3573\336\236\262T\201\352T|\324\271\251Q\215XCVU\370\002\254F\334d\036j\314R\355<\216jP\304\375\354\342\246F\305H\255\315XIH\300=;\324\303i\034R\240\014\330\315YI\n\214\003\370T\236fXdT\312\346\246I1R\254\203\255L\263f\245\016\017zP\324\341%8IOV\357S\243U\204z\260\262qR\002\030S\014\215\t\340dU\210\356\001\371\223\203\334R\312\215\031\022vj\2369\tA\3159\217sP;\222j&$\324D\220qL\'\236hb\n\344\n\252\340T\016\265Y\326\252\310\265VE\252\356\265Y\352\264\225RQT\345\025NQ\326\251\310*\263\212\256\342\253\310*\263\212\215\232\230Z\232Z\233\272\227u!jn\3527\323\325\352ej\225\032\247\017R#\325\224z\260\222b\247G\346\254\240\'\007=j\310\220\340!\355R\241\347\232\235B\263`\034}jN\000\3109\251\020\343\004\364\251c\223k\344T\302M\317\222?*\223~_\203OW\303u\251\225\315X\215\201^MH\254)\352\376\365 zpjP\3075\"\261\365\251\221\361\336\247I*\302?\275L\222`\324\334H9\244\020\341\276S\214S\247\231\374\225\213\25754G\010\005L[\345\250\310\3435\021`F\000\315\001w)\'\202*\t\025\227\250\250w\340\324NFj\007\342\240z\255 \252\262UY*\264\225]\305V\220\n\247*\361T\344\025NE9\252\256\247\255WqU\334Uw\034\32564\302i\245\251\205\2517\321\272\220\265\000\323\225\271\251\225\252ej\225Z\245V\251\221\361V\021\352\314mVc\223\025b=\316r*\3021\035jej\2207\241\253\t 1\3551\315Tq\223P\224\3074\215\367j\204\347\223\\\253\n\211\2523L4\322)GJr\232\221jL\214R\016\224\345851;\200\300\247\255;\255<\n\221z\342\254\241\251\224\324\203\255<\032z\232x\340\324\273\362j@i\341\251\340\323\301\342\227>\364\3655 4\354\323\324\323\263N\006\244\rO\335R)\035\215M\033\342\254F\377\000.*\324#.\0015u\006\016\001\351V#\227o\007\275^\215\270\251\343o\230S\236O\236\243$\236\275)3\305&I\245#4\322)\214\0050\364\246\355&\232\313\353Q5V\231\302\212\311\272\271\007 \032\307\235\3015\000\366\244+\353Q\262T\022t\250\034\374\265B\342\271\206\250XTG\2555\272RQN\247\255;\275-;<\n\261\000\334qO \251\247-H\242\236\275ju\351S%J\270\301\342\224T\203\255H)\340\001OSO\006\236\246\244\006\234)\303\255H\264\372p\300\247dR\346\236\032\234\032\236\255\315J\255V#j\271\013\373\326\204-\214\036\265e@.\030U\264j\231\032\234y\346\216qJ\026\227o\265\033i\n\323\n\212i\000Td\324N\302\252\3132\"\222Mb\337j\013\310\006\261&\273,x5\n\356\220\344\324\341p)\255Q9\342\252\312j\t\017\025F\342\271\206\250\232\241=i\207\255\'C\365\245\3174\3454\365\247\034R\255\224\237Zx\353R\255\004\020sNZx\351R\3062\302\256\034\234\016\240R\262\355jr\364\247\201O\002\245PEL\2434\374q\212r\361R/Z\224\032)A\247\253f\246SN\316i\300S\207\024\3658\024\377\000\306\223\232PisJ\r8\032\221\032\254\243U\230\332\255#t\301\253\201\211\333\316M_\211\376Q\315N\255S)\251\226\244\002\235\201Lc\212\201\246\003\275T\226\355\001#p\2527\027\310\001\371\353\036\353P\31085\225!\232v\357\212\022\323\007$T\302\020;PP\na\030\250\235\270\252\356\325]\336\240s\305FNEU\270\037)\256i\205B\365\021\250\3154\322\216\224\240\323\267v\024\365c\322\227\004\032\221jd\353W\004\213\344\343\034\347\255\'\336n:T\252*@8\247\001R\257^j\302.O\2659\200\315\'\361T\202\236\246\235\216)\271\305875*\275H\255R\251\247f\2274\240\320\\\212\177`M\034\2122i\300\324\210jtj\265\033U\224nEZI\006r\0175j\336B\033\035\215_F\315XCS\241\251\205G,\351\022\222\314\005a\337k\221\306v\241\254\306\325%\224\360MW\226I\333\236y\252\315\034\316~f4\253j;\363R\210T\016\224\245@\250\310\002\242sU\335\261U\335\370\252\316\336\365]\233\232\211\333\212\210\266\005V\235\376CX\rP8\250M0\323i;Q\320\322\203O\006\244\\g\232\231zc\0252\240\3019\351O\031\253\366\361\006\204\237J@9\251\025sO\013R*\324\3506\214\320y\315 \247\212x\247\203\305!\353IOZ\221s\332\244V\251\001\245\3158\032\0174\375\304\256(^\264\273\273\032\\q\220i\351\232\2363\315[N\225*\2675idP\177\n\232\031\300q\223\326\264\342\220\021\326\255#\014T\236z \371\230\014T2\352\320\304\244n\025\201\177\252=\303\355\214\361T\022\006w\334\347$\325\264\205@\351S`\001\202\0055\243R\231^\275\352\0221\332\232[\025\0335B\317PH\365VF\252\3625U\221\352\002\324\306j\205\237\212\2513\326CTL*\026\025\031\246\232oJJQOZ\225jd\025a\027\212\220-[\266/\367\027\275?\313*\377\0005J\242\244\002\244E\035i\371\244\301\243\270\342\236:\324\213\203J}\251\264`\323\200\247\251\"\244\024\360i\324\240\323\201\315;4\240\322\343<\3203ORj\302c\031\025b6\342\245\315I\021\314\200z\324\267(\321/\007\236\264\221j\245Wk\360EX\376\332\n\274u\252\263\337\3176Ys\315W\002Y\016]\215N\221\201S.\005J\036\202\325\031r:To/&\242g\r\323\255@\356A\305B\322T.\325\004\215Udj\254\346\253\273S7z\324\022=T\231\253=\2526\250Z\243\"\233M=)\264\243\255=jU\251\343\253H3Sm\253\226k\222@\\\232s\347\314;\252E^)\340T\212)q\203J\000\240\014\344\n\007\002\236\264\277Jv\3363IJ:\323\301\247\212ZP{S\201\247\016\231\245\006\244\340\2504\006\300<\320\rH\246\244S\203S\207\002\245\017\3059d!\262*\353\312&\201r9Z\246\360+6qJ\220\252\365\025(P\006;R\360(\336\005\036e/\233\212<\332i~*6z\205\244\301\3105\021\227\223\236sQ\263)\034u\250\013T.rqU\334\325y\rVs\315D\315\315@\346\252J\325U\205B\325\023S\017Za\246\232oz\007Z\221jU\253\021\366\253q\212\235Fj\345\2332K\362\212t\247t\333\261R \342\244\002\235\333\203\326\202=z\212\024\022\t\024\244`\n\\aA\245Z\221\027\223\2321\212LQ\212p\353N\006\236\016h\3058u\247g\265(\353\305/\"\214\234\346\235\332\234\032\245V\251\001\247\253b\244W\253q8\362O5\037\233\363u\247y\231\024\365l\3655\033H\001\3057}4\311I\346Q\346Q\346S\014\225\0235D\315Q\261\246y\230\353\315D\374\234\3665\004\200\212\253!\252\354j\0268\250\035\270\252\222\032\211\372T\rQ50\323OZa\246\np\353OQR\245Y\216\255\305V\224U\273D\014\373{\232\236ks\020\311\024\304\346\245\024\243\000\344\366\244\357\222)\300\r\271S\370R\362W\036\224\270\342\234\006)\303\257\024\275\261E&)qN\002\224qN\036\224\017J\\\343\232z\236)i{R\322S\303S\303S\267\323\225\352E\230\201\2674\236g4\361/\275?\316\343\031\246\027\367\246\371\224\206OzC%&\372<\312B\364\306zaz\215\232\230MF\304\342\242/\306\337Z\202d+\315Un*\t\rWv\342\252\311Q\275B\325\023Tf\232zS\017\335\246\212z\212x\251TsV#\025r.\325i9\253\020\313\345?R*\371\304\366\244\253\022GPj\262\014\034\032\230R\0323\353J8\351N\0370\372S\205\246\220\212AN\006\237\236)\t\355M\316)\t\'\245\030\342\220\361M\'\212nqHM&\352v~Z\003PM&\3527\323wsK\272\223u\033\351\245\251\245\251\205\251\013Tli\214j\027\351Qn\307\r\322\240\224\000r\275*\263\325F5\023\032\214\232a4\204\361L\315\002\236\2652\032\231}\252d\315Y\216\255GS\212\236\334fQS\3101!\3157\324\323M\246\232a\342\230\324\302i\204\320\341B\202\246\242\315\033\251\t\244\311\2434\231\347\25586)\\\343\2453p\307^i\205\251\245\251\245\251\013SwQ\272\223u.\352ijn\3527PMF\306\242j\205\215d\261\250\330\323\t\246\023I\232)GJx\247\212\225ML\206\254\306j\324f\255\241\342\234\307\212\237O\230\013\260\t\342\264nmZc\230\2278\364\2522#\304p\343\006\242\335\232a4\314\363OSR\203R\251\251T\324\312\325*\232\221M<\032\\\346\235\274\343\004\320H4\323M=)\264\323Q\234\232a\250\332\230M34\204\322\023Fi(\316)\245\251w\361L\'\232ijB}\351\205\251\244\321\272\214\322n\2434\023M-I\273\336\215\336\364\206\243cP\265b\261\250\311\246\023L&\200iA\247\216\264\365\247\216\225 5*\032\260\206\254\306\325j6\251\031\262*%\220\244\241\207j\3504\375b8am\340\037L\326m\355\351\271\270/\3335\\=!zM\324\3655*\232\225MJ\246\245SS)\251T\322\346\234\032\224\032\\\321\232i\246\232i#\031\250\311\250\311\346\230\325\033S\r74\233\251sK\333\212c\034\034SwRn\244\'\364\271\240\363GJi\244\315!\246\223L\'\232i4\233\251\013W0\355\223M\315&E&is\357J\rH\255S+qR\251\3434\3654\360i\340\323\303S\303S\203S\303S\203S\303S\203S\303S\301\342\236\rJ\246\244SS+T\252j@j@\324\375\324\360i\300\323\263\357FM\031\367\244\315!\242\233\324\322\023\317\024\231\2434\240\323\303S\201\310\305!4\323\322\222\2234\003KM5\300\003\3158\032x4\360x\247\003O\006\234\r;u85<5.\352]\324\006\247\006\247n\245\rR+T\310\334\346\246\rO\rO\017O\017O\rO\rR\006\251\025\252Tj\2245<5<5;u8585.\357z7Q\237z7Q\237z\017Jn\342(\022`\021\353C&\344\316EVpGZa\351Q\232a4n\240\234\323\017\024\334\343\232C\216\242\223\004\216)2i(\315(4\340\324\354\320Ni\246\222\212RsI^z\r<\032p<\323\301\247\203N\315(4\271\247\206\245\335K\272\227u(jpjpjpni\341\252tl\n\225Z\234\032\236\032\236\032\244\rR\006\247\253T\212\325*\265J\255O\rO\rN\rN\rN\rK\272\215\324\273\250\335F\357z]\324\204\323M |Sd;\215B\325\0314\3064\314\321\232\t\342\232h\315&qM4\334\321FisN\006\235\232J);\322\321^v\r?d\313\017//u^\000\234\367\316\274R)\240\272\247\237h\301\341\353\253\211\021\307\341\203\017t\301\332\321\240\263\222\357\375}\253\004\315\322\357J\343\243-\343\261)\2266\375p\\q\233u\033i\317s\000\360\317\352* n\232\343\267\325\003\013ley\004\232\021\277Np\346W\333\320\300\250\223\234\020=\234\342\025(`\351\200\246|\230\262\037^\256g\356\215\032-\000\303~l\367\3779F\330)@O\204\331\223|\335\221\257\305\000\311\314m\021y\341\264\316\252|\243\366\247\035\343i\315a\245\020#\245&\204\276\205Xh8^\345\n\003VQ\\\204`\375\007\256\246\213\301\364\337,\244\343\n\335;\356Z\035\350E\310e\260/ %\331.9\001\000m[_\275\254\377\211\\\364\317\341t\371\361\3649\027a\000\3752\273\201\217\234\202\003\320\021Y\r\322\263\014@c<\235\000G\030\343\375m\336 iW\325\352\367\007J\002\225T\350H\023\262-\317\213z\205\000\000:eu\007 \222\277yi\274\214\276J\260D\272N\233\253b\322uDgL\377\243\305\354\360\271\371\r\300\216\336.\276\236 o_\314\234\017k\335\"y\020\321\200\342\004\000\000\000\240\224\023V\000\010\307\215\017:\242\332\004\330\313\212\025Vm\321\243\025\001\360\244\325\257\230a\336\252\031^\0328\264\312\210\003\301]\277\337pU*;Z\350\017\267F\314\206\310S\000\244\0263=\003\320\001{-\332u-\2206TI\242\277I\033F\234\0164\372\345UV\033\200\014\332\314QT#\000\016\320H\'\337?\346\323\307\r_\017\036\334\271\007\277]\004\t\233\014\354/|va_\033\003@j\0028\234\245\227Q\024\000qY\240\222\373\035\000\202\001b27\001\310@\276\003\000\0008\311\306G\321\032c\373\t\000\000\3009\276\246\027\000\000\000\000X\"\327c\r\000\000\000\241y\026\033\000\000\232\340\356\n\000\313L\017{\366\316 {\377~\200r\323\025\021\000\000h\202R\036(e\375\000\000HK)H\337\276\247\027\266\231\2350\263/\000\000\000\307\363\210vr{\006@\370\335\337\236\215o\314\020~\260\000\330\217$\000@\"\366\201\233\364R5\364\322\016\000\250Fr\004\323\000Rq8@6\2451_\372s@\016*hh\211\031\013\220\217\265\377\215\336;\347\247}\275\267\022J9\360\002\310Ju\004\271\335\326\200uK\301\373\322q|\377r\24166t\2734}w\321{\025\330\213\331\017\000\273\220b\tmv\337*ry\" \000 \225\331\"\021\200\336\331\375U\022=\227\032h\000\000\200\276\335\367\245\321\367\247\034C\034\344\346\034\010\000\000(e?\271T\274\236\252\271\027,h]\315\267\007\3261\377\222\023\000T&\244\000\000\000\000\000:Up\037\030\270\270y\002\300Y\276\246\027`1\321\003\000\000\215Jy\220\357\004\366!e\000\000\000\000\000\355q\240\003@\021\247\3407\2113i\342\246\003\300\203\204\010\220\214\335pb\006?9\001\300\203h\310\352:\362\206?/c\017\300E:\000z7\270\357\005\000\000\000\000y8\r\004\200\\<\362\000\000\000\000\035s\350\017\000\000\000\000\000\000\000\000\000\000\000\375\030\374\241\000\000\000\000\260\220c\004\000\000\000\000\000\000\000\010n\364\337\010\002\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\017\177\000UW\201D\216\024\213\243\000\000\000\000IEND\256B`\202" diff --git a/core/res/geoid_height_map_assets/tile-9.textpb b/core/res/geoid_height_map_assets/tile-9.textpb new file mode 100644 index 0000000000000..5f23f1c934c95 --- /dev/null +++ b/core/res/geoid_height_map_assets/tile-9.textpb @@ -0,0 +1,3 @@ +tile_key: "9" +byte_jpeg: "\377\330\377\340\000\020JFIF\000\001\002\000\000\001\000\001\000\000\377\333\000C\000\004\003\003\003\003\002\004\003\003\003\004\004\004\004\005\t\006\005\005\005\005\013\010\010\007\t\r\014\016\016\r\014\r\r\017\020\025\022\017\020\024\020\r\r\022\031\022\024\026\026\027\030\027\016\022\032\034\032\027\033\025\027\027\027\377\300\000\013\010\002\000\002\000\001\001\021\000\377\304\000\037\000\000\001\005\001\001\001\001\001\001\000\000\000\000\000\000\000\000\001\002\003\004\005\006\007\010\t\n\013\377\304\000\265\020\000\002\001\003\003\002\004\003\005\005\004\004\000\000\001}\001\002\003\000\004\021\005\022!1A\006\023Qa\007\"q\0242\201\221\241\010#B\261\301\025R\321\360$3br\202\t\n\026\027\030\031\032%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\203\204\205\206\207\210\211\212\222\223\224\225\226\227\230\231\232\242\243\244\245\246\247\250\251\252\262\263\264\265\266\267\270\271\272\302\303\304\305\306\307\310\311\312\322\323\324\325\326\327\330\331\332\341\342\343\344\345\346\347\350\351\352\361\362\363\364\365\366\367\370\371\372\377\332\000\010\001\001\000\000?\000\363]\336\364\340\324\355\324\360\324\273\275\351wR\356\245\335F\352]\324\006\245\335K\272\224=<=\364\271\245\335I\2323I\232Ph\315!4\204\322SqHz\322R\032J(\244=i)\r%\024S\251GZZ^\203\212\017\245y\241\246\236\264S\201\2434f\215\324n\243u\033\251CR\356\245\rK\272\215\324\241\251\341\251\301\252@\325\"\265H\257R\253T\212\325 j\231Z\246V\251\225\251\341\252Ej\2245<5<5<5;u85.isK\2323I\237z3K\232ZC\322\222\212\017Ja\244\2444\224\277\303F8\244\357M\2444\224R\212Zp\035\251qA4\225\346\307\2554\323M.h\311\240\322f\223u\033\2517R\206\245\335K\272\215\324n\247\006\247\006\247\006\251\025\252Ej\221Z\245W\251\025\252Uj\231Z\246V\251U\252P\325 j\221Z\236\032\236\032\236\032\236\r8\032vh\006\234\r.i3\232)\324Q\232(\242\232zRc\212Jozu\024\230\246\221IHG4b\224S\251GJ;RRw\2578\"\230zSOJJQ\326\202i\247\25534g\024\231\245\335J\032\227w\275&\356iwR\206\247\006\247\006\247\253T\212\325 j\221^\245V\251\225\252Uj\235\032\246V\251\025\252@\325 j\2205<5<5<585<5\000\363N\315.h\315(9\247\nZ(\242\212C\326\222\220\322w\243\024\276\364\224\336\364\204RR\342\226\214sN\2444\224\206\274\351\226\230E0\212m\024SI\357M&\233\232i4n\245\315\031\243u\033\251\301\251\301\251CS\303S\303T\201\252Ej\225Z\246V\251\225\252tj\231Z\244V\251\003T\201\252Ej\2205<585<\032x4\354\322\203N\315\024\345\247\212Z(\002\235\212LSH\315\004RRc\232Z\017Jm!\244\242\212Q\353KG\322\220\364\2444\332\340\030TL*2)\204RR`\322S\017Ze!\353M\311\315\031\2434f\214\323\201\367\247f\2245<\032pj\221Z\244V\251U\252ej\231Z\246F\251\325\252Uj\2205H\255OV\251\003S\301\247\203O\006\244\006\236\r(4\240\323\207Zz\323\207Zu(\024\264b\227\024\334sA\036\224\230\244\305\030\244\305!\351IHE\000R\342\212?\032;R\0220\005!\244\315qR[\202*\253\302W5\021^qLe\250\210\244\246\232a\024\303\326\232i\244\322d\321\232L\321\232\\\323\201\342\234\r8\032P\325 j\221Z\246V\251U\252ej\235ML\255R\253T\212\325 j\221MH\r<\032\220\032x5\"\232x4\352p\351NZ\220t\247\216\264\243\255:\224\nZ\\\032LR\021HE&(\305!\244\"\232E%\024PO4\203\024\204\320x\2444\225\312\260`9\025\004\200zUy\"\005r*\263\306\303\255BG\250\246\221L#\232i\250\3150\323i\247\212nM\0314QN\006\234\r8\032p4\340jU5*\232\231MJ\246\247SS)\251T\324\212jU4\360jPi\340\324\200\324\212jE5 \245\035i\342\236\265 \247\216\224\340)\300S\261K\212\\Q\203I\217jB)1HE!\024\323M\244=i(\2444\322qKHOJJ+\005\221[\255V\222\337\236*\253\306\313P\260\004c\364\250\0361U\331\010\250\310\307Za\025\031\024\302)\204S\017ZB)(\242\212p\247S\201\247\212\221MH\246\246SS!\251\324\324\312jEy\025\013\307\336\253H\2305]\226\242d\246\025\357I\327\202)\254\274q\310\250Z y\025\031B)6Q\263\332\227e;e.\312P\264\340\264\340\264\340\265 Z\221V\244\013OQR\201R(\251\024T\200T\200T\200S\324T\252*E\025\"\212\221jU\025*\212\231EH\242\244QO\024\341N\002\227\024\332CM4\323\326\230z\323MFi\247\2456\233HE%\035\250\242\212P*\274\213P\024\346\230P\323\n\201\324\325i\016\032\2438\3175\004\221\345x\252\214\204\036\224\302\264\302\225\023%0\202\246\223h<\216\264\2058\344Tf<\036:Q\262\224%(Ojv\3126S\266R\204\247\004\251\002S\302\323\302\323\302\324\212*@*E\025 \024\360*@*@)\340T\200T\252*E\025*\212\225EJ\242\244Z\220S\307Ju:\212CM4\323M4\323Lja\353L=i\010\246\236\224\224QI\2121K\212*)x\340\212\211q\273\232\206C\211\010\252\316Npj6Nri\031A\025\001\0308\250d\217\034\342\242*)\214\225\021Nj6\2175\031B\017\024\014\3644\343\030#\"\243\tN\tJ\024\322\204\245\331K\266\224%<%<%8-<-<-8-<\n\221EH\005<\nx\024\360*@*E\025*\212\221EJ\242\245Z\221jE\247\216\224\372u8QM4\207\2554\365\246\236\264\224\303Q\232i\246\236\224\224\230\243\024c\024\224QED\322\007R\030sP\222\007j\211\260[4\306@Nj&C\217\245G\234q\212c&Nj7\\\212\200\246\r0\246i\0144\323\017\035*&\213\007\030\250\332:n\322)BR\354\245\013\212]\224\2418\245\tN\331N\tO\013\203\365\247l\245\tO\013N\013N\013R\001O\002\236\0058\nx\025 \025\"\212\220T\253R-H\265\"\324\203\265\224\230\000\340\323\202\203N\362\351\302*p\217\332\235\262\224\'\265;e8-.\332P\264\340\264\340\264\340\264\360)\300S\300\247\001O\002\244Z\221jE\251\026\244SO\025 \247\212x\247\nZ)\t\244\244=i)\010\246\032a\353M=i\244RRRQF\005\030\242\212\244\033\006\245\005O=(g^\346\243\336\244pj69\357Q0$T.\024s\232\214\340\365\250\331\027\034\032\214\216}\250\307\245\0035\"\323\360\017QH\320\206\034\n\200\304A\351Q\275\276\376q\3150\333\274|\365\251\221r\242\244\021\322\371t\241)vR\354\245\tK\262\215\224\241i\301i\300R\201N\3058\np\024\341\322\244\025 \353O\006\236)\340\324\200\323\301\251\001\247\203K\236i\331\346\214\322QE!\351Hi\207\255Fi\017Jm\030\246\342\220\212J\\\0321F)k=\206\rF\362`\361Q\027$u\246\207\3055\2459\316h\017\221\326\243\221\270\353P\022I\240\222\0055[-\203R\025\343\212P8\247\001\212x\031\025*\255)\213=\251\026<\036\224\366\2002\036*\233Fb~\234\032\224(#4\340\224\276].\3126R\354\244\333K\262\215\264m\245\305.)qN\002\224S\3058t\247\212x\353O\006\234\r<\032\221M<\032\220\032r\236?\032vh\242\212P))\rFi\206\220\322Rb\223\034\323OZ)pi(\245\301\254c!=\351\254sQ\223\212\215\236\243-G\231\212\215\244\367\244\017\315H\030b\231\362\347\212z\236*E\024\360\005=W\232\231W\212\224.i\301)\373\016\336\005E\265}\rZ\214\361S\253\nz\310wU\353i\317\335\'\203\326\247t\347\212\203\222q\212aZaZiCQ\224\244\362\351\336_\2651\222\241p\005B\330\024\302E\'Z]\242\223m\033)6\322\204\247\005\247\205\247\205\247\205\251\002\322\205\364\243i#\212R>jk-&\336)\010\246\342\215\264m\244\333F\3326\320\027\232x\024\354Q\212\361\021KNQN<\320\001\315J\243\024\026\347\002\225GzR\334\320\036\244\006\234\0175*5L\255\315=\210\331\305:\324\341\271\251\345|\200=\352H\217\000z\325\330\010\365\255\010\0161W\243aV\021\275\352da\334\322\356\332\335j\304\022\374\343\025\257\031\335\032\372\324r\000\262t\353M`1\300\244\021\3654\322\234\323\nS0\001\245!\233\356\2551\341\227\031\305S\225\033v\t\241l^E\316N)\255\247\310\017\006\253\2742\304y\024\320\344u\030\251\025\201\251\002\203K\262\227e8%8%8%<-<\201\322\223\234`p)@#\245\033i\010\246\342\220\255\033i\002\321\266\202\264\233h\333J\027\232pZv(\305xfx\245\034\232\224\014\nUZ\221W\034\232By\241W-\223Nf\002\243,)7\324\213\'\2758J;\324\211 \251\204\243\024\365}\334\036\225b2\001\251$\004\374\302\244\211\276\\\372\n\265\003|\243\326\264\241\223\003\232\264\257\357Vc\220c\232\235\037=\351Y\260*\345\202\231\033>\225\261\031\306)%;\345\030\355K\212\017J\215\210\024\314\0265*Z\344g\255L\220m\344\212\222DF\204\340r+<\331\263\276\342\274U\330\255\306\314t\252\363A\"\261\302\325W\210\267\004T/`\031zU\031m\236\022H\351Drs\203V\224\006\024\361\035;\313\245\021\322\354\245\333F\337j)pi\301A\316i\245i\002\322\021I\266\205\030\315\000PV\233\266\215\264\241y\247\205\243\036\324m\257\010=qSF\265!\000\nM\300R\206$R\214c\232kI\201\201Q4\224\3170Ry\235\351\004\2715\"\275X\212E\3163S#\215\306\254\306T\325\205\03052\260\306\323I\235\210G\255Y\265$\256\356\302\264\021\306\320j\304o\315YW\367\025b98\247\227\315i\351\262\000\370=\353m\024\021\305B\331\216R\r;p\306i\217\'aBD_\223\322\255Ch\314\303\002\264\343\264\n\200\021\315Ha\211W\346\003\036\365\023-\257#\201\364\252sK\020;W\265T{\215\247\212\221eyq\351S\010\021\307A\232\032\317\013\362\363\355U&\260.\016V\263&\323\231X\220*$G\214\340\203V\243\000\212\224G\236\324\276]!JiZB\264\005\245\333J\026\202\274Rm\244+I\266\225\000\004\344v\246\343&\215\264\205h\013K\267\232v\3326\373Q\212\360|sR\247\002\225\263MP\t\311\247\026\002\243i=\352&z\215\216EFO\035i\255/\030\024\325\230w\355R%\302\223\367\252T\235\025\303n\251V\343\315\227j\034z\325\350\337h\312\267N\325q%\3713N\206m\357\217J\222\342@\241W\2715r\311\307\220\300\325\244|\212\231d#\245X\216R\306\255\243\361\326\236$\253v\356\341\301\025\320Z\334\345Fz\325\321\373\316\253\232\014#\030T4\211a+\267\3345\243\006\237\267\357U\301\022\3047\020\024\n\257=\337d\374\352\234\227\016\343\004\325Ww\003\203T\345\363\013g5\036H<\325\250\'+\323\025h^\355`1\232\260\223\211\010\303c\332\264Da\243\031\003\006\242k\005q\300\252\027:Y\031!k5\355^&\310\034S\343\301\340\324\245\006)\205)\205)\245)\002S\266\322\205\315)^)\233x\244\333F\336)\241pM\001)J\323J\321\212]\264\270\024`R\021\315xW\227\212xJd\207\260\246tZ\215\230\232\211\211\355Q\222{\232\206K\200\274\016j\277\234X\362i\373\201Z\211\262M\002\244Z\225\030\243nS\315_\202|\340\356\347\275[78m\275\252[\031G\332y=j\325\331\315\322`\366\253p>\325\030\357V\221\30052\311R\244\240\032\231n;f\255\302wsZ18\000f\264\255\'\033\205t\266\222\304c\007\214\325\325x\313\177\016Gz\237\355\020D>f\014}\005!\274\014\270\216<\037Z\2554\354\331\004\325B3\336\220\250\2462\034Uy\"\366\252\355\037\265 B*@\274g\034\324\261\261\004`V\265\254\347\0001\343\336\264\342!\276\3575iaW]\256*\255\316\224\245\013 \315`^X<,]G\025^6\r\305I\2674\322\224\302\224\004\243m.\314PS\212n\312B\264m\342\230S\232]\270\024\323\212a#4\322\302\215\324\271\317j9\364\240+\032\361\027\307ji\'\030\002\243e\307Z\212C\306*\006p\242\240i\217j\202II\030\315Tv\346\242/\203OI\261Ro\3174\241\251\341\300\247\t*X\344\333\322\256+\003\026I\346\232\223\264ro\007\241\255X$7\r\346\236\303\212\272\317\2624a\334T\261M\236\365a$4\343.;\322\305)i\200\256\246\322\301\236\307\316V\343\275\013\033\007\301\253\261\025\214r\016kJ\332\342V!P\034V\325\2742\025\014\355VG\331\3429s\223\351I\347+\003\265\200\366\246\023\270\365\243h\245\002\202\0054\240#\245F\320\217Jg\222=*E\200\021\322\224Z\363\220*e\215\200\003\025\251g\300\003\241\2558\210\316\017\036\365eA\0318\315T\272\263Y\321\206\3203\\\205\375\263Z\\\222\007\031\346\226&\016\271\024\362\231\246\224\244\013N\010)\n\374\324\024\342\231\266\232@\315.\0050\201\326\242w\002\240,\314\330QR-\264\215\353N\373!\034\232h\207\234b\246[s\212w\221\3074\206\"+\303\n\214t\2466\007j\202F5VV\300\346\251\273\0265\023\032\256\344\324\016j\023H\rH\257\212~\372P\364\340\365*\275M\034\254F\301\326\233,\256\256\020\2163]&\237\264\332\241\007\034T\227/\202\251\355\232|,p*\3628\002\230\362|\325r\3220@c\326\272+K\341\024\001\030\344\016\324\246\377\0002|\240\001Z6\227\001\300\336\240\257\251\255\3536\265\333\271H\000u\346\247\270\324\342U\331\021\374j\232\334\226|\223\232\267\034\200\201V\220\347\275?\316\027\200\277\2454\333>y\030\245\020\0009\024\2065\035\251\245\000\346\223\217Ji\351M&\276~j\205\315T\230\374\265M\252\273\365\250\215D\3435\013\n\211\226\230V\233\212LR\032\001\247\203JMJ\035<\276G\"\2654\373\274\305\214\364\247\264\331\227\361\255\013w\312\212\260\317\362\324Q\313\211\263\232\330\266m\300du\255\2100\000\253\242s\267j\360;\323\225\271\253\010\325a$\025j)*\344r\032\265\034\206\247\022\236\306\244Y}MH$\247\t\005J\257\236\365b6\367\251\363\357NV\346\247V\310\342\246S\236\264\021\315i\351\363,N2kxH\346\264\"\2235j&\001\276aVF\037\356\212\2352\007j^Cf\254\305p\024`\212w\234\033\245H\262T\202\\w\247\t\271\353R\245\300\035\352u\270\007\241\251\222l\367\251\222Bj\314R\343\275h\303?\313VR_z\235\033#\255G%W\223(7V\236\223\250\260;\031\270\255\244\272\004\343=jF\270@\247\346\250~\324\203\223Q\276\245\0108\315T{\347\2238\r^\266\233\007\255^\023\345z\323D\234\365\251\222_z\230K\357O\022\217Zw\231G\230)C\363S\304\331\357Rn\301\353R\306\365n\'\253i\'\275L\263c\275Z\206~y5u\'\036\265*\312*t\224z\324\236h#\004\322n\310\353B\312\001\301\2512\010\3153\200x\247\t1N\337F\352ajajc53u&M.i;PFEA \"\230\016x\242\274h\2551\226\230T\323\031p*\0229\250\231j\026\025\013\n\201\305V\220T\014\265\021ZaZM\264\345L\324\241qP\312\241\252\253\333\206\311\305W6\1777J\r\263#q\305_\267Y\004@\023RyM\2735b\030\361\326\257Dv0\2558\\\025\004U\2255j\236)*\334oS\254\246\244\023s\326\247\212l\2663WQ\2163\232\262\222q\315J&\301\353R\t\271\251\004\271\350iKg\275H\262`Q\346\346\220\2759^\235\276\220\2654\2654\232i4\264\036\224\200\372\320x\2460\310\250\010\303R\366\346\274\200\2550\2451\226\242qP\262\234\324n8\252\3169\250\230T\016\265]\326\240e\250\331i\233h\331R*\342\221\316\007\025\0263\326\230\303\002\224\247\312\033\035i\2142\300T\312q\306)\306JzIV\243l\212\275l\3308\255\004\"\246SS+\212xq\353R\253T\201\370\244\336sO\017\305=$$\325\224\347\223R\371\201W\002\221d\313U\204\223\025a%\251\304\207\024\345rzT\251!W\255(\256r\243\245X\027\000\216\324\033\201\332\244\216|\367\253)(\251D\264\343%(n\371\240\276(\022\343\275H%\006\227\177\2754\2754\267\275(oZx4\036\224\323\301\243vF)\204\342\230\307\2757\020\000\000\000\000\000\000\000\000^\304\342\035\326\211\016\000\000\000\000\000\000\200\010<\375\005\000\000\000\340\331\334\203\002\000\000\000\000\000\200T<\"\004\200\261\271\226\003\000\367\314e\001\000\000\020\2121?\000\000\000\000\000\244\343\361\000\000@^^%\003\000\000\000\000\000\000\200x\254\r\005\000\000\000\310\306\033\"\000\000\000\221t2\313\353\3440X\241}\000\000\000\000\000\000\000\000\000\000\000\342\261V\034\000\000\000\000\000\000\000F\343i?\000\000\000\260`.\013F\342~G\003\357e\001\000\220\315\320\003B\216\023\000\220\222\256\317\250\334\306\000\000\000\340!\026\023<\215\252\005\000\000\010fj2\323k\262\023\000\000\000j\231\226A\004\303\364\344a\016\024\270x\r\207:2=\300~r\'_zy\177\377z\034\225q9\367r\032\235\253\254\336\016\034i\340#\333\322J\321\n\323\251\301x\346\337\002V\235\224\215\257=^\307\037\310I\241\001@^\337\343\002\343\203\r\277\257\311\273+k\210\013\372\356\263\312e\2106s\343D\376\021BJR@j\223\000HM\322g\233\034\001\231\271N \n\022x\340\303\277\242 0C=\000\000\310\304\014\200\277L\370\001\000\000\000\000\000\000\202\260$\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000.\276\324\010\000\000\000\000\000\000\000\000\000\000iYL\014\000\000\000\320\265\017\265\301<:\355\356j\346\000\000\000\000IEND\256B`\202" diff --git a/core/res/geoid_height_map_assets/tile-b.textpb b/core/res/geoid_height_map_assets/tile-b.textpb new file mode 100644 index 0000000000000..83d160bb7eb1f --- /dev/null +++ b/core/res/geoid_height_map_assets/tile-b.textpb @@ -0,0 +1,3 @@ +tile_key: "b" +byte_jpeg: "\377\330\377\340\000\020JFIF\000\001\002\000\000\001\000\001\000\000\377\333\000C\000\004\003\003\003\003\002\004\003\003\003\004\004\004\004\005\t\006\005\005\005\005\013\010\010\007\t\r\014\016\016\r\014\r\r\017\020\025\022\017\020\024\020\r\r\022\031\022\024\026\026\027\030\027\016\022\032\034\032\027\033\025\027\027\027\377\300\000\013\010\002\000\002\000\001\001\021\000\377\304\000\037\000\000\001\005\001\001\001\001\001\001\000\000\000\000\000\000\000\000\001\002\003\004\005\006\007\010\t\n\013\377\304\000\265\020\000\002\001\003\003\002\004\003\005\005\004\004\000\000\001}\001\002\003\000\004\021\005\022!1A\006\023Qa\007\"q\0242\201\221\241\010#B\261\301\025R\321\360$3br\202\t\n\026\027\030\031\032%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\203\204\205\206\207\210\211\212\222\223\224\225\226\227\230\231\232\242\243\244\245\246\247\250\251\252\262\263\264\265\266\267\270\271\272\302\303\304\305\306\307\310\311\312\322\323\324\325\326\327\330\331\332\341\342\343\344\345\346\347\350\351\352\361\362\363\364\365\366\367\370\371\372\377\332\000\010\001\001\000\000?\000fy\245\006\202sE\024R\346\224\0323\357J\r=Z\236\032\237\272\215\324\340\324n\243v(\335\223O\337\205\300\250\313\323K\323w\321\276\224?\2758=8I\357O\022\373\324\202S\353O\023{\323\274\363\353@\234\372\323\204\376\364\3617\024\34158M\357N\363\250\363\275\351<\336x5\007Z\201\256\000\357Q5\310\365\250\232\346\242k\234\036\264\326\273\000T/z\000\252\315xI\353W(\245\315&h\242\2274\240\321Fy\247\003O\rK\272\2245;u.\352Bis\201\3154\2654\2650\265&\3527Q\276\224=/\231N\022{\322\371\336\364y\336\364\3417\2758K\317Z\221X\232\224\023N\016iw\232w\231F\3727\324\261\315\203\326\254\254\331\034\032\014\240u5Z\361\326H\376\225EO\034S\213\361L/L-L\335\315)z7{\322o\244\335Aj\226.Nj\342\364\306i\030\250\352j\027\231W\275S\232\353<\003U\332R{\324fJas\353Q4\234\324M\'\275@\357P\274\230\255\343\322\212L\321\223Fi3\315.isFM\024\271\247\003J\032\2245(j]\324\273\271\241\245\312\201Q\227\246\026\244\335I\272\233\276\215\364\027\244\363(\363=\350\363=\351|\317zp\222\236\262sV\243\220c\232\233\314\024\236o4y\207\326\227}\033\350\337J$\346\245Y\215=\245\312\373\325Ig \021Q\243\344f\234_\212\211\237\007\2554\2757}\033\351wqM\335NV\024\027\311\342\254Dp*_4\001\301\252\323\\\340\340\034\325\'\230\261\316j2\3714\322i\t\305F\315P\273TE\252\'j\205\216k\244\244&\222\212L\322d\322\346\2234\271\243\"\234\017\024f\2274\271\245\335J\032\215\3704\302\324\322\364\302\324\233\251\013\322\026\246\357\244/I\276\220\275\'\231I\346sR\251$g4\355\3705*\334c\275J.\001\357N\363s\336\224K\317Z\220J=i\336e\033\370\244\337O\022S\314\270Z\257+\023\021\"\233\031\371E+5D\315L\337F\356h\335K\272\233\273\232P\324o\346\246\023`To9\307\006\253\226\'\2754\322\023M&\230Z\243f\250]\252\"\325\0235D\306\272|\322Rf\222\212L\321\232J2isFisK\2323K\272\215\334R\026\246\226\250\331\251\245\2517SK\322\027\246\227\246\227\244\337H^\230d\244\363=\352E\237\003\255)\237=\351<\337zp\233\035\351\353p}jD\234\223\326\247Y}\352A/\275LS6f\245D\253\010\274\325\250\305XE\251\225j@\265\"\245H\022\234\026\227e\033i\245)\245)\205)\214\265\033%l\342\220\365\246\232i\351IM\244=i\206\232\324\306\252\362Ui{TMP\265B\365\003t\250XT\rQ5@\342\252\310\265ZE\252\262\'5]\2435\023\302qP<-\351L\021\036\342\221\2414\337,\251\247\200qJ\026\224/5*\2558\246i|\274\361I\344R\030\202\324m\201NE\311\253\260\251\025q#\006\256\304\200\n\234\017J\231\"-W!\213\030\310\253\212\203\024\241y\247\375\237\214\212z\305\212xL\032\261\032\325\244J\235R\244T\251U)\341)\333)vRl\244\331HR\230R\243d\250\312V\241\024\334SH\246\322\021M=i\247\2554\323\032\243j\201\352\274\203\"\240j\211\252\027\250Z\241j\205\205D\302\240\220a\261U\335r*\273!\317JF\200\025\346\242hT\014\001P\274\031\355PI\t\003\245Wd\301\246\355\366\2441\322l\245\tJ\023\232\221V\244\013N\000R7\025^F\250\261\226\2531 \253H@\253\021\266M\\\214\234U\250\206H\255\030Pb\254*\363\305N\252H\245\010sV\241\036\264\366L\014\342\232\027&\254F\225i\022\246T\251U*@\265 JpJ]\224\233)6R\024\246\024\250\331*2\225|\214\322\021\212a\024\3029\244\"\233M\"\232zTl*6\250\034d\032\205\207\312j\006\034T,*&\025\013\n\205\226\242e\250XUy*0\205\207JC\0368\305F\311M\362\263L1\214\324\022\240\305Rh\362i\2060)\204Rl\247\004\245\tO\tN\333@^i\031x\252\322G\3151S\232\263\032\032\262\2203U\310\255\310\251\266\355\253V\300\223Z\221.\000\253H\271\253\010\265(\2175*\307\212~\323\212EL\265YD\305YE\251\325*EZ\220-<-<%\033(\331HR\220\2450\245F\311Q\262U\254sMaM\"\232E4\212f9\246\221L\"\232EF\302\241aU\334u\025\013\016*&\025\013\n\215\226\242e\342\240qP\260\250|\262\355\212\231cU^\225^A\223Q\371t\2458\250$\\\032\255(\310\252\254\274\324.\r0!\245\331K\266\234\022\237\262\224&i\306>:T.\0105\023.i\026\"Z\256E\017\265]\215@\034\212\260\244c\212\031rj\325\252\340\326\244``U\244P*d\0315i\023\212\220\n\230E\2713B\305\216\265*\246MN\211\212\230-J\253R*T\241)\301(\333F\312M\224\322\264\322\224\306J\211\222\244\"\220\364\246\032B)\270\246\221\3154\212a\024\302*6\250Z\240q\315@\302\242aQ0\250\330T-P\260\250\231sBFG4\327\004\234\n\217\313\311\240\246\005F\302\253\310*\264\211U\3319\250\212Rm\244\331\355N\021\323\266S\3262{T\253\007\265<\3041U\244\207\236\225\030\207\236\2252@\007j\235c\003\265J\"8\251\021\016jm\207\035*X\201\006\257\304\330\025ad\346\256C\223\212\275\022\2265a`=qV#\213\003\004PS\234b\225W\0252\212\225\0275:\245H\026\244\013N\333\355K\262\223e\033)\245)\nTl\225\023%\004qL\"\230E\024\332a\024\204SO\025\021\250\332\241aP\270\250\\TL*&\025\023\n\205\205D\313H\221nz\225\243\003 Ur\234\322m\2468\250Yj\007J\201\326\240d\250\314t\337+\332\227\312\247\010\371\251\226\337=\252d\203\007\245I\345\200*6^i\276V{S\01484\345\213\332\245HI=*\342[dt\251\222\320\223\322\246\373\031\364\245[2;T\202\022;T\261Bw\016+N\010\016\005i[B3\315h\010\227oJ_+\322\221\255\311\\\212\211\243\301\351OU\251\321juZ\221R\244\t\3058!=i\333}\2516Rl\244)M+Le\250\231*\034qM\"\230E6\232i\264\323M&\230j6\025\023\n\205\207\025\013\n\211\205D\302\242aP\262\323\031i\321.)\354\244\324-\035FV\242q\212\204\324N*\022\231\250\332:\217e!JQ\021=\252X\340\346\256\307\010\003\245\014\200\032c\n\204\2474\365N)\032<\232\226(3\332\255$\000v\253\221D1\322\256G\010\035\252u\205Oj\224Z\202:R}\217\332\245\216\317\236\225r86\216\225:\r\247\212\262\233\215N\243\0254k\220j\t#\347\212ENjeJ\235R\245T\247\205\247\005\245\333F\312M\224\205i\205j2\264\306Z\251\212i\024\322)\204SH\246\032i\250\233\351M\243\025\033\200\rB\302\241e\250\231j&Z\211\226\242e\250\233\212\2265\371A\365\251vqQ\272UvNj\027J\213\313\250\236:\217\313\244h\275\252?+\236\224\341\007=*U\200b\245X\200\355R\005\300\246:\324L*=\2715:E\232\231m\362zT\361\301\216\325e-\363\332\254G\001\035\252q\031\307J\232(\3115z8\370\351S\010A=*\304v\343\322\234\360\340p)b\267,\335*\362Z\355^EF\351\206\305M\022\374\265\033\246\r5W\232\231\022\254*\324\241)\301)v\322\355\243m\005i\245i\205i\205j6Z\240E7\024\204S\010\246\021L\"\232V\230\313L\305!\025\033\016*&\025\033\n\214\255F\311\362\346\241e\250\235}\005B`bjq\031\030\247\201Mu\342\2532\363Q:\361P\225\346\220\307\232i\217\024\206:o\225\317Jx\213\002\224&)\341i\016*7\250H\311\245D\346\256E\037\265\\\216\034\366\251\322\016zU\310\240\343\245N \366\247\213\177j\2328\000\355V\226>*x\342\315[H\260:S\274\235\307\245Z\202\330\001\234T\262(\013\212\244\351\226\251\025p\224\205i\2339\251Q*\302\245J\027\212pZ]\264\273h\333HV\232V\232V\243+Q\225\254\322(\3054\212a\024\302)\204Sq\355H\313Q\020sI\216*2*&\025\031ZaJa\217vqP\262SDY8\250\335v\266*`\277&M3o4:\374\265]\223\232a\2174\323\0057\313\305F\353\212\210\212r\250\247\034b\231\212\017\025\021&\214f\234#\315J\220\363\322\255\305\017N*\354q{U\310\240\317j\273\035\277\035*u\203\332\244\020q\322\225a\301\251\322/j\2368\361VU8\251#\217\236\225qW\013QH\271\252\355\035.\314\n\002Q\345\324\212\230\251UjP\264\355\264\273iv\321\266\220\2554\2550\2550\255FV\262qF)\010\246\021L\"\243\"\223\006\220\216*\"9\244#\212a\034\324L\275j2)\244S\031j2\264\335\274\323\014E\244\251\031p\270\246\005\346\224\257\025\003\245F\027\236\224\255\214T\014y\250^\242\3074\034\201H\001cR\210\270\246:\324$`\323\220d\325\250\342\317j\265\034\036\325i!\307j\263\034^\325z\010}\253B8\206:T\342/jp\217\332\236\"\030\351OX\371\251\204u\"%XH\275\252P\274SYj=\224\323\035\036].\316)\3018\251\025x\251\002\323\266\322\355\245\333K\266\232V\232V\232V\243e\250\331k\037\024\204SH\246\221M\"\230V\230E!\025\033\016i1L+Le\250\212\323H\244+L\333M+\315*\'\314N)\254\23757g4\2458\250Y9\250\33103Ud$\032\211\272T\014y\244\035iH\251#\0035+\260\013U]\362j2\244\232\236\030\211=+B(\270\351W#AV\022<\232\271\014#\214\212\275\024C\216*\342GV\222!\216\225\034\253\266\232\204\032\231W\232\235W\212\2268\376j\266\261\374\264\214\224\322\224\233)\241)\002sN\021\363\212_.\225W\025*\250\305.\332v\332]\264m\244+M+L+Q\262\324l\265\211\216i1I\212n)\010\246\025\250\310\244\3054\2554\2554\212\214\255FV\220\245!Jn\312n\316jA\036\027\2450\307I\345\320c\343\245W\2210j\007\373\265RE\346\253\270\250\212\346\223n)v\223NDjq\211\332\220[7qR-\267\265Y\216\034v\253q\307S\204\"\247\204sZ\021\216*\324Ur1V\025\260*)\262\325\002\202\032\256\3042\005YD\253\021\2475eG\0242\212\214\255\005i\212\275iU2jU\213&\245\020g\2651\241\332i\241i\301{\032xZ]\224m\244+M+L+Q\262\324L\265\204G4\204Rb\220\212i\024\322\264\302\264\322\264\205i\245i\205i\205i\2739\243g4\206:iJn\316je\217*8\246\264~\324\303\035&\312\215\341\310\315Q\2322\265M\305@\353\315F\313\201Qc-V\0220EN\221\214\325\225\211q\322\203\032\216\324\004\036\224\355\270\251b<\325\325\2140\247\254EM[\210g\025z$\253\013\201R\001O\330\010\250\2360\017\025$\\\032\270\207\212\235\030f\254\003\305\031\311\245\013\232d\230\007\003\255\"\247\024\364R\rYD\350juN8\244x\301\031\252\357\036\326\351H\027\'\024\340\0108\306i\341r3K\266\220\2550\255F\313Q\262\324L\265\201\267\2326\322\025\246\342\223m!ZiZiOJiJiZiZ\214\255\033)Dt\246>)\236_4\357\'\'\245J\"\343\030\246\264U\033G\201Ql\313S\231\000J\315\271\003&\263\344Z\254\303\232M\231\024\337(\206\351R\250\300\251QI5aT\342\227a4\340\270\245\333\232T\\5i[\256@\253~W\035*H\342 \364\253j0)\3435*\323\306iJ\023@\\\032\231I\305L\204\346\255\253|\264`\223R*\220\264\320\231l\232\225S\212~\312\231\007\0252\212~\316*9#\310\250<\262\r9W\007\353Rl\347\353K\262\232V\243+Q\225\250\331j&Z\347\361I\212B)1F\332n\332\n\323J\323J\323J\323\n\322l\245\tN\021\322\371t\323\025L\221a9\035i\302:kDOj\211\341\366\246yX\250g\000-d\3162\306\251\310\265\001\217&\234\261\322\262qQ\343\006\246L\n\260\244S\270\246\023\223OPML\221\222G\025\245m\021\002\257\254|T\252\200T\201i\301y\251R<\325\210\341\317j{F\024Tb<\232\225!>\225am\217\245L\260\221\332\245Xy\351S,9RMDS\006\236\242\244\331OE\251\321j]\237-4\245G\345rx\246\224\305I\260l\006\202\224\306Z\214\255F\313Q2\324,\265\317\201\232B\264\205i1F(\333HE&\332iZaZM\224l\247\010\371\247\004\247yt\253\026Z\246\020\322\3714\276P\250\244@*\234\244\016\225Br[5FH\3175Y\342>\224\317+\332\232W\006\220\256EG\263\232\221b5\"\304jC\031\3059!$\363V\222\337\212\261\034 \032\273\032\200*\302\324\203\232\225W5*\307\315Y\216>*\302\256\005#\246i\360\301\232\264\260\200zU\270\241\005zS\314 \036\224\205\000\246\223\306\321L\330M8&)\340S\224T\361\212\230\201\214SvR\204\310\243\312\366\2451b/\306\230V\230V\243e\250\231j\026Z\211\205s\252\275iv\323J\323\033\212i4d\322a\275)\t\"\2200&\234\0274\276]8%8G\3058GN\021\324\251\027\031\305L\261q\322\231 \333U\332J\255#\223U]KT\r\0175\023C\355U\236\036zTf/Z\257,X5\032\246E\036Q\317J\221W\035EL\212\t\351S\210\301\024\005\njtaS\'&\247U52\n\235\0275j4\342\246U\251\320T\200T\201s\332\247\215j\312\246j\334I\307JVZ\215\227\212\257\267.H\251\024qK\266\225V\237\264\324\211\305H94\375\274S\221j@\234S\214\177.*\273\246\030\212\214\255F\313P\262\324L*\026Z\347\302\361AZ\215\2054D\315\332\245Kbz\212\220[\250\2450\247LTF\321I\3105Zks\033dR\'\241\253\010\234T\236^{R\210\2151P\0203@Z\\R\355\366\245\031\247{T\361&\324\244?z\226\214\343\232\23662\014c\245\014y\250\235rs\212\205\327\232\205\226\241qP0\254D\217\332\246\330\000\245\347mFN:T26j\273\234\232\217\034\324\212\006i\314q\315\231\2517\340Rn;j>\364\365\034S\200\346\234\026\224(\247\205\251\024\361Mj^\324\224\344fS\305XD\3343\353H\361\372T/\037\025ZE\252\356*\026\025\226\020\n\033\000Tl\334\323\010\030\252\362\036qP56\236\240\322\271\302\323P\374\265j!\221VPT\230\312\323\271\306;RE\030\363\267U\203\362\256j\031\016\344>\265FH\216s\212\210\3061Qd`Q\346\307CN\270\342\253<\304\236)\201\234\236\264\361\0337Z\231\"\305N\0234\361\016MN\252\000\305<\001\236\265*6:T\301\262)\254\307\245\000T\213\322\234)\340S\361K\212\\Q\212A\311\305(\251\000\253\020\237\227\024\366\002\240q\201T\344\034\232\256\342\240qYg\326\243cP\261\250\331\316:\324\005\263Q\223\223OU\024\360\274S$\031\024@\001\340\325\304]\274\032\235*A\332\226\234\230\r\223Ng\312\221Q\036\225\023\255B\351P\262\324\016\206\241e\346\243e\250\032>j2\224\233h\333NQ\264\361W\240\223\200\rX\362\325\2057\311\247,52\251\003\212\033\177\255 \017\357O\010\306\236\261z\323\200Pi\301\261N\023\021R\307?8\"\247-\221\225\245\334M=sS\2408\251E?niB\323\200\251\024S\302\323\200\247\001K\217j6\322\025\346\225E=\006\346\305I\215\217\305H\033\"\242\223\221T\344\250\036\240z\310v\250]\207\255@\357P\226\3150\323@\346\245^\006i\301\201\246\311\323\002\240G)6\ri!\014\231\315H\254i\341\263N\3158\036)\001\000\340\367\251\004G\251\246I\036:Uv\025\023-B\313P\262\373T,*6Z\214\2557m&\332]\264\36485a%5/\232@\353J\'\247\211\351\353.i\342Oj\220?\035(\363N*=\304\232]\324\240\346\245QR\2432\237j\262\244\036\2252\n\260\202\245U\251\000\245\002\234\026\236\242\236\005<\nxZ6\321M\"\227\034P\207k\323\231\262\331\240H\0055\334Ui\030Uv\250\230V\023\2775\0035@\3074\322qM\243\275H:R\343\034\323O&\253\316\010\031\025%\265\321\306\322j\354r\203S\206\356i\300\203O\024\216\017\336\035E>+\257\340\224s\353R;\'\250\252\304d\323\035y\250Yj\026Z\205\226\242+L+L#\024\230\315\033h\003\232x\315<\014\365\247\005\346\234\026\244Q\212\225A\305H\277v\224\016(\333\315;fi\301*EZ\225W\"\244U \361Vc\351VR\247Q\305H\0058-<-<%8%=V\236\026\235\260R\024\024\322\224\230\342\233\201\236ha\305D\331\025\021j\215\216j6\250\315s,\365\0335GHM%&i\301\251\341\205!<\324\023\034\203U\341\037\275\255$\371qVQ\263R\017j\220\034S\263Q\355\033\263O\330\016\017B)\301N3Mt\004\006\025\023%B\311\355P:\373TL\276\325\031Z\215\226\233\266\214Q\212x\024\365\024\360\rH\250i\341)\341jE\025(A\212B\230\024\241i\341i\341jU\025\"\255J\203\025a*\302T\252*P)\340T\201i\301)\330\305L\";s\212B\234\321\267\212a\024\322\264\302\264\326\025\023T.8\250\215F\324\303\\\2514\302i\224\204\323K\201L2R\253\322\357\245\017\223L\220\361P!\333%]Y\tQVb~*un)\340\361N\rN\034\323\207Jz\212v\334\323\031=\252\026OJ\201\320\372T,\225\013-FV\233\266\223m\030\247\001O\002\236\243\232\260\200T\233(\333\216\364\345\025\"\212x\\\366\243e8-H\026\236\242\245U\251Ujd\025:\212\225\005N\242\244QR\252\324\252\224\2730\300\342\237\271\215!\004v\243\024\3229\340S\n\323J\323\031j\026Z\201\305BEF\325\033W$M4\232Bp*\007\224\016\225\t\2234\322\364\202LS\274\321\353J\262\363\326\234_\"\243|\343\212[I7K\261\217\322\265Tm\025\"\277cR\203OZ\220S\326\244\024\361H\325Ji\325\033\024\315\340\214\220y\244*\032\242t\364\025\tC\232aCM\332iBR\342\200)\353R\253\021R\253f\236)\352*eZ\220-8-(A\332\234\006:\323\302\324\252*@*D\353S(\251\220sV\020T\312\265*\245L\253\212p_\233\245+(+\221MbJ\340\323qHE&)\244Tl\265\023-Wu\250\030T,*3\\\205!\300\025^G\347\002\252\2719\353Q4\235\251\206CI\276\220\261\354i\310\374\363S\253\347\212\224`\255@\312VM\313V\241\273l\205sZ1\374\303\"\246^\005H\255\332\246\025\"\363R->\232\325\223{\362\236\007z\226\333\230\206j\177,\036\224\306\214\342\241h\315Fc\246\224\036\224\233)\n\321\266\227\024\341\305H\265*\232\225qS%J1J:\323\200\247S\200\364\251\026\245Z\221EL\242\246J\235\005Z\215j\302\'\025&\332\224\304\004Y\250\266\361Q\221M\243\006\212i\024\322*&Z\256\353U\335j\006Z\205\2075\310\036\265\034\215\205\252N\374\223U\235\262j#\326\232M3u\033\251\340\324\250\376\265b3\236\364\375\271\355L(T\346\246\216\351\343\030\006\264l\356\322S\206\340\326\232$L3\300\240\371j\247\221\305\n\300\3645(aK\277\212\202i\302\016\274\326l\322\033\211B\257AW!]\250\005N\016)H\343\232c-DS\332\230S\332\230V\220\255&\3326\320\0058\nx\251\024\324\252\3252\265<\032x4\341O\035*A\315\270\244#\330\322m\246\224\245\331Hc\3154\306})6\036\342\224\'\265=R\246E\251\224T\352=\252dZ\235S\326\236\253\371PS\272\322+s\203\326\245\013\334S\205H\246\244V\247\006\346\234\016h4\200\366\251Q\205<\340\366\250\310\346\233M#\212\215\252\027\031\035*\273\n\256\342\274\276Y2j\273\032\214\232a4\224\032i\351H1HM\033\252E5*\265N\246\246Z\235\rXCS\251\251\223\232\235@\333R)\340\np\315All locations generated through {@link LocationManager} are guaranteed to have a valid * latitude, longitude, timestamp (both Unix epoch time and elapsed realtime since boot), and * accuracy. All other parameters are optional. + * + *

Note that Android provides the ability for applications to submit "mock" or faked + * locations through {@link LocationManager}, and that these locations can then be received by + * applications using LocationManager to obtain location information. These locations can be + * identified via the {@link #isMock()} API. Applications that wish to determine if a given location + * represents the best estimate of the real position of the device as opposed to a fake location + * coming from another application or the user should use this API. Keep in mind that the user may + * have a good reason for mocking their location, and thus apps should generally reject mock + * locations only when it is essential to their use case that only real locations are accepted. */ public class Location implements Parcelable { @@ -95,6 +104,8 @@ public class Location implements Parcelable { private static final int HAS_SPEED_ACCURACY_MASK = 1 << 6; private static final int HAS_BEARING_ACCURACY_MASK = 1 << 7; private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK = 1 << 8; + private static final int HAS_MSL_ALTITUDE_MASK = 1 << 9; + private static final int HAS_MSL_ALTITUDE_ACCURACY_MASK = 1 << 10; // Cached data to make bearing/distance computations more efficient for the case // where distanceTo and bearingTo are called in sequence. Assume this typically happens @@ -118,6 +129,8 @@ public class Location implements Parcelable { private float mSpeedAccuracyMetersPerSecond; private float mBearingDegrees; private float mBearingAccuracyDegrees; + private double mMslAltitudeMeters; + private float mMslAltitudeAccuracyMeters; private Bundle mExtras = null; @@ -156,6 +169,8 @@ public class Location implements Parcelable { mSpeedAccuracyMetersPerSecond = location.mSpeedAccuracyMetersPerSecond; mBearingDegrees = location.mBearingDegrees; mBearingAccuracyDegrees = location.mBearingAccuracyDegrees; + mMslAltitudeMeters = location.mMslAltitudeMeters; + mMslAltitudeAccuracyMeters = location.mMslAltitudeAccuracyMeters; mExtras = (location.mExtras == null) ? null : new Bundle(location.mExtras); } @@ -178,6 +193,8 @@ public class Location implements Parcelable { mAltitudeAccuracyMeters = 0; mSpeedAccuracyMetersPerSecond = 0; mBearingAccuracyDegrees = 0; + mMslAltitudeMeters = 0; + mMslAltitudeAccuracyMeters = 0; mExtras = null; } @@ -689,6 +706,91 @@ public class Location implements Parcelable { mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK; } + /** + * Returns the Mean Sea Level altitude of this location in meters. + * + *

This is only valid if {@link #hasMslAltitude()} is true. + * + * @hide + */ + public @FloatRange double getMslAltitudeMeters() { + Preconditions.checkState(hasMslAltitude(), + "The Mean Sea Level altitude of this location is not set."); + return mMslAltitudeMeters; + } + + /** + * Sets the Mean Sea Level altitude of this location in meters. + * + * @hide + */ + public void setMslAltitudeMeters(@FloatRange double mslAltitudeMeters) { + mMslAltitudeMeters = mslAltitudeMeters; + mFieldsMask |= HAS_MSL_ALTITUDE_MASK; + } + + /** + * Returns true if this location has a Mean Sea Level altitude, false otherwise. + * + * @hide + */ + public boolean hasMslAltitude() { + return (mFieldsMask & HAS_MSL_ALTITUDE_MASK) != 0; + } + + /** + * Removes the Mean Sea Level altitude from this location. + * + * @hide + */ + public void removeMslAltitude() { + mFieldsMask &= ~HAS_MSL_ALTITUDE_MASK; + } + + /** + * Returns the estimated Mean Sea Level altitude accuracy in meters of this location at the 68th + * percentile confidence level. This means that there is 68% chance that the true Mean Sea Level + * altitude of this location falls within {@link #getMslAltitudeMeters()} +/- this uncertainty. + * + *

This is only valid if {@link #hasMslAltitudeAccuracy()} is true. + * + * @hide + */ + public @FloatRange(from = 0.0) float getMslAltitudeAccuracyMeters() { + Preconditions.checkState(hasMslAltitudeAccuracy(), + "The Mean Sea Level altitude accuracy of this location is not set."); + return mMslAltitudeAccuracyMeters; + } + + /** + * Sets the Mean Sea Level altitude accuracy of this location in meters. + * + * @hide + */ + public void setMslAltitudeAccuracyMeters( + @FloatRange(from = 0.0) float mslAltitudeAccuracyMeters) { + mMslAltitudeAccuracyMeters = mslAltitudeAccuracyMeters; + mFieldsMask |= HAS_MSL_ALTITUDE_ACCURACY_MASK; + } + + /** + * Returns true if this location has a Mean Sea Level altitude accuracy, false otherwise. + * + * @hide + */ + public boolean hasMslAltitudeAccuracy() { + return (mFieldsMask & HAS_MSL_ALTITUDE_ACCURACY_MASK) != 0; + } + + /** + * Removes the Mean Sea Level altitude accuracy from this location. + * + * @hide + */ + public void removeMslAltitudeAccuracy() { + mFieldsMask &= ~HAS_MSL_ALTITUDE_ACCURACY_MASK; + } + /** * Returns true if this is a mock location. If this location comes from the Android framework, * this indicates that the location was provided by a test location provider, and thus may not @@ -839,6 +941,14 @@ public class Location implements Parcelable { && hasBearingAccuracy() == location.hasBearingAccuracy() && (!hasBearingAccuracy() || Float.compare(location.mBearingAccuracyDegrees, mBearingAccuracyDegrees) == 0) + && hasMslAltitude() == location.hasMslAltitude() + && (!hasMslAltitude() || Double.compare(location.mMslAltitudeMeters, + mMslAltitudeMeters) + == 0) + && hasMslAltitudeAccuracy() == location.hasMslAltitudeAccuracy() + && (!hasMslAltitudeAccuracy() || Float.compare( + location.mMslAltitudeAccuracyMeters, + mMslAltitudeAccuracyMeters) == 0) && Objects.equals(mProvider, location.mProvider) && areExtrasEqual(mExtras, location.mExtras); } @@ -876,6 +986,12 @@ public class Location implements Parcelable { s.append(" vAcc=").append(mAltitudeAccuracyMeters); } } + if (hasMslAltitude()) { + s.append(" mslAlt=").append(mMslAltitudeMeters); + if (hasMslAltitudeAccuracy()) { + s.append(" mslAltAcc=").append(mMslAltitudeAccuracyMeters); + } + } if (hasSpeed()) { s.append(" vel=").append(mSpeedMetersPerSecond); if (hasSpeedAccuracy()) { @@ -944,6 +1060,12 @@ public class Location implements Parcelable { if (l.hasBearingAccuracy()) { l.mBearingAccuracyDegrees = in.readFloat(); } + if (l.hasMslAltitude()) { + l.mMslAltitudeMeters = in.readDouble(); + } + if (l.hasMslAltitudeAccuracy()) { + l.mMslAltitudeAccuracyMeters = in.readFloat(); + } l.mExtras = Bundle.setDefusable(in.readBundle(), true); return l; } @@ -991,6 +1113,12 @@ public class Location implements Parcelable { if (hasBearingAccuracy()) { parcel.writeFloat(mBearingAccuracyDegrees); } + if (hasMslAltitude()) { + parcel.writeDouble(mMslAltitudeMeters); + } + if (hasMslAltitudeAccuracy()) { + parcel.writeFloat(mMslAltitudeAccuracyMeters); + } parcel.writeBundle(mExtras); } diff --git a/location/java/android/location/altitude/AltitudeConverter.java b/location/java/android/location/altitude/AltitudeConverter.java new file mode 100644 index 0000000000000..74ab4034ae80a --- /dev/null +++ b/location/java/android/location/altitude/AltitudeConverter.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.altitude; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; +import android.content.Context; +import android.location.Location; + +import com.android.internal.location.altitude.GeoidHeightMap; +import com.android.internal.location.altitude.S2CellIdUtils; +import com.android.internal.location.altitude.nano.MapParamsProto; +import com.android.internal.util.Preconditions; + +import java.io.IOException; + +/** + * Converts altitudes reported above the World Geodetic System 1984 (WGS84) reference ellipsoid + * into ones above Mean Sea Level. + * + * @hide + */ +public final class AltitudeConverter { + + private static final double MAX_ABS_VALID_LATITUDE = 90; + private static final double MAX_ABS_VALID_LONGITUDE = 180; + + /** Manages a mapping of geoid heights associated with S2 cells. */ + private final GeoidHeightMap mGeoidHeightMap = new GeoidHeightMap(); + + /** + * Creates an instance that manages an independent cache to optimized conversions of locations + * in proximity to one another. + * + * @hide + */ + public AltitudeConverter() { + } + + /** + * Throws an {@link IllegalArgumentException} if the {@code location} has an invalid latitude, + * longitude, or altitude above WGS84. + */ + private static void validate(@NonNull Location location) { + Preconditions.checkArgument( + isFiniteAndAtAbsMost(location.getLatitude(), MAX_ABS_VALID_LATITUDE), + "Invalid latitude: %f", location.getLatitude()); + Preconditions.checkArgument( + isFiniteAndAtAbsMost(location.getLongitude(), MAX_ABS_VALID_LONGITUDE), + "Invalid longitude: %f", location.getLongitude()); + Preconditions.checkArgument(location.hasAltitude(), "Missing altitude above WGS84"); + Preconditions.checkArgument(Double.isFinite(location.getAltitude()), + "Invalid altitude above WGS84: %f", location.getAltitude()); + } + + private static boolean isFiniteAndAtAbsMost(double value, double rhs) { + return Double.isFinite(value) && Math.abs(value) <= rhs; + } + + /** + * Returns the four S2 cell IDs for the map square associated with the {@code location}. + * + *

The first map cell contains the location, while the others are located horizontally, + * vertically, and diagonally, in that order, with respect to the S2 (i,j) coordinate system. If + * the diagonal map cell does not exist (i.e., the location is near an S2 cube vertex), its + * corresponding ID is set to zero. + */ + @NonNull + private static long[] findMapSquare(@NonNull MapParamsProto params, + @NonNull Location location) { + long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(), + location.getLongitude()); + + // (0,0) cell. + long s0 = S2CellIdUtils.getParent(s2CellId, params.mapS2Level); + long[] edgeNeighbors = new long[4]; + S2CellIdUtils.getEdgeNeighbors(s0, edgeNeighbors); + + // (1,0) cell. + int i1 = S2CellIdUtils.getI(s2CellId) > S2CellIdUtils.getI(s0) ? -1 : 1; + long s1 = edgeNeighbors[i1 + 2]; + + // (0,1) cell. + int i2 = S2CellIdUtils.getJ(s2CellId) > S2CellIdUtils.getJ(s0) ? 1 : -1; + long s2 = edgeNeighbors[i2 + 1]; + + // (1,1) cell. + S2CellIdUtils.getEdgeNeighbors(s1, edgeNeighbors); + long s3 = 0; + for (int i = 0; i < edgeNeighbors.length; i++) { + if (edgeNeighbors[i] == s0) { + int i3 = (i + i1 * i2 + edgeNeighbors.length) % edgeNeighbors.length; + s3 = edgeNeighbors[i3] == s2 ? 0 : edgeNeighbors[i3]; + break; + } + } + + // Reuse edge neighbors' array to avoid an extra allocation. + edgeNeighbors[0] = s0; + edgeNeighbors[1] = s1; + edgeNeighbors[2] = s2; + edgeNeighbors[3] = s3; + return edgeNeighbors; + } + + /** + * Adds to {@code location} the bilinearly interpolated Mean Sea Level altitude. In addition, a + * Mean Sea Level altitude accuracy is added if the {@code location} has a valid vertical + * accuracy; otherwise, does not add a corresponding accuracy. + */ + private static void addMslAltitude(@NonNull MapParamsProto params, @NonNull long[] s2CellIds, + @NonNull double[] geoidHeightsMeters, @NonNull Location location) { + long s0 = s2CellIds[0]; + double h0 = geoidHeightsMeters[0]; + double h1 = geoidHeightsMeters[1]; + double h2 = geoidHeightsMeters[2]; + double h3 = s2CellIds[3] == 0 ? h0 : geoidHeightsMeters[3]; + + // Bilinear interpolation on an S2 square of size equal to that of a map cell. wi and wj + // are the normalized [0,1] weights in the i and j directions, respectively, allowing us to + // employ the simplified unit square formulation. + long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(), + location.getLongitude()); + double sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.mapS2Level); + double wi = Math.abs(S2CellIdUtils.getI(s2CellId) - S2CellIdUtils.getI(s0)) / sizeIj; + double wj = Math.abs(S2CellIdUtils.getJ(s2CellId) - S2CellIdUtils.getJ(s0)) / sizeIj; + double offsetMeters = h0 + (h1 - h0) * wi + (h2 - h0) * wj + (h3 - h1 - h2 + h0) * wi * wj; + + location.setMslAltitudeMeters(location.getAltitude() - offsetMeters); + if (location.hasVerticalAccuracy()) { + double verticalAccuracyMeters = location.getVerticalAccuracyMeters(); + if (Double.isFinite(verticalAccuracyMeters) && verticalAccuracyMeters >= 0) { + location.setMslAltitudeAccuracyMeters( + (float) Math.hypot(verticalAccuracyMeters, params.modelRmseMeters)); + } + } + } + + /** + * Adds a Mean Sea Level altitude to the {@code location}. In addition, adds a Mean Sea Level + * altitude accuracy if the {@code location} has a finite and non-negative vertical accuracy; + * otherwise, does not add a corresponding accuracy. + * + *

Must be called off the main thread as data may be loaded from raw assets. + * + * @throws IOException if an I/O error occurs when loading data from raw assets. + * @throws IllegalArgumentException if the {@code location} has an invalid latitude, longitude, + * or altitude above WGS84. Specifically, the latitude must be + * between -90 and 90 (both inclusive), the longitude must be + * between -180 and 180 (both inclusive), and the altitude + * above WGS84 must be finite. + * + * @hide + */ + @WorkerThread + public void addMslAltitudeToLocation(@NonNull Context context, @NonNull Location location) + throws IOException { + validate(location); + MapParamsProto params = GeoidHeightMap.getParams(context); + long[] s2CellIds = findMapSquare(params, location); + double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, context, s2CellIds); + addMslAltitude(params, s2CellIds, geoidHeightsMeters, location); + } + + /** + * Same as {@link #addMslAltitudeToLocation(Context, Location)} except that data will not be + * loaded from raw assets. Returns true if a Mean Sea Level altitude is added to the + * {@code location}; otherwise, returns false and leaves the {@code location} unchanged. + * + * @hide + */ + public boolean addMslAltitudeToLocation(@NonNull Location location) { + validate(location); + MapParamsProto params = GeoidHeightMap.getParams(); + if (params == null) { + return false; + } + + long[] s2CellIds = findMapSquare(params, location); + double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, s2CellIds); + if (geoidHeightsMeters == null) { + return false; + } + + addMslAltitude(params, s2CellIds, geoidHeightsMeters, location); + return true; + } +} diff --git a/location/java/com/android/internal/location/altitude/GeoidHeightMap.java b/location/java/com/android/internal/location/altitude/GeoidHeightMap.java new file mode 100644 index 0000000000000..73b6ab556ad07 --- /dev/null +++ b/location/java/com/android/internal/location/altitude/GeoidHeightMap.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location.altitude; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.LruCache; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.location.altitude.nano.MapParamsProto; +import com.android.internal.location.altitude.nano.S2TileProto; +import com.android.internal.util.Preconditions; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Objects; + +/** + * Manages a mapping of geoid heights associated with S2 cells, referred to as MAP CELLS. + * + *

Tiles are used extensively to reduce the number of entries needed to be stored in memory and + * on disk. A tile associates geoid heights with all map cells of a common parent at a specified S2 + * level. + * + *

Since bilinear interpolation considers at most four map cells at a time, at most four tiles + * are simultaneously stored in memory. These tiles, referred to as CACHE TILES, are each keyed by + * its common parent's S2 cell ID, referred to as a CACHE KEY. + * + *

Absent cache tiles needed for interpolation are constructed from larger tiles stored on disk. + * The latter tiles, referred to as DISK TILES, are each keyed by its common parent's S2 cell token, + * referred to as a DISK TOKEN. + */ +public final class GeoidHeightMap { + + private static final Object sLock = new Object(); + + @GuardedBy("sLock") + @Nullable + private static MapParamsProto sParams; + + /** Defines a cache large enough to hold all cache tiles needed for interpolation. */ + private final LruCache mCacheTiles = new LruCache<>(4); + + /** + * Returns the singleton parameter instance for a spherically projected geoid height map and its + * corresponding tile management. + */ + @NonNull + public static MapParamsProto getParams(@NonNull Context context) throws IOException { + synchronized (sLock) { + if (sParams == null) { + try (InputStream is = context.getApplicationContext().getAssets().open( + "geoid_height_map/map-params.pb")) { + sParams = MapParamsProto.parseFrom(is.readAllBytes()); + } + } + return sParams; + } + } + + /** + * Same as {@link #getParams(Context)} except that null is returned if the singleton parameter + * instance is not yet initialized. + */ + @Nullable + public static MapParamsProto getParams() { + synchronized (sLock) { + return sParams; + } + } + + private static long getCacheKey(@NonNull MapParamsProto params, long s2CellId) { + return S2CellIdUtils.getParent(s2CellId, params.cacheTileS2Level); + } + + @NonNull + private static String getDiskToken(@NonNull MapParamsProto params, long s2CellId) { + return S2CellIdUtils.getToken( + S2CellIdUtils.getParent(s2CellId, params.diskTileS2Level)); + } + + /** + * Adds to {@code values} values in the unit interval [0, 1] for the map cells identified by + * {@code s2CellIds}. Returns true if values are present for all non-zero IDs; otherwise, + * returns false and adds NaNs for absent values. + */ + private static boolean getUnitIntervalValues(@NonNull MapParamsProto params, + @NonNull TileFunction tileFunction, + @NonNull long[] s2CellIds, @NonNull double[] values) { + int len = s2CellIds.length; + + S2TileProto[] tiles = new S2TileProto[len]; + for (int i = 0; i < len; i++) { + if (s2CellIds[i] != 0) { + long cacheKey = getCacheKey(params, s2CellIds[i]); + tiles[i] = tileFunction.getTile(cacheKey); + } + values[i] = Double.NaN; + } + + for (int i = 0; i < len; i++) { + if (tiles[i] == null || !Double.isNaN(values[i])) { + continue; + } + + mergeByteBufferValues(params, s2CellIds, tiles, i, values); + mergeByteJpegValues(params, s2CellIds, tiles, i, values); + mergeBytePngValues(params, s2CellIds, tiles, i, values); + } + + boolean allFound = true; + for (int i = 0; i < len; i++) { + if (s2CellIds[i] == 0) { + continue; + } + if (Double.isNaN(values[i])) { + allFound = false; + } else { + values[i] = (((int) values[i]) & 0xFF) / 255.0; + } + } + return allFound; + } + + @SuppressWarnings("ReferenceEquality") + private static void mergeByteBufferValues(@NonNull MapParamsProto params, + @NonNull long[] s2CellIds, + @NonNull S2TileProto[] tiles, + int tileIndex, @NonNull double[] values) { + byte[] bytes = tiles[tileIndex].byteBuffer; + if (bytes == null || bytes.length == 0) { + return; + } + + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).asReadOnlyBuffer(); + int tileS2Level = params.mapS2Level - Integer.numberOfTrailingZeros(byteBuffer.limit()) / 2; + int numBitsLeftOfTile = 2 * tileS2Level + 3; + + for (int i = tileIndex; i < tiles.length; i++) { + if (tiles[i] != tiles[tileIndex]) { + continue; + } + + long maskedS2CellId = s2CellIds[i] & (-1L >>> numBitsLeftOfTile); + int numBitsRightOfMap = 2 * (S2CellIdUtils.MAX_LEVEL - params.mapS2Level) + 1; + int bufferIndex = (int) (maskedS2CellId >>> numBitsRightOfMap); + values[i] = Double.isNaN(values[i]) ? 0 : values[i]; + values[i] += ((int) byteBuffer.get(bufferIndex)) & 0xFF; + } + } + + private static void mergeByteJpegValues(@NonNull MapParamsProto params, + @NonNull long[] s2CellIds, + @NonNull S2TileProto[] tiles, + int tileIndex, @NonNull double[] values) { + mergeByteImageValues(params, tiles[tileIndex].byteJpeg, s2CellIds, tiles, tileIndex, + values); + } + + private static void mergeBytePngValues(@NonNull MapParamsProto params, + @NonNull long[] s2CellIds, + @NonNull S2TileProto[] tiles, + int tileIndex, @NonNull double[] values) { + mergeByteImageValues(params, tiles[tileIndex].bytePng, s2CellIds, tiles, tileIndex, values); + } + + @SuppressWarnings("ReferenceEquality") + private static void mergeByteImageValues(@NonNull MapParamsProto params, @NonNull byte[] bytes, + @NonNull long[] s2CellIds, + @NonNull S2TileProto[] tiles, int tileIndex, @NonNull double[] values) { + if (bytes == null || bytes.length == 0) { + return; + } + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + if (bitmap == null) { + return; + } + + for (int i = tileIndex; i < tiles.length; i++) { + if (s2CellIds[i] == 0 || tiles[i] != tiles[tileIndex]) { + continue; + } + + values[i] = Double.isNaN(values[i]) ? 0 : values[i]; + values[i] += bitmap.getPixel(getIndexX(params, s2CellIds[i], bitmap.getWidth()), + getIndexY(params, s2CellIds[i], bitmap.getHeight())) & 0xFF; + } + } + + /** Returns the X index for an S2 cell within an S2 tile image of specified width. */ + private static int getIndexX(@NonNull MapParamsProto params, long s2CellId, int width) { + return getIndexXOrY(params, S2CellIdUtils.getI(s2CellId), width); + } + + /** Returns the Y index for an S2 cell within an S2 tile image of specified height. */ + private static int getIndexY(@NonNull MapParamsProto params, long s2CellId, int height) { + return getIndexXOrY(params, S2CellIdUtils.getJ(s2CellId), height); + } + + private static int getIndexXOrY(@NonNull MapParamsProto params, int iOrJ, int widthOrHeight) { + return (iOrJ >> (S2CellIdUtils.MAX_LEVEL - params.mapS2Level)) % widthOrHeight; + } + + /** + * Throws an {@link IllegalArgumentException} if the {@code s2CellIds} has an invalid length or + * ID. + */ + private static void validate(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) { + Preconditions.checkArgument(s2CellIds.length == 4); + for (long s2CellId : s2CellIds) { + Preconditions.checkArgument( + s2CellId == 0 || S2CellIdUtils.getLevel(s2CellId) == params.mapS2Level); + } + } + + /** + * Returns the geoid heights in meters associated with the map cells identified by + * {@code s2CellIds}. Throws an {@link IOException} if a geoid height cannot be calculated for a + * non-zero ID. + */ + @NonNull + public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context, + @NonNull long[] s2CellIds) throws IOException { + validate(params, s2CellIds); + double[] heightsMeters = new double[s2CellIds.length]; + if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) { + return heightsMeters; + } + + TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds); + if (getGeoidHeights(params, loadedTiles, s2CellIds, heightsMeters)) { + return heightsMeters; + } + throw new IOException("Unable to calculate geoid heights from raw assets."); + } + + /** + * Same as {@link #readGeoidHeights(MapParamsProto, Context, long[])} except that data will not + * be loaded from raw assets. Returns the heights if present for all non-zero IDs; otherwise, + * returns null. + */ + @Nullable + public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) { + validate(params, s2CellIds); + double[] heightsMeters = new double[s2CellIds.length]; + if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) { + return heightsMeters; + } + return null; + } + + /** + * Adds to {@code heightsMeters} the geoid heights in meters associated with the map cells + * identified by {@code s2CellIds}. Returns true if heights are present for all non-zero IDs; + * otherwise, returns false and adds NaNs for absent heights. + */ + private boolean getGeoidHeights(@NonNull MapParamsProto params, + @NonNull TileFunction tileFunction, @NonNull long[] s2CellIds, + @NonNull double[] heightsMeters) { + boolean allFound = getUnitIntervalValues(params, tileFunction, s2CellIds, heightsMeters); + for (int i = 0; i < heightsMeters.length; i++) { + // NaNs are properly preserved. + heightsMeters[i] *= params.modelAMeters; + heightsMeters[i] += params.modelBMeters; + } + return allFound; + } + + @NonNull + private TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params, + @NonNull Context context, @NonNull long[] s2CellIds) throws IOException { + int len = s2CellIds.length; + + // Enable batch loading by finding all cache keys upfront. + long[] cacheKeys = new long[len]; + for (int i = 0; i < len; i++) { + if (s2CellIds[i] == 0) { + continue; + } + cacheKeys[i] = getCacheKey(params, s2CellIds[i]); + } + + // Attempt to load tiles from cache. + S2TileProto[] loadedTiles = new S2TileProto[len]; + String[] diskTokens = new String[len]; + for (int i = 0; i < len; i++) { + if (s2CellIds[i] == 0 || diskTokens[i] != null) { + continue; + } + loadedTiles[i] = mCacheTiles.get(cacheKeys[i]); + diskTokens[i] = getDiskToken(params, cacheKeys[i]); + + // Batch across common cache key. + for (int j = i + 1; j < len; j++) { + if (cacheKeys[j] == cacheKeys[i]) { + loadedTiles[j] = loadedTiles[i]; + diskTokens[j] = diskTokens[i]; + } + } + } + + // Attempt to load tiles from disk. + for (int i = 0; i < len; i++) { + if (s2CellIds[i] == 0 || loadedTiles[i] != null) { + continue; + } + + S2TileProto tile; + try (InputStream is = context.getApplicationContext().getAssets().open( + "geoid_height_map/tile-" + diskTokens[i] + ".pb")) { + tile = S2TileProto.parseFrom(is.readAllBytes()); + } + mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles); + } + + return cacheKey -> { + for (int i = 0; i < cacheKeys.length; i++) { + if (cacheKeys[i] == cacheKey) { + return loadedTiles[i]; + } + } + return null; + }; + } + + private void mergeFromDiskTile(@NonNull MapParamsProto params, @NonNull S2TileProto diskTile, + @NonNull long[] cacheKeys, @NonNull String[] diskTokens, int diskTokenIndex, + @NonNull S2TileProto[] loadedTiles) throws IOException { + int len = cacheKeys.length; + int numMapCellsPerCacheTile = 1 << (2 * (params.mapS2Level - params.cacheTileS2Level)); + + // Reusable arrays. + long[] s2CellIds = new long[numMapCellsPerCacheTile]; + double[] values = new double[numMapCellsPerCacheTile]; + + // Each cache key identifies a different sub-tile of the same disk tile. + TileFunction diskTileFunction = cacheKey -> diskTile; + for (int i = diskTokenIndex; i < len; i++) { + if (!Objects.equals(diskTokens[i], diskTokens[diskTokenIndex]) + || loadedTiles[i] != null) { + continue; + } + + // Find all map cells within the current cache tile. + long s2CellId = S2CellIdUtils.getTraversalStart(cacheKeys[i], params.mapS2Level); + for (int j = 0; j < numMapCellsPerCacheTile; j++) { + s2CellIds[j] = s2CellId; + s2CellId = S2CellIdUtils.getTraversalNext(s2CellId); + } + + if (!getUnitIntervalValues(params, diskTileFunction, s2CellIds, values)) { + throw new IOException("Corrupted disk tile of disk token: " + diskTokens[i]); + } + + loadedTiles[i] = new S2TileProto(); + loadedTiles[i].byteBuffer = new byte[numMapCellsPerCacheTile]; + for (int j = 0; j < numMapCellsPerCacheTile; j++) { + loadedTiles[i].byteBuffer[j] = (byte) Math.round(values[j] * 0xFF); + } + + // Batch across common cache key. + for (int j = i + 1; j < len; j++) { + if (cacheKeys[j] == cacheKeys[i]) { + loadedTiles[j] = loadedTiles[i]; + } + } + + // Side load into tile cache. + mCacheTiles.put(cacheKeys[i], loadedTiles[i]); + } + } + + /** Defines a function-like object to retrieve tiles for cache keys. */ + private interface TileFunction { + + @Nullable + S2TileProto getTile(long cacheKey); + } +} diff --git a/location/java/com/android/internal/location/altitude/S2CellIdUtils.java b/location/java/com/android/internal/location/altitude/S2CellIdUtils.java new file mode 100644 index 0000000000000..5f113877d4161 --- /dev/null +++ b/location/java/com/android/internal/location/altitude/S2CellIdUtils.java @@ -0,0 +1,653 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location.altitude; + +import android.annotation.NonNull; + +import java.util.Arrays; +import java.util.Locale; + +/** + * Provides lightweight S2 cell ID utilities without traditional geometry dependencies. + * + *

See the S2 Geometry Library website for more details. + */ +public final class S2CellIdUtils { + + /** The level of all leaf S2 cells. */ + public static final int MAX_LEVEL = 30; + + private static final int MAX_SIZE = 1 << MAX_LEVEL; + private static final double ONE_OVER_MAX_SIZE = 1.0 / MAX_SIZE; + private static final int NUM_FACES = 6; + private static final int POS_BITS = 2 * MAX_LEVEL + 1; + private static final int SWAP_MASK = 0x1; + private static final int LOOKUP_BITS = 4; + private static final int LOOKUP_MASK = (1 << LOOKUP_BITS) - 1; + private static final int INVERT_MASK = 0x2; + private static final int LEAF_MASK = 0x1; + private static final int[] LOOKUP_POS = new int[1 << (2 * LOOKUP_BITS + 2)]; + private static final int[] LOOKUP_IJ = new int[1 << (2 * LOOKUP_BITS + 2)]; + private static final int[] POS_TO_ORIENTATION = {SWAP_MASK, 0, 0, INVERT_MASK + SWAP_MASK}; + private static final int[][] POS_TO_IJ = + {{0, 1, 3, 2}, {0, 2, 3, 1}, {3, 2, 0, 1}, {3, 1, 0, 2}}; + private static final double UV_LIMIT = calculateUvLimit(); + private static final UvTransform[] UV_TRANSFORMS = createUvTransforms(); + private static final XyzTransform[] XYZ_TRANSFORMS = createXyzTransforms(); + + // Used to encode (i, j, o) coordinates into primitive longs. + private static final int I_SHIFT = 33; + private static final int J_SHIFT = 2; + private static final long J_MASK = (1L << 31) - 1; + + static { + initLookupCells(); + } + + /** Prevents instantiation. */ + private S2CellIdUtils() { + } + + /** + * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in + * degrees. + */ + public static long fromLatLngDegrees(double latDegrees, double lngDegrees) { + return fromLatLngRadians(Math.toRadians(latDegrees), Math.toRadians(lngDegrees)); + } + + /** + * Returns the ID of the parent of the specified S2 cell at the specified parent level. + * Behavior is undefined for invalid S2 cell IDs or parent levels not in + * [0, {@code getLevel(s2CellId)}[. + */ + public static long getParent(long s2CellId, int level) { + long newLsb = getLowestOnBitForLevel(level); + return (s2CellId & -newLsb) | newLsb; + } + + /** + * Inserts into {@code neighbors} the four S2 cell IDs corresponding to the neighboring + * cells adjacent across the specified cell's four edges. This array must be of minimum + * length four, and elements at the tail end of the array not corresponding to a neighbor + * are set to zero. A reference to this array is returned. + * + *

Inserts in the order of down, right, up, and left directions, in that order. All + * neighbors are guaranteed to be distinct. + */ + public static void getEdgeNeighbors(long s2CellId, @NonNull long[] neighbors) { + int level = getLevel(s2CellId); + int size = levelToSizeIj(level); + int face = getFace(s2CellId); + long ijo = toIjo(s2CellId); + int i = ijoToI(ijo); + int j = ijoToJ(ijo); + + int iPlusSize = i + size; + int iMinusSize = i - size; + int jPlusSize = j + size; + int jMinusSize = j - size; + boolean iPlusSizeLtMax = iPlusSize < MAX_SIZE; + boolean iMinusSizeGteZero = iMinusSize >= 0; + boolean jPlusSizeLtMax = jPlusSize < MAX_SIZE; + boolean jMinusSizeGteZero = jMinusSize >= 0; + + int index = 0; + // Down direction. + neighbors[index++] = getParent(fromFijSame(face, i, jMinusSize, jMinusSizeGteZero), + level); + // Right direction. + neighbors[index++] = getParent(fromFijSame(face, iPlusSize, j, iPlusSizeLtMax), level); + // Up direction. + neighbors[index++] = getParent(fromFijSame(face, i, jPlusSize, jPlusSizeLtMax), level); + // Left direction. + neighbors[index++] = getParent(fromFijSame(face, iMinusSize, j, iMinusSizeGteZero), + level); + + // Pad end of neighbor array with zeros. + Arrays.fill(neighbors, index, neighbors.length, 0); + } + + /** Returns the "i" coordinate for the specified S2 cell. */ + public static int getI(long s2CellId) { + return ijoToI(toIjo(s2CellId)); + } + + /** Returns the "j" coordinate for the specified S2 cell. */ + public static int getJ(long s2CellId) { + return ijoToJ(toIjo(s2CellId)); + } + + /** + * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in + * radians. + */ + private static long fromLatLngRadians(double latRadians, double lngRadians) { + double cosLat = Math.cos(latRadians); + double x = Math.cos(lngRadians) * cosLat; + double y = Math.sin(lngRadians) * cosLat; + double z = Math.sin(latRadians); + return fromXyz(x, y, z); + } + + /** + * Returns the level of the specified S2 cell. The returned level is in [0, 30] for valid + * S2 cell IDs. Behavior is undefined for invalid S2 cell IDs. + */ + static int getLevel(long s2CellId) { + if (isLeaf(s2CellId)) { + return MAX_LEVEL; + } + return MAX_LEVEL - (Long.numberOfTrailingZeros(s2CellId) >> 1); + } + + /** Returns the lowest-numbered bit that is on for the specified S2 cell. */ + static long getLowestOnBit(long s2CellId) { + return s2CellId & -s2CellId; + } + + /** Returns the lowest-numbered bit that is on for any S2 cell on the specified level. */ + static long getLowestOnBitForLevel(int level) { + return 1L << (2 * (MAX_LEVEL - level)); + } + + /** + * Returns the ID of the first S2 cell in a traversal of the children S2 cells at the specified + * level, in Hilbert curve order. + */ + static long getTraversalStart(long s2CellId, int level) { + return s2CellId - getLowestOnBit(s2CellId) + getLowestOnBitForLevel(level); + } + + /** Returns the ID of the next S2 cell at the same level along the Hilbert curve. */ + static long getTraversalNext(long s2CellId) { + return s2CellId + (getLowestOnBit(s2CellId) << 1); + } + + /** + * Encodes the S2 cell id to compact text strings suitable for display or indexing. Cells at + * lower levels (i.e., larger cells) are encoded into fewer characters. + */ + @NonNull + static String getToken(long s2CellId) { + if (s2CellId == 0) { + return "X"; + } + + // Convert to a hex string with as many digits as necessary. + String hex = Long.toHexString(s2CellId).toLowerCase(Locale.US); + // Prefix 0s to get a length 16 string. + String padded = padStart(hex); + // Trim zeroes off the end. + return padded.replaceAll("0*$", ""); + } + + private static String padStart(String string) { + if (string.length() >= 16) { + return string; + } + return "0".repeat(16 - string.length()) + string; + } + + /** Returns the leaf S2 cell ID of the specified (x, y, z) coordinate. */ + private static long fromXyz(double x, double y, double z) { + int face = xyzToFace(x, y, z); + UvTransform uvTransform = UV_TRANSFORMS[face]; + double u = uvTransform.xyzToU(x, y, z); + double v = uvTransform.xyzToV(x, y, z); + return fromFuv(face, u, v); + } + + /** Returns the leaf S2 cell ID of the specified (face, u, v) coordinate. */ + private static long fromFuv(int face, double u, double v) { + int i = uToI(u); + int j = vToJ(v); + return fromFij(face, i, j); + } + + /** Returns the leaf S2 cell ID of the specified (face, i, j) coordinate. */ + private static long fromFij(int face, int i, int j) { + int bits = (face & SWAP_MASK); + // Update most significant bits. + long msb = ((long) face) << (POS_BITS - 33); + for (int k = 7; k >= 4; --k) { + bits = lookupBits(i, j, k, bits); + msb = updateBits(msb, k, bits); + bits = maskBits(bits); + } + // Update least significant bits. + long lsb = 0; + for (int k = 3; k >= 0; --k) { + bits = lookupBits(i, j, k, bits); + lsb = updateBits(lsb, k, bits); + bits = maskBits(bits); + } + return (((msb << 32) + lsb) << 1) + 1; + } + + private static long fromFijWrap(int face, int i, int j) { + double u = iToU(i); + double v = jToV(j); + + XyzTransform xyzTransform = XYZ_TRANSFORMS[face]; + double x = xyzTransform.uvToX(u, v); + double y = xyzTransform.uvToY(u, v); + double z = xyzTransform.uvToZ(u, v); + + int newFace = xyzToFace(x, y, z); + UvTransform uvTransform = UV_TRANSFORMS[newFace]; + double newU = uvTransform.xyzToU(x, y, z); + double newV = uvTransform.xyzToV(x, y, z); + + int newI = uShiftIntoI(newU); + int newJ = vShiftIntoJ(newV); + return fromFij(newFace, newI, newJ); + } + + private static long fromFijSame(int face, int i, int j, boolean isSameFace) { + if (isSameFace) { + return fromFij(face, i, j); + } + return fromFijWrap(face, i, j); + } + + /** + * Returns the face associated with the specified (x, y, z) coordinate. For a coordinate + * on a face boundary, the returned face is arbitrary but repeatable. + */ + private static int xyzToFace(double x, double y, double z) { + double absX = Math.abs(x); + double absY = Math.abs(y); + double absZ = Math.abs(z); + if (absX > absY) { + if (absX > absZ) { + return (x < 0) ? 3 : 0; + } + return (z < 0) ? 5 : 2; + } + if (absY > absZ) { + return (y < 0) ? 4 : 1; + } + return (z < 0) ? 5 : 2; + } + + private static int uToI(double u) { + double s; + if (u >= 0) { + s = 0.5 * Math.sqrt(1 + 3 * u); + } else { + s = 1 - 0.5 * Math.sqrt(1 - 3 * u); + } + return Math.max(0, Math.min(MAX_SIZE - 1, (int) Math.round(MAX_SIZE * s - 0.5))); + } + + private static int vToJ(double v) { + // Same calculation as uToI. + return uToI(v); + } + + private static int lookupBits(int i, int j, int k, int bits) { + bits += ((i >> (k * LOOKUP_BITS)) & LOOKUP_MASK) << (LOOKUP_BITS + 2); + bits += ((j >> (k * LOOKUP_BITS)) & LOOKUP_MASK) << 2; + return LOOKUP_POS[bits]; + } + + private static long updateBits(long sb, int k, int bits) { + return sb | ((((long) bits) >> 2) << ((k & 0x3) * 2 * LOOKUP_BITS)); + } + + private static int maskBits(int bits) { + return bits & (SWAP_MASK | INVERT_MASK); + } + + private static int getFace(long s2CellId) { + return (int) (s2CellId >>> POS_BITS); + } + + private static boolean isLeaf(long s2CellId) { + return ((int) s2CellId & LEAF_MASK) != 0; + } + + private static double iToU(int i) { + int satI = Math.max(-1, Math.min(MAX_SIZE, i)); + return Math.max( + -UV_LIMIT, + Math.min(UV_LIMIT, ONE_OVER_MAX_SIZE * ((satI << 1) + 1 - MAX_SIZE))); + } + + private static double jToV(int j) { + // Same calculation as iToU. + return iToU(j); + } + + private static long toIjo(long s2CellId) { + int face = getFace(s2CellId); + int bits = face & SWAP_MASK; + int i = 0; + int j = 0; + for (int k = 7; k >= 0; --k) { + int nbits = (k == 7) ? (MAX_LEVEL - 7 * LOOKUP_BITS) : LOOKUP_BITS; + bits += ((int) (s2CellId >>> (k * 2 * LOOKUP_BITS + 1)) & ((1 << (2 * nbits)) + - 1)) << 2; + bits = LOOKUP_IJ[bits]; + i += (bits >> (LOOKUP_BITS + 2)) << (k * LOOKUP_BITS); + j += ((bits >> 2) & ((1 << LOOKUP_BITS) - 1)) << (k * LOOKUP_BITS); + bits &= (SWAP_MASK | INVERT_MASK); + } + int orientation = + ((getLowestOnBit(s2CellId) & 0x1111111111111110L) != 0) ? (bits ^ SWAP_MASK) + : bits; + return (((long) i) << I_SHIFT) | (((long) j) << J_SHIFT) | orientation; + } + + private static int ijoToI(long ijo) { + return (int) (ijo >>> I_SHIFT); + } + + private static int ijoToJ(long ijo) { + return (int) ((ijo >>> J_SHIFT) & J_MASK); + } + + private static int uShiftIntoI(double u) { + double s = 0.5 * (u + 1); + return Math.max(0, Math.min(MAX_SIZE - 1, (int) Math.round(MAX_SIZE * s - 0.5))); + } + + private static int vShiftIntoJ(double v) { + // Same calculation as uShiftIntoI. + return uShiftIntoI(v); + } + + private static int levelToSizeIj(int level) { + return 1 << (MAX_LEVEL - level); + } + + private static void initLookupCells() { + initLookupCell(0, 0, 0, 0, 0, 0); + initLookupCell(0, 0, 0, SWAP_MASK, 0, SWAP_MASK); + initLookupCell(0, 0, 0, INVERT_MASK, 0, INVERT_MASK); + initLookupCell(0, 0, 0, SWAP_MASK | INVERT_MASK, 0, SWAP_MASK | INVERT_MASK); + } + + private static void initLookupCell( + int level, int i, int j, int origOrientation, int pos, int orientation) { + if (level == LOOKUP_BITS) { + int ij = (i << LOOKUP_BITS) + j; + LOOKUP_POS[(ij << 2) + origOrientation] = (pos << 2) + orientation; + LOOKUP_IJ[(pos << 2) + origOrientation] = (ij << 2) + orientation; + } else { + level++; + i <<= 1; + j <<= 1; + pos <<= 2; + for (int subPos = 0; subPos < 4; subPos++) { + int ij = POS_TO_IJ[orientation][subPos]; + int orientationMask = POS_TO_ORIENTATION[subPos]; + initLookupCell( + level, + i + (ij >>> 1), + j + (ij & 0x1), + origOrientation, + pos + subPos, + orientation ^ orientationMask); + } + } + } + + private static double calculateUvLimit() { + double machEps = 1.0; + do { + machEps /= 2.0f; + } while ((1.0 + (machEps / 2.0)) != 1.0); + return 1.0 + machEps; + } + + @NonNull + private static UvTransform[] createUvTransforms() { + UvTransform[] uvTransforms = new UvTransform[NUM_FACES]; + uvTransforms[0] = + new UvTransform() { + + @Override + public double xyzToU(double x, double y, double z) { + return y / x; + } + + @Override + public double xyzToV(double x, double y, double z) { + return z / x; + } + }; + uvTransforms[1] = + new UvTransform() { + + @Override + public double xyzToU(double x, double y, double z) { + return -x / y; + } + + @Override + public double xyzToV(double x, double y, double z) { + return z / y; + } + }; + uvTransforms[2] = + new UvTransform() { + + @Override + public double xyzToU(double x, double y, double z) { + return -x / z; + } + + @Override + public double xyzToV(double x, double y, double z) { + return -y / z; + } + }; + uvTransforms[3] = + new UvTransform() { + + @Override + public double xyzToU(double x, double y, double z) { + return z / x; + } + + @Override + public double xyzToV(double x, double y, double z) { + return y / x; + } + }; + uvTransforms[4] = + new UvTransform() { + + @Override + public double xyzToU(double x, double y, double z) { + return z / y; + } + + @Override + public double xyzToV(double x, double y, double z) { + return -x / y; + } + }; + uvTransforms[5] = + new UvTransform() { + + @Override + public double xyzToU(double x, double y, double z) { + return -y / z; + } + + @Override + public double xyzToV(double x, double y, double z) { + return -x / z; + } + }; + return uvTransforms; + } + + @NonNull + private static XyzTransform[] createXyzTransforms() { + XyzTransform[] xyzTransforms = new XyzTransform[NUM_FACES]; + xyzTransforms[0] = + new XyzTransform() { + + @Override + public double uvToX(double u, double v) { + return 1; + } + + @Override + public double uvToY(double u, double v) { + return u; + } + + @Override + public double uvToZ(double u, double v) { + return v; + } + }; + xyzTransforms[1] = + new XyzTransform() { + + @Override + public double uvToX(double u, double v) { + return -u; + } + + @Override + public double uvToY(double u, double v) { + return 1; + } + + @Override + public double uvToZ(double u, double v) { + return v; + } + }; + xyzTransforms[2] = + new XyzTransform() { + + @Override + public double uvToX(double u, double v) { + return -u; + } + + @Override + public double uvToY(double u, double v) { + return -v; + } + + @Override + public double uvToZ(double u, double v) { + return 1; + } + }; + xyzTransforms[3] = + new XyzTransform() { + + @Override + public double uvToX(double u, double v) { + return -1; + } + + @Override + public double uvToY(double u, double v) { + return -v; + } + + @Override + public double uvToZ(double u, double v) { + return -u; + } + }; + xyzTransforms[4] = + new XyzTransform() { + + @Override + public double uvToX(double u, double v) { + return v; + } + + @Override + public double uvToY(double u, double v) { + return -1; + } + + @Override + public double uvToZ(double u, double v) { + return -u; + } + }; + xyzTransforms[5] = + new XyzTransform() { + + @Override + public double uvToX(double u, double v) { + return v; + } + + @Override + public double uvToY(double u, double v) { + return u; + } + + @Override + public double uvToZ(double u, double v) { + return -1; + } + }; + return xyzTransforms; + } + + /** + * Transform from (x, y, z) coordinates to (u, v) coordinates, indexed by face. For a + * (x, y, z) coordinate within a face, each element of the resulting (u, v) coordinate + * should lie in the inclusive range [-1, 1], with the face center having a (u, v) + * coordinate equal to (0, 0). + */ + private interface UvTransform { + + /** + * Returns for the specified (x, y, z) coordinate the corresponding u-coordinate + * (which may lie outside the range [-1, 1]). + */ + double xyzToU(double x, double y, double z); + + /** + * Returns for the specified (x, y, z) coordinate the corresponding v-coordinate + * (which may lie outside the range [-1, 1]). + */ + double xyzToV(double x, double y, double z); + } + + /** + * Transform from (u, v) coordinates to (x, y, z) coordinates, indexed by face. The + * resulting vectors are not necessarily of unit length. + */ + private interface XyzTransform { + + /** Returns for the specified (u, v) coordinate the corresponding x-coordinate. */ + double uvToX(double u, double v); + + /** Returns for the specified (u, v) coordinate the corresponding y-coordinate. */ + double uvToY(double u, double v); + + /** Returns for the specified (u, v) coordinate the corresponding z-coordinate. */ + double uvToZ(double u, double v); + } +} diff --git a/proto/src/OWNERS b/proto/src/OWNERS index abd08deced793..ccff6245ff2c3 100644 --- a/proto/src/OWNERS +++ b/proto/src/OWNERS @@ -2,3 +2,4 @@ per-file gnss.proto = file:/services/core/java/com/android/server/location/OWNER per-file wifi.proto = file:/wifi/OWNERS per-file camera.proto = file:/services/core/java/com/android/server/camera/OWNERS per-file system_messages.proto = file:/core/res/OWNERS +per-file altitude.proto = file:/location/OWNERS diff --git a/proto/src/altitude.proto b/proto/src/altitude.proto new file mode 100644 index 0000000000000..1010f67b501d5 --- /dev/null +++ b/proto/src/altitude.proto @@ -0,0 +1,54 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.internal.location.altitude; + +option java_package = "com.android.internal.location.altitude"; +option java_multiple_files = true; + +// Defines parameters for a spherically projected geoid map and corresponding +// tile management. +message MapParamsProto { + // Defines the resolution of the map in terms of an S2 level. + optional int32 map_s2_level = 1; + // Defines the resolution of the tiles in cache in terms of an S2 level. + optional int32 cache_tile_s2_level = 2; + // Defines the resolution of the tiles on disk in terms of an S2 level. + optional int32 disk_tile_s2_level = 3; + // Defines the `a` coefficient in the expression `a * map_value + b` used to + // calculate a geoid height in meters. + optional double model_a_meters = 4; + // Defines the `b` coefficient in the expression `a * map_value + b` used to + // calculate a geoid height in meters. + optional double model_b_meters = 5; + // Defines the root mean square error in meters of the geoid height. + optional double model_rmse_meters = 6; +} + +// A single tile associating values in the unit interval [0, 1] to map cells. +message S2TileProto { + // The S2 token associated with the common parent of all map cells in this + // tile. + optional string tile_key = 1; + + // Encoded data that merge into a value in the unit interval [0, 1] for each + // map cell in this tile. + optional bytes byte_buffer = 2; + optional bytes byte_jpeg = 3; + optional bytes byte_png = 4; +} diff --git a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java new file mode 100644 index 0000000000000..0d9aeb59dd075 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.altitude; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; + +import android.content.Context; +import android.location.Location; +import android.location.altitude.AltitudeConverter; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; + +@MediumTest +@RunWith(AndroidJUnit4.class) +public class AltitudeConverterTest { + + private AltitudeConverter mAltitudeConverter; + private Context mContext; + + @Before + public void setUp() { + mAltitudeConverter = new AltitudeConverter(); + mContext = ApplicationProvider.getApplicationContext(); + } + + @Test + public void testAddMslAltitudeToLocation_expectedBehavior() throws IOException { + // Interpolates between bffffc, 955554, and 000004. + Location location = new Location(""); + location.setLatitude(-35.246789); + location.setLongitude(-44.962683); + location.setAltitude(-1); + location.setVerticalAccuracyMeters(1); + // Requires data to be loaded from raw assets. + assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isFalse(); + assertThat(location.hasMslAltitude()).isFalse(); + assertThat(location.hasMslAltitudeAccuracy()).isFalse(); + // Loads data from raw assets. + mAltitudeConverter.addMslAltitudeToLocation(mContext, location); + assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1076); + assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f); + assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f); + + // Again interpolates between bffffc, 955554, and 000004. + location = new Location(""); + location.setLatitude(-35.246789); + location.setLongitude(-44.962683); + location.setAltitude(-1); + location.setVerticalAccuracyMeters(1); + // Requires no data to be loaded from raw assets. + assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isTrue(); + assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1076); + assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f); + assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f); + // Results in same outcome. + mAltitudeConverter.addMslAltitudeToLocation(mContext, location); + assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1076); + assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f); + assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f); + + // Interpolate between 955554, 000004, 00000c, and 95554c - no vertical accuracy. + location = new Location(""); + location.setLatitude(-35.176383); + location.setLongitude(-44.962683); + location.setAltitude(-1); + location.setVerticalAccuracyMeters(-1); // Invalid vertical accuracy + // Requires no data to be loaded from raw assets. + assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isTrue(); + assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1919); + assertThat(location.hasMslAltitudeAccuracy()).isFalse(); + // Results in same outcome. + mAltitudeConverter.addMslAltitudeToLocation(mContext, location); + assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1919); + assertThat(location.hasMslAltitudeAccuracy()).isFalse(); + + // Interpolates somewhere else more interesting, i.e., Hawaii. + location = new Location(""); + location.setLatitude(19.545519); + location.setLongitude(-155.998774); + location.setAltitude(-1); + location.setVerticalAccuracyMeters(1); + // Requires data to be loaded from raw assets. + assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isFalse(); + assertThat(location.hasMslAltitude()).isFalse(); + assertThat(location.hasMslAltitudeAccuracy()).isFalse(); + // Loads data from raw assets. + mAltitudeConverter.addMslAltitudeToLocation(mContext, location); + assertThat(location.getMslAltitudeMeters()).isWithin(2).of(-19.2359); + assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f); + assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f); + } + + @Test + public void testAddMslAltitudeToLocation_invalidLatitudeThrows() { + Location location = new Location(""); + location.setLongitude(-44.962683); + location.setAltitude(-1); + + location.setLatitude(Double.NaN); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + + location.setLatitude(91); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + + location.setLatitude(-91); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + } + + @Test + public void testAddMslAltitudeToLocation_invalidLongitudeThrows() { + Location location = new Location(""); + location.setLatitude(-35.246789); + location.setAltitude(-1); + + location.setLongitude(Double.NaN); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + + location.setLongitude(181); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + + location.setLongitude(-181); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + } + + @Test + public void testAddMslAltitudeToLocation_invalidAltitudeThrows() { + Location location = new Location(""); + location.setLatitude(-35.246789); + location.setLongitude(-44.962683); + + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + + location.setAltitude(Double.NaN); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + + location.setAltitude(Double.POSITIVE_INFINITY); + assertThrows(IllegalArgumentException.class, + () -> mAltitudeConverter.addMslAltitudeToLocation(location)); + } +}