qmapshack-1.5.1/call_Uncrustify.sh000755 001750 000144 00000001706 12527654570 020205 0ustar00oeichlerusers000000 000000 #!/bin/sh if [ ! -n "$1" ]; then echo "Syntax is: recurse.sh dirname filesuffix" echo "Syntax is: recurse.sh filename" echo "Example: recurse.sh temp cpp" exit 1 fi if [ -d "$1" ]; then #echo "Dir ${1} exists" if [ -n "$2" ]; then filesuffix=$2 else filesuffix="*" fi #echo "Filtering files using suffix ${filesuffix}" file_list=`find ${1} -name "*.${filesuffix}" -type f` for file2indent in $file_list do echo "Indenting file $file2indent" #!/bin/bash uncrustify -f "$file2indent" -c "./call_Uncrustify.cfg" -o indentoutput.tmp mv indentoutput.tmp "$file2indent" done else if [ -f "$1" ]; then echo "Indenting one file $1" #!/bin/bash uncrustify -f "$1" -c "./call_Uncrustify.cfg" -o indentoutput.tmp mv indentoutput.tmp "$1" else echo "ERROR: As parameter given directory or file does not exist!" echo "Syntax is: call_Uncrustify.sh dirname filesuffix" echo "Syntax is: call_Uncrustify.sh filename" echo "Example: call_Uncrustify.sh temp cpp" exit 1 fi fi qmapshack-1.5.1/CMakeLists.txt.user.044d553000644 001750 000144 00000045447 12623424631 021124 0ustar00oeichlerusers000000 000000 EnvironmentId {044d553a-749a-4a4b-b707-1344605bd255} ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings true false true Cpp CppGlobal QmlJS QmlJSGlobal 2 UTF-8 false 4 false 80 true true 1 true false 0 true 0 8 true 1 true true true false ProjectExplorer.Project.PluginSettings ProjectExplorer.Project.Target.0 Qt 4.8.4 in Pfad (System) Qt 4.8.4 in Pfad (System) {b3896d75-6773-49ab-b85b-a90805c4269a} 0 0 0 false /home/oeichler/Code/cpp/build_QMapShack -j8 false true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true true Make CMakeProjectManager.MakeStep 1 Bereinigen ProjectExplorer.BuildSteps.Clean 2 false all CMakeProjectManager.CMakeBuildConfiguration 1 0 Deployment ProjectExplorer.BuildSteps.Deploy 1 Lokales Deployment ProjectExplorer.DefaultDeployConfiguration 1 false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 qmapshack false 2 qmapshack CMakeProjectManager.CMakeRunConfiguration.qmapshack 3768 false true false false true false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 router false 2 router (deaktiviert) CMakeProjectManager.CMakeRunConfiguration.router 3768 false true false false true false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 planetsplitter false 2 planetsplitter (deaktiviert) CMakeProjectManager.CMakeRunConfiguration.planetsplitter 3768 false true false false true 3 ProjectExplorer.Project.TargetCount 1 ProjectExplorer.Project.Updater.FileVersion 18 Version 18 qmapshack-1.5.1/CMakeLists.txt.user000644 001750 000144 00000027672 12624371570 020221 0ustar00oeichlerusers000000 000000 EnvironmentId {f0360923-ff9d-4a52-8b8f-72d5ce639493} ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings true false true Cpp CppGlobal QmlJS QmlJSGlobal 2 UTF-8 false 4 false 80 true true 1 true false 0 true 0 8 true 1 true true true false ProjectExplorer.Project.PluginSettings ProjectExplorer.Project.Target.0 Desktop Desktop {5eb63cc2-9ef2-4dc1-ab45-bc82e79614e7} 0 0 0 false /home/oeichler/Code/cpp/build_QMapShack -j8 false true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true true Make CMakeProjectManager.MakeStep 1 Bereinigen ProjectExplorer.BuildSteps.Clean 2 false all CMakeProjectManager.CMakeBuildConfiguration 1 0 Deployment ProjectExplorer.BuildSteps.Deploy 1 Lokales Deployment ProjectExplorer.DefaultDeployConfiguration 1 false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 qmapshack 2 qmapshack CMakeProjectManager.CMakeRunConfiguration.qmapshack 3768 false true false false true 1 ProjectExplorer.Project.TargetCount 1 ProjectExplorer.Project.Updater.FileVersion 18 Version 18 qmapshack-1.5.1/qmapshack.desktop000644 001750 000144 00000000446 12527654570 020043 0ustar00oeichlerusers000000 000000 [Desktop Entry] Version=1.0 Name=QMapShack Exec=qmapshack %F Icon=QMapShack Terminal=false Type=Application X-MultipleArgs=false Categories=Qt;Utility;Geography; GenericName=GPS device mapping utility Keywords=map;GPS;geocaching;waypoints;tracks; MimeType=application/x-gpx; StartupNotify=true qmapshack-1.5.1/MacOSX/resources/QMapShack.png000644 001750 000144 00000266634 12572350112 022120 0ustar00oeichlerusers000000 000000 PNG  IHDR+sBIT|dsRGBgAMA a pHYs+tEXtSoftwarewww.inkscape.org<pIDATx^TUwM.CPF1 @AAPBKRi̝vٙىy{̺l{$EllfIٸn>Oxr="+puIy^ҨQi޼**y爈 . "ׇ֭=2krUٿxY3w4@;@T].C=f* #F_)Rq1 ܹp|9B}5;sGɆxuĪKj('֖VJHѢcDDDD΁ rPc}esyYٲܼy^}FGZhK֬-[,ݺ^WX,G0Hh`i>?G1c9sGqqW@P&UQ7oay&ҡC\(1 r Ѿ|9Ro?/KK@z:|G_G^uH:^BR~a)Z4zzXEddܺ+ǎ]rSPK#Xgw3$"$WH^չ- """ b10#-!!~Iǚŋĉe#YA?_k~7o"|UeÆe~1 `E߲J._*Rd~)8pz3YKHUvmB}X駫e]H\ggo(_Jy1 JF˞=VGZ0slIL9[;K$tʕ$C> 6?I ZdI+VzQp{Dd(Ԇ`ɣp#(Ⱦ>{RG:0- Ǩ?@ʕ+,C6ԉ%?~ H3U ̼l^| $ձctI"""pY {JY >t!@Y=#"P2R/9$0 I²Iٲ9ᇫʠA&n 9u ; $UmĶm7%*aa2mZw\9,YK\Dg[sIݯ?}z6ymK{KaRZ~8tTV?kK rj.d|10aYg0:$Y.H Wg4wߵDDDd%&\DgKe}&p9BЏ#@ ӻ \Wֵ]Q?l㇀#y@jS*GЏ`*P9oo*={-Dfۘf|]gyᅺx b(aɓ3$RKd޷-ڶ-%RAN>&0¿{%3glpJn= A?1oʋ/3dj~;%;RJ>YQɑ ,""". "ЮYh:C< K`(Uʔ*5<@6oޣZ(B,^~؋R(v KwY>|\wx%+DDD b9$&&Oϓ>./G5.Z۵+-]#2p;v:Co do(F90 r.]kOJ\\x{{FQxt)B}w^}R^;DD`[ALL9!`^eL cU :-"""" bR$7օ O.7n o݊@"66A=-/FHD-" m-":uC'o}|W `ADd[L &(*+V?ܣw TiInR~aMD2ڂq[eދ:ϕ+H5+&>XLʕ-ǐ "(&\DdKclwG\1@7ߐ&T(uw__)3gW-N-bVqK".-~zY,>Û "J&\Ddk޽KT:Df >;6huݘ_\"WգPūÜ yʕC]gBaazlpJ":I3g,US\. "Z'Xa'VdF3DBqYxҾ}֭4lXX "&\Dd/u$6cYI1c:63.Dȕ+rM9r=z]._X]3gn箫h?Fg-?:fcɖ-Oc1@D~)ip$ޒ[Ξ}ErƔ^"׃@UY"uQQq:߷,_~TɎ37ՁvH"Gx,3j .#ۚ"" _YXZq5ڼ<<fԙ37eŊ˥q ҳ2v Q"ϥ0uq-h_f 1Ƚ1@Ddgu'"Er˧0Deǎ Ҽ$;wG=e3C|rFs>;{,]ԩM1&ЩSYqά3Zhɗ/LM¸Ɩ\ύңr iӦ^jH".Egd͚ u G^km!Qȋ/6Yz3CD ""+`KR0EiX)[[Wiܸa"$88T~g[<9o2v,:t8ɖ-DVcDI_d?69{eQτSBFXZ3p_ƼZ>""l4t2{̟MՆQu8:r#DFFGdeҩi7yͿe͚kMxɑ#T~ry6U!}+GO[:C aʔp!9v\ Ƚ_q L4/u3ߨZYՁMI[ V=5nB^,׬9%Oߔes褐xyeKtnތ8IJ>fPIpgR]K@?׮Txu[Ƀ3Э/ 3^qHJ=6W.\)˖_$kVV9?}C;(Wd??ϓ?9/w?_#oDBCu;f&J(X?ycեz|*͜QHr3*&Cf8>\&^#GgoQ$_)P T*T%EdyhQr?>8IwM%'7ll\Wxx}!RRnٵ9Tw>a+>V|3>s?# uXYf Y6>6 8cs6D8L &<ϦMgTpzEvM΅޽dϞKryM*/XOz K甜9S'o@%J}[ /%wM[e"lt]舘A2 gйs9i޼TGpOBk2+\8Tn_2ZDmɒ#Ҧ$u#UX:/K' 9HS'pKNm~A:!#G@ɂC9N5[/"-&\dキB۫ZcD BҳgEȖ1ʝ??}:4.ZhK]_:xzvngMk?`D_҂D fbD_Ny꩚2p`-\9>}ѿԙk֭K-"6rF2d>Qbƌ2lrTz#=]s:C"7sfI|4wqOlzB9R"aa뙍D bu9Ys:Kڛ _7`$J֬z;fY-￿RZΰilYu1,$2S)D.֭Xi7]tzMwf[؂Ϝt=j齌uPXsvJ #&mUg$75ٜ^#MRxҮV>g).^|Dbc/GЧ0'!>g1_rTWZ!#1 hyԜ7E:cDܤڵ=Z$O|5 $:֠>HU,l, IR=r ڵ"]{$JƏ-?^h;t>|U̹t)R""buz+Q"f nÆd:̪#{7ر'kشF:uS7.E~Rog~V 8̳KjT4n\DtR5.c1@yzt;2j!N R{`>Ӗ^Ԡhlb햀:K- %}ԗ^#ktiU]0{~3EDRϲqe$Y!=֬9)clL#7܂9fQڵ]Qj2]={!g{ln0/fzQ S(B[q^Og-k~ݞ]={~ݗr5H6&\tz&MڮGD `\J;5K"TXv8XƳ3mtVo83C,;׌PlKtnB\T*昈ԪolQ\VYz%9g钐[lH+uuNb/J}=|r rVwߒ:u*5D\AzAf`KttDEs.D״ٷl^/h% x͙xeҥ CRK,@] 9&\(p!Y oHP֋~*y 7^YԍG/}FvowB!Z={xQFr3I|7rȰaTSĘ12Z wX(r>_uR+W?#1IPFҠtAyt]:wBh'ɭgS\?<#'A"?9'N+Mͮg)]BBPxW'|t 8G} ඉL gH`2(s7EYXr,LiwY!lrk4lXBo qc͞=P^_^N {Q= `k7yLo /j[ܸ1h;~=Zb+/@ ̅)ժݝHU֭Ϥ_Ѣߪϼ x]3 :LvӁ׎sguςףk) Ӂ PӢL@YԴ!. dMϝ oٳ:DNW \nsUʸGeòqiwGtj۱[7RzcKMk,Yxmd I߾5dҤ.2m.ٰ\xY=)ѩ_-[?$˚5mrXbt ]k΋zz7Α@P #nU[a rW_]*#F5Zw_cdGSF^s엇%(g,"gf>mǑ4`XQZ>]Ȗ42c;%NjuXy99~kqCĜAM)z| V^߄5(z4)z,HwƐGJ^RdLceaXoczHiթS@ ^Voے3ά0Z Rn?i-e~A/0MN$Oגc]g@Jr7x'!:չs9 .VŃ(fA]~^=!a8FkR<`qiذ*aaQ%SRk$N$IdǎgҜa%f-EGcz8iL4҂xɟ]Iڵ+?yҏ# G!U(ΜA9đQӔρ.Th}ڧ2er..8dvT{azȵk~w&qɓSr;F´1L!3eӉ{*P`e\Lz)IFQ YWܹY]d'#{oY._Ô~ik֬Xu<oYF!sg:Ct'uc0e#:,(ܻKOBqƞITHx>PoƌkO>&[<. Lؽu~iĎ+%Vka*~T?^˗S/㒐pE=N9@"hլY@ʖͩ;bVm'=߸aZ#j>Pkq!E1J (:sLB<thrn.c1DsH웓a,gųkWѝ!r=ҿcY̞O]1ҏޛFLsKft2f:#3ܹ[=\ԩSP6n|hCILpk>Y11ߕW/ OUf 9Ɲ02@#ٴ,E0][堣Yy#@4fHŊv3PH疒%tBq`s׮ z6q" j,M̝|4TBXҀ8PsBj[7Ձŋ:n_Nϸa=}dtHr#ٝ?o.hYn}ңufyaihݻ/H5%ļc׉ץlQF˕5_+ò~C'5,\U^M #س{꺶_=k2ex?:5)sB H~#|8 $LJ)Ut1plw\ m ?;Y޲{a2˰a/n 7Zr%RC@7W_m"_~ !Ҧak!KDٳg޲șu̝W5i;xߙA:}TEF$+Չ7rm]ңzYM92õܜ RyTCS<ѵkeƌsS   Hoז^[*_}h%Z*III3p}QjTsܗMޯ5#$?G_Ȝsli?9rvcB0{v ǹx8ZT`A,AL Kخ#$'nivG{d6d0n3VEOJ*-׵ӏOˊ5㣽$Ys5^'/1_bdzF6`A: xdPsNR{хk,b>:G޷"Ǐ7{@Bm̲Ɉ1OΜ}D)aI7oN=߼i6vZ#>>QO֞wR^ufqM54;w!iٲz0fеkj`fMKL9~}'!Q_nA Щi u',pt͢kY""=yyÜ`H飿f5HfggzU gv<ŽΒ'L1dJ8;@G/:FTVJ$mۖpѺ;%/T>CwFwmÒ^I:{'O(K^ 2~|7y9{]{zݞUX :8e:řhgpfVLz,/:;¬תcjᚉ36udtdϞ_Z,g!ɋFFoxFo7kvJ~)X^FѰaa=c},5B)S(ٿ5A$P4l P0ض>a/nӦ햗^Z,/bZ;XB߁vh)S~SyzL6M"ɻ6k=ףȑ_WOl4XӧLM>_! +ݻW3ziw-'DKQ"xY5-aaaҦM)m#Z?7g$̀bŊJ4$Pҙ`Exxܺge-ڑ:rT/ٛѣ9vC_@Frݒ~GOc*ѣ]RS5dܸF+ea}#Pʗ/g6jTD L mL%>` 3L\#0G)NL/L1bLNYQU¥SZۍv킦3K@S0/?*a?Kz^>P3|X."k> i,$sq}#W_~Wq t^j61rN/Gϒ%K 8gO,:n*HETp  +c=~…pu |ݛ#h#|'ORn"u9!`J d˖O/ rW@2].(FU_y1xw:/mtd, \bwү_e=2bے%!!Qw0~*{UϤ65R#C6ԙSmʕ+P'z822Na`)H:tXtZ^fm;GTgRBa{;Xv԰T;"YV<KeV x}8cGX =+_r_azj N35}v}fFOke9s3P̛g7.G͚ԵͲ%ZDЯ5.U}F2Ԫ_gɬlfcؽ xn؁wW4bGQԴwrtu|vF0]Y#ה;{w츨cFS ?KΝ_'pqĚL\6(gb ">#D8pn2O p5u'PƷ Pt̘vz=#lpZYY ҡC1ٹ;O[wKk9Ffk $)W+ 3|o3fsߜ6wG GS"c2lX#]֐Hc2Ar ~eo>$ٸ)S'Yk{\JF3gM`gJO\^qi޴s `:t|ju7rWpPINE0sH #uku*H(" B}DEK}M:cNuYS|-;8̣B8m823 _nٷoѾ{6Q eK}*VITwK 俣8ɒ_6mzZOQs . 7# 5bO}uנ/OZ(!ՖU͡M߾S_8<~ͯk<`>.\Boˊ`_˨Q({i]\ՏduJ:w]ۯ\5DD/Sg[wV^L[w J-QkO-8ɛ78^D~˳0}S2iٸ#3;g_3g#wɡOԬD9rg\1DDFIŊd>zV;b5kVLVxh+KdB1;O03hB=zѢԡ#UsɄ U<˞_|@ZؤΐuN% R?<[Li f}jL{WfǷF6%'olY45kDݺ e&Yse_(zu]# T؋?4,e0۵낼2YIVN~f6hPT MmeG[&Lp)]ʖi<pA(V/u].ĉeCw]rQyP3c%ͦTMJ%KK6- 7IswJgNIBf(?1TgQOz?pLj]G%l˗#lyKw tq9so ::^ڶ"+W" n1ZE`̪UǨ3G۵+% 3ZFc~5/7ǽVGȓ,\W]KìY׮EKETv{a ԩu.3g:;'&x{ /_X:w.+oHWNIDD%jڎRUQwlYh9&M~ի1UT/_~RkBvrܱ._kYsu%ҪUY( 5lS)m1t]ԩkW]~>(N /g?t,mٳ碌^~ejر!VLHH}cyVݺ\bwNp&&&No(tZT3Uہ)G~LMJ-G⋋U.h_)p4;'LMO_>:a 2M~}}Jx=KkAVu4J[|%y꩚k=ӵw2}zty{(ohY[;KF =0Z>>ҲeI߿[o* ;V{?d+,SP 5l -!ݨ՝5y#%wҧOmرZ|S7矗ʔ)+Uz#-_~T׺`; {':Jwi*ە<\$< ΝAB]""{Adg!reYbpn_.=7E3浚hfBrv3N/LwF"BBCCK:G'ӣX0)_:I޼ÎDg}5iTvVys=Vce{MM8~z{?##&I*T(~*W_=NaM3Sg6v+Mɓ)7ZKʅ$g] gX>hy%v-bfel\T50-ֽ5RNٌZ#T*o{Zg.*(%>X\bbd޼)0*X)KbcY¤T0CH׮;œadw@|lY)XGE_ԙ9AeUM[u&eXi߾Nvf͚`]O?]#'oP-{&興 3"}Aw,Hd3 RD.4FΝ ſQ.)R$TVRmNŋ[4eiFzSr; O-e ҁ^k|\)E-??fbˤI;d9F˖lټ?@w/ǎ]SúEz#UԫW[[̌JPu,]M)o:-*U׽&ᄏB>_}nHuk,|:%7Xִh!yd솀Nk^/\O/{cFBC}IBb enw?|7nJ -H\J:yR>#u[Z)]: ^^g{?iРtVN}$$Ė2Q։GTVBmΝ϶mF lfD e6ivT?sIu&P[֯Gub^R=A] ӴW_]"#F`4Z>d T)NaH$(Ǝ ?b|-vC ""īc^ʘ u ʆ O-FΦ-Fro?[~mJMHeᇫH6tP}ʏ&ٳ8u\4Zi[ Dj,&:#T ¥lQo^3}^UP[bGT!0]I=|0=8 .H_'A#/!VxsG}X.kܹUhG࿤ {J"vȢEb J dUב9y;zϞU˜,(%J{Rrt%XLH*ydǎgɗ_CQ 2(Q"92hָYѺQRx2:, ʆ]*UH?p`-ubi_wmˎ׵$s}.Mʕ˥v$> ևUklbqC5dF< PռuqݻIpp] 8_ƎŲ&saXu.,gV{/_~heHb wdfH9uȱpDF[%#Fy|F=1@d#u #HjY-\8TN|hVr f /hiР|Isi֬t1c !!:C:v,9oԨ)7b7ی)]!S \ԅ2ԹY0@B|e$Yo_hvg:3o;/gŋˢRRH=K͙3WHl,~v\Lr`9bt*\]LL:uOYtV^gޒ˗# a z-*wO=z&w3}mL~ 2fL{4GPF~yZ2`@u[+Э[y_$s2"Q䧟:2'mó tRK[be__R ]3\;SiIeW ~Fˣ3-3)SZXZ8,A{NE Z>^ zyrXRU6}UQ|֬} :Kqӎ+J#oNO ز$$=]aCHQ갮VJnˮ]'#f'Ç4Z3yNt:3w wJΦORcJ"Akg-W$ ""O+#%5YzeN'Ov}I//_6&W%@3g69}eչKyϓ %w?ךÞ=R,y;Aʗ+˗c GNכzK"ĉv)YU94Z%J˖J_%SLS~ufn &]UOeSZu\>_uAQRLǺ$ӧLfYʕGk%/-Ze-[vDZRY0]M`|Nc^W_m bI_[rh#0-@ͮwBԡCX5CŨAF}QEKBydCR֤/z5FŬ ݺU3M֩S7H ELT??oL׿wOl׮=)s\,OcD̼lo4U7N~Lf4ow ի({W-KD m۞9u'C^[*/"kDٵkN/n܈?Wg)(W'R*^[ΟT4{WV>#FOiD_x`^ED~%I*^y HwV-&KٷL6m:%Z7.e\ ʐxu*(BZ.e9v;;=qORQ zIPGlСRHufbTRT/k-Y=wD5kݱKhT||(~)Wb:TF$ 'VSzo3g^P$L6m::YS<~{Jeȶҧk?~lvNhĖd@J,:4 !z1!@ȥ5=9^ykWM诿I-[KA3>/>B\mNŋA>HϞu;+3̿gzS/IBwcyy2~6ED>03׍=\._F+FV-kuUzH=Ean3G;uʔ]b'#;v` *aYy%y2T #^uˏ?w̽tA!CɇΓ/\?Ly)?]>7oy_6A&:w_'%$$H&OH-4Z!MSp6)V Uc@7tRC]KjouM1ϝGJ[bKQ""wdY3 1Ч@-{QϞen;JE,:xQӎ@lLy!|.~5x1|ր#?ϣ% f\V?6 L$/Kr__ŊzÇ,@ג)S (GS ck{.GBYٌX$Wސߠ=|u3*Æ5W7*n6Z>o4*"ai9~:sLJY>=˦ʓ'Loh…CЙ3{뵪iiܸ4hPh833gDD䎬I֦GDD !A?Ke JH魲-)CύB<!; qa*sls[$KVo8})ѷnȥKzر{C}Mt%3}Z.a1ղX?wVƴ$`?E=c+2=bm;/udzv$i4=Pdrzz/u_R% y#Et)V <͝_*W+%Jܿ_F}jyM$}Mp5vrUyE$xo%"w$K>+ ޸nZMoΡwc`?=H/_Vɟ?Dʖͥ~z2pIE;AXÌ}K ##xa;w[H9|.yYپuA"#Q)->ڶ-%SvOqd#2j&9sElW%vzV5u&nt:K 7C Sr魷?hzկ qa=H1M!QWXkח-͛7Zk̘Ms-0%+W.﫦$mDD%QΝ{M*!F6{@,Kbf/˃3j,gbVp0)a)*?^*V̝mW>!}LV:!'Ob"*n޽D@FLKά'P߸O?moHMnf`Je$ϸZ$"r r0e;K՝1R\yZҷo.ML'&l}G[,8Ge֬}2}^|kXc#:!ő%<[;!t53Z>I.[<P]r,{YL3Hn6Iԙ~~oݤw9ڵ3鈈i6i5wpA11x:V%d2n\Giٲz'.=KDC(4|{gûk>n\Odd+\QƼf#FR?9{zᶌ#H rSGbYZI88Iu359 x?IS\.7wZ1XBP  l *P)\zkZuy{WdƌF,%Nu^:@*]!e3C59r\ٲNm1嚾joƃ@C2꿘jk6JHynjs=(Vƍ 'v!Çƍm%6-&&uԖ"SOȔ֯?:Mՙil q˃^ooo;LK&O&~B0qIҢE9co]"W~p xޖJGrog`6A7uƊ%zU׸ԂGRy?~L-MCE?WBٲ k)Ŋec^4Z釭4s //zgt)B_3Eg,@sIyR'EfFu-ɧK/-oݠΒ~"{>gU\NJͳ.ܤlϕ+P.-ܬ5'޲z[bX쨴jey7mҫ2eF㻥KPF,%.șPH9rѢԡsQ1,Ik oի˗U(}@=fe eS^6ZV[n٪ +V<.͚V ]֣ڡ#gmz@תU@oe'nHbߨ8?% w4~13V}nEVR=zV'Ѻ;/utt-tcڗ|?7ob4"8|;g'sH7F}t :_rōs(]:qx#mOfڬgaoYrU9WqCD(ݺ6_kIݼLt8PX؝Ś(cϥhf>LE5Y^x>߿U24hPz5BuYF,Kvؗ;hQQs:8b"o"{R-ɓqҦM)>RN8rlrTbcH6DDw-:3b>[n z#emRf, 30wBs gjl_eyhYt e`8a3fyq,o@G_5(v*FA;b`KkhRvAmO;S pM:"\339t(=3ty}@\L8$#%RGW_E%ۙШ^nͿ,7g˄ [ǜ[֬YO`IΜ6I@aB"G6 4 ~.7|o޿Q%V]S̉eÆz{v?xȕ%Isp(S*@!Æ('cʗmy$֭Lvu1eպ #ɝ={SuVO/P7towׯ?}Q\Y'Pu/i/8+&L,?IƏ,|NAq} 'nuw؆4kVL}@oIHutTI(#Fk `w%CR9+fLgQ>v3Ho[7m4G~zE.Ij~eTQgw3g^3".Z?˵koQעE w7mŋ-w%g`yz{kעtB`L0EɃV矯-]VFg?eƌ}F˱0ο"uv;i;w;xƒzW6,"J0ɸH^Y: "rmePF=&HeԈIҨDuEI˖%(M'r_K0@\7Α(OS@N /l7Z6~(':cY~Ȍb?֍PdAiʥK*sJf^܊&'NhԩRHCPk +W>Ō6Aǎd̖bOD(Im$_|h͛$@\f֦ jo6Pesz Oݺyާv+ _SaSP`s<$ P? HuCY"=6iZEF\~k{׮=iɫkfkW䩧~>HoM9aiD侰{7X:C =㻾r}C ;間.ƍ;2A@jĨ`YfW!aIDc2͆mˮ~rӦ햨(XC3;z8WLRb^SᥗV~K ~$44^x*_FJ4(%Y^{  ?i7#C,ѣe}ڵ[z;HSr"`?mLx$maaO%K\Dã7f h\(%$rMI:ػ^="8w h>zP6P{,@*γ0IBCߦm +kT62qbg}`wߵoO9'ͥwJ2|xKMc`6@ZoϗQ ^ /c-_SHDQ sz‹/_/ӧgQ_fݼL]m;g=ß3ΞN,-""WРAaN^>2j:5F^7RO sB=gÒ3a `%ʨH)Y2|Yw/h)]ÌEڴ)%^^)j9+^Ɗ ]DML2dڊl uCu /,2Di"ÇPgMa"DlOj;B|Q9z^?t!VlzEٶ~c(U){Y-oD_梀UUGu1X ][{4)*V 0Z+VmrҬYY=Q"cJLL_%)WyQwE$$ebL}MfYFO> G'  K"JAdd- t1ŔFѣs^H=zi N}J ߂eӦ38Y`y!!$~~ꋊŊv([U_…puIJ)Q"LdKrf;uDS22wCFN޳OFnC'-[V!;,Yܻ֭gkwcINOմD=掘$u8heR|2~f/\P/YJ}ʕ.BG'dݺ'~])huf$_|Bm˕%`$J yeϞ6ѽ~mv'3~-g_Y2uj8IRH6 ?p{ho,_ݩCGpko0%5+He3'qq#n=TWtp'뚂G_ H=n[zhѢ;jHgWqYiժ^JQLNnhɒ#:7{'k9%qV}dܸNvur{Idh?h]Af):{Nϒ!J?oQM>3%AWN/g6od楂2avm?_0C=Jbb^}1YlL^~}XF\պƍU540;Dx qD!AڷOڇ?A(F((:|Z-2s^u=gV]ٵn]R0췧a]1Ixd$֭ԯ_hm۶GՒC{X ߵ뢼>~r6ZEph>nuIPuv뛬MMRŒE>lDGo(ɜ8qja4?x\̣{D8~|Nff"RpX1Ѻ׬Y3JY%))^֯?$?R^}<< Js7szG"#GH61{f` ;uŷq~XWRMILMǾ%Æ-:sQr}i|ߔ:b}ec;/GʤI;Dw& WܶGB]^7]P↓w=f)SPzh!ha_>ط,nr8`MDSUJ 3=DtP9LU^`{;KKﵝ$k֜4-|rb>Gɴ*8JDT.k-Sg$HHZuW2$$kgNV3ϴ߹_No7>[#7n/Yຐ ʈMSzU{#9xru36l8O^p oN"< 1JX~Mb;'.%'NOΏ L`ϋ5ʖeG3畞@k˾“oyR;;(zzJc&Md7ob+*p KyjmZe:"9GD睮]A(*ٛFqȑ "@&ɕ+HBB7VN{X=8::KFDK߾ dIg 1Rpn޽LMƍ(eTO($^H5Huܔҥ-G-1nU {*3KDwqPk٦Z(XG/ӧݻ3ˌ(0 ;'Ц'-tŋdڔ_-O?=_=L~zGO?uų996mu_X^iٲ>Qoq_5 >|qgԩT^\ŋʕTCɤI]-/3Qo"7$L)__!>:[ۮ̴hmDK]!OkjҤZ5h9!n==۲iɂ_<;C$j?ۅ0{o7;U s3}k\JNZ4s)s|!3g*(|,\8:BhlRX>'L.Eȹsz 9Ӳٰa/V3қ#"ʈ ʕʶm%:ڜ|d -Fa}reMN4&2A3u|\w :lUHIHH-y䐋12 7iذԯ_Xoe2x}8qV=i%JdM̭[1pd~o|k{*yHYyu35r ܹs-A*套ر(\OD ܗޟGg="W r@JeϞKF+8O4Zi̟OOP[M])i$  +`3n &(dڴ^ҧO%ݚ6m;]Yc]trrKٲ9>zsߚD^:y[T U#""Q\oȹ19?9U2nSR~!mwV8s斮+գ)g³e럐sKB_'uxk zz@ŋ-獖%g˖G5+&kJrֱLa{@KR-Tw\\:TXƕsC6Ӳ= TWWr%RV<.|Z~≹*G^j"w+ " ֽ%v䭷Q?[ p)Z4Z[:.&[srz!t ?~.KGIѢ䣏Gj". SAƍ먏_*/B=҆S$K %l d؎YomBaY_F <˫.W^Y$Q"#UEb[o׆k#ZKgɾ ?ܹ+*~][uJ"F+-'v)>X)|Dweo>R-$u ׇ u~"y睦e 1 PMvW<$HɒY*Wc<9ƌ,O=5_"y9g2ǂu31FH޼aRJaM,K?6T^\1KrU-Y^Blp"U5ȱLK'Ez`z8|FBB_~,eޛpgoɫ.i;OСKgDkuRj&r!*_/WL&[4#W֣GE:;w6Q{A 0G|WrqN<ثq".EgIG#VzwzEf9f4~|4n\heٳur#zN]Ή[baKꑴjH9CeҤ/JC2#W^|:WB9Ie >*Yy 3/Y\e(\p"?8#º@$[bTgK~ԭ[H?{?Cԓy-4p`-,s4ekOFu"-zԩGL3R$ɘ1qcS-ڼOo7j"mڔ6'$ٻqNpSFf0/UU^*$?^]gOk ԙDK'tQ6ʰaU'iCcA.Y?^8q]|r<_}1F$;=MExo܈a!R7|Ve޼!zG{V-Sho%́ r:[.mҤZ5h9/_3g9s͛Y^Rr yᅺҮ]i)P )+ܺbQ_IT…sJ,>+.േ?O;XʗύO[ɬYe׮ r-{;d`܊sK׮eK%8p<YE0'Z _4w[q|u\V-~}~Ned܇Ȅ t2lڴw$54m:ј~xDÇ/_Onjou3C֬ҥK9|ԩKry|Iپho5jWdܸP0o4aBg ɮb믧2ן^5B"[ ȩ=-YrD>9r 3kKF6?.!;q Yr.LdLƔkS^jBz^ժG2Sƙ+[8#>r&;gV[j̯F%K/Od_xk찄lqOuϐ!wȵk9AI%K(CgGo&!::F&ӻ yu/\!}Δ3ʔ);G0ےșp @&Xꄴla$Gx2n\Ge_Ug>Q}>|Uj0 ́ԯ_\j.(yKBBL]NI$o v*ݺ(&EdPIKP0_@}0_]y9sY7ob9gmɗqFF:.7%u嶆Qjl{.+U*|Isu׻,i}omi}ݦzK?cb&-bg:2{v3$oF eL@@u=2ZD΁ r%(0d`҆eiտU9a= u$JvvL@̆Zz&$Mbz駛7-*J` e\OSHtGw IM3G1fSC;Nz!a@jԫWJsIOA`Gƚ63~|'i# իfGSByyRx^XZ3*2C)G;w. ٲH99(^h>cXr"r& 22NF|`M&oiYr:u NoZme={U}t?=[*RybV2|x/^ҫWnH xyҹsY}oDEIc-J{cFne 9sZ_(ٹ@Vgic']6a6^-%w-XC\xuުn(C/=ŧz<- jZQzQL,(v>{EG:!ӦVDD ]YT^.k W3H͚Ew;ObYA& M?opdذF^.5nILJ|A3^XR<Ňرz^Z"C,hINR% %$$矯Qg9%I֬-mg 5b0'袅kKWRZq ,ɛ7:(m,hݺmG2+hi֭kWi۶zqۍKdGN?s/kߑYeP$y`d]H-LotJz.;NSJ,Ir/ Jl,0KG'eU&MʪUV=z 4ObcJ8pYڵ*GbM=kSQ+[ɇ 6mJ% *VlVhh }$66QΟ%gφY^ڰaUsٿ-h̙SeѢes{õ!l~^Wpru͗h?kr2kVo2Թ?l'a 1;Q~Z.)?l4m:Qr/Ju`/QU+"2کKJX w vzL{tUɺ2lX;Y!7Ni,/5ksY%g@]<(9mIkv駎ҡc OƮU >[ˡ?TO^}5WdD\\|_hGuH /vE= 9+^um/]B"isN2jT{TZ #{0ZO4f*Hru)ԈeIRLA1RVJI(HD;H瘑u"~Z@bKC\I,Y;*5|PgbnK293MΝÒ, G޿ry}"r$Z91`'o)Av$ bhę4?>}QQ,{Wk#&tց9~IpTrFHhh~'_~ #qsRT>y*{L&N`Hy6еk/"ޏ?v=0>v?O+g1c:Hǎ!a jX ˾flɖ-$""}Q)Q6w "gRvooo><v]O>Yΐ?أ,zK+njٲ.rUjAYk!xvܹI kmAy L~oG]KzʈǫˣV zրߛL~y_~g2+{BAlg6 p N!Ǐ_Vg%-h ,]ueȳ1`633sN#UHiy$&xMZ$8Oj6RyGu끛5+nze>b*QILF:y[=t!(7 K>rcUɓ;uP<4}0СmT /{I]7! MUv$.^>>-3f uJ|ygQz%HZȖ իl܈+)A|QJN=0oy:RwZjS9-.ҧO%1"ij.b#TCWϯ+ܣ e/ gSE d̈;#/FHr9T!Թ.L;G3;6i*Sf>DpaQ_$1ҷc*'vK҂D6^SpA# W7ȚpرX~d^7(_~Z^}N7_^GWOR~/ C?\%T;>$ٳ-}QTc42OuXL$쓼&LxIa/ZO)Svvò5뭬Y9z*7 `J# G7Z(|BuR:jUݝ[Y ¥[?%ٵu=k,lذev-n!MKl~/&9skHf6n ǎ]P-uhRBn)_>nWŊ~²uEuqLBtwj֬f+G;-ݻWTq#KV-xuf#"[u((WV*H6Եb= kwkO?Mm;-V??oĉ ~3l套˷߮Qg||ʕ-[#a^r*aJGNYsR^}[wDbTG~&9{>|EU'wI9u;=AR̛tXhDZU xɗ/N:XjKKT]BaB\w;h9'l ܷM&i=0g?c SQVpG$52gY yx}'<(͛%,0/u>t,]zTbbPoT+l ? G'йVG=7oըQaYqu/t8r[6N oUA9zziҫWu;DtX=*<}SQSN}ԪUP֭{Bu3>~֮=)>8Ibc1 ^ HFl~7[~mj?,.aF YD>2}zOҥ?._ֻ$YMS^ d5>2 @ֿҥԙ;d_o(h+Rپw.>.fjި io?Kg&J!s@ɗzx`9޳>KDD^4~5>'7>+udtC3 ңNָ~=ZWe߽?62Zm 5t-~=JmV-G]kke,d 9J|mG2&w ^5mK1z69US_⋋U !%ҥKyr %<2[ƌY&t, @رٺLQ"-WWAaٶ~_>|NNzk$pmFBLQ2l G(Qi-T-wڪSkTƖw[GL٥fD*m=xqLEљ<\m=u+$u\8D'#\_c4q}gXc9,ؿ[ԪUB}vAj:3%(lbʸ0:jT;W-\W!Iu=w\o||^ǟQzkYz;tR$uˆ^WVD@-\xH{{c-V:u*g~v]''O>9O٦Qȣ5NoJ^{D;v6#OƏ2thC{짟$dR`/Pk kh}"#;w?hg dƌ*-WkZr ,i!rmKgDQkr5R}ft]ʹsS;7:#o=C&LX/gϢ7Ks9Q|A3('ʌXin(y܇e˪zBvOKlHxΝ3ZW/@=[޽|mku{'>@F(O?=_db'IoJص:ˌ@ RvI @pr:ܹG#SgS#G&шmJٷkA5 ѩTGR=T'rViVz5S׉F;uTVWoժH|is_[^+2^Kgm@\9)?N^{mzu qN&@D x}T͜_aA2eJ73J郊#F:$}̐_ݬPmR3{>ifRٰu5fåA22c玺OnܰoѺ;I4ΣPP]0M]FNaT,*|cVKqra;g8A\FdZ]'eG}{kF]ϭ[1#TTiذ]QWϯglIg+}L?yc{ Rv|@[r*~K;/H.˱cz_{#T1sU|oNRtR\./!v鲶ڵ+% 3Z pAN?BW,>>^r zO?ܣΑ=:Y`1okwE D)A@ȾuwmТů:Nj+ QzfګmyٻˮP\k'dnJՑK/,k퐐32op8p#Z㛤rmF 7n<-7ZDt[TTHNg%W1OX/A>zCM d^ MFl%I:yꢷKN *悋"tdcoȋ/3=DD Ln2o(ZX֮} nE>)]beذeRl.1ش,^|X'$/cis?2$I``(Ur97omcc`$q>}SF mu]?cL_g$QǃUT+f2dzcvqZ:c}D~U/% g`࿌5(XpD燕I]~T!"GxxdѲ$پ}Th{*=(,7ظ[wJ_U8fuG-'gNt=-^^ޒOs" PH$y'cI(Yr\eDt[,\} Ɣ)dΜZedڀ>$/Ph`3PHif2txH{7l#||Ɖ FA{%^cZ73ڞrѲ{%u۬YkW?up ҥH9w.\Gș37a  !a_͚+5Z3f<\u/7#ZEFɁu醺EeY$ 33"O Wɛ+EQCe.zY"J/lZNȗ/D}AOTFxc:sgzufE^٪oÍ\)8̣nAxf 𑈈gɔ);ՙ{/I.{kUQ7c癩W:OJ~Gsf;=TYfTY+IJ//d>G=(ydWg{Nx@A-:%Çw-J;d C$))^k5TuuキRz!/4BbL䙐qWyK׮T @}e[͊&6q HY"I %_|h5ʗ%FBs:2 WQRSvA=::}*Rf^tĈuYƦ/Xp8LM&TQQۤciz;c;ʸq DϭŲeGҥp{{/ e5xdX6(# wsG̒u՘5Ͽy3]Hˋ8{mc==%|e Ju/ޒm+y6,>`ninMkn>TMׯj%IBBbYy:_\e310`!"J$[tZhQF!p1Qɛ롱=1[k]>̛t\@ ~iܸLKam 9[KtvD a,[_ZMo}(_ZdA #7nϔWLOf].Z}y(-LX(,=%ȑms"ȫ9Pau0G:믏RyRLNY|ƙu*3f<1 F5:{L] nZ|>_ D-C؃"ީﺠNJfQ]WF:v* 2Z?8÷WQr8fݾ.f~c+0j2x"EX9w 9%"J u_g/`BrU+"x07$ٳ`/ՙ3փ.WHNeUq].sWgLn +$0CJHHܼM¨ߪ/ 3 ^~6lxNOo'=7bAJf70;&`&ȅ z 3^8(/ZС |'yݧNݐj~WQ5kB8)L {%614OG$Sk4Gs}QCWڵh=qO@@ٝ$?7K7TMU+k'A•>QQsDITzɐ>|UJ['O=eO Nu뎪%I\ܻ˄aFs +B]1'r_}4nGM^26QD2[Aը8z I^Y:C2q{5jܸƒ\P 4n\N~B\Aҳ'(D-JJ* ӧk7ȵ`Mc͑mȏ?nÇϪGqmRF1loٍ3ʈ$'"`A/=ȬY^W3XvD5<#͚Uɓ!VVhxHZW?)Vw"WqRM޹48q=pKkO#g6bc9ԕ< ;Ǝ ]3,˙3/Wڷ&_|S֭,Sv~*KX2wﯔ>X#g.͚URCg)Z?QfȑɊGhQ J鐁~rM]DZc[ 뀂J2[h>|rED7Z ;>z eL Rǁ)xy͜Ku+oZصauF<շ߶!C-"q@J ˦Zv7%?B~"'0rFy9mq"3J?>&ȭ[r-c>wÑ{U4C{^Io6TDD( XQTD _@P&H% 7gwB e˽w͙A!w;s Dv>#"/& D(|q_;OEͧ%ȷlҥ1"Ax*d Ē%#Э[}I*p8{VA_e;o_D3P('LИ1֐NmZc"+c?.<]k^ziS^9 &|<0BaaڕO/aXt$7/9~}h31ax Ν'tDDJfP3ѕ1bTsEQbSDuᄏNE֙)GQɓmw{yʍ #S]lױlY1}jzM\d |uܙclsqBZf">>5k&ছW"B<ϺGDحVԩPGGSˣW{r֪l,_[ KNNCx*FM~ieCMt7C}"2^GY"+?/Hw|DRRfO*O_]oݮ{D$* ǎʒv.ѣ7 .p _c=i(G.Hd<0n\k f3x*$ؼy7%W'~g%wwmǯ9f|k6I;"% Ip<ďn(?VN~ 17o~:u#5QQ52,T\?l0L ii2疢r3SbÆ=x镨]5LTɥ^}u Z|ku뾎fC߇w˶mql ID$)㋛ܟ:7|n S3~]JFÆ0w50?X3FDqP2|w+q޼R: ۷#,W-3㏮yuYuwس0?N8??ձ;Aǎiӷ0}/8s}3O(eanJ> 7Xd ~#ʔg:v,k"0LpC}G-"2G] &L/Of;^}ŃJy?zNETs.Ap>v<5ۿ(~ywc°al.w͝A}u坿c/b t)m 8Oxl6@Ev۩"{rK(Z4&4Ms᧟Y7UgrAs H/Nʣq[71Ǚqu08(cql|7cN:I>Q^Qf9Yy@&$+ˎ'ԍ ׷^>`ݺ'X[^O;T5z@u39 <?xS~.^=wÆ]ycRШћؾ4"K2~!.ArcbH9%%Ǐ:\32u= Wg-"7izvL"IkI\IADސqbNO = w n=_}Ø}c3{cРJNNsY--ڎ3~CW"Ul 7믯-M\S354h֬:v, bQDz)oن? vQ'rmD vI_r=~MZA~/zUF||V\@D&Ic="'&L _'X{V%7t2^?_.-ZtI!*U޽0of7_I˅U'ȶDd$뿁k\_ ,,OYT2ޙxA'{ʾg}%0rdcLE"(߱A7qǷ-{&DFEFrI( &d/˜1螕ȍl#%7(Yj.ZS/\81_nDKP7A*XZ;Xz}su0cFgGLDư+kIVz4Owj Kt$`dCZz2cDc>d[<`g2'IAС:z6m &&3S>=cРUfy ]17/oVD=pMuuRR@fN@"TcSdFNrg9Y9^Tb:1$$`ʔ^ذn̝{`7BHJp{_ X_~Q Nqwo%{󣉬Z+Wq? O=G'#ұr#6ey r_xd]q5/߾k׎ƒDժqF4fLCut&]N`H&LXLYOF(ǎmz .y V11E2ͨm2g&jDNᥗb0rdm+ s&'v̑jޭ*_nl6L\S_9E6yRD6wd;ƍcpm SBxլeq">ٺsذUd%Q^| O&'Lc]::)נn]Jn?'Y,Ft &oߪh0cu6} KkCϞsp)?]Y hxv[;DFJjUƾym:ʝ`| $$DlKD$9"_Ie/3sx뭡榾H+'7SDdݺ}e˶(y R~ظq3/04R&OO?սܑv?0T3dܩ|yo`ᮻ8'YT3yazDVש*"dG;#[ʖo/tzKlxu{U7\E`=2ѠA1YP~U+{Tl<\_HIcxw` 26mfcѢU$C(ٶM_*,,Ç9noƩSt?Z(~0RAj!Q[uY bʔ6hB?vQQ@ FQ+fDVҢXTCsOy~٫vE2R՘Oʳ20}/7zUwݨ"yL,#rVJ;$n}TFɒEth2  t|-Sk<_X"{??(o-r% |`uwsg)fۣGE|Al>;v<[O?݋>mg,dQy_bb4ʖPLH@?&Lh"#][7k%VêU7!4ԹOOOA?v R wԴIL@2Q| 4)>}`f? 3"22\}`:" 3q!0X6mưV-)ED0bغU&s*8Idf^} [n"nÛoEDB⑸زv%ņ ^KD u\EXWvQ>)'|[t'}wy߹siX`]ٓOGXX,ȑ]7KHNj/vP=IpIGt70`@!!]V{ODuu_Ul=w_$_{R' WŜ9Rd+߱cG!,,L9Zv@ _d| $LzޞhG6uLD7S,Ů]R5J6,XnС5Q$8ȵۿFfU;v܁ٳo@ǎ5yRNvF5bP Unr"MnD I]w51-ynٲ-{擞gp71™3 <,7ϲ15]sk%4]&d|/)x7n\cLIrga8'wc1T_HHxJEFw(7^---gϦ_a„Eظq:g1/={ʑ\i`rQz*)vD?c漩"wct|uaf*2.;O[边ItZ?|=~uV xQ#m8' P *$'Ѷ-w ?36m:"~^OS 0Шumk/5DF[eVd+>>#G~f袎PrQ:<ԩT̛I.,,>VEeridZob$&Mj{bQ~rG}c<ˆ~ۇ [Ȉ?,،ܹP3(_̘>k<}V; Uw /tlJxf2u.>3(OYÇMm5@dlOgD ꫫ⧟bذy8t8o?~7pL7kaԞ*k'kF1O付0ٖSH:Eu|p:#S ZȈ;y/ea̘o@yTf ;4+Zt]*?}Oz1 +IEϞpuȷnDn:f:;nTl戈(7-ڎ;O;222[y!**LYYxд[X` 9;({gԪFZ˚/s%c0`ٚx|3ȖFMĐ%nݲ裾ODH"!h{WQD{ϧ f?~x:Ν@?Ge މ}")9>zw/ŸEf\D@ W D6|PlgQ@rI(̒h׮:,@+D;66:C\9&(wVއkOɝUALs9w~uZ38y㉿.3eP#?ٳ FsD"<),)AQ^}8.…ei ecd qb":""IV(?z3csLϋ]M4_;ĒmM1EYSҮ]BE9%xr")&y5?ރ/܈QP͎!mLLTT!ԩ-3Dd$[wmS?JȽƍȸt}b[Q{BtFЕ}+vk dΈ=_%iՎvD5y-5V<] Ü?#c}ѼyIGb/4Sr»6L;Ij^xaKvwkx5:Ν xq_Y&3df裢>C'U;?6Qc;t̚u=7^綉΁$Q {$u`t!Q-_Z,eFꞵ,Z :"o֬zuRv(Νs$DZBrr|ګzWɏáCw^ :O >=_ 3)lѨQ#y U{7ʓo+ĞEBBY]=zTt`*T13}Y$kO%g_z5+)6Zs] jժ`p_3&&XGDD'u3-۩{TtۖÇ` ܫ^X`cO[OxVh%g;-*D1= u$"2c@F޺2/dP/!2_|^ܤv8$Tw_7}=ڶ-?Q>1`BZ}86#?Bȓ)7El97NZ kYMU"yB=@h\d+WNDǐ"{- s0`x8޷O>#E`ذϱkIGrIhԨӳ[d ˿Bغu, yYXR&1 {eܹעJ"MeML"O2*Utz))P kǎ:d\j$< 8P3}:6uop,]:>WÐ!5wCqeUFx/{Of^-KݿQ~‚% pq&Ul&Yɛ322f2ɓۨ$-zܻpA4o/ċzHH} R0mZOǠʔ=SvL;>p>"bJ*#۹S]^j$ #No5wJ.nݪH*[]?9;h"3@˖1aB3w'hxYf`HJ2 "a̷{>6nB;DDDƔ 8w^~:,Lر5tud_Q9c*GY6DmLTѢ:r[ʮg<(V9G񥘘pWȨT@캻j"+'0p`$$|-7ɖyu=JqLyvUƩAugIU}#iX 0)k-[晊=#N]!9,N83~+Z!dt6,\'֭Z0gn*ȈRq}ЪU)wIZ9 y)@pp!#3601wpY!s?m|KR-HLDdT!3S.Ui]+Bv*}&C ZT)ki޼:1Q5J'P-X0X:k% 7eU"aZ.m:rݻOaFI2Huj,qI_"2٦lXfscrիs{\Ee7FFqEŷ}͞[ZPޣtHH]T)VGޖ9]Nh0`ڵcT$K^ TG;{ޚoV7cQ޽+DĂd6uuc(YR uɚ󂬏s20nB-<^}u5?|?v:Ŋ=-f~K9 Dd6'OmJ46qկ5K  pyA2UkWye=z|!wd3V-Kaٲv;&L>P"T+:{y/&7CppOV99G\%yp)iشNn)FdBSL&.Gy7=f|>Qo߲j>4j<$>mVĒ%ux34UEW rKcnHWZuZ8d;gK䃱 g?s8t脣>|B ]j$L(55] ]QgȓlotwzUBѢF\%c}r/6LBhjP_~9HI%ԩ{^71#7/3g@ǎ5TjT_~Ԯ^興d:"2?lv~Mnhɓ{8RhN*U6tR^˯CX4'RC\w0Cdˮ/ G3S ^B˫ SYDd&_'6?K}ꘁyȄaVǚ5pxaQ)mT դڿ$$By׵Χ:n_?m5Phas>Z[Se/;8ys)@߾s]IHWqY3A!2~Pɓ={T yϮ6{uن矙 +a޼K*睳;+[ N11!tE}VՑ+db6V9Sq _9 V~2 C%U|s{Dd7J"+8o"PI}xaXfDDHv gqD ĤItz%-?̚;w}٫LƑـɞ~{D˖NɳNS; BǎeKȻCJ kUdWz[~ >h(8zj/: wقΰ[̳_A9s(ycмyiuOd ZuZ [f?40ծ6MXمg>i2Xz#"O;x,QUmPoe-S+qku sj!SP~9}cϞ(Q"RQX*1 &Cݳw'2 +'\~$0ɔU3m2RvI71yގ'tDdϗ RNQ$Ϊv wFVd]@ft@|j/<0r|1@n1thM|*'W/I\I +VH2"SdO瀉:pi4o>KsNDu@SKGѵk>}#l6\k  ab:^[MӜΝǰ`'"wbfР;T$;ȍ+8Dp.8?[V܁޽?rʅPDF^n{*THŸN…G̑+/[W;|h)#:{x:&"wbܪuR8|xёLLvEe^*Yٳx:D 5YZ0{(bbwb˖֓{YK_xqߚMfpz~7aßo}"r& ɖ-cUOf`eVyZT##+s5kU5m*^ !$$~:ǎ݃N9_"i׮,][tt|7WGoc"r&c)ƒP=IW-#{̨5DnSK :O]A\[Tp|(<}Ud7 (H\QX6-{fÂ9ӌ͘ GS> ӧRgFjW- Vhpu\- Ӧ̙#tLqW?Tt#!Rb`f,Μ Jwu1I@)>:fnGTۧ!߷;{蘈܁ qn&tQ=YiUyRډM=zPͦƺؿ:{ȿs?=';O] \#OetJ}}[ ݺqK?oj֬$ e ݺU@RjiD6l}sTKWlm:&"w]yݴiXɴ֫V*DMv)Q?oZ5NG@bb&Lu؎zpvs}ՠe$yPVQ yې!5t7;@^= F?%מO?폿~>R&q޵4 "wa A@o)8@$|"hڴڶ-#?8##98 ݿTxx :wֺi[o'y_S#_Kk!TI^ɒڽ;=d(wWOP|X_~zYBBaG"QJ44y˷n5uX r5o-SLUvDFrǓYzPEƵvPDLԯÇTtѦM)7TRŋAWСûh]1W'q&z%Sq}Ạ u5OM7V 'ȉ 2P:uIk2ЧQ"l܂;d`ŋ`ɒ-<7d2rmu2r7z`&*ʹXfw@Fk ɓQ#bI]J"Uȋ:44X[e7 ү:&Ceu꒒0鋮]Iwtʙ5k&蘈\ 2_~w#5UnMʐ=dud-pǸ9~1믯2D(Q^,\8kK؂|.dag=x@0thMXq=4Hgn!UDg?1ňR w ο11@OL>KW:DLQ}`utŔO"a]:& õԽ++SF_vݡ2]S7obbBu܁ ""7s5ZKd~6|&/^ F ?/BضM xݛ܍ ""*?쫎ؾ(֮={dGR՞gnݢ:*W.}wrYw% "r7&4O{Ʊk <ď˓'F#F}-Ajj#"2@L&k6Y||r]3Xx݀tάq1ccOCBPXQAtXO?OEg'.+\S/MldKVojٲ-{dvvD2Ϩ(vdI/S`vy $4;6o*:^Xb2]֮>`Od~OٮcݺQV7o|NH!11\\̙4DF>@ҥhB'_2:vDѪl,_[3N]g u(S)5?zAɍD֯?,/{HBy]uwN/cCdy]5ݛ)̟~?) {_1>d,8^gJXXa "";μMƎ\iΜ~Z.XU9'&)ƍ?x/qܰ9&o%?$'_:W$-{-7 < [W̩IZls2gdqM_aNyzo[#c&w㵰eBɀ_>K|VC+Rοk :&Wڰa4U$bRѨQ,]ʙD%Dy+1qBɀ>{/iaa;y^d$M>Ѹq .]%JDLŽŋG(!*7Ds9.,ke`Cdupg_óeFH8+.~3Ǝm{j*H\T(5NdX|Ԯ]~>KdL\@D7o/P sۉ|_)C6oS 4ٳ)Xx3|g<>|ڵ{]ݰLCH&4nUU]5\>sA漦{Xl2Us:&w4IfH&[ L.qqض^ \u}~"9zW˖} &7Ҳ_R a͚]7n#!Х=h[u ];ؗEQeu@\͖ocrr家 #ЈrFW1&/g~QGNoW] &ݲo&/n |l/ w=du,7"u:3ftRG)x<!!!"6&⦛?:]f^{q.NH2@YxTL= 6$D߾UuL4ztCt\[}η3 +Xd|Si)"[p0eJ @t9~~ҥl*@8_oQG{M:u76DvyubǑ#PSDkםɭ1ꈌP!o6 3Lʻ0uprO& W_< "-5ەɇM ZxyȃW_ݨ"&ӌ_dwˎdT:WG)>Su^Q*:1y`DZJ/$@ VirtUtyd/3dMv4l)筞8$OR*T=2?[!ʻin_yDڹsr#/I |o?;)"zv23 e/_ o1GNjjowe6$Y " c61c~v(V,B!ʟ aP$aH B0}SDѺuU8O ~ |俎[oM7EloRn QQ2 Lʯ,|s=DZpK$ '~36 ] #ԑ[RnË/v1|_X/t f]%"3`H$ ѭ(\ o`UQKCtDjjojmXGrAXQýOZ7H~ @WVJq4iRB_}n#JR!6)#.&B|= $ 0:n>vjXp= 96Νk^`"""pnCv=pm ЪU)ClթS9,Y2^EgTe g /?W 1:{`<2gׄ6o>qǑ#qhDٲU-J50m"õ~$~俊WӧÌQVQ]նmi{S{dxuFUd,M%ѾAdN%T۟?\oTٶo1De'ls&K$)P~QV>' 0z> w ^S |-HJSJ͂gr/;Hśo19=SÆuφtX[aΜЫ* WMsdž]du1@1p5FgKG022֮ݯDt5Wþ}2z fDkFǎtȼw#ᇹs7U${C<--w0@m=a_yl"ۉ(77.I$&Ls~z6F ,YSkpM'dd\m'"scHFv<*MéS\L߅bŊV&MnkWջqmbDPZtظq?RRgm-|22X "-99MG7IBT%"ʽ={TGpLLE=趣P`V'Z-3nev_T=Ο "-%R?:t>3g̿Q4jTLGN޾9ضjƌY6Plش鈎eheɌ3B =# K/;Q^ XQQT\k;-j6DyUne$mٷ jxVE!!, Ms'})8` E~s]"ʵGi#*4/'@ɒ x鎺Od=&PG,Jlə3Qܣ&#"kbH[h`lXtQnhQJY|5l6l.%wi]"ٜO͏Iv|5,;"#Ur7!!u1@Zii\:A5+QnԩSt Bǎ9zYt;wޭc"k{[dXB\@2}/hO-ӳaփ^zFd^:~ /G9O9yFtºOdmCD{a[@o v,KjZD'R1Z6mjΟDVVQ5=fT?[OJĉ]лwe' V݄E<}/㍑ r&# 9;B6u-"ʍ-[ꈜ靬'bgU;Z^gH*yhQO=Aȓ9)?8 i} Kn=r=,تc"?<5P52K2$ mVõ6B.u*$TjL=sDll/N7b Y.i\~~S)vHBg1@>sg)5 ~Qn nI~d̝{-z0W^SA{RU}EYHzzny\lc+pW]dOA@PP ¸ YD妍a<ΰ ʍԫW-r^?Ս(Z4VR44V ??~!6"UOi׼mɒHL|XE2h/_+#"b|Қ5'Uķ8p{="oБoKM]={d}#H#\tz:u*2GOf__V:t^;P~ 8AI-ZRGOe⋿uLDnhOo,68gCɓJ`nFZs`?:w'UItȚZ*ٳ&ЇK/߭PS)4,OOW,g1@>W9yjg v]+OdGSM-S|͛<AAC[_~%0xvC{19xBV,E(Zϕ(#",;&NZEn ,ج#"?eX"1w/JGD_,xK[f:&;;G>)N?9QrrӧeO2ѨQ1Y3Ο믯T>8(g6cQ2y6I\xСXr,s:"6&'̞:fo_C!?L KD(1~|Aw?Dvѽ Jr| }K:t(#WKS'V3 駛ԑܽ} {'4mN9R_YDsuL仜.W @鞤f֥uZJ!1Qv!>',\:Amw"q>w=;p଎!KnD$qɎJ9 ;ӱk]}uy B1z勌Ydyk t"Щxye bIǎ[kg9jUJhʔ6u{ȵQcܸ:&>&~]5Qs N:p]io_?QGWujiO>BӝKp),XE]k躤i2e2lg ;v,E0쿏& 0mJ>=_' +Vt9K_~ة#W50{@g[cNk0l<ر3`G~Uul-I&F=|t:wL5y窟~~6df>Dw߽>BEWN/ vւcҤn&Փ$'PSv8'Jwahܸ%v0mIDAT h1 +V"y{/J݅xk,UX]zdyn<)Y#k˖3]w]i!{!&BogƖ-2E\.:Ď;阈ꦛ!.];m}Y71!HY&P 6Ar;=*Ș ۶턎ȺICjjaXbcT MÆo/6׈CrhР,9I2?1![VuLV-sI3u_!ŦM{7͛~r&vwY&Sdo8^z d;uru>t:J%<1%Jq]uLDꦢ4.~a^cVGaJAHʕx5d(LvK] Pus2O}0݃oard(tTCE{y[_}?>&\aʔ(U*^ER3\d4_=DeFv⦠bC[ny #> 4b2;@fk"/DFרT֭+OnTrzj^ժCl$ vMwfEf4RnŋǩpE@cp1ǢE| g}fmb-˗oT7)!ILPdbOׯagyÆ1Jj(O~gHKCډ+޽wgUeYɌ/ȫ K[xr"?~EOSﭧvm4~|u==)Æ1JB_vq(S&Yg8+2#K7r=vE@qx(UJ$ob,i3ܐQhdf^KFVh#y  *س0BB}&uDt5r9uYDӭ[u`u#:yk6DDWRDJ=W ¦M{u)LEG[ ʏp u|VC *r݆-[阈obEY> Ç|wzc^ז~νvضmf;԰_5gHE),"wP5YHDӢo%6uc{ >|:7};ߎqstǐ!5ѹsu4]4"+I\{wsۧb„'5Ƚ ]ԑ(hٲG[O(V}ETR {M ۶MwtoT$#_IΝCzqɆJ96x^, 0YMppc0Bm͚ ]l8{,jԸ4 `_8ȼʕAǎޣkÆЫWmɺK@*z5kF8w&Nlʕ f謎r" ;jۯYj%3̞ȾQX֯AL >3Ğ=]u+k,Zd~:{6dqG̛'Kh 5rdm7Y:&@,&JŤЦ =4w1NuRu}nVDDѶma'u\OȈ 6eJkudFUҺO}*ydc]wSfbɀ_G›@DT0sjO9 nq1@>{I;ƍc!2ctd>:U3tDv}R y5QN0i!23'&Mj#Сdy [pYy:;;DcԨFpor2s [oaY}۶Y"" a*2/7S%IDd,+|76Xv;OH,HDD "Y 0!H>ojdUIIhĥ^@YDDd}L)$^*24[Hǫvsv &:Z3+YRe5YR_T5E0@M,.&; ˧dX rysЫW-5|N& Һw…eY$2]]"*\̳\CB TQ'N$L#""&&.2iRKud:bc]>};E*!>Tu5۷ YD?w9?|Wg}c܎Ut ygcuτu on<.bbuȸ22XF89]sM%ݮ]tDDDdMLPGn 2\Opy&?(!;\ЧOu_3:"""&&Cn&[("=v XEp?Crl}J. 7興Ț Idt̘YDƶ?V*uilh*$ݡC1@t:s-YDABBۮ]'ѷwHH[/UfQ g1@jLLwNd۶I_~VllxrYD9P'[E*&Ndd?WG_!:ʛCuDDdn[W_mGm߮C0@tZL VdĉT[JJYT|"29sB`ݧ(Tz|޽_Bd(]zNc +z(7!, PMDwY_ի %aj?Ċ32{jR Ef6- wB^sT'OCF3Qd\L]A@ƌi"&E L"sx1aO`ϞSw֭tW6N%"SNIIbʔ/TO Ǫ&_~&8ĩ+ 9VچQ!#b*M렎`]vYYD0sZ:L􃎝Ο޽/]zz&bbāPs#88ߛW lVPl~y '+K._m̯i:"25kcϞG+֮DwTޤ2@DVK8^r5;6ot$XgȘ ʅ'hM2`56 M="{_Ց/(_ħnڵ0jWR>ꈈx|عs[:vcR9ܿ[D4cF'uTI+X1֑ 2,;{oq~*pͯ M…[Ѣ8rQΤc}`<7ӱk9" ;O8)11@Kc6FRM +Y|Vѿɠ]\'Vvzh„EyXg3CD94SuBӫWuosm:"a(,`%+ʠ7Tv;6LYgy\Ef&?[~ -ۤZ:rGSu$lڶRd4 ʃ%#U$SLyfqq!:"2 !66 I|_JO`LXBش6LZ$Ȋdfvb0kV?N:_(}d}k`߾ӨSeWY->HD'ȨpiT_SuJlؾul "7y[ML'^L d&f*V:<9oذ4~9sKvjZviSZDd}ȐwTt?͛AŊ/"+[u]];N舼 "7jҤ8&\xch 3UN1̙O5k7]UΜ3TIT77:OI/##Cڷ[3@dsSu?QPtn5ع32`LymcQRQr2 @fR֐ͤI-u$;Q*{9/Y27<&}~(wF`IeOnfI0v>gqէv r޺K "ټy e7 _J.#"T)&9ĉT/>YY2+OǜHu;yM,؂y֨(7-[]گyٱ%7&G@|T,]K)));-Q2&<LEŊ:"25d)yzx㗈y'OVg<s={;S.WǼm[$mٲOEf^XAhf:kY+;|+o4:nLyPE1dH1 }v/c"s&.s7f6[o-S,e~bݵkSf1@dvmW׷UdZN2{!=-̙!+#pmH6nȌd+=稈[ ݞŋwy G D^`ٸWѠA1G۶ъL]A=0p`u)} ..˔Z"2ѣ[{~xtLpMv4lȟ?O`?Z$ 0iR[y RlGJ:""쯽TEf;ݠ#&~}ΩPp(z]"c1:r+ :ѣG8Oyy;:u"3ڳZ"+UY0sZ#o`K{7[d^^[UGdt{d+"3ڶ8J"Bq;1yD^Rb, zݺU1];.]s=:r$YGWVV" l"2oي V6G]{i}yQɒw2ݷ K'$PAD4lX-u2#;x𬎮Ď>}蘈̙41]z[yB&MZ)D^Tx(wd[%KFD ܸ Ç"#ɐ[!9]=zpkk0}wG'a>1yD^āhU$O޽+ȼBCѺg7.?cYj:^*22u$xU {:#O#TwGS % /Mnþ}w|"l^1dHM5ԑ'd["cڵ$F ?@JLOQS' S "&_ Ӟv-ܲl@ѯ{3g$ n㫯aÙ(SA̜){& r?؃߮{ LyQ||(7o׬,|G{Y7/::M=ߖ{t.>bV]5yIDGY?GгXz:+3 {V0nu B:ܘ3W4mʔy-msK8D1`@5u,ȮYէ 7o^!!RE!55S.GXCxLj2UkeI'0@EQQLM~y 9L}P&kر:2Gfz)7dff~FhCx!46u$ N$;#[lĎaø){ e>[zxK3Xb+6m:{.LyQppXmd#~?x6þ DաCY#G3@fՎk蘈鯿diطOޛy?܅ "/s.~? ԪU1?|xΜSq|LJlYN:{ݧtteS'Q]v:5_T q|l;/_$֮unA5"_ѪUi#GRt9IcĈ:&"wh㽕y3?܁ "/ vvtƼyz(Ԯ]*/jذ:r| )I_nǠAuLD2k:)son~x_ r&390dHm8pz|ȇ5iRB}fyI zVѢHJҽ+V, 5k&˴i+ԑ|#Wc˘-…#C,GV$ AAw\^G]H<@lrXKtLq @n~v4lX ݍ9OkXf*;J.cj^vayȑ\OnKPGd%+Wgu\ "/ ]LÍ76mrP/N3w. [8""LmV 0^.`G˖}~"Oز:r?k /cr%&,,Kr&o RTw&&jHr%"a>u D^rb^{;RJ:26fCݺ%mX]7T]CT$40k5yѣ:"k ćcr&,,5.eM77}"R # 52O ZaQmDH\yj.x뭞_?IJDv9̛IG*Ly op?Q^Y@c%KGsQNQ8zt"#SuDV}!{Z 2p޽e=-UVM˯C9"" 33 gΜ=1D^_(/ʕsvǚ5uAV\ "/ e࿸o6Q+gUaКGDtAJ0(YתUtD1pb"tDDy|BEJnAΝY֭uD1phNQ#.5j$興RR2uDVuD*t "/cRQQtDDy.o-v)c"KIa 8r;= D^l6#ʯMK:Xr%S "/cR60yJ˖MuJʒ "p /p`SA1@e7;xi"ʯ]+ufѴm[FGDDe]69 łuDDy%k*{f阈R\`u潡0@eA5٢KQAWUͿ @8g4]^J V ʮ»l"/Cx$H+b_D5xp u4͒zUAXDT5uDDGIutDD%U۷/"3//cf:&"<:;Աʲ6c`X:͚Yo 3"ohev/ƍ>ѢnCpklvtTNDTڕE*nҌtLDd&,66 u&Dz$&F>OwPG#>>#F}"%'s uI-:&WaY&:&"W֭"*VIf'hc"+ +O*:&Wa ё İaR\iڴhYΧ__GY`nL@|||U Bt\k*lXa@|RDD%VeȑuLLJw~{#MF> +xGDVeǍ71D#_uqW"70cѽFkX+ơfMɆ\ "ӧwCP? F].]*rg"3,FG M"V- Fj/ILw_sTy D;3l>OqcIy Sl6f)"OQo~ҟX.O)h ;rQ~UQR,BCR'{Wq$$10eJku?XT"u 2qm P^1@d+ƪ/Yh׮,jJ}" b EfĉՍp5)"ؿ|-kUtv(}f~ei!9Lxc=  & Ībm[ Ulj('mۖd?sVvlʾ[S?UQKn4zUVǷnd_d=`;rCe-Jb Z, !)IRP|(2Pn ̜C[ldky.[6Ra­Cݳ_1cA泰rv<@n IRv.-aauS*7EZEѨQqkd,9_:\uBLL FѢaZ5^} WHjݸ4uv<'!2c7*;!zB|fr=7"YꨣPx>glZuZ 2ߎ=ߩxZ֬zُ֬.]>ѣRDK 9In5+:uvT{&NO?.7͞HG%v:f%5o^֛iGDHMRB[^ІO>Uu2&L 2ozWN A=\"(w!ʕ{{௿!(qqamDDDDVNQܳp}-p-o&LHPnĢEuuRJ߷㦛c6SdLޒ~Xbțڷ/sȺϤ 2@%l9#""""BѲeic;L՜YD~;CtLDDDDD "8:zzJŠ]2ODDDDDVSE MuLDDDDDVը*UUY(^<#G}"""""& ꡇZc2jëvCXX"""""& j([6NEJ(+3:{<YD6k5 R P}+W^nU1@d`ZwKa>|ި](  gʘ 2oHs@/`]ر~i/6m:ϐ/aM,^CGDDDDdux4k&LB "hܸ8Bt5f^#""""aZytaȧ0@dp~؉Culڴ II xn[ 2 Zv|zuRAR8f b+K 2:uu*~;w^z {-k 2ѵ$'YUll(&NlcELDu:YYط1@dA =鈈 ")Y2JGH#"""""+cD"tU!$$@ʘ 2Eu v4o^RDDDDDduLHB+Y۷ 20\LLZ.{DDDDDֵg);&&LuKׯ*l,lΓx5L=23 "p] #G1=O>ـ?ǭ_݋GiB|6D&%%JDIGDDDDd[øqߢbzE׮w.&L$1Q,%;%. zk'haj:1@d"qq׽>aٲC]CԽs y# "3 CRu/ʗq4"""""+HN>";*THkuǭwHLLrs?YQf%Px"u>>}W(D&SJOJJW㭷zbhР>Kcd5ɇwȑd#""""@?tXNr 2N?r4Ǝ @DDDDK 2ҥ #::D#} ݷYYR̂ "T`uP<x|D&)SW;O1@dBxJ c|U9Oe1@dB~~2xw%Ǹ 2!?s90@dBt6fQ1@dB $+S&ZGDDDDDdULPϞ{רQ1U1@dBŋGy*;O-[ "뮦P&LYD&գGE-(^< aGDDDDDVMJ3|j޼ʘ 2!Cjd"*,zdG""""":&L,0O=^EP$ 2V-/y"JYD쳝1 YDЮ]S]EfGTT!1@d3ftVp~&  Y 3ftү1@DRH(Əok6mDDDDDdvL&|DDDDDDD> """""""`0@DDDDDD """"""L&|DDDDDDD> """""""`0@DDDDDD """"""L&|DDDDDDD> """""""`0@DDDDDD """"""L&|DDDDDDD> """""""`0@DDDDDD """"""L&|DDDDDDD> """""""`0@DDDDDD """"""L&>|QN "z1l<||z}. "2,1s9ٳWr "S9w.C̙?]`Ղe1yr7>Z@ĩSuDDDDDDƔ)K2/ڿ/_:"""""aLaݺ2e䩿qbHIa('.z,-KaٲGDfק *0عsJ.DDD@ZZ&6l88r$ǎtg:'3ӎ,#88AA @bb8%"Qj{Zw략0@DwyLCFF>sy_~9=zT=""ʲvaժk)ǎ$2(lѢڶ-6ۉON;کS+R3&{إ3ʜfD`#::(^<JEZ8iSի/ȗ'38|8 FŊU"&&L "Xtњ+?u㎆1Ǝ'0o&`R"}\MUM!(]:ڕ 7E&EΟDrrcfCxx  pp7=_i2AvEdƀV-u&crիBB8항Z?kǾ}gpD*ΜIs8$@RѠA7.ʕ3Qȴ쯾 9:= D5D@EEd@ӬYI4o^d~ f=zWK\,{&./G~.n"Z-=ȕ~grA\\{ݲ`lh/㨵дi n]QS0!&iӷ$)73DD'Si?_ƶm_ʠ_lP҂ bKd$Ts rbɒoa\ w8! E)+E֭ˠk򹪷@TLDQ+-r' כ.&OWH*ʠ_ee<-I`Fam咒‘X睐:<^w~f Ȳ_\^={cC\^@˖%ѫWeԪUT0!&lsIˍ4uX ͩDmRnݺp2cض8OW)vAӞ\ l.\H`GA<Nҗի'OaK/…[Il>'wIBZ5Qc㦛2@. 1@d:*)M&MjGiD 6l8?<acSػ48^t+aI HN\F8Gm۲СlŠ{'Vާ~G__~2zę$lZ8t)QbXǫD 1@dOIEgwT޸q4ʖ["5yҿk)G'dZ\7dPzq3D@_ı6ZOڵڷ/λށg;]ز8vD,E RtjpwT;D 1@d MEZ|Un ^zU4s78nd*޽se?ݗ͊&(QY-j;@2{xյXJ =B~`QA~6$&Aj񎢛R8P℄0,$@ 1@d fll{DdvG: u3J}I ~d:t&K@q g~#,Ù3|"/[yKvbL~ @bb8"" !,,Uk+ƍ;ZR &5̘ ڻh;^OyNHHDs4F%j2j`Kwu-EUX*hSUʵN  Qa'Irι8'd[7}N콿^WԞX1yQ-/ǏwEYU>W^efM3<".dN9|to,\.яo|"ڲWՒk4|=tm&~=zdLr8[! Νrظ56mj-[vƶm;b{:_|{99iowe[`ŊM~˗o*{,-*pxߛo~q ʕg?\e׿^^.#kĎJBD4|d0cԨ1J~H|C~T `ӟ.ZuWqVe(P~yOlւu].nvEq;v>_)-[uVVnY|MGq~/mT綗-CVk9uyo[[ )3ԩcFT7gزnݶX4~\?Uuz\fwlю+ Hky(.XleqoC CsqvA̝;^YgEs^` Ջ߫uYxc^տ_w{___ɯut7m~j+on=`CUk5oyˌ^ST `vYߋ!=}QΎ}=zk}?eủ"xs{zw=go{Y"刪:u|̙3\b3ɓϠy |smޞG=!\US..y~}.-{[[B/{ϳe-鸾>+?rD@[ :2fӧ)S >0ƏRN9thLB34 4䱸{kv ]ٝ45kKW{WŲe-jXrKڕ^~gG=$^Oҁc_\  Hc-엾ř\'n (g~䑿qt,Z>niQ$}tm9\,O&~n]rd-;.r87WgL34 4⨧fsB|gtӨrH7/n[ZbѢueK.eo.Ngs̡q%skY1p3 q}+{^E:70FEHO…ˢǎM#Y׉WݲϹbswxᇏ'D H@#F묳W_PQwkY&ĂM4?W3,ѫ8?-fϞӦ*/gBЀ4;\{ݗ:eu_~1xs۠AbȐ- \&ק _o}{_r۷or)/ƦM;mZl;:;ʵׯ^ su۞y6s{ӅԧNuJ/+nXlUq&\SE-w2#f:(G#gQM$|sK_I=5SKYPZ^}` [lM=ׯOyܫWW|j};vm}hk뺆nc/zz;+Wm3eGo._k/Py= aPQZŜ9Cǔ)! f7)^fz@~zިmoS^i>ɗ}/'5cqq/ef̘^~tҴ9sLL4 b q1WC 1w?⪫.^OG9 tR\/g}Tt4 @YdC}S`xur喲W?g߸5ZZ~O"װkݞ|}?G}Qo)GkE ?~h$رL_m?;{93pᅯ+xkm޼e; B9yO]3tK/Y| b^dto}u1wC^@gۖ/-{WR۷g=ÞE~} { еu]/bР(qA +Ix`uytkkG~~\gذQ=$7oY4~ښ{F /A@ז-^!C B./v--;bZl֭;cϴ sh|0U.ykm{3o߾1`@s5ۀ}>|@ddQz4@z]vG,[8uA'ߢ9]@׋#zϖzA|>7}oR6޽s܎5(9f|YsyϣYP\Gb//s {hn_. {]?c3ѹxcq Y4s`J4iX{sr* 搅}GK36Gfڠ .Zk}o,(a'hB94/O<8hubԩqo*NФxQ|1L+ή/ZNC/m[\xǍ7Yw܄ ĉ]::~w׿x-nl9) -qOza\v)q!G*"?c{ss-R/;rI-ַ]wa#QH3q8q? ohͧegYm3Ϝ7xqQ3N=7nH h4ʉ_尥ǤIGm{L@5pv޽1no~;6?4^kJor~{cuqu ?Xq&9X\Uߟ`gE͎N;$MC/AuM޺x%K6=;~{39);@3ɹjetұ*^1}QIЀ/ڵ[5<_XfYq6@aqG168Knnzp]g󶀼4?1sx{И2ed B zƊ⡇׾6?3[A7MG;'8 E?"z24 @ڴ5.\]x| wŲeOgX\Wҳ79㬳iFر `_'Œ~_qr'4ΜIeo~M;$V?67Lë Yzw1cTOЀ='6ĵ>W]uO<\B0+!g̚uX|smj?p`R`{W7 dY~9X}0#''&h@KKKk<+ի,愁4%=tF'63:h%v$?˗w,+ٜ5' OB)IũΊ/~MA '@4 oݺmpy޾8[_J @=w .xU}1y=:g qK[%q啿y/֗4 @5{xիIG߾ne4 @cz-k;߹?.|^qh>a /,ˑѯ߸8!?>}dA m߾+,/<ۘZ[b\ݍ?ώ׾~ H<֯ kT+-e`w9(4 /V3f @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @PT*@ @4m0dH=;\KXG~S<_|#-[i7iZΉ9d|Iz聵I׎g^n{uV&p@yhbF?4fϞ^>4ٳg*OZv4Ϝֹ%%8b\:b'ww -Am3f;Y;4q'ᇏ<)W^@^zG{zŋ?ThƐ5|K<ԇkc0aи 6N q K4wԸsE3_gt?k_7?bot^0//?/.hv @qiV /ZJZ.SO} ƍc.-Ra#N{=?aG'P(ZZ6Vhmm[@ӧWxИ8qxz蘘3gR̝;%;nB3KRIENDB`qmapshack-1.5.1/MacOSX/resources/Contents/Resources/en.lproj/locversion.plist000644 001750 000144 00000000644 12572350112 030323 0ustar00oeichlerusers000000 000000 LprojCompatibleVersion 123 LprojLocale en LprojRevisionLevel 1 LprojVersion 123 qmapshack-1.5.1/MacOSX/resources/Contents/Resources/cs.lproj/locversion.plist000644 001750 000144 00000000644 12572350112 030326 0ustar00oeichlerusers000000 000000 LprojCompatibleVersion 123 LprojLocale cs LprojRevisionLevel 1 LprojVersion 123 qmapshack-1.5.1/MacOSX/resources/Contents/Resources/es.lproj/locversion.plist000644 001750 000144 00000000644 12572350112 030330 0ustar00oeichlerusers000000 000000 LprojCompatibleVersion 123 LprojLocale es LprojRevisionLevel 1 LprojVersion 123 qmapshack-1.5.1/MacOSX/resources/Contents/Resources/QMapShack.icns000644 001750 000144 00001064720 12572350112 026030 0ustar00oeichlerusers000000 000000 icnsiic07&PNG  IHDR>asRGB pHYs  &IDATx]xTU>3 A ҥ(Q\QuQۊbY+]Q M.MwB230yfn;|dGRHfmS#FUvWfފ76մie5fL7ճg}jG;KqÂ_tKܼtȆ+6z^4c6{>&'5zQRGHߪf̸Ww8ѕ_Rsl*=s{~ƙN~IfUC>!M/r/`WoS5k xeZzg+iڵ~ [Ip|< 3rႵ$e_s.:lݻ̮pM{w>"u. wnG2e6" tk)G6~gTbb)Uvy)8{:}ϞS&RRTW^xrsjҤ b1_~٧/ߧK믯zj23Ϫ)S6;O#GgՉ9*;:>OiJD:ZvkʔrxxQlRjr'lnLo>p:u ;eΥ˷S9E>ܹ\|f"1TlXIo_D蕋۷)?~*eQK;չs]5`u<6T*_>QuPӹ M>1M\J9رl`nyQ7[0뗓lYKݰыTٲԠAU͚ K 99VsjϞL)TI*WTQ կ_AY+~  p5U.\[[wX \LY[T8r\IIꪫ*COV.+XcBlCZnB DyV}~;ᧄP#׬yݺ_dV͛WQ;ւyzjg;sdvb h4AAС?:Uj6ϞIrQOuu<%&jݺjb Xtþ}֭GUI J7onۃD`oB[Iv0ʼy;[o-PXo_u 4qNvVV֯e2B'OS7\yPSnPG؁\2yF}K5P'OGeҿ~ ;w&4ɓͫdS"azej„8 ˨=UÆ%DZ_բE[^ݻ׫oX-T.uTF)e3g:x0[CvyL@&0!86\u8>;۞@G`-R1Gԍ76" $9=>GPp3^j&vޫWhb3ոq+UnY p|Z (du&TO&Ԑ!q2Ynq1bOcwY5^~7RylԨ>8eƫ'bzG瞛/Rqz #N k:# D ϼd%eG~P_g`*b ,mzꫯ~W ASh'}1<G>bSUjWWYڥ:Ƨu/r+">>y᧞מI"J22Y.@~[ { aI._8ҳ}fӧoW?8P osю]? a}14Cxrݜ6eZMiSM䤤8oэWr~9sOp_k#a|{睍$< ^jÆ#{cU2 =6m:E@d6þoE={Cf&NE>EYe_~gGB܌}h7+$I,KP΅ҟ.]^ ,vIj89!hB(k_<(>۵*`ڱ#SM {V VX(HV,TQ &?\f;sW:v2dIr/ĦڶM8y}DAvQW ^0yOppܓIX>JJaE9ې"1t4k $}|m;I"ԥhÇR]>׽LQsOS3!b!4Yw&yq

Wxhp[~a+%Y?5ûDŽ5PJa`Wk7QuWBQdl"<{qٲWFj˕PrPvmhK R>J ;k8]XG 17w.`AgU ez^ڹdz啮 =7&,_({L P +{3n B!?q(0]L<|\@ͭmo9sblA|vP$we _Na *ʔ; L;]R#G΂؀m~{ĉ J dNNhߺv#oVuq[hva(9u`bJnVUp}SRԄ:;x؛aEؠqA$hD= ;5BY1cMuʔ6 zÇ#b޸8h" BhUHتU y4\;˗W|F8^Y~=S{o3thB k|`UTSS8n܍UEm(UҞ='?Ym 8p:Rv \V +Fv'a0I Oq5)MӹW;&~}}jI @3hD,(4ovx>Swu5k 1MM%i9B,jJ0b߉*j^|K'm!Jv *H_F`ʤRѠA ]0eiaW):E_PIBLnm!fƎU.y_o/t T[+: ӹ\7ܱ#y%f *t[EZW!S#]'o|/hg L[D l\Au~X@l!вeȸSrJZ@P$6Æl`yPY g`!e;lL ӥy0i,ni=A~;‚~h' J R1.it wkd̸R4;d׉0 {lOW;  В{;@Z+S_D ˍ?N_'?~UAH2@݃C U%0KQŃR}h8@ m+j^[G' K oRoW$вȑNzA/GP]~b)z }Y^#fXфݵY=6SV5,#<ɹ^kmudGV܍:ET߾ .08)bȥZ$QQq4h*ymA2ۋsJMMF_c!,"`@0:>8 iGw>{ F^8.)v 15~: "Tb4̧O ڄR0o޽k[߳䏂3 AWv(jef_R ZTcAʘl(!?E})|o0b#;">fAIRg|E__sENp&Jo#IJ*]ԫ )@r)3a1ƌm\Sq%pat)-OlIR<= Uf(PAAԊF'D}Tru\9O5z;@k^P뱝Yqj!"q5iV&E"P5Ϸsk6CLu\UPԩsرs4*GFZ/Z pRJw:VW ë|M{\i` ni5?z'=pRQKK6*x.sO>u b y/QGJh糠EjW =EO؁USvLWzz=*қ1IGb$c)C,I; GE~ ƒfDVZ >)UP\j5Op]=KDE(jDjLn|?%^o`)pU-By&zӁ,7J>hصx@`CSw>`>璏?ӪU~+_{޽끦z0"} >LN[qVJ jH֎&'̹Fbر mpŽ"<&}/Ʌ.a8dpItfY; -ҲeZ(w;"FO q^t{)73sj"B'UWb7A_^>:W:nXP? $xj\PF-h-Sbh=Cm 7G.GM\1y)ks*DRl'cY a>81hϼ^H =/}b|['={y 虷Q0z'۷kQ,_@G &1"=_e:[|) U{SNu0IWM"gΗAN^0)@v4oFWTw|e45#ˌd}Ȑo !\^P-ElaO-( !ZP,\W=_/]["qOhjHlƨ?> Y",cD5f50]yLAkzk S/adzA<E "ٳ0ԘX}ND`5D D7H=:2ꉧ͚J qEMD6(&,~iidOuJt~Uqfg׭; !7"sF)}}(*$i3#HG&\1Wh'%I-akӘ=u~#ԩ}nެ$Hͼx@l g@:ܭ-Җ_EZ([mLjC5w 6*F`9^}};r`gNu-ȡg tqB> Ѿ#YΥ(N5fL-*Y*!zU׸FIQd+qs">aEĈ@%1ޱ%ZP&|#[(P6Unץ<@CFD],q±øp#PB~}[(n"F8ho0j.5='V P| cx 'c͟ _Z02{Bg!Cq́jxG>B!6ЂUkN"F{|2YR@3qd"#"Co^xxj¬-M[SXF4D2j@`G) P8'RiH ?uk -[fscǺp0m68f%{S'W7"s68nJ2|Ll.b`6y{L6|^mHԍ[byhӦmX7 =[^.6|A@CE⢹ b`C 0Qؓ'ϻՃ%jIQ!0 Eĉ$mf%( 3%&~݁&F#P\Qk)4i#|݆e}z0Xn~WK>MWئDgz:##KeP  ,W ۴ieӡEd y͚h؀dKӷ]2IxL^ݺ48Ѐ/#j|xaTiBŎ0sqDNg6q=N#ՋƌYϳ;+ >k0n+ F9ͮ9*>9NdhN$m4l QAԱg"'5'.矟L8`+ykBX |Z$AV `0KCec MYs2}~9ﳭZ{w3yr0ў-9 \r!w+[!2YsPalYe Jy?ď.̝_ b4VkӷX}E|%7^yuQAݲcQ"DyժYZeFWڏPRH\}^]TIՏl"W?1mb,;/D!K@KHYv@RW$XaLT;D%Nl.h U0m|r-{XA'i@?{x^\HњGE}h?hPs1W$D誑I!zVu=/SCU23FdΫ߽CU23FdΫ߽CU23FdΫ߽CU23zD(uzԊvKobZI6֛ ^fUsiBծ60µ%9*$g.$)sz: G $yIyܸ^:m\Ըq=gt5lX[`(&=xzq9+YfmS#FE#%.i~S<={֗,db~$1IENDB`ic08YMPNG  IHDR\rfsRGB pHYs  @IDATx]TE iCCYA@ADYPTDŰ]]˚eAA9gtx_t}{oxԩM6$v8&f^/^}ؼޘު  ƂM4jTQw_ѯ_3Sѥ&ſ=OL;vYPP0"YTPRQB@!N긂@P |  ;u C@!ajP0,0ԩ+‡AAPS: >B@!N긂@P |  ;u C@!ajP0,0ԩ+‡AAPS: >B@!N긂@P | vatԩysȑ3p|V:tN>|u ZvܡCMqe Evv)u B@!'һ_f(l۷~DȥKgV99AdxckQLF((e"R= 4S=8'{\8XLY#znU2=v,6Jk9 HZLoXUTZNt^HA!@Ð+!2Xe*RSM;C|YZ,l SL&U}w C!-XClt H+3.^Kvta!Aj EGff:$| _)QFU͚UF>YuFd w>OӱAVe K2dl/b9ACNSk-rւI/%MV ~l'āPzM)w> %#N[xoz5E~NZ5`ns[#/eSȶ)ïH3+}~%*U*%a)ZMg'0@뼄s7EJ [KWqyyQ1ijHu5-t+v ~LE`XS"%꛴l9 i 7mȭݵ n6QT:J1r&47W矿 ZtŜ9Qaz)]JW^9QVEy-gG_$O%KH޹sR?ņ9Uk1cHB#_] ت':9_6Dj ՋK.Us^6o'^|qxbɒ+>{NV 6X% N+, |kc]|$:v) \gpSQK_6ln'3A)W9rV^#ۋo"ekG)n}nWa@8nC'ڋmrΨ4dpxEq å^VTZ xN)@))rMzu( #M@rL}Q8F@zb>.v3Pe^YBnvJ]s[o!~iSagW\zGROoyB*x]e'.ȃr[GVlmP9l,2{LR2̙kPy2Uci fU=6>]{1{$1N*^)cXwԓ!)ܳd`L:'Ϛ6y15jgmqH;BEr.05rM4UMYHȓ޾I̢7~..ݍOX1P΄zYGQԐ=䵉&x093F#4F|o+GhѢ26z"f:ki.\RēO5b, n*HNҶ_o̘!CZ?=>@ݠһmWE%2u\IY3uq}rVС׷,歬'P>fX^"w? \b޼@[cNX~8-ڃ=k?J xȸ#|?ȼ{G2XeY)7/|| qyJq'q [auѧOCVt ;U{R/({s)O1_Eq;A #s%gޱd&̓ /«=fJɅJ|:?lovbK0& *W.*D^ tۀ9";2Kqp ;VX, _dqRMI¶bx,:2!U*Ex h2̢SRǻT w?_qC MMR=\~J}ubw@{iHl`6{7ET&{oVm HUf[~&̮)? Jy]Mʩ|S{Q"4 #2RJH2.M:Hy2A 6?AӁ\%<»ݒ;3#6+΀G^1F)PZquNJD^z}DfaϞ޽'!ۮ5Oj·-zg$ Ҁ]І]{v\w#8k{ 1hԣJk 5kKDݹwqTHNR)U4v:}:_RS!O=Hb\*]g>ykSe.j4)zxkҠE^1uj`5)y^)7dv 0aI$$7& HifPvQ }.%u 7< Ds@<†<"Y΃};n M 9\#ْ)]3!QLgDHYcI\U}ݲYp䔶xh'?k0pC hNi 34'6A#ߙDSm a(sew.ߩE )8O: &`#,Vt8-zXő'IF9/j, }?*)@{9m_Ы؃dV~ɜlr,Xp+()Гv_~̘KĠATF:G2%2%[Iɕ&=B] [adS $ҤSc9 zdnIb^ṞoHBAbv}NZ37"0ءDS^/RDp8E YoĈvd U|/u9M^9}v>\'};#]f_>oux$,С yuq93Cx? pa4Fjei٧w.j`Q'u||-П륓|b3њ1ux%Sdԧb)ya?֐y.`rX`& %,Hz_AFf Ő}&ڲJ r9jƤ?SRRB a=L͚UBDhn SX|M/y3JGȌuN`/[q/{{`wj wI;-5`UIJvS$ׯl@@R"+ 1C,dj) Pϓd6i竝ɓ@' 6Mj' VlTv@[޹sikMo#MLV: ҅45j]Lş&S4KHH5W3Fl=.E+En4fIH' k>dm>C4CF:;Rpz*Gꪭ;hvt : 5B{%_{)zEkT%ڽl髯Y+y ;Ò.m h L&M'Çϐ' eH|&֓"O\79-+-Yi_PVo5Gsq{M{t(hTCs=7P#7i$"N0v <FN*'c畀*f7mN^}`vJeWX//DI ygLDF߾c/AH2pAo2 z7ԈLViE.(0ëQ3lZHX]X .{ػv}b}Il, } tdRGs1zf Po}Ff{ J_f=yL{\  @N&͸Bn TtJH8P )Bx;MSu:!%/BS dŸ %G~_~KTuA&?h(pV}ֱY,34iPCH0P`ln:nf]cXuO5Ev; Njt2dsl KpW]/ZfЙFilm3fJ9s6?n\B0i׃̕2+5gǯH}A`qƭyK>J>?x58gy3KTbεWQA0ޢR}V`٪pY:OH+ ǃkwbAd*N>Pu]`1/Ŧ2e1V 1p` oKׯ)) iosd㳕]P# W3ͥVOfv:#OoM6|sl֬PRTA(7zXsQ (b},})^uU#PM oRT]< %"؉ʳʘ\[q7ҽ,JFOvyo?k${Ձof"F|bIY] W駖"%woE\a^[PX_Gu 6bTu hdhf1w-Ӷo?dg:wI3ӽ2hd{^B+t/ZN2tLց^G5PD.>WK/,z5c'{sI,w2KEL۶S&Rt1Fwb"W_dީ>),]z˸}5A3E+_F^y(08 | b*N3pUZ'N\{{構n]ε]]6}g6.b'g }n~{9~gu)I[Pf-;ꄤSTn9;IMarNj#:v+N@"w͡@DD8f&*Ce \ $ErҞ<CHD OZȻ0V##7B M81fj2O 5S+7D~Cr'>+V\;g@G*:2Td[G*Y,f8] cͅ|>h.8csnu Sn_/djc/X3 J8fܸE0^D'a9pG.`d|:2ZGxw I~d>suj OziӦ z_L{RҢ!nty]/.4Ԑë9#Zxq@)9I W:}; 3pmE31A57Z=iNM'E)ܶ0'q^w4}ċ*s:Ih:4(#sW:Ӑ71Į=]&QSyS~q4UFS܈?2~b`^cHf7iǺu,Ϧ%D-=ADE! 80p@Be] IV7yOTOE1DnxZ0@{"׏xD>tF3ţH(C/7{p@gpLB!8ƺo Un@ ٦@lG'HWF6cjP߁>Qƺ7;&^ Pd <7?5Fͯ戻b`3p.ÿFP%oz+^p}Vn(DXq7o^IuWX5H!X:*qC"LI^xa!iQ}ة9ؿPߤs37@KZ뜏)Ǧ`;wM95Oݯڏ@°aeM k`Zxn塆]<J; pH1zdgk04_~Q֐Ɔ GޜlToD( bȦ7OK}4ɖm<=Ԫ78Pn>"k.`_v-AL0?.F@L5  *$!Mڵׯ_TX/|DSeAa`qݡ"0T)}S6M!4;nw]p&Q4%!nh.Mz ^."]̓IvH&ɓ t-z& J T؅ZB6i`:Jt{.x>{+ʨmˍk`0@pQ:xMJU"y[K53ge2N7سn|Tw:_MTd;%q}gm1bx*v>^i)yͫQdh׮{"~cբhrs;o~:GJCW^ER {&4|/{nM5'a3I7 @sԋ/ jr9TVP G86ibi^R QX[V(((|I-IAW) jOgf1 uPL D?o sjP6o]ŧ`4SWJO@O7 0w~n5:^7@*Nc5_:YD^CBwObr0o,"yɄ~z}EdLō#߉0T )]ѓ3K-v!, EG=Gpr $?'ypevK׋g[xVSG7оQwYHR eF,xvA׵Mc3 )M;/c_=_8 :g!,zs ;YN'"|\" D O 1e_{Tj n޽ mkwO>nv*Spt0Mz0Yfy2XLn:d7J jp社dzBQ2fL ;39Ɩ?zٹF$k.4'nڴ*P3bwv_I3 9r&5owXp'ĂEI4LpݚQC@~2-~.x:DzVK`ma7X@3v 5 p16#{swQ315nLL0{8Ƒ7ʈ6l֮= :msihaHWVUJmYw|ޘd J= UH7dzXO\w] *r2#D> _N$ڵ3*ٮ3 *,g˭sޢs4ؒ?h7$Xѓ5z"-!'+ݩ -8)%p\,^~/aӯW^NBSH7!f,Ѣ!Cm3y ǘ'D lP"zQAfZl3twM:ߖ>x`&ҥw@d>i2 ̟ \ :*54ܙ|tW!F"n+l97HhngzxmXr331=z}Ⱦ ƾum6 |%LτEtU{]'Nt|d':gK c5G4JUުUv #N:na {+>ZDվ6ws=}-v3%"M$yMhj68T^X̬SN)͸~oi7:iUZzjlb?6 7_%ΨJneI縉ᶓH#66Z^VT[F=yNFIx^ggѸ pEAIZ{޼mns<7k&5i,bܸ>%~Etz2U$q0L1 G!A 6"Ɇ!{&ʹG&tHe6׿4 Y ={[X"tonftolYHO>3#NnQcD„+9y07Mm9D4I#;`]2[Fiz8t!W&šT(݂ELG _FqE +2OAz|H:$F[]^o7=\tZ3 IM&d4MEBX}߉Ȩ 9\_$yU# +B5GҭU׳l,Җ-Gb+Ģ0<`dڈ>|ZrXF$!^};v^pЊaN'rb A= 6%a$ð#- cX|OE.KgBi ϒ% ?W^YJQ/C'Ýk  Fe7tqMm3Kdg]sԸq%A㉒A(]0y,Q"H _/{yلf j%ᛯ AU)8Ǩ Ko=IB n8e$:aݔjqjb~f/Zx`9GtO5kf?J߆GT)QPI ^zixe9X^`z<ʢRRr7iګc[_~9W@Mw?46)HnBy٧Q[X)-_cPP[={rvǿtR"hf~+K#3O?J*1SI=r$?&Cv}.z9?Lg#9~>}%nBQ -ZCW}~]qۗ 2?Hoа%=l~C u͒?y@X ʳI@" >`NE¨cwfO nOOj•ȓhH=BkXܾ5"h>g1rH2M?eIxdu>Բk bDzOawqlfW᝾Ut(bdXkIdDjRľ}ǁD¸ճip߰w)/@ԩS IС@TCa"dO>Cw~j$zk;\oNykYDG%CvFⵑ?4,XtugXI= z_&j"x{kU$ S̢{A V^emxmrM`ԁxVx%/z\]+/"}krn m6&8/=i YQ{\^x7x GQX_:6xIqU01KrSaH hr i3E2ޥ. 2e ĮdOD:Gzұ6[d:/ >?غͿg)"-L>RRe߾ lg1L?b`ڈn^:SL.O  S+-T`҅Vt28$@G{ 4$yK#]ibٲ$44o9HD6xKkzV4W;T`Zq/f`M3|Կ/VEn q=H\we)渾E ٲ%I<ﻯqԨQ6B^ ŵտ$z|KٌH#\{tn삎T'͊7l8{IbѢ<*a ɝ{9nmbCͪ ;诽v%QtsT4%< a^}(D:cQEfUEB'Dҍ'&<7S0>ƛTM@pɫICo6*ږМP30ZntD 0 M`u*1oa`"s`< Hl߿Qg&bW5Vp~"1ܵ( 89%ҵm[M*x;ɝGn{EЏp:RݑG4n@qHYY`U?x7[48&`Oh`h\E.L^j!Wǰvrp 3lWnfte *ٽ6o.=ҖK= )n=L۶DIZg-R*(o: ;S?0%Cg @A{Zr/oޭ8Մ=UW#;6SC^GڨɁ&>w٠H%yi+W $5h82"fΈ*Uk4O+hMElp\QZL>7͜xbi~dtz4(UY^GƯСĪUŮ]'Ĝ9[ZTj=ನ__ Bjl=;FA_=Tlٲ\F4pyS,B M7rTՀHɒ7E1}IQ:tQDgh&)w߽&)~q~ܹ'H1͟ CYJ@$DA1~&= 8]$ۏ6T Z%~J-y/ "٩bn)d @< !C<u?2q+#zgЏelR+W MC |V'u ʦv&0^K6mG.(*h7@eAZkXNj+!@42_܏Z(+CuɇK{C* hWfcJ! *Q njtλs!ZNY P\ P,Y'rp~o|-=ߙ2E˖U+nh%4D^OBaҹwOEOۂz_XpxvYˉ̈4qPqu)m~Bl~TL;ޥ |cǚ:~kΜM⭷ŋ']{мs{c],v:s,~mvW_]@dL"'Ky5FcT (_fАi<~+.d3NrĢE.I[,V9wRᩙȍ5;ggHҚ$2yvKI>X6?Xj'W6 vJ:2I,TJTP7İamn\ܯp(cS.5*f&|aa֭ŁF:q"O|yu  1px O7e(^/MDDDq fQ^%q-}ꦒgA+:VC;WtzPgf;\ q`SqchQ.nL8dR1kĔ.m;Ӝ'&tLO|oX5;AYW"`XS0dqgl"-[KwcI{@*}=zNm]yj7-Yp:6:Htpf3qڗU7 .._J"1rdH %)Nw1s]ޏ#f]pC=c9snm8)b"ԾNkӇO? |v,~lѧOCu93g/@ZIuop@"{֬ w H.u{(hC80Kܬ<}&Q,zǶ$Snbߠ(UZ!ܹ6aj3\YxUqOQfŧ XA׽KXX]R_ĿTAf1 S[tWfo@y) Fs5@s7!"kL"2)#q}E];Qc 8TT^DA.)V--@_w]sy U Gdf}\97$*GsYn=GDzb>qR;z3V 33hy: 0?8@6⿸:v<|G_ !@V1雜]o|1 ϥu̸C ng߄+`hFP?W`;.o#d꿋yvH -*P#n^[->,' j3bO}WC8C˖& x'R~3})sZbbfP(3ElB )ؑ'zSšp#W}W l`J0+@lzVQ#Kw;/p2eMxQYȰJ @ o˿TeТ>-@d!tLdajS(6PL'z Pl @j5PO( DA@@!b3j P&ꉂ@Bf@TzmHoR".uwյꮮW׵W\XPQQ{eiI=w.dn$d&)0ؙA@h!(l@X1UrKoCT*:-l|PM`k  -ܰA;\jԇش頺j=D/}  Ppt>Jռy͓w߲T A@n3{A'(zdyg5m&@v&@_A@HlHO=9XhMu D" $ :̙׫n(-x9 2A@A:~  $ )o~T9صA~N$4e"  S36q?+ᬈ! @"Pιsc~B&A@ojʹsQ. $/`PP >{ef @qbW9" $?bXf( C@bA@A  PA@(0  @# @c  P aA"A@G@2CA@AD ɏ0e @1(A@Nх" '?)1dܪM:w#bswA@@sPkxc#)0xpKKf A@X"p߂؃.cĦBEˇIA@HbM#c$m  ɍ0}ev %X"A@F@便2;A@Aa,a @r# @r_  `0AA@A  NA@D@KX  Wf' X" %,rPA@HnH+A@,9( $7$  ` A@aA@KE  ɍ0}ev %X"A@F@便2;A@Aa,a @r# @r_  `0AA@A  NA@D@KX  Wf' X" %,rPA@HnH+A@,9( $7$  ` A@aA@KE  ɍ0}ev %X"A@F@便2;A@Aa,a @r# @r_  `0AA@A  NA@D@KX  Wf' X" %,rPA@HnR{z2;Adx 4866ccb zm>*3"@#he1E`ӦCWs9_iS]uVk|Z*ş}vz N?qRNY$f3ZaZ[WS[|4iE߷`'bªǛmQ Ѕm 9>1ܸzj.~}]@ @܄x^>)jLitMJUdr8oF.jժ_?)ʿI꣏$, |>pȲdaP ʘ%82FK@# @߼ho]-O+evkU-9]Q^*TȀu=𿅪R%=P=z% 3a;)EHQ>8l9sz챟T΍O #@J  Sp,ZO^;w䎛wtȑkmPď0+P~Z`6Bgۧ![hQ[5.{D>՚5aH Q2(A0W2^MjTctPURW\1x1tvi]6Q#F 5BNi˖?}{$&KB6U\jZVdd>S_LY(c{A@#C80KM~5k+RTNuUVV5o iJj۶#A7 ^XS#^u\)^㢗yxa{S%0$+geUѯ^kԨ3aF'/ϭ\.7v/R|E2tLU]/1rar"hN] },uYe˚Rt!yoV$=K*̇*!CZ ;>bYN%3QAl9&LX^|q6"_v2VP'. Fld*F㙩RSTfuՙgn8p# @-0<\`_D1XB91UI1H)I@D9*= ` իW#նm1 R4hgKn{l{E3gnֆS^Sxp×`8zsJڀ\' aJaڽA8Uo3]tQGgaОz* c8y<OnUNcjnոq5XYUc@: mT "bɚع(WdO;@$vVpz睅x1331`<R-Ə]:Za*A@p*a]FeP{ -:䁱f|6W^ .h_2X{_oS:|8ǘ)|'MZT{̆;){WUkWV- p |Sj2L'O ^;O߸Mꭷ΋HOd`Xj? Jǐǩ6l8 MVhOb@($N0:6N7ժkT "13F*㐳abpׯ1iH$;č:4l9t_ԻEFK#&=9Tݺ`ҝ)Z;mk3Llz .cQ G@<լYLØ0azyFhR;;}JR̿a1~'hfv23`(R׏>}ZA:/[.dĹٿA 40XCO|bP R>2}wG~TKpa%^{tp i喾 #6?_ F=Q 5D#A"Ի&axpQʃ$-BvjC˗Q!vJfQ:֋Y즢 7f6gb"ҸXԀos O0Wjbe5|ogDJ_Pe"RY_"GD(+S2.HRu'8x45J֮ݯS]j0)y:8W*2ٺu-խ[=' uL- -x-+p$sq\-E02.C/;.b|s ^_or?Ag;[o-+Ǹ>샄C =c@mu}gt+R+O,ZoF~Vb48t(Gu*q3zl 钪}čaj.{J}*\g5dE0,4C)ȈC{o?<v"v5{Vu\3 Dݺy#0^MaTrJPQR %э`%x!gh]`p&  R`L>ߥKwC}v42 1zm۷8;eZ=A>}(6@b 'V#7k?`'Pײ\lvo!J,mS/P,srnf&- sY̙0Tذ!u}uO2 jW3ƇyDo̭eZ㩥ʅq1H:a`11>|۝;c=HKKiJ-05ҩ Cœv TQ24rݺJ@GIYb5[KpP <VQCTժ&DUTUt+xј&ʦM3L F %D4R@X["0imQB^P`J_ڨQ`;[ԎԿ=B]uU7gz2ZuW݀(xxfwM]q ǐE)x?L,fNdB/ߍ@4~7"h[WW=z4C VO87UZw_Hvjv o&`Ix53 uw\;o89!^6ߊ0vM͛N;5QzԇmˆC$80Cm:m'&0"w 4IW *5bSuΖ\'-eaV|1a u\tKaBF㵜m7o^Ciժj޼&^]%M \*Ě蚵k-@'uM|QW^i}WS Y`L/T |NhާR:(C`)+vIt1Mfn(X$9@ʤI+_VK0?ADF:^gjdsNkԞ }C@FHԷobngw" Fn5yy;.|vAت^ziNt kA,\K{/6W @ڜ8q9 w(BȇȞ9wFpڍ XW*62fn~+VʕպuPAA ،G >*8W&tuu\J"*ȝ7UFpcp}Zޕ3!NIp) b- ֋/<0 P}.E?\\.]m6l8aY1:rH꫕P5l1>Q(q`;d>w]|6FF%vSױcnԨڊW1˾}9Wgi&{d4-LTkq|,n _ ܖIWtFBLwjtߚY u j?gYx7>a1[  A~eń>R%Kv/X>h {>tNwi5;Iʪm[(UѠ.SReذV{e0Lw]J}Ύ J,_xyyΡF[`P@UZ}F`%ޡQ 7&15?‡r>FB@ed=>A :% #G~6`0@yd-W#Gr֨_ cXc;Cgׁ]=dܻZ[TA={4xne^yUԎGkOXʑ"y.fcvTB$k)Oe0jiGf*kC5fLg4(q8pl\Ӟeڴt,` tR3Z2'BjԨ͟[KXI[ŻToꔾ*%!^&cH.) w`^`jhtM{;h<$%4ӷ6T,$l5ڕ]76k"QE1h?mx,F=.wa hvEٳ8Ta_2 nϞCz\h/}X861EK v ;%,ӧoB_;ՀEa^r>E*aԢ֫\nϼy;~5;]8R`Q=`z>Dȿg|֜x^j͚} >jTH u TV³|3h1|!2 -nEp=2{郾mEn]c}+ !I{qK?G.G0>dx>d)ďˡJX*&6lcP 0} 7tYI&hd99n,d&7Kfd:{w';cjܸZw_?ob͹S"$/ ja74IKlظIj ISoB:ÀUu M7} 33$n&! ^8}wb] 3C/mH.J `-QCf7{k6m{c@>R~0s=)`-FLDx>.mb &"@Ͼ w6~-v͓t>"c}ьGH#v]fXcɒ=<)t} k765;hK/́|jd`g8AtkkI{C#Zؐ+*XC2PZz`vlJCK {GJ bvr}ZNRH{ȓz%f͊X1Èq1|Zgm,wcVzt,FcQ ^, kCtop[b}|ZUOPnT5R˳ct"4WKIn?)􀮿Fj91f:'/l³FQi0m._?I{sX3L]0rmkZjmZO}ҘX1A,0+`{7߬E;0*aWrY"vC3ޡp_nN9Î˿AeVg.wb{`aGABj?mCg$>UAcT/N4d 7, b iGw.moPDD Zb`'yL+Ur9M#_I$O0]1O]D6#}w4E&Nʕ{B-1gɼ" SOM/G(H4"m'T=#h@*ߵ 0pT/}A>"/Un][o7-hP:@Eή"m~C[_? .'ET4_'AI]tQG ̞EV[oFxm6t/m#7bWwݨv~sx뭆ʑ#02 kg NZ,1d3CYWG➴4b2QQ KK1@Zz=UutHvZS@`f 3nI9 ae5V2/÷C(w ]FX\2lt0-=!r7갼Là:JFk'ů4\!?aUc3E StpWӜҰHbH.4 Ig)n4"iq#IDbğaO<1 @pã6wbLKײ}*ϸ.n#Oj$ILn[~pwYwKbI.5M|%¢|Gcg/_ھի%#GMþv +}T<@2W@X„Vw Dۉ {m2>!)@pW5pSaR4gkh/ji$˫Đ v`=^::MZ!}*fP،N5`X'b׋]Ϟ o)t25k脻TͩSzm|bydd4^ 1!Z AG7޹}}QcܸnDuLo'/O%oK(ʶSj׮ %ts/7H&C1V1 D9>gYj=Ja5u}B qꁣVU _*3? 抽KUU]#VߩGL4)#"\<)i3g SO댕&)[SvvN}M=ƍ pdz|ͅ< I(kBaJu2fЭLѰxlTm/ ;vs˅T}TiCtcJIK2F k jժ}\hbq<`Jc.< / QFXI8NR9ֻ^jl%O# @աS2{Z!^$F m"~Z|< "CAV]0|ڊ|WRtt3fc0 v`Z1ƍΝTәr;ի=B4zPjG5]95e@tӶmhLZA$?$tEF5hd;#m˖C;O_Z4̀iBKS(v?sy_z.EEV-[һTh6޻r\,qTaߑOoӈ\zph5 _B)Ŕg` _Uz0ܬ>oA"/w.0m`rEo\O_4e;` K4xZ`CPKX(x $*h b i#%5=zۗ 4㺫2@FgZM SXm{<\MiS[~JɁբicN=,/^ [kXN#:XE 槟6 ^rB]E/Woߦz7MީʮPLWgq29[ U}p3J|͞EP $b7\R޿`ݰ=UVگcp"Xs⑸QaFJm@x$ ֤n `iXDV$ސk2]j*"fÅ w}v@Őw#.\sr( $7z$rk+YVxvTHVA5jh&Xa^R+V*QݑRRd2+W״٢ay.bcie5YOu rYqM_q) 0q"8bH9V"N~Hԃ>"]ƀED.iRGG/qn V! ئdj*LA]-)=&Wt#dqՠwhbZB\*gL$ǎCbDܥB;1pUxou̡Z dK, Izh {K.>va[4YgUƝq HxX F4QoΟv %сkK] )¢ 5m0ȇ, K5̙81 ߉x5^i"X!@;*dD1j#ޛv׾0wEDK`r+Z7 @1_/lv=:=H; ,0-SnC ixެ}t?KI~3^@u'!+(`+wΞAύGVE= P]6(?CY`iG| Spwm#D unZ+FS1~vB: $\Zo"8i*!pF<,rme*MGA;:!.!gne4=9 H1(f Tx{yp% eV>&j:N.:i /E;NqѢ{Ho aC\R tjh'Pc3gng*yIȥ!V琹 &761z~fdUCivM~j)o-:+_<ӡ?|Pi% 7 tiTߟt)u۲Y3'A.}+^|lڨ 8`u 63ܠbE1iCY=_ygoFrYM{qpd a8:j;)uĈ꣏bBH?KƅTmibLc-:}3Zp30E2!ӦmTQe7.928dm< CF`&jG\ֵb쯸kC: o#%QW~o0!i5b!n?얈Y0ji57F+F(XD08⥧fe %'xݭv#X=QD֣jԨM9\#:y7d_cWŒW\i!3adc)z/C92G7bgLap'\;ou`dU͝Mi2M}CcfM}:,>-d>z) U9gx+1+KժIep$ڶʕkrm}?:4n*v#ZiӡAH\H_=P(:yiemzz1i|L;|CU73=w[oiE%-oP4R qPsVt9=2g6HVb [ ̳t)%3;\n"wC/4}C 6]of w[jWmz&a@*Cmݺ|O|Hy [`SEu)8Z:@V4Sg}DC1yUy,IxQ׸.:p3lccC#3f؈J$~q߿YH6mZJBpYV=cf0p<i 8\gt7`;F$I8jhה>cmX$?tW>gHA# =BY hE$WgABUnSlgN\^y\Ss7߬D6#ZkNB3 n+"sn W~zVk!Ib #_z衟_bg\9NT\UwI)i^GG9x0ͽPYA::chD03J<նmH60mSh.0VQ>ixo8=SP',6d~n'^@n _5_{mwE#*ƎWKw `9:Đ!--c~o-8{kWcٜLj<X'MZ W1k7k1;[n#> HT;7it)/%05sD;QH"@DbNg 8ZŚZbo5]U4xY=Aa{$q24bx)EIfվ}KO4ϧ4?鞓!Yǻ|* o7r^(ި/ w9@IJ W)UcN1&`eKm_Q;7IL U~;~\i$Ȁx|$}Zwpj m` 3IqN fX&x7Vݺ5Ԟ u2yTH8 ԁq A~){_koADƟ'f³̂1Ҭ7f۱OdFu_2r?v,>v#@fQٵ+9yŊ:,ӶIAӱ&nXׄM(XܮV.1 $:>ڥEMVkk);<縳`)%Goߦ M6@C^UyJ;Dp?^^g'#H!+g$,@|OPǓymBE]xjW9c "ݶ "/.qu7㳱oXs/@IDATC #FkM:^(w=T$J:Ů4@"/b?'χ!1+cծC;&H}:7tSP$'|8Բep;ኳ[[6vj֬:BuxGoOxyn/|UL0%4}VڷPNj9m<1CzNnذ1!`R5fN96v(fᄏs۱?xOu@lA5B/rg.8Nќy )J OJh&}>ߟQUh\F#dHV"8u}f܀?70@s̰ǩTM$kIx\ ը8?MFZc!vo\H9<ċn+5zt{5n\|¾Ze-g3xS3k7AQ$flm61!l7Wu g:@u'7 幤(# }-%2$4`BGxxzϏοhW<&I;(o׮t0^}wc fEDG`>ыun%rUJ&T=6v^ E!RH\d^ pBzNlYR-9T=Ow&81E4nF{eDd=ݰ'-MTS\]yXԡ~a\5k"8# n Kudrc5Ly^ceu}644 JZM343>Wx1B%lZG q>y0F;`4OVY6DY0Ԉfi &O3A>JuT/p.=.tw PmdÐ,nxt x≳!죃2ժUՌoH,{ @IEgX0QNJ׿N3Ð S4NIsoxo: 8#AGvǎ6Ԙq?}IX!uǍ\mL}P"L=;$ŌO]~%Vo7Wy['(I]IwkP:!Ksx셺kC32Fر1%NӦm@KP#4Ki4|#f~,)p?s1xa ef- ~D6{|i tQؼp]-;M(;VԈ$7Rj-h6dϑhS: G(wG:CUϞ ug. B`G9oC=-`crxUMA*?[xdH|evJի1=i2e=. E_r/jϞ6ڶ3DTWMI`K>l)FH;8'Ҿbsi}6qf Ěf7KKzk"mՉ77ж4%FZ.l$#1 EBf]썰;W`P t.4+ڭ1S!.i/6nH<3( v6^S yy0~Ԁէ^ԡC`օO(JtB]Gv6!_ q5avht_h@\pkd:pu-??b6ur1t"/*Kt,+f$PL0czz4 -*PFgO0>"u(] j>O]yeWXk1$vxĬ@3r~ U ,Us__[2}%{Cĺo&F4Df*m)FHDg+v9rG5kyy00$eE!4a̜uV 5a¥XЪz}7:0 o1 GcLhe'~Oؑ^ kv:{CW\ZfALhە2!?; g_鯙fj9s!饉?2l^t.Ȑ8>Sm& $R`T !ֹI%g#{ x98."4,A1ąu ZӾ}]kBfEjoo> @Jh׮BP?~yV̈́  ?CKph?~xe8 {j߾ꪮhxX1 Q9?#̒9fL ~/; 5pJ9D> 80NQak{K5ߘ駧0OJ  PO;q/jTiHr62+}'BuHR3i e ]a62~xЭrQ_iG=찀F~#h.{QgBFk]}lY[n]GeУGX܏yv8eS ;s*U_mT~EJI+i5QV+kd*UJ/q"n]2ؗƱC`,fxOT7ցe^FK$#ڲ@P48S v6{BPSy៱pCq?' ςTo3Z*=]J|^}M-!/j/ ћ25膶e!, zT6lYI2f꥗fE$m}MMu/}S.؃t?g>AO[2=so85>V*e\Y?~z]V؃ϮC\Ce~Z{$o-7\NE?˵jUnmS_7^Pp^jy~Աfo+F iM7O7LF}]h/s֭ + ?"\r=GE·"]v۳?1v!1CXJG; ՠA!/NXovډ%Ñf+AgdrQ.>]PǏ; /:?\ V׫(|u:5`#G@I  #aڽD`@ɝ!"e(!i2|E21˗_}wΝ^F]!]R6}.@Ou]) wh*Ӵ[Ŋʼn+4fQ"uFizÇbQ>Ik]=V#6<3L <㰲H"~z0^Uah/sҮ쇌?3aue4yU Ht\./~h,mՐ!JLM.mLG?V2Kb\}_1wf;wU3E9EU0z{kQg_@go\HhraퟁEb7nBr燄?uWw5ao '>K͟-wgR4i/7CdMAZ`TI P %d!HMym;w;auflѫ.ʭc >ʸfp+ 䆵t,[b`|x_w=(cst zQF=X}ňzAYe ^prAgǩ?t} /]¿ akhtyRpE ,U 8Ywk4.m׎i:ro׫WCxҜSlQ8ОW0Z{F;G\/QI>K HeԯoxlX$"8r-G V`-qnqkc%3|헦~ tsa0<.wMSGgriU(OChs[DzcpPbu\u% ݖ"С- q5CF VoD*VNڭM!-̆?Ë \ۃymq:Hv2`Z)`jf@B4R=j8Z i3h-jS*^;:hT"8Q @~6ǒPfmmJZG,[n ƽY6}h.NŅa_3^i>\=e_{m6ڿ kqq+B0@]rI<*Xmjd3$/Q}lmf!oҤ"8!')9s3& Eo~PkBcKzҔm b{> ";Ð|mIW뮛;nx).]ڱj9џ VU{8^Uo/d1#2nTiʈ^~8l$HgnNY*EYg5Yc )yP%Yd}i67S˅jڃj}Pe)&>+a^^>v鼎ϹZuMr.N&%JH/4BvBO~sl0סÐ&h#o…OX$P8t#vJo聘SA?\Ӣԉ 7 FDzqS9*u`eϗҿwװax0s!ϚLjq_t-cGלC':)Nl v50?`(b;w7g"FRu2.]A9Rd7~4m٢E=DAS)2e6%KvHđh`qkz#k`1#`s:e{vv-a=ciކ!lucu%ڸA23STǎ  ոqݵ'Pg L޸VWє`)W mۘNIAFv*FLriBlҲeM*FG2((pIc;!V 7tG܈QK@3gfُ3m_g"Ur"8qO<q!EuEeGzL;[`ҐHK>hizB2 *84bI[x92rd{ S^ xDeeB(~oX[U?~4_ hr#wb]iUD L&\pAGt|3? :J]w ,ܥ,)aba* S\RZjوPES__sw}dvH=s +@k-2px s_2O?3`O}| ):nW_2 kV1_*N;j:WΨQnU-n1B =̐RF@op,4?!:u*wvqbHLN&cS?O1;袎V>5ݪo!ӧ>Mu/"1F'˦kUųϞZ3yc,dDbt.?HәJoK+ςڲ:,/U+h-Z4P?F}2i[K(bfgS 8ȼQ1++P|tzw',Z,!3n.y7"zrumGગc_q n]>wS;*`~p 2z:XVG\`&עˆ=PN䲈 {D[Ӷw x]0 _w?~6l?yL*i U۶ija u"IL',7vNKV LݦLYDmْE:w֍|-Z⟢&]A'jB-=U.m%mb_HN:^($ϐL= 90E,v[(-afjN]o Ś"r&|ܤb{'@wׯLj"@=' ݤ~q=Bphny=: 08{љs"ֵO?= FkZ dn @k+DB0c[IVeW\5 &W~z3$S) IQD`ΜUaXѻkj'C|yBP(Nv̍@@_˥b ѶvS2kx/2 FNR7*^z<z=Sy")WFť%|AM&C q Yx.!h֡CLKÅY"pe 6zn\  G`dCS^ Og+K&XL%2lL<س @4jK3*rmJKE NO es_nt xI?4 !gP@9a(RsU;w2Pqu­]DOq:r8`O~ɽ䫿 S22]wݗ:it9Uz䑩E8{Wo' 3Qf/vHOO }/-H5vPp =JĤYdd`kLL&^ د, _T"!zuŝտ<{ե~ 9TU6޶b`T aɀJڋ\pVaۻ(LW3*#,q>AHOr%U--,ɑPENS'K1X'//q]G5c;0jA 9缇֖Lğ, }}2j?uu=StB9Yb/Xc:b$. r",Rĉ/au!"0Y0UWuA8 w*MFk~00YƂxPrwбљGJr wLcxz)tOqG?YuxS4k 2c'w{ر. ]![VaUaH+hτ۳ kiL ˟"Dlld[QIF yRrs c{CIFӛ5Fa˾}"<~B^1k{ݿNL&N\O{T?ޚY3nA&3fl- RT^(FiT g a*᳠41%g +T'eG"C)n}f (%1 ^:1シw6'@㤭[E@LQP7K D~R@#aNՓ㉃=ǎcFH1*dALճgQVڵ+7߼xn`[ kJ#n p~iM1~##]rr5:F:J܉<# KyvB*6:9ضE%== FϼwXQ7F49 &D E1`jj|m?eʵO~յSR5k Џ>z7gQ~z.0NsQ+WN` 0!3nKx_xEjq޽"G*NDժeRiq,W 9zY(R}UVVu-5{kմi7BG(>Z/oCcWU9Sh3H"uGN.u݅o@3؁"eQ7[YCUXF 6D@\GZ۹D7Zt7t*UO}t Fݩ/ޡmh_}uӇ@TE:aճ ZI)G B6zt$YZ[?~DВGH[%IGjRYm'_! =HdTˋ/bOW0 =LڷLF5n\w5vl[uM`4LfLAi w ku%%$@W믧H0CMp5>mHHRp#.;m 4GCepfgà[$ӥZ*tK=I+# իJK>, XVS30hsn嗫e$^So Rv9hx65Ձf u"㨁2PRQzip&Y9mO=ig.J#s?+F#n { 8X zFiYyRep7Y>}ѷoSH(^l:!ChL\G3MT R[  iK8tBK׋5Xg^7^y,w3\FyY>u(}w!T( >d@Oul7LCȑm&$uTTabB`}AA R Nl>M^ *0h ? en e8~;Nñp.աC]$)>ˏr wID:tvDU[$-Ɉy %`z*&Mj֭kb4FL9D{4kV AU.[)%Z1,IIuP?`ӥK/{|s1mߍ5ov5kb  OuJiKNP6v{#Đ! AVVU& \N贈FW Б@1oTL9\)C(Tr"w1 8 FẀiU~pU3DN3QQݻСչW')7z }%penH4|,we:V=3ƞ\#N: ګM z:{yh"EG]8t#;UMJm\<6D0/ԁJr'f={CrԤIkt~Z@xFlEcϞ1FȰ⼑\N^y^}~do`9o6?_[z#F$l؟{E˪S4;w+q@j0<dV=.ˁǍT^tٖR$%@PGjTvц?4""x#0q g_ϒ{tu3SX+NkR3RMTsgva@ #T0{˝w@ÈgtbݣbqK?p[6GkWU^V^wonCR?JDJl lTp'.":夜l&OA7$'L؍}!@5^A` $I;3lUԷ]cך1+}{f~{a׭[V&P]zxKX3R%!AKXs0j~Um/ZZN[τTTZՀ:6$R;J$-@:vxlAr-QY ( ũ'h*wEh)E}Z Q#X=z4Pq#mPN?=K[3;~Z !& ?F{FCҐ&2}fφxGxJGR"Oq[{C ibl.SÆEHGs7#_]ߴMxslI"DŽhB,(Iԯ_z};['ď,}tw`RUggf;{W@"`A,aƚ3cijh챑("v" wXȖޙyٝ{)s|A+GN1Iu+6"UvAQna_Bn,`Yڟ=r #W/J@@@RޭlcŵRƌӕZ³A  gw7] G!b+4B: Nѣօ"@ڠ1euif G=.Kֵ_a,aO>VXC2n\7#qχ`C.Er9in=x$вe~ P(CKYVe(Mzz:_sd"{99*@lG=Snҥ3V떡Ke!IpL-rTWYwC>Wkprbp/\nV[@R SN=]#]|,h:toA瑡CۋHDҲzS#YRq) !țof|ि .VŊF?il/&^モ=Lw W ^@mVB *?ԙ|4~l;\mvE}l|q"fZ)-gB87z;)FTJ2٨eX=5o[wz/r@K_%&i$Eޖrd~QWˆB'2u*(Lć)$K`ˉpPr6qU2AD24IbtK о\2KҧP{B _KSݿ+ԹJ¯Y?ٟ@eGB kق4]*s箯P ݨQvC?Pi_OdĦ# =J`rIdMyx>[4co<c'p)NRJЎW^9xm xU_nT4ٵ6lW5Mzs|ɔm+lUIZX E/;|J>!\w(r&ʫ']7~>}Zg[ j65=Oi}JBT0wEJz_tf\{h?*'"tp9ЛN_чQy? p) \fzO0Ş@ƷV7w~mTW7e&NTB5^8vt0&MgH暑 MN nZK֬ىDU`߼T+<mJBgA)HEz.(1hw?ބўC<[V/[LH`ٲlɿ哌4M֔bl:;MmۦJg”ɴiT}:Qz[HQ!DuNnѣ%V('6|jgh'׸r׮bCOj(h5*SA55mWePxHiU=nxbG9>lHSQA~ +?q+.3v5[`5ztD[6 :-(׉g&`])l߾ !d{ʉCwytQŋu Ɏ=IVO.8kíYl(2+nmK֥%}ߦMcX&tB e{Æ`$K:&[ Wb *hs/xmɁ>;=IO8S?y|U?Unj5YxCq+W8_vc NhZf 8 쳵_c8gsE\+@ ?PV Ǜ=w /LՈA1tE {)キƬ wEXa]ֳgK`~3Y3}(f[N˴<1c}mر?_=yݨQڎPVhe&,27@mWJ(Ĩ7;oc_(کntJI1jzfN{4|ݟ)>}d'.ƕ7F t_骆ն ;o .DWTN^K6Qa˔aUtR%j&~>bvHYGfڄW/[oe69{Ԩҳg^X4ɽ_4((|Q6ͲI/J@@T~cb3K4 #k(g%ؔ):sK`+z;THZ}F,sȽ:0t@9?X@,(Gag bN :-ev1ٌ c*)BjND:4-CvCqٵK-[s}W!Տ,EÕkn9^pHpF éƍ|p.Ks?` tfFS(z@bu1y:8';Рv3K^6ӱ|*$,;^M55?5$8 L rVӆ qjت&K[Lt35?@ Knh ,fL>Sy,l fh;;hu' p8>zÌ>{v,\}!eR'ePl4n۶̛w|=v+> ' ; !i*dzydR9ܙ2s9xv}?7@ۏCĺb|3#tЯ0`jvCCys(K/aFB) .T*S/PWH(4(gޥ/UT耐䂻"2yLS0;?u+@U:ڧ+/ǝpZ6mISG92n\wήs@LE6FCb4]s(A^̢6w1&ù#֦>q]j .W"aI[DfG5gdn]՛X~dARtiXl!=z4JjyÚ#KJv"=Q$c6-4ĒC'P^7 fUW^YnBB'h3)XԿ,.(z짞ZОNi߾4lCO`4,*rqrqa!2 쬳AQt*}ױ{ZT aCbH`o"^88eVQ:1i6O^{m|:;s*8ڙB 戃f)h< 4,#Ү8b}"3Kzu\s͛.GD6uIȑХ([NmrPqHykm _*ns+h=rU1@iFx}ѣ pl sʽC3ebJ$<5$eP*Kو&Va)~ߛD@͟7oRkOW_=W5U\.2&Oi(F^[G 'tyY2gZc0B8Ig:jϜb.P)2SIXY3e h]nX&LSHdJy_m+\^ UΘ½CW2`@[#ن ù4w5u?l=X$[`nSG@TgVLi._ΊK:M_<#8XꀧC0<wr,|'F:8P,0>[nY-JO`<($trnU: F³@Y<2\N|ԒzGG)($vTgջ%Knٳh2@F5gSGW!t/7ЖV:ˍ7A*oVgJffZH 7iFH{l\qkxW}ף] vƔHVu”^|q|(^sKaf *pC5%1?rU1O#>Z`NRKp1'<˘Ƽ̰ Y`c d'v-k·G] \ z_{jE0ŹbA1($r"[/Ϸ8d7@Mw&Xnݫf .<ߑni{{azI}W_TrBŀ@&pV%iX)AgRW_m{ϊvxiLxv̭}L@^?a.̡tfdC;P o8LV ̿]feWeԩ).Ub%TBf!6u:2AX'CCW?܄5fy)ҥK.Y1%0|x|KCjXk痄\FǎMpn7x(!7-N`BwR#K?*H||,|`ly0">H#*+Sg2#G&r, 꾡s@ ;;' g"7!+?;7v}Kp5 <'luᰝ;,D=E(m> \8P" 7?CŅ9٥S![7TV˖92jT(Bᆛ˸q]B7ړsBmNqG![M0x tib^ {oCcxOZ0hF2O1KJ*a94 h5;U) zːuqnCC[4P}0)ӧ76$ N?s=1ހM 1ϰվC+?DD`莒n k5vjnc+䬳c&u٘W }j ./WTc;Oq@_{e˧nxٳ%,@n\wMAF*Nߵk L('3ŠG2k2Qo$q1 x#z<)nKɣNm}()%Vh2 {1g)S&c^TxUonh$3>DZn`-\|c]w͕o~j_zECΛ7C kRvZBuw @셣2\9a *<3V}BJfxvU3. F!4%2U۷SѶjZfA9(w9WSxʁ9X;@( p M7^[ٖHpM-?pJ1c0Pj/c;Q}K P22r0Α^XjtobA?$4xb@^$<ny<xR 2|JX>mٲer'Mg: KjeXZAc>rσrl `#N/7%ܿT3XGI$^>psLi,OΎ~">鶕ۭ[*N*fA' ͈:f"4Jjw??[s^o7]Br`9/H ,UK;Wvij5^(+~ NDAn@upK;ѷ_gO m_Wςw߽[HMd3e.3 QݘI 8쫯{Hea5#^: %T]3t6c( 1գ*j 4\/xS{*22Y%@OSYN;+b ۵˃9XEE'}-7ҥ¾#QNK뿹\K`̿>ky‘"-Tg?ԧX쪪wZeȈ$!{]S L>ߒg/kF"Px| ?Kgrtғ컍Iz&lPpN@sbTo@EB6рUY3GV텇88RfN(0h?&HޡGt1˦ME0m&{MȊO^Gܯ:x8竆$̸Włd f*m'V8Q2ؘ1Pמ;X_0BYkUpi2B]y`U1%R7>ڗX.p umR9N zΝfD4-h4Y%*jJ1܍R|#[M>@ҭ[s<8`+$ٌjtEǦy AhÖ1 ƏzL/Жmasn#}v'ԩZq*Ph<'YUov 5|^$-)T1=Hܯza6ݵ·GTMt!OުĒg(jǯeW?NEڷ:?՞S =P-'Q0/G.1c:V2!p䪫Þf=rmWofu8a qӃ0@JJ*¯zD,NOY;H/HBh?(MjDpr1PVj[񹯜/cċYS:P뻷p\)[ 5HJrTϤĉݱmmm* 7nՈmUQZ| ȗ% $ W@U;N}3Ŋ@Fo7+y%++C<|xH|?YY. DJ@b|gv䚚BubG]1Р2夶T{ig[nc$|ƃjyj<3HU@r챝9^~Ү]aާ /šðPױL$@#@ q,颋KvߤӓDCC2Ŗ nWoѥKs%ЖCo/2 >s#3 FI@JO8;"K}R)^O5¢Ex嫓E_VJMv>n)$:dϞCT5&! *W7WȮ]8#[7+8Z%{<9j;kô{UקfrlQ{ +\֦B<]b,_28 ʖ{#HhX`5jȪ gXnWW_aJ!5}׮b'yKgt#%&GlۊcUTlԪ!5ы.v4AҁWlxᒎՉS*r^e侈.YiQ{UTg~-fiAa̙VvVW՚Z+tE su B,Ri"=`*GU[2aBw㣈X1:J?~]^ԩB&h] okM<gכxoq@z 6-]&ڑ3ga4!@5U;XPs\8r8<Ơ _l#V)[c:'G}SYuTPǬG}; F6WW۴Q# {c f#/~k2X`Na "":klؠx Qe[uO1eٿ*|Xm;r*f?˵ݺ5$Ծ9@5-L%V;Qry罌{цRӽ#u^k4%C 5gC^\{UN}jǪA^IW^Yf!&P}+oփ\iUXR ɓ{ȑ'pf-pkx;TY>_QȫW+ ɣjhs&ҽ;Ⱥl盧Xm:eʂ;p<K'x\8 8` &^IH\HVnx.6tX!EBg{lwyp P gyy]{#י~$M`!-f8>uHQѡHc(X"xS%pw7\v`tq 98mo?p@R_ ݗL89|cO:{н_>K a>x{<Ѹv5 4{}Оwjݪ5̜n3<Ϛr˕.^8B*&tj}"&|7lpkl) AHqL'RuWf;%L7//_vQW 벚hzKΪe1]]r( ֫vH97oNtt݌!<=0Kc֬=z?nG`Ѣ-r-!~UTV|%So{ݓ_ThY#дUUPYRP 3^WHc-0k!ᅏ\b./C w ?3^a: ݿWaW`3EQ4l[E\J1+U:HY1ȨZ^8醠"ҧclHYè.Ĺ*gO)EæMj߾HMdF{Xfԡϵ׾@+jtK@F}B+BmU 3ó#4] }vFc[8߭dXq@PiUVV)ȗjxIU[ //mٳWw X})#ܲ$.+vzꙑ2vDJuw/g_4תu>M"]h. t;.=eĐ3,RNfJd|Q'QO_if ;+uctT^}:fcL;._6o-{KdU-Ze:$)aOu<J90xIߍҭ B`(#x=fSHcߐJ}m _FczSzl-TC&t?NIh^GwDU(Q1D^1ٶM}A$pQqmĬ}!g&23 ,*g5_Ja]4C!&*^h#jF|U*/^x$g~P k~/ӦKXyD z:+#9[T_dD)V"yɈc&M2W(zKs-]ii%<啣ɺ_pC)d?W; T4: "i_`xx _>xrL?>X3rð]sp;`Z$ucWKΦ(q**+j׶5 ѣ4jokꥯ+C ,+ u*!%%%cGt/E@Hǩ+q* lu& 4fDϞ-] PUx~Ha@9^y`ۧ(XPzc& .-`L$#PRRr(tbfd,ZG]muQ&8Ψ\< Ctk[*C`^ RGuu8ؖg}0tS(]@|rn$ݝ]|+&AK:4iUts0:}<"%3f F>nT;HV{ d77T 㦷SIzL; \x@֨SX+^H)TI^9n;WQn^H~~SҺ5}z{RN=7?)qJ30mK6]UE۴ürJ&V,ݟTeجx%77[nXnYͭ +/j޲YJ>}@U4$*Mz0omYOjﷇSjUm=#ּK(>f%kuC%R kcbf8FuVu[!Æʵ׎H\Yh8RUu, _KEuxB@M 5/,9x0[.`y&PG%GU 1`<$ TqE[Bkhd;JE=._CzYl6ή\W@n蠥ob Ug.]ԞɮsQpwuBn*Bvm=ɮ]PT;x#}=v$,Lz50V$ǎ,|Z̓WwImplEJu*[o\Hs%qrlzV?`׮bW.k_=I6/Cv8T[D ;WUT?7rqtθXwаSuns/<8z뱜 ڵ{ց~RPTN폭r!NА!mw[뀯"NrimoM Z&=im6Š+6~@o瑍\7ɑ#̗SNiE1%d#Ou^=Pk";7@f ǣO E-)]ՑPmOmjğV ]_|zFFi%/_[W hp8{Ie }d~Zpj-y@]۷^[HZZC'ci?^jW߸󟟣. M:ňZN~QMY3\իU{8Ԟ9{֮ ]4P2yf 9.07Oӟ.1,(1AY.2y9e˂T!|-16ONjcp!z333T-/&W]<ѮM߯L^YBHH rVXYҀi>G$1teBRQ^5{H%@ rɜ9keփ;  UhUA.SVgz3^m)Xw¨O5 <)۶j2 } T-#hۈgV d`K(X{BۭZOv.ž>̑H"!0|gԍ8裓eĈ|ɝdk)+W* @F`M mm=p+wYO'_З؛ƾ̑H%G´j **+!ӦM4Lm:f9ҬYvPb3HR@Zӈasg,vKqq`{%99ρ@..rY}pΊ@ mi+Xu0e/!r%GHH6<|"vo !|bئ9QW@AaaSܸD* ]eJv6o?'H99|G *[rdpco[x$@$@ILJIҹ;6j3FMnD{+n<HH(شjW[5O9;G}FnYfΜ*Md& 6HH!"d~,|^AZpquk!;j &  T"@ zY,:/Z|%lrv 1 ug,Y~ Q)5#bVJ,\dժV:B$@q'v>9ge)@oеks vf)L|Y" HeoW_=O:t3&MG@KyP˪r ̟9 bI/'NT/jXJ .9V L@Ȩw]d@逸^ΔA@HZtha}Gwo!m67 !i$@$($yǟsN?9heezC%ٿ>gMIH @D|f9dH(&ML $ D@ *.*wˌb?|2#QɋI TVzС )+k4pHZSҤQdj.bQ,1v[o ˖mA2fTVeÆҵks;7uO ;uHdmǕgm>X-o@(+zrHzK7ϕnݚKdx`@PZZ)۶ɦM&fҴiVٴ^nǨWESEBJz:wyQE3>ۈl?eVʜ9kc;ߐ@m:,^]^|q2Yf/N.Yf|Wc\D|Pj,c.Ҹq187n)yyY 1CUcej.裵[+u᥿쓯^5K8L4]~ժ$kdr2СYԨ#8[""WQ6V\#%%_rcBUJqq9 3cGRZC@ka)M f  v)s}mSru#YPlU B`̙̊K1H>tt9sȧQ:I'0%3 U4f:GQ{g[dfRZZ :7PZ5{76޿*o/HVv7:q}늷F_U~:s;]6c;хƫMFz Ppرj^e<[LeȽSΝW˻ﮒիwcg:c5>Zf4AwҺu.fMWْ뛙 3O}t>u%%:ֶTO߄v=wJH L.i ٻw|&/S2aBw2}tې: '7|- :t/S & ˘1r=7IM@/ކ-hVo~̦+n} (:<8P)˗k%XO3^:C˰aS۶qNiC2w:o|/7v 'Z`mXUMqjC#ᇿZdW]57D׌O _]W "n|[A RskwZt_wVc|95y!eUVu4 Y9%SӃX:8Gg'/*zS/TTyf~~#W=U՛X}U =Iaa,X,0ᅠ`+Y5..:۫+hsӰDֽҷoqI_\Jd'bMY~4~(u *Һu>wrG+jZGE/\ǟC@#yRiI-gH( 'cb1UP7X)|~e^WrcI@l/ C`8,$`TA54uM  kݛ_JMZ_=}0ō>|}+yrG63u\?p :W_m4\$̱_ӀgDR29$>/qBs NM m#, ŝ|+PR6W]6~bXgX:>N+wo`8Y`cb'9Bp ]R72)C Hq.k֬߆# 7jf;33MN?l}NVRAY`}g{ 2{jãz۹3`od]I@}{ wʽz 't9PD.ˢD۹s3,|;2lX->n.C2|wru_**˧׀$˗2XÃ*}<}h}ZaM;(@iVQQOX{,KMK~ yk$1]8-}qz45O,O?ֽ>//+E{M=Jx [a;@M7[=a)C&Pn<&N3# -&M1B!̬>pp[kj"!/̦RmI:Ϻ.~$X6[̒-r0T&1 ACp?l:z<'\nD2p@n#;,69WijMdҤ_4B;D|߸a5F5ق%=rWY4wWq뒺.{C^ ƒ^gq ik Kq]e9YTaYк@U{ŰSn0=H _kfCI™H,./3|ܿ*]/} :hu v`p {{+} _xk4|:#ֻncP\J?t{*m_[e ċ@Ix| @xm­F?;.oᯃ3 pOtUa/2 "tpg98MФ Kh/:9ՒЊ0J@TųIkvY#xhPugVY {Dug}82 ]kڵY3L޽‰v@ Znd8G}^Xhl=fg0Y= 4Lm)Loig 7M}'o}`U [wo]矫ރ 3/FmR  tp(~cک48VPDT!pt**\l? 0@r//7tyw[?Z/5i Fyœ7guIj_wC^]j~2졇6?YM_TT~".|u!鰞P'G}epTn8JҐ5@./ DCa~D~½AV^u/Ozj !)V:]xg F]9h6~.ׇ-.=UyCH{d" C_ES_)EEԷYb+Woru=1&b& F.)߹\NĠ?J]E,C頠{l~ 6?f9L$@ P7뛠Д)d5dH7s:>F_%O=vݫjs@PNL$@$`ɕWOIE8uFJ ~^r>kJ5S^Ja{ ?]/B`O.d_vꖐUu̘Buգyťo5@ y? ĖYqņM.;Rb[s D @m-w5A~[+*p |,).Q ߓ @ lNÇ9 ~RPr]C(".h$A!ηSZ~opIH >[8?vl@8PԌ {lub#@=5 Sӯm+ӧ3]-jwYoy#WsJ! )$@)E@=VJ}c g>%/TtP75˖SYg-X"_iP'@SIUk:u̯:0POf>v믯M>@ {ct14vU۶&U*N ʞVH!"alq<Ki[? M@ݬRN;m!n>ۙSQj ѣ Wg1 ":PX{K#1 @4Y =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M $THOwM$@$@%12-ͩS>`VIHH 8"_}7/G'p8vqb"  I@WRmfbW,? $n_" N] @ PH-}jra"  {P?e2vlƅҸqќ<O=5EnxAAHHHt,/[a?_:vl&rTW  ؏ƘHaaMi w]%6iU/0?IHH#~*(h"'v_b})hӦlFyeuz> ?'  9[r `E y V[AcLIENDB`ic10PNG  IHDR+sRGB pHYs%%IR$@IDATxTeVҥ* HXK1&j,QFMLILL4^ * * H]`i-3sg󜝙[=w{{VI.+gowd#gOR~9#      6m%--AuKi~Za< ytV2ZsS͑h9L3 K5h.\Ps!?J?Kv>eZxͱ1g"      r5gK^O.=Z\OC oezD1>W' E O8,edY]'T|WO[u&`      ޭҶ]c Ç%)-?_s$@$@$@$@$@$@$@~#pDKCD;y7t'~׍G 073su=Iu zku֓ZVfxg"      hx' N_\vi!k=        \|;⋋j-FP!8gd3 8@Ltm5 ͖6m\6nF       8,{>,ZW[@tߤX?&       P&aaRZEʩa?b  'Q 8@5LVJ6eI׮AU $@$@$@$@$@$@$@%-k#=z4?VJ;_HHHHHHH@n̞];u'U+      :aa3gDZq71~!      frS522e-6 @X`}4|rh-c HHHHHHH*go~G@_IHHHHHH $|1"{f˦M!(6HHHHHHH23s~?+/      Me>oB1 *sK Y>HHHHHHH@ ltP·lO 2͛IxFFn(m#      FO8+j HHHHHHB@AA @+pm![F$@$@$@$@$@$@$!`"      ufHHHHHHH@ PǀHHHHHH MfIHHHHHH>$@$@$@$@$@$@$Pn2H$@$@$@$@$@$@       F@FpD       44& |HHHHHHHd6HHHHHH(3@$@$@$@$@$@$@&$@$@$@$@$@$@$@      h(h7M$       @# @@#l" PgHHHHHH MfIHHHHHH>$@$@$@$@$@$@$Pn2H$@$@$@$@$@$@       F@I趱,tƖ H Ʋ7Y^HHHHHH  `L@Pݸ(ܹUPՙ%     hX7ܢMWY #;__i6K$@$@$@$@$@$9gL1&!0''4%6!Q$@$@$@$@$@$< YCVHHHHHHAGVHHHHHH%@|Y: 8  K{t      p qX       e$@$@$@$@$@$@$86$@$@$@$@$@$@$`/ IHHHHHH(pm`%HHHHHHH^˗ #PJ (/K'      G       { P`/_N$@$@$@$@$@$@ @#n+A$@$@$@$@$@$@^,HHHHHHAGVHHHHHH%@|Y: 8  K{t      p qX       e$@$@$@$@$@$@$86$@$@$@$@$@$@$`/ IHHHHHH(pm`%HHHHHHH^˗ #PJ (/K'      G       { P`/_N$@$@$@$@$@$@ @#n+A$@$@$@$@$@$@^,HHHHHHAGVHHHHHH%@|Y: 8  K{t      p qX       e$@$@$@$@$@$@$86$@$@$@$@$@$@$`/ IHHHHHH(pm`%HHHHHHH^˗ #PJ (/K'      G       { P`/_N$@$@$@$@$@$@ @#n+A$@$@$@$@$@$@^,HHHHHHAGVHHHHHH%@|Y: 8  K{t      p qX       e$@$@$@$@$@$@$86$@$@$@$@$@$@$`/ IHHHHHH(pm`%HHHHHHH^˗ #PJ (/K'      G       { P`/_N$@$@$@$@$@$@ @#n+A$@$@$@$@$@$@^,HHHHHHAGVHHHHHH%@|Y: 8  K{t      p qX       e$@$@$@$@$@$@$86$@$@$@$@$@$@$`/ IHHHHHH(pm`%HHHHHHH^˗ #PJ (/K'      G       { P`/_N$@$@$@$@$@$@ @#n+A$@$@$@$@$@$@^,HHHHHHAGVHHHHHH%@|Y: 8  K{t      p qX       e$@$@$@$@$@$@$86$@$@$@$@$@$@$`/ IHHHHHH(pm`%HHHHHHH^M- 2)))R)--3߱J~77i.Aŋ$@$@$@$8N$@~!PPP,\PP"EEȥfB[XX"囌EE5Hs-b%%%FҚ [Hi3mȃ=/Xȓ#G $'|"9|8_?7(8|@[%1arώlJBB$%EW>HHHHFHEULL1Y=x0_o?,;v;mNpsu\+a*dG俣,Yrlc <OF-}r$#9L쳲r%++||ã(W[&ETKc XB*PDŪ VN;IڵKVu @P(Ҽ 0=zLcz惲~}]_?˺uY:ݯfkЌIkOl*ql 5i6X{h*`2_+gfʞ=Gs}-[6Xm1YY߫ClMڎ,LJaҿS3;KI^ P'H1 5׬/ wˢE{TWdLuYQ{kU+(Y8IL \xa 'aB_Xs 'slR{sd.|fOL싊j%0&$h;[ɫ3"i.{E~j_:B;\HHHH>ǖ% x@+ҥ{o6ɷnԉ.=utQXgfL$=M֤ Gy>ux4SOMh{ P([:f5gg Ac&5<&ZB[cLȒ[n(=Vr%y:ƃ!    0yYXƤ*HYJ:lN(oTݤgbR 5}͘$ ?>ct9AڴO$#Gvܱގ(ϵ#?Cf&VeʦMY啶&֧5@#Us(N˛ՇK3`rȻUAЯ$. ? PO,8[`Y_"Ӭp#lT{l.Æ6W>kvOe۶-J +ݘbRkj:Q:O6N84\WݻKWSOmfHHHG$ I۶ &V^OgLF`S!hUoe r`<N9svȴic2tlOzDk`W~'ebS6Xz    !@@+֔&u_~y ;K]rL0^>N0Oڳ稬\O;4Z-ft0Y@8B]O c@)kSDDiJ(ӧ$%  h(hԷ'{ r@LV/ʊBLS8IKk&ݺ-Lɲp*"b !),,\.g*|ke .0a"   PPn' ɓBms>5R~k9 &0+~B^~yNBI?2wJa $AӡBB2Bg[r*!  h(h7&@SdeÆ,.EEm߿*GN+5kʑ#X C`eK dtOC8E$@$@$@$P $@!p )PE8ysLinvH.0 -Ԑ !Eaa˗jd xJ`ǎFxcMeXk<HH@ p4ǀHQ<{qcLE>p8S&P,B~*4ceԨ2ztGѣn' @( 4r]u P큅r}?5KégaqIY O'u+WfʃΐO>M&RP\e-*(m2fLg9tcZ MUPi̚6Hp BaH$@@x x@K?< 8TC$3@O<H 4 ˖-ߘ?5҆ahU_,pTfWiFb eMc"d$&FIbbo J۶$&&0hPS& hh4Iw>]kj٩80ͩUcH@ 7HWcX\B3w]>XvjMsmp9:sH'>c'HNmsɅaL$@$(; 8آ.X)|[꺝|;g L32rԄ*Wv<"6͛1AQ'r`uTҶa+15W]Mu&L̗!C}vrc{UPEx % $@ I੧բץ IL^(&ME<N!,Μ="3_fLYf*kуh&bOdlo9ɪ cMQbyqq^H &Vp4O; X"SaH  ,\[9MλvQ|Ygkw.V!,g&z)Rl & meHl"?MK V ?I"lYwꠚ5|Uv8lV_}̞F+T)}Q40\.䊼 4$ >M$ƍ"vuD0*8uEBINmx>/_NYg#:8 R}ru¿]2JT/| 6?^HM@HB@BBt"x.۪sd]%ٸqƖOW^9+O0׿_,JcHɫنeDE$@!Li9C2MIIfUxAcV|m>(^c{ /!09`"#dy9Z&:aeb_+Wc{rM_iٚ#V+3=' @# O#ɍ%*\ Z]Qi!U8!4m_*2_Gp_ %r=hlwމL=6T. xf͸ϵEuu15΀(J!ܬPfѺ `[q7IČ9bHAaf59 *ƌvRA={e}j>ټy]* /X Jg? K.%'p JQ8RWzs L{lQ3~2e:H38Y) YDZ?${4?L 'ɽH H ̙CȸYQXvH˖pe>P{ן%(Sx6 WSHmh-Ze HiF|S~qӪP,$,^G/iU1Yes12^(4S{c`^ĺñK4"KWEYLBxcߟoU? niY3TMς?9yZ:253ΰ¯n,]vbCU%8zPƎ}8;_k?SM,][|K蘫>7G]R+B9`]IXw"PwW"Տ&-P}FYgu_=k68u&]30@B§ᗓU# CMϝ)H @(h^:ers~`}kbVA{# ˳ j Aj xQ]PycR J<̹fE `N0x6r&ؠ^uU_]}L7·3kZ` UTTbV.=?˾#9gor/ȊXhFcTh,[I"0dȫjζJtžFiżts Uku-V[$&haZfU-eFeu@]JB;KIU~0nع,[WMlܹ[lX(pgveRRt0 6𿔜 P K) [9 ZL] \ Aɻݬ}rt%\#GrCsv>'= ljejyW@l ^g-}M}=Q\fMeNF~r i9)P̀$Sj9K\xwAzfQyXj0<[咟_& ;|m~؄u= kdhYE;裳e үv ub/ii aXBOʀ:G/\U/MQ֧s#lmR^X 7x 涳$gFfQnU(fNMsX͂,=X7WIM-x7p8_$-tR Ǐ8'J= y-QCkRRFFܯmFzunCAt)x;@9RPa?7f/xn {8l\qܥС|* By!a ݸL K+N4[]BHᅱŋW$Twt;W[ KWPՁ Oݺ5Wes1`ۅLUMEV%AB5[0**'Z˖PK5]FBZ@( 1v&hTMhl꓾vӱ"W ?tyjOZI%7khGڴ" J\p}-Y~:qo{V>w.+?'ZPxA`g OH;9N&[4V$gӵ?^iD۵*x@cxߩ:5{x! G󎌱[jfi*<;ɘ@X(j- Fy!XӦmܪ^sk Y 50w  hM\tZI:8s8_ vوb@ p.:xX%0tߡby2yp}gkjܹ;5T]=[ER 'x!#9ӝw^7j5]_G".fVen0yxb]離y[?t{nd?{9a-nvnf ZǟձT[BGhH3L2~>f0T{YDؾlYQ 5jMفs.2jTG# 0LC8MȆgQejmTim~Ashn$ $q,\ tʘ 2q]QLhFG s]5oxUe'SPcUqN,أu2e š&VH7&dxqjSt0N.]8Y+Vdm8C̄3!x51 o$|0i6N 6q33s0nSOs{d nJu<.ꁟ|F.xnnbHC J+WfE}LɆ%7m`|j?GDMxedЀ$}cjt]32t}7^jcY#3WѯYVS-U%wͮ~;BBsƒh!aRy8/MH_*?X*Z{xϬFp}X!yͥL랭;VH $&_qۙgv6vc5Ob mա*M1SOM7*@zx0ƤJN|E%iO4cPZE5ռ&Xj>#Ձm%SYy뭥3-w m>m}W9¶>)Lt?լنEDO+..#ʕxA͎a bj'E'3sfZ]cǕRfNNјݻPgXqb^DD]AmRSr -rLed#-#ސY-IIⷕ~ŧ%lp͎  D,AukV'Ax=,\K~{<\m%T1Xr?o: ҕI$ɚ5f{n 1nҦ'ؐP_dXvpu'g`4y+yr#Gw wK{.t ls{𭖉D^{BoN'jer&RFl)8Qea=KCOjhgUsQC7H=j;z{lL9DЗC]XmTh\ iX ?8kVU~RMí4}>~dQvƌbذt~s0]Җ-q_ڼ'﫼ggU >Hv>E2O]&}_^pnGž /QnキRn}zCRuw,/.Zk 8Ɗa𘯶]G嗟TÁ!a;{z5+I.sD9D~Ҙ?j]!f_Clٌ|x訂p-Wa W7 8wΨk&4JO KooTYk?מ۷.s>ٓV{7AF4?~@w_]+$mRzjSk=BJ$,7uy(׎t(;Ww {d:u=2xX10 ٗά@mO+<ɬY <֖JJJ`+XxnҬP\]}i)w=L 9ӦcԎj^^ JVJT6B+.pk+WJV0g6Y!O?¶_ejnD*MWTo-+h/u>^1J]fT}(/̘Uoebχ/_`VǑgÂ|P s=V+~ٮ[Uwj߼_B ɼ5G_1Oǥb}I| m{WsVHnZE:M)t(.mYkJ `ny]SP!IreNxN /q'/UrML\qf$**L7n<(%%yR8D|rIɪoV+ڔb2{Q3_,Sf"EEa0 l2p a$d}~B%.'P 9Bg}{.[Օ RwO0/p^ho7o:zU mw\\ ,.Un`%._uU}lۍg L_mwj,9{Mm'wΩmM5D %84uB[4 z*ݮc#V_. :u[U6س H$afTz0CU*U>N$̬8葪4E0G8 {'$cysI ^TT,ؤy~*Wvh4HRۙ$m5l55F=m*1hquU/͌7bBŸ?ڿ[a+{_i`S[ 8kBT-\W덊 HsCIsz o-gYn Z1:yﵙ5;5;GĻu0#"kk:Ik>Dۨy~O2!/$LKwQ~ڡ)0T0YSh~Ʃv萨m~/a5YÀɊ)AXˊ۽1PDnT(\ӯJ@+110+lӟ/z]fk„0 D 8F| kĢE{5zn.dM,&HM&T 10560)LĦXMUʀL;8VAC +  KT`m OߪQf6Т!͸Kgo/X8.rLJHq@`UDB$MMjtno}yD+(\>ܹ7Ë橧ΪWNJe{=;CxV kdF3`QAJ7hiZjVAWX\U}GǿϜMÆYV1ԁ'ZTPpOc, 9&RL$@GhQ*c]_$|Z҅5W[cU4(W:-pJ? x0_,Sשo3G6Wcn_%Zo_v{n@8L,.xIϞ2~|wuwS+ƛvnC)hݔ`]RRA3STwm%fBWb^΄v8r\,8[Sϵk &0o@l &-CoLU /ˌkt?k>l Zx>F^{ISl4pYߴjUj*P~?iQx]{L$@$=^Zx/_߄y|*'@F1K px_w*[Ǜ>oB0V`c"]NSYgu5h~k8^+r=ÌaՒf]ڱw'JDDy3~>;;Cj]{F&u}Ju.j\'靤Db6c LN?uua2H K7ubL..'\|qo6mD`؆;3khc 9ꡎxLoݬ+RXihVҵkyN!ܪͤ%v&MZBOy?UEE* l+{4k湦oVz˵8k{YEɦ9Z3훫]ҩ˹ر]NTI>XrpcSfv8} ,ϻ??K.:IP/[N{ wD}:Ԟ5LOKP4y OmHhcS70CC&Ccȿ:#v=I'2kD'qusU_/1_N<0}AϊCjc6D2e7OŬkAӄHH rs${u:>5l$cѡF !!t#Ft0Ms 0W*ը&V{:s7o\Ux 33 L஻jThy^ H=m<.^CW $sJMU{~Mܷ)L9r9pӚR_HKNWDx)TH]㭷2ExE.L]O<5_}eK-.SeUr2kCyZzb^_ -a"5bD@(>XdUM$@v7\(W^yxڰ| ctyf#G6mukDEEeXg {w^o gxǤq7o>bܮaY5P0:nV1Tnbq> ~^fkv7  UpAv}u3S-46V?;xŅ*4;gz.RGCSGh=0-Zzş>|GB贴frp:=dbGZU-HHKl矯,0'0u `2]5~rptW@5*vs *Tk MnmʞwoWĂuo;L vm'wn*)udG@c6L6m2ТEz{Ã,@NVJ{߾UxF\~yϙ g1AHBDN|4׫T1вw+XA$BՈ '^Tm򏽵8“?^nT;; ؆<0D$@AMW/ި H.5,,0'2u [w@mK$2$jy[" -[UhWUnnyԨ5~s4[Q|INNf✟vZZ]^ޅ w &'p;|2c ; vk1!O*VGliէB`?~K~V{u;L}57<%"OZkeXRWpa~ۄ/ 3 L5~MMMG gL3JV-5g 2}CBvEcǑJɸK`*WCSYWPljx Ԏ49pF+qշ U(彗Q z@H&8#lHz=塇~bgoTvѺyz Kڛ@uLuY/vJ̨xbB%E]T*J^x\&>м XZ Q @`RY{5g|U=ՠڧODu; r U*Q)%: o5z9vmURRjJYRZAe5BԉE1Auɒ kwNaW2SQ9]ia=9DŽ0صRu:S&D&M"ǎ|:5,fK=j=fmc|ߨX4.v~~"!~'h0@Ƃ1)f<4wOUO$3>|C2 kNXTA&©ԫP'ډF?XU5].pB[ Qse>.\ ֚l~Xv]Q{yq֠☪aa?rq { aMǻ_ fX8Z:'PhuXZGw=̵ѝgQGKuJ#Pa.fgÔm05UC@kRWΐ;8I}KL.ukHLRP###un:DVYv "vĔcE݊]75~xj3pZH|\rIoy4YjgzcKUU_}4*28}BELjчX<c(>{ūBPrq Xx9ImB[?RJ8SzxEi-I2f\z TzqڤIF*6}4Z´PZĎ5/1N4iծ8)_&?MQl bׯvgر]l:`V7]T6 mrM5[Jٴ頚!,A"إ 0T5VB&=H!z,S;W%o<>ylIIj/Sc% A@{ȾԫW+ӛ+7K\_rGFǺ &R>ɪz)B8wgzLe!wjδ`iРFO yj8=tz3k;| 7ƫPpmpSx>uJY#ЬY_р|~̈́VǍn<>K;$>>JmGUT;=RS WUp-tOLD x1qÍỏQjQ:ԉa:4{QQᒓSdrvvQ>z*Ŗ-tegF'Ĥa̞8ڹα"f ʪ2jU|z3ezsWGgVSq|EB8kB'x &@ >r͓eJuҾtR+u J&kdmʝ; bGE5Q[o)E/ke .iVl =쮕orJdy$@$peΎU?㈲)ƼE83'g@{Mx"\`BZкu4&mKdl<-ZĚ7v\U2v Kʊ OPDL_zivg?t$>@?0F-' (IGz_&Ee{p@=?8M7MڙKUb5?xMp6ϵ!(@1"·I@E@UM%Ɩ/eW=;H sμW]B|^IHa i_绰#z"'ѣ{'Xȁf,4V[BVо} O={r75*CXųfm"3gnUe++,S։r`e8]q.S/9Fwss9/MqG4a~inp&\'b$H&L8Icy3Au}pٰ!+X$0F@B a&5kP3zBUL[ ̗5kiR$i2 fdd˔)eBkaǫ @e_*',GGW.iVf9eK~~{mr!Cj +2L0?@. ]pA5vTyEzm7`LM+]^}WݏKH60{S%x0ǠZan&+WfWP3Brw 6l8Q]?blj2p{7V|Bw -tmMpm @ HBBsN?R޹s WA@^V_Ä~mHl1~|3aa94(!tU$` 'N."nj"o1X8cK:Q2_,B1 M`4 Z|kODif"״o`E~w}cwQ]KxIpwhqW^K[-ub-C4}enʑ9{9#iڴ8 .gNF1mzƳXG+agx`@!ڪqP)Vk6fT;wGr(L @1sl4eJ{:.]>}A@!m_Mf[' H9ᇣ VCo= ӯ`7K}GGGrƗq2ۆ c#\H,#x4 ~{ &S֮űF󄴺߲e 5ߘfΜj"lEKexg֭;A_Q`` +9n:}Nz5Ok]OlIǮMpJupfҶ X@ ((iР&JzeX%<yH:B@YPPv~TZceVnKr@5q-,,V,(dLٞ%V 5kV'{QطarZu y!Ҍ [ӏ<d@dAO1 g>S0)W'di(Z H3yKԱcO?_vւX=F%F7wCN[,#PL3!< v׮H C _i,YrJ Ag&A4yrkgO eĊn))޽㏘? ʱci|1wblx`oY^~vR@rbcG,111Jݭ[]9_P6E@i?њ;y-KcvkzZQ`޿uRn4oTiݍ$IX}  >4yZy6;Jh {/Tx''Ijdޥ\UgB?GKIɦ뮛2j9 Ūױc$f=_a.}-_~29M eFp` L&!>ԩ]۩g_!jZpBjO5iKp3w۸ ڻ723Y1N <~2˙aʸCM(U.n2d-87Px ߚV_I5گ:$^"  Pӧ3ꦈNvIuFy#@2u~s@cG0.I\N9։ZQ.쟛A۶fKt@"w,2na&$5,pp^Zwq1ⱎ>`J |p1_7*.U`c)sU<6CYO7+(aЩ}\/UVD@.:sOE8MjQi&$`%4&XeE<6m: 乹گ\8 G+T9B+\B?4<0E.+$'_sB ￿WBCC#MVZD#N~BY4kV6o>E#ˈ}_fMkҥ F<~/~m3%&+}ym}ZP^}fl۶29\z%|ry|aEE7:6mk׵MZ(&rNW>\6j-Fx 6%tCIo`>9*Y8w.G"[k=z3>Ԃڵ˯EG8#fsݻCKzYQṰV&Úk̿']Ȑt%%ÇN,"|5E5e+! <%g" ݗ LX}bi2ߡCCUey%fWǤ:ڹ aDBeG9s4Ҷ!pp7Rz( aieIb2|Ƒ^SԢE+()n+U<蕻]>`)"PB';|<]~1Eye#`97 .}JB ФI-E\k!#髯ƕI]uy:֮M6?--swF |g7Я|S~tT(,cEts97n~#٧)zkw ۸Û9i32&Uܹ xJ땸۔ oM}fREk /dqعu;o@JkAjY@V0/OjQa1c>}Q=6 @8D۪tV+N7k勐 ~e^rrrHkׇ%|KJQXݹ:Ǐ{RX)JMfEH޽"YKսs@˼J g"Pz~mF@~ )06Oڴo.^j?HF3\ l(ڰx[mU/g+"O8p\ PPHp?lx'g]ѵ}J\Vfѻwa|w uE^n}A@p;:6nBKŢu(YX*(}Qi˫PI.SұUg5%G4Vm_NiR駟v ~O:r#}Ԅ F/ͥa?(bN֛W T{.w_O! 1SS0]1C[ؐN {]ӣt c78K8_zÄ\4O{^բ?iC#zMػw}Sc 7\{oO YFv˧pzG|;oË@"o߆Ի: AlׯOL8\JSSRpCQe/T7m:?{rּ,4wyvL{ (X&WYgt6?@`Р&`~Qiq 7ѫQ= &ޡ͞-ssPl [yzvA@p--[R¯k{uno [ % `zE*Ր!͜˜qXzB7,|F &J)KGng3``=`oREKhuw}?qM9pR!gg="LzkBZ>dKӭqU`}*)f3,@bs)99ϻ@~C/#T裏F]|+#SYv9_?˻A@9FHgg?XGB TB 2g)ZHڵ :Y###S\ 6#ٿJ3Rx1'7顇_y"@&~.sKѓO.g }1 JM͠g]< G`̘V#( bfv1&yNp1c 3$Ϲ+‰ ` 4th;9s<}X⋱-|xvYDu^=\5KOK̙_Y*$}R۶v⬭$@пin)//C /fƻJB!A@6BByK[Eyvm"soCH/O;ڵPÆh,Cʟ@^@̆|_op=Uq-Գt̾癙=̝oі7'J|7.&QtiNQQ ?H7<6m WoUU4E]/ nE֌FR.(VFɓs^ZɃRH1cZ^ ?hogݔStpb.9y:u*Es[M]x7)C5q aAo29/vKo7c1D>98zAfhwԛܿ24kws9tn -0Ռ=+ 3y sI܊*RD(}2ըA}6BCr]jXg(B7D$؅ '=)ǧNs= @챼JWJ񹋢p-VnBY@ݻ7p*?Nm{~_7|t5׆҄` A~Js?8D*ټY;(P?>R_!A:{l M6~uMBeեjs`1;NYש=rIdb12#a>@> Lݛ*A+xG|(<-μMϗ}|tnIQrYz`2;%< ޜHgn|QuB9r{kX2QQpf[LkZ* x'}}-~dE. "#Z:}6ƫnˊ,Q9+WN d̙,5#i ?KP~-> B*֭JLQtL ="JKer/ؙ}g AǎLbQlFph2E<]ŝI~bg!!&ˮPzsp9v7JStuE9$72hY@99G}pfy+wHd |gXk8t@ڻo&:\t|'Z3,D/Nwڦ*x`,Y1 nP |OL0Yfl2h`ů|[| BhUH܉@1[̙GxwFQvvcԫW}Er SۅMP͵ kqFэ7fvIY6 :ohҳ';&Rm[U0A (($lj?!`H[gsɝw֗_v,'Nq,=*E+=q̍EfTbߺD !fyIڵ > JEBb6(fVb.T%{wNj2 N B_jz۟{,'SV6~D`?vDtf!_:uZ7z¼uxa@.2+,C{.}FXԿ;zC1 s`z) xNdH),()  PL:5l@]̖A"2 ?7(eUIA'n]B{w{{Pdd0^K'<#!`na=о'aȐfVWD s A%- wPЕWZ|_A .s>ȋ)5n1őZHo3~q;L[A@@TT8CGիCI_j-Yr/T":H2KB}/69hPGvK[oV+$ƤS" ok )#A!X[":b-5]W̙l:C3wx,x>,;5o^*l!L;#GФImm\ |_^dLzɥo3ڠP%#>9ub"#s):Z" cY/D-]m+=W@alO_J#( |' eM.iCo12(R¯颽e C Z8$,Kp7XIe ؂ii4u\:w7Uȹ`$S8x<~ߨ:Oi]p ϖ֛cuc!VZ5g/Vjc&o ""g@SR&uLZHp. 1~'9ГiZsp~֬j?2  %%~sH"GazE &= GгdZ"UTzkjвe ukU`l 8<˃ˈ5%f⬩2 àՉ/$8oD,(Bg~g9]-p6o=}qʢ=)) ti9b|3EmiJj@g2ӬSiHkmnX*GZo-畸8}h53{6MIɤ5kȔ)_zz}||k?yt]WP7GF#$}L@77WexjmXj>#@F֎@׆&!KHr* ( mʋk駗Qa! ,;p{ XzOk&Gm1_(iE.<6N@D@hOup}|sw`N x룆έ5iڴE/Ic;X/ r;![Cߝygrqg |}L69*GS:U >0Q*:{ll_b .A(6{)7x/Q>b)AVz)<{]*C@f+kb`IIiTo!jkVFAu72#cz/4X^rEZh{|~3h nKZŨ|z)R7!!0GBo!{]H?k"{kF\,ȕ[Νg)&e! 7@|ӅlE ;;5y~ĉLB!H~#GS6TvB!V:RmCGl$bcҵ׶zz5c{; nEGр?tԴi ?i!A@z@b֟ @@ƌ ̣$Lyl30-%yo>|MA }t1LˑBo͚dڲ$?~A'&rkWD0߭ ش< M+&Pاh2}vm~ WM܎,uDBrBrVLߴa܌\^r}A@ bik&?IFlȪtݮ],X# -nL|#Ytp αJs A؀+#`| zu!:ҥ0e빚\2喹J<{+=(g, .a .g@|1  `@6ܮA%gassԯ_#oqmxo%y+)Ѿ}g9o1D PM\ݰr #( Cl_MRVb~F G !AݏA#+.R 7w :iKbj֬6uuV?h3~|^|p0cV[!`AvfB?V>gGȧԱcG@hhd&MpQ&$8}:28C@tK:v n|oӤImS*HB_}5ZpN̤^sz;e}?󕋌+ kRjӦ#|w!TߦBNsA_'Ұa|{~2IW*~llG  ZXV| 5L3f"~UA@f5tSS^u#[]sMO:ʼ5f+x 9tm]+9qZ?9VYmAǁ2תtk| $ݻS/ӧӸb%%PLL59s¥.V:q*GTcqeڴ^y{t'Ћ/~̹:uҸG"@k&⑷Ǐ>A2+ %'{/& b3VBw#XGW5eZsqD]gmQ&?bGz*{:Qmٰg%\8@@)9[P@\N!z/A-cF@G{52xC@x=۝ᇢpjsىА!MyU6a>""ym^? G{@g8ft%%sLJM֭͡cx|}ª|4`QDU֢0Ę $&JTPŠ{mӔ1cWfdK RMu_c*㫨>99[u\Ղcwy~% xE;f9ssZ@-q-_6kJד-nݪvw_FO?zg@37Ӳe DYYt <OghImڍ}/5oCvS27P@ȐŲ5FYۜlBst-sDA_x u6Pa!ރb i[/]@Ӟ6,_߅}Rl@@MҺk7\n壡5hnNjO۹Wjߚ~q-Z%'LҰɮa峘WθDеkvL;*h17 I_}uEhelݻnqMv)4oI 4D`Ѣ#矇w؁@{2v:EOh8 i/E(t?aKHCapjQr.\dZHmrI>9o#aԻw ޽EQqq ~N `يwWr9êǿ| Y`Z|?(+ ĝxdDGk^L|߼ox_ԬY '#Pך_[C\!Ah޼&` 2jjc=ÊzJ w,hÑ#8 _2 W62`{.؃Kt>}pAX%+@=~a3_G!REW,!Лy⮅~4iRCp< -v50Oeײh]yT~D;o )) ($>wՍڷh=mX! 'cH5U*}"liJ׳g=ZA i59T>dr=3e~ԥKc>ePq:^f}xPPx#Ai_o{9<<BlF@P!¹s9 i/ϕԄ|OKNQ<ᦱ [  Ca!d6V4]wǡ4-lYck0t&YAu'4jTgd1k׮u9q I 9lyЎ V'3ҪJMܝ#U B{zm6)(Ew1_A)O8ର.M&A@5nIAiEldtYw& RGxaU^U#ZУ5QSݺ%l}6?D3g8V2޽8֔{,LAn䝟?[AǨJR4>@oEVҶ]*?r|׵XD]քز~_={R` L ×igt͝W9%^yzL,c#ދ-,pmxTm*~sr 7wNּtL@ [0a΢}e.jX(XMD`'|1y׋49'ӋVر]\S&ӌywϷ+WiZJƵem3MJʰG *GX+i@ǘ 4ujޅgW4ȗ_ng sVGNx1:1ދ}H]pg?|+""±'`&Iu[M9Fp!>` Ń'Ea ‡X}!J͊XH]6Mt%͛dzKx^U"5״UUea>9###6AwmiٲiC Az t,YL?O!%?t ]l:zx1o"#CUE\xaٲvKz76n1#W ӦMɬ 11bjd#'Z"Pn5D6-A@ D'[ԩt.!PW/H%9_~9FMN` UvWV!sP4|x[ 68x+Ã8 ! Fee`VN]5*Gjܭ3?!C}6n<^ WMerNz h͜`لM{9⋭ +A ]VbMzMyJn\Hp :[N-"`bRdfг.=W {Y:J8#@Cnt:jU~y5kx.0?E_@Z5 Zs~ƄR:11@[̼ =mp1ae %wVnvshJ1yg7p oεqZ%qħׇmf{R5 9rJ>cg ^!<9'̥|^H!uFvmSSȵ%oWpsw~jBv>EiXmBX@(jNoT4 +;Ĕߡ(}"F fpŁwwB "WFP ]wМ9P }u*kOk!{n >4\ZqhP_|a+G-g;yC7J* 6#c6D$KWEw7[+Uʚg̍\D/VV(k薔dra/F?$!\IMΟ}%ۚs-oHG$C"ј]qZfPP ^Ŀ 9~r8X|hVho]9cǶbw"(%\OFSqB1 %6,W_]o}or Ts8`]9/(("PqVv|sfÄR Ļ)Tڮ]g8*.wqX`41"/M'hǎ3"v˱#цT'AE~VykӵoGqҤԧO#a%T7(> Ipg|J-5,Y9:1Ƹ ؞vdd38*:6Oɣ[RϚVlB :իNynv!!=y"}uO.h^Ī'I+w޼r%&LƟȑ#i:#K6\]/`!j<]{mJzK `I0wy,hd3:*[!ZaԠA YNVDrkhʔtD&[ i?s_nM ҫ`_*wߟ|1GV7A1cZ^tР9.o1LE9f!M7u/EߜVՇ x9YNx\e]v\V @1Hwi߸fs*^r,Ә=tlzs}6lJ@w_ԢLzh1_vTTT̓5zl%ӶmhjժN_=FjaG}"@`}6#iȐf,WU0V+?}YM*}TкO>o~~-)Uݠޞ*w̧4uu$xӪrF]|1zE灉s # @wC@(evvS&~ %QdRG#>A) 3;WLcp:t==$г݈gER5si5dI@ ߗU8[o 8Va:[)+@s3Yt.U:⾎'* ޝBoWS25n1}wfe3СC GƵ!r8!H||+;a`5M-  !!AP N]]R8j]A,7A[p}+#/oE5m ~p,*~ \`76mji/؟qD8gIϴi=oȥy:=21"OyQN) ѣͯ#.>L4t 6?ߴo n}[O$mL;'OfmzZMK={Z/K q縯`*&11zk@|/Es*i23L۶qld&q)r=tr'P[TSuݛʙ `EB(mQ& o piq[ w,*(}(fΆՕ+.WYM/>ʊ>nγS_2?&@뇀lRףo}{קhfҰhJYk#:~V7 =zt[4yrW/YA@LD7olStpO*;s&[mٔCǏ_"ê? k&o% ktt7A}L6Oi0>}WWлmcYKo}ZY9={֯\f;.W^I;7><|07(;zËV}á2F9Dau^^=!k^[K ـILՀ]wude7+W&)+\@ӦqOyͿ/U0ԓxEdד[IfK k5mmd4aOty| ?Vm1'M܈ Te!LV#.n]Aņ9[3( xf*b05pY@M_h+?:+ [)϶iׯo?@p‚"a`,7>8I FQ_p~nRAt?!`OX5?8D7&sY*\(*YE$h4vlJSkќ9w7@H`bϳR'rp ㎄D 7 鱫{7}D|T<[1\?)tqvJtYNpjaڹsmN5 \[ok4܁W[=A:Fhn]A)%x8bW z%܂=PE[n c4rg11c->ـo~6vdZY9 \wԱy!W\ F8(!;)9NnJXTTks{c7UܸL]\0gXZۑVW]Ն~{w<7J{m$l&HnuzňWCw9:j.J3" ծ]f4mEb޽?&`Ê̝FjqnæU͚0-? V*Ѧjш$2; AG,x`tS'и&]q8 _ ]Csf(77]pV4c?̩j֦76CBC#9FWY{<QTTsEo+'ZFWdas"_S˱&fo?,ӧ%̫cD7@# (;]Et٤{:DG0?-f#Gַ %%2xչx%{!sL˗*kH#MH]cAE0`5 KLg)$ b`ܻ75 \=gԢEyBHK9^$c e%oc̞*ˏq-dQGhMWTA6aN ~@jjERت7"#}_ȸ.P3|^ERxWEDTzMԠhrk !VAߠ! iJ_P~bhCYB7Б`j[.%6\~5HJ8i}YX"h6Ka~ ַ/qMhݣ(橢F|( [w*tTGV%ͧ()4Â?$_@=7Z xF.kHIqbۉԬ(oƂ*ލx#f1ۆuR`AE!!-̵\<~KK&(iX%qHڡ5qbkR)wiѿ!x6y V^zi0n]?نB_A@vIzkB{:v,UNܣGیֳkRaVP1aR.BVLv(|(Y 7LV )X!gRN-衇{*4{^={?mv `wmiw o' Jmpw WeݍOмy!!}ZÅ z)Ҁ TDڷoH?0QYw^Oy41sĚ#`$vt_nVK8od+7PB~8_H}K|z" ӹMy%rvねúiң̙)f!oDZҔMN_Vʇ={RYfL֣bQNNmr#4k>[*yEQLLڰz8+`R@@ TD3@>= X(ҧ])99EhǎOd8OOIeP~|MlA=|S|1yLkiसe,t869t1ҝ 8'ծ]q}26O:Nٕ~3:p ȥOXxmL=\';_7TR@5k.e":^x-6+#p)6=u]WЊ{tEEO3 P+3r@b&  PfFp*-Q8<*уNLVTss!Fc1o&nlZ{b_|ҧ(/Jggp,Ӌ BʿhĈʍ`خ2%k!G# <'-6"mBwWq&g65'?@`׮AƎNk[Fѣ[2eIIHgWWoXF;Ә1-2 ~ &tÛkFÆc8i=nFZ"x { _do_B=V~sJNY ܫز01%~b .]KD{A.u 9oذ,jkR^ICk>(qnK/Լ/iPp7:tABћoۀdpT Mi_Y|gzԿ?VB/fJ&kkk}(X`N3 5A ^xa"ӀeB͞DQdYA@֠TLJcRMɮ!P@W_ݍߍ\,8>u!#-/ rP>cH"U=4yr;fT$ӆ;L܃VCl\`ކLUˮLۖ[N֡pQ\9:?TO r;Y<Qx3 dcR'(zbF: 'A  `Oլ5PӦ >BFuKڛOW{0_Z hc޲kC+Rm\MΝkSddե 矏%X8&qPiɿ:[FF9p;Ҁ sNNYHKqEHJ?}dv#rԬ0]$PY`иq<,'.=oVhWa/:Wr=˭q}|CQ98851) x-p+ׯK< }Up U5,oO'۶ի*uQxw5ơJq(7 L!Ax#o?#k DNaL&+E6Bul>Z^}J?A/8@697(%s0Ή$d_˗\pCG+ZZ *k0Mm4f̏TT7znjiVbc8E  j7ₒ}RS"ڻ z.QxV<!;):A; -~հ@*97@R.Tȵ ={[ロ]@]J|LmJJ&&@IDATn:D:Tz тiH@o{pqѴiњ5cn?cZ ߳@ڼ5Ò2~(l|JHٔXW\J EnxC#={Sn8-'d-@gm宸͜9OŒMv\_ %Q ="Vz~B:L!AӐ!R߹|sB,YkTJw͖ QΈDmeR(we< #pUmhɒ؄լHS8""&MA|s On j2ي@6V)S;z+nnE az7*351nE|؟T@E{IJXGn( GTd_@讻:v,N뒾+uP^rM/CBW١6LU2e&|ږsG\9շ%<;d'11];w.PZQޯ:[ TF)&FoxmjwbΫջ" E֕ h::p,-[&𝇢 7CL0mo69syOz?ݻj=j՗)'VB WP@@[[֣jKS՜NjjWfp`Tt?%>ge /^| s֭ŠƇ*&׬Dyhei*swan1,#c0|,&%@ 44:vWFT\\BH(Ҟ{;t_E7QCQ#ZE;MH8ih˖tdOQ6'9XCN1Xj׮BHxl6UΊ #Uf'a.K`DRPI B1_862"8 JfG[Z@U?z%99zh[)#ٍŲkK/q Ep*,,}sx7J!pm+/ַz`/WeR gm`[[sa$(JXPxRr arf n "6jV,낀 {8qM03yU:Gp! [NN!Q~~1oSy G֭J]ԣm<9^ C?UW|䑾twsJCFroC/qMX YTP zu.U.\(r:( ڷr.z UÐBNC@!jUi]H v ciڵ+^`K=|(~1i5n0Yr =]`VJI{PyD-gdѪUr xa_p-PC!88PeEyu|=zMm -Qrr{\ 1LJJXf G. &Mjb%sDKqG :j)ʄAWؽ;ECx%O|_/&!(&a eJS 9itk_~U/:gi\|x2Pt͋n!|s'prWrA;I.Q\{- @*C@(c|T7ds+*5,[^A@$Eڵtx&G|}F06Xz筼Pϧ#8]9jƉ^tx͎`HGtj￿22w9sִt*=S}Sj$3QV$]}u;]܍68!j;0)$ޅ|e}$:z4G>WFt MTwiR`  P݁>p$3eJl,-_C )ҀۺQXSRr% };+@uԔqcӝz7ٜV ͟n=Ex@XA/I`^P  )ލ@xEdT3M,--cń`5UjY4p|>rܴi zs yD\>8,ֺG7Xe`w7?Uj>^CFDp tpUPl 8ObAXG1\Zq: ‚9 *lHH +)6Vd\@'\Z0bH@R yi&MЃ>pɤ n!ze,?f1oAC=rRPP S+k0Jo]ڟDXr\G`3l'`@24[1pN4#sF'O4w$fEZ=Τ{n9m1C~/lJOkacÊgeeA1!(xAHLwdYD`.4hM?|.RC/<^{JY^^ - D~%94D7U~y? plλDOrAQD(R_ E;)p!CC S}-//Yu+#lG૯/fHz)#XU^)NEJ)8dWSs~:v `QXȪ>;TUaBh4ujg CTD`Μt}_?e|1pA|4()I~P ގ@@@ۇ! NC@A[J(ǭT\7(Z&5o IF!x9sg>G?nIAA=b/;e N0dvp//һ  j|+8(ET{Ib^ /-Zf4CX| Awm 7 shRSsH.\<~فǗ0$xnDEȑR!nj9hV#pMsd>k¿~:]1=+]@ ! 2ȕ@;|elZ -P,5״+u$އ>7PϞ}XQ}vWUA*.gˣjUR"r,{5b yǛ`2DA~|y&e?*l>΁RսpŴpԻw}" @}] Bcx4yr[-`^(A(**\D3gn.]Pll |lСi ]fjp:JOp\-C-зNÛ{&{• hR@jGM qnڵ%  R&0m$ /,C:֭&LhMU=/\ ,b?Iᛑݏp ؇+_7|ୄ9E?-q:>u*zh%%p V\W]{He':; z< [<[?#DHU~|p0k5t=4cPj14th3:'@9!#PLMr?/ FWp^wQĸpeںiSM =Re;f% .zSpjvYVl_B;󀏢xsi$!!%{&ҤI+"* "T("R(t&Ho)};N˵ݽݽ|>Ny;w3<{l֯?ame[p刢$0;@ 3GP]V#ShhV/؞ .R<|[!h\In&m҅O_mLR> 矏rܞ]2D1Ϻ-h:6:Zbg"7JBђ%]̀t `=M86X>ҩ&}X 8'`?EA&EBeȐTxG?@bb~ Ż|Iov^?8: $fХK8^;lՋ=ԦkSHoX!b'^I?~]Ϟ.4I@LI p,)ʋyQޘ1YנHE5.Z&ӨhRG!djL:u*w8 W1]u6̀z${[Y,ga=JKyԱ<86>)ﮓZ#qSV*ESΕYR C˸[KG8;z2p]KDUo-@^,}}i^%>Ꝗ-˰gP`k] G$С-XWeKտ"? ''W"z;UٹI1 ֭企+ >%pb"]{e)|**MϞlݧ?#YO ȫ).w^0!L'=Yk_FERddQ RLL~*PۜoXez:"HrҬ6_b"HvC2D6%m! d'`Ǧ}P77ܭ+mڔ+HU+uF6!e0:fMmQBBjdz3g{ڢfʗ_glq&X'#Lê HH:2ɿVOG*ƍ+ow*`"6j=4yGX;؁V(}D jeLe%)DkSU,< c2LYر"}ER0峙YڔN`o-0׼wfgIfWd,@ ރz,lA F%>*U sZ}oEjSbeHN7۷u#+z Օ-N@C8ng0O@Ԭq6Uam G*倀3))$iSYR(ˇ](g4RFɞvlTJ,`=R'Y&_iQ+pFӂ QZJ`.5Μف%hH^бcW.@^cl]},:;ԫK{>D>jrv' :uY@|Od>^ΠwB{i!o Y+1ŷYu]!q?.>ͦ1V;]n$dfO q>5Xt0ьxtLq[- @s;CׄVT8;2G1Bh$G"I@s 24m_g~j}Ik0=dKxGbEL!@_GXC]y^Yݯ։gqz{,XW pr")˩k|2nCbp@J J t8!Oz)h$lʋc}A@tud?4&Z7z}w˞wQU+i4{& NϞKnwȣ$ЦM9c8@^ݻ)SD'*FUpUl|y2jBnw!nXdܞϳv7dw7i֬(13~:UӲeUn4jww F_:iWSYj6\TlUhZv`5N*hZ/*'0g&݌A&Oڿgp!}5kw7D&w$&&i:ochAlCt] ),,ɱg9EW8SDl` @*uVI@G}9rd=֟Yz׮Q,]og5 :B2C+kֈIaҤTJ,'x[HřmU/zӑ#cit >e`+/M CM@zbȢ! (M**݀5$UH'dzڻIݻKPF m|۶8w.T=M.!.y˻`A_N?:Q%'иqn wŧM UT7/M{Wge[ Zd:8͙3J~\$~F஻Y\'>7`}ML۶-3Jމ#úuI W'4YL@;HOw>mР؊+ӣ >pQ`K&Q DGGPf%u/'% @$Mzɖ'TҼ+l@nw fw;"`O?fk7]?e)ު 1rرMFR$?WxQs %D 5L0#n'P谷 `t1b&m[N Lx#->z,Cqkm +yKv {NDZԩSOي`?=3ʩ @SoFH>E"X @*=hswqJU^Ϙu벴~p[jj:+ ]c2(.)1,G }ʕssbk l/6Kܹ,+\Jm4<͛Kj~ΧɴoEV]ӧŋS nH˗oX-dkV? %l)o+>2nG| 4nku C i)mpB(/<2)TI.b.7ucHo?/ L z_[ػ.+ğ O~&i?iĈ|t_-[" x>6X#Egffx/$ ߲ޓ4655OF__~9A+We,'c[Ȏ6)|m~IղkCT1>ߌf}Ũ@֬9J;~m{3ij".Iz|[ֽeFڢD))B6Ij>z;TH:yfL>Ze$օ;kMYYSȤs,^ѢGZpz|\" XF^Hi.D*FݺUIӦ"_|3zٶ빋 P_*W.}Anɦ΅ M`ɒCſ? Z>>uHsm)87 ʫ;Oj  }pZ B `3EF1%4 `f9]YJmU|6 "?Hx_*ϵk7h}.7O?|;N/j&]7G,M˖˂k( 7rV 'QPLW/MҴ>T `|'8qQZ`3~qKX9+::ҭ<%d?WG/RÚzJ hASB!MT>tjѢ2ygU"̩S[ڭX4jk2@@/V"o?M{wl~;pք|~2ʶp UɭZ&ӧ}`7l+tz К\Ѣ=nL̐/q$g7}TO !l@ᄏ% ΝLG VSFХKbn0th]p' & xz^ +EetM ]wOvX>-Ldu~+  )S~xT#+; ׽@*()55xcU֛x/ V f"?SL!(IJ2o_~yeh(A %(jXƐ!u6u+Ba*04-[NinV ݑ>%I,tUڰDn7^0AupF b$~=^gfELj\M.c$ %JDO? et%1QcIqIkJ+V ek vyU1  kS~cٳ!O|'_ ʣY| r܊^j渮 2~(E1hϷI`$hlwOi̘V,# $ q&O}6_~y._~6m4|D Yi}TT>v%K|dUA$p%Ü 7!Ə&}GBK{N&MGtz=Q[=. cԭjٳ(U99^xh>%~1lYD.J2`Ԗ_MA   olvA@r:GIy|V'inҤ$*/[b@^%;YdKuޫq mUx- 6_GM?>*}h+JK|dueI$$ŁupX 8#OgPLn]֯R-vԹ<B֭P 6@&?9ß$O&5 6mTںח htcn5xb|r+%EuՔ5ߡ{eA2b*FԣGLZ'ٷw$'q ūn /QbFu;RaT!ЦͧnQGK噪^b6) @RGwSPm@ &7{x/u^/֯?ƢȤ86s?i5vUZ5 vUnB:͞;O(rNֆ.\%u+fרU4cF;^pu\]N7޸83J1grO"QٲhРX(J0=@uרQvVGC|k@v/ o[K@MWEsBsnwKPn1.JYHr B~5hذ0V0Le(Q_)s4bHPZd8OW߻yIbǕpÍrCr&W&c ;U -X˺- WBǎyE1kKKŊѮ86nIC|GǏ_N7/KRjj}vZVkaqObСTxL{ƦU .ÝU*1ʗ/N MիN-|Me/޺ BaAP-( իǨ&ybh@@ wG^DQq{p\RSxRd%|gr[f#.`DԡC] ޠA ھ}gZYe76$V\Ybw_9mI)1ʥm@@Y)S,JdoV(@G2I2v_ nw<ӖxnA0QnY'R^*Rx[U"͜y՜ے$83RȤ5kbo.$AlقI~F 'p( 0~Oh#gյv+Wwq6hAfG4n\3Vqz6ZF'PT_lǎyQ>i(6h[76 .}!Jͷ:{//r [kMP ~i䖮aʱcTz [B@ +r)_PzYA5(|_z_Tg{Tk*Tez/9@4%PT2b]裷s3+2A}>>4K[@>_.P=:ZkԈIuR98O]n%*U*ןP[ 1zut%JH+Wќ9ٳ& :"`큯u DɕV* /ԁ"#9uܓFԩ$m4C޽q&MLmڔUUƩSXy+@hh>z!@( ʠIӮ]1wi!ꫝˤMdOeZzf*T>} 7YG"ЈV!CrjlUC˜0A'˙쁹@:t,D;|ғ5k<͟TJ0#ީHr}`*^\hd+@2rD: C$Fh~@@/KOx GLL8 Ԅs*˴nqr?N{9T @ Ve {/,GCW1h@ lЈWL߆Od꫻^=Yԩ}8&c6wbFL?U}\˭[4 @>6Kţ6\?2(,,jU(#y(nwj޼v[%_VMYwbPզChϞ2Q(i `O￟R!C~GN]2oe~󔖦v/ #k} 2;ox,^\Qw4-@/TZX'JrXnnV7 `Ĉ6DYKH_mbdr~kgyvpyb3iŒ%bK5kN#bq"`aSʑ5qa |Ɉ x䓁dsUyou?v8hmBq|*7(?.K'?N=AE< .;ayyf]\l>;N)=Qx9W'ıShӦ'ĉaOs#$ N$,V3Wx4zر[,LYvQڗu)ԷocKJ|*kWG-tv˗L+hf>="QhSڰy%Ìo8W^&jr荃@o_Oc.7ܹ+x}I'1O3%07Ry/0 c{JŋG{_-!֬y/'´IXv$OL G˖ T;*U%ԯ_BҼ-&{v!?Phٲ =E:fq/rtW&GQ=гgu[#W $-D;:|"a_njiy`<)2\eԾ}b %TI$ʟ_oV 9/+dLh!l8wΛ4}oPQˑ]?8SQr9)@zGn6ώ;z0ϰ| <ij4m:[M8b(+a ;6J-~wVܠ)SPzXmۖ2Γ lR3 q)״tbW~8Y;we+Yt~gIqtK vzQ\y= e@E)\B"X?  `(;wc(y!,GB7~(X0W}i&~G}t'V?"ٞǎhSe9JmrBx?P'W_f'<_rɁ;w1w3&?aYht#w &m?%0`@-b![d?^-?l򠐏ۣGVPB0";Ql 7 X̙="2|x=Z g!ў=RBZVxKvWMֵkVZ :u g? 9-}w },cV|c'jk{U 70-GF4ّ4ti66=D֟ΝPꥩ@119Oz"dY TT"o4ؐn?c{pHRb;׽{ew@}wumI_tTE[GC(>>F:ʮDZcX娢$ @kPPN1sB?tF[Cg .}RRd4t.sqejhAAYrNt8 AjK/9Wq~=,ֺyj#5-ZEH E߈S,o(dOKi `TZ}Ӎr; P|4ՐOh߾iTtQNWԀWO4A"q)N[АX,EhSW 6BHcNc1pW/#YS>)+K-d:vSʖ"1 דڵdkfPNH =ymP:-o֬4?AE5>"-[6]wlY""Bќ4qb ~۝N#F|˨ض wiA 1(|-[`JYzp b>sosqH?  9g)1Qy}5 i̘ěJT{Jzy껽۞Uq#o z@@NisoiOѣћX4iR6o>qoЯ NPe)yA\$|ł 5yb%8=|eBW$Լ@jQ'HjgKŽKL9^ PF3{ITUj. ( q-|h%;]&\^JV4ҝT)/Q8(MNX!hMڵ$6aҺnKS˖UE%8g"tuOϠW_D [hs[ʼPk<4iRy5YH~.}P7#f-8];듓dD) 2@]W&AlϖR^_,oLL8(rz[B]ʕcTZbȖ9% 'YCPR4Zc}詧N;P]ko/+y#t1t$Cy*9ރ bu 1 ,du:aE>/~0ɹ>.کDIPHVW@XY!9x!5_K "e*@К@T#Ӛ9Ӓ7ӤIk\8I'KNONභڷ'y~\[tt"2C_Yzдik B@`2=v;8zL-Eei##gp|F=R8*C9(^~y9zU+QdzGDD_3"9j A;3@B"-dH>qgмea?GXy@n)O@=\;̜Ig d +g(6xr\k; /I:+%B3gs̈p6%qy&ڵrX{ |"pu8g ?ɭvMbyi8[nj $Z\&>>#0~|3Vh}ovY8({7"lܭ sINb:>A4 g3e& O> ;~ȭ"IԴ$f`s*֊A\! PW"*-NQJPh[上 &Ph~j֬Bb˰n&\u&H\?qTN1Ƿq@+Px iӽ|&ԢEUڼ~> fyZpG K/u$98ˇJY4_%XSV<@II~qҎYp~ʐZtD6 +VCgy.dPuxgC`0 ( U-u_GXփ:|XvB:J? a (\4   |e>n9rml2}{5+EVөAgv+V,D?x?Wq~4fL;zQQ M |̞OP*+⋿_ʳHY16l =t< M`9SlzIG ( @үNSyǣpJMuׄړ+C5;^ % Z۩SE5/G lE֐~{ip @@IcǿJ֤qY[k\+SW|W |Fj*Wd3:rDVDgD?FUQNyb2jwkެn#-.Q -:.\:@ΝSݺ'&_UO?o08yNHL3g%"fmKskJ|=E j+gԩ6[ :hSڰQAXE*OsNVH̓ ,WGH5{7Ocy=XtK`P!6K <{Nr,O|K))4r7;Yωyb{_).22tqϼmՅl,K5jZ?&R650LJ|@ @Gxh._cϬpɓl .=|(!! ;[&a3=bbfҳϮC.s:0&KZll٘=# T t}ӱcz=2g=ʳ"A Ӧbs):uu\=c(4417xKge>Mc"? [<,~J`֬+2篏50ϟ3܃ɒDy(k&EDfJSrtlmPjPTlFCT ?׮%Qϲ`2Adno!6%UggO׻رM_Z-MPl?>>%Wl+Zt:[Δ;F+X2 y f^PI ,^'˹uZ#ʕił A-11|<$XI jIrAY d! 8K;@;wNC24u F$ʀRi7W1G:uqJ'R׮_5=oB[~\|$,d*Ur:_% \j4yrk?xU&*E ܀KvwAcvs͚#7e?R>>jW?aEl8Dx饎,D#KON- 33r٭0:(ރ_uݷ[ :ӝ4~WMV%(d%#MP"A'ZBJ> HgeAjӄZc'mfQ1 "|Fc@XP??ąʪL1+>L2(xn1ʾ [omVl09gǖ  m˹$j=wxY q@`8uu1*fLE@IΞʯ4+fW䣇^Lr43UwtW URH  g'Ȉ< 99L\|EHzUŁMYϛ޲҅<p@DDy/!w{Fݙj` K9ϳ8bWD&IڔMM'O^_+hJ`Μ\BGe% ~3a  'F- &T޷4)f^wSxE He˾J SE 88n/Gb"E`Zy=k9igȑo 7*(s[q6\nGjz_&Jl_…_V@nٳuVA 6qO@4(U*^ni ~(ۅ@@)crb^ t\El̈́^IrqGbp$M(22El&%_0v,>D'J`C0ct# wpIq v21 DQ(ͮ8&"NzTraAw?y!._~fμͻn! @@R E B|&L(@1=8qwWL"d/ DT=x ݻ:.,B EC~2@̃@4vlSO2"xkX#2+[ihA 1'.^Y% ͞&MZ/F;uD`^t5XDI%V@HSIӡC}51c{fQZ, @@@(e#o1_W8G}N`ԠA#}<dz7>p8O_kLWN'0p`=>^N~@@@@}P5@X?Y;Ŧ5k =JEfmZKkd__xeѣ>9^E\ -(_ {˄EAdPc+LfO?Ɏшq%q?|f/Q&Ũi:uSZEc!*YR&?ty(~|ƨzRԼyic )AEkoEFJYLJOϤKE s'Z|uM䡞]K` 8~j~-Jס_lOP xaCVټ%`䳗IX՚x_a~_+@ۋ@B(Gmei J x0@ʅn /cPz<6`-^c٥-QuV&0#'݌-C@MI4ȤEV`8ʃ>ΜD3gfok <Qޟ.˯s'iΜ.IkWe' 2ee٢pi9?(](uP'аa,@~$XW5ߥKEZ{V_КZG}%.F^/)A 4kV-^%A(8[oÜ?U>9fR!OYG@@5 %L- xK*5I2 `^Po27 ZP>G}&;fO("8sQv6UCME[]=.)~oI 5cĠٻ %&e~÷ѣbmPNq!7TRLcWc%[U@$N|[b_E3I|܌H8ſR/Iw& ޘ@YkA~ 6mԷG,?wQ{;\5 (ғh9DQйsWyk '!>Z`\+gFꚦMKf78Ý8ڴl20+f}nmM,d8"11_?A0SkbTu&v8Aי\MU, O)K2dE8SJ/3ĉʁ7 `F<И,۪ ibFF&(<#6#0/(۷h6o>M))2'IChԨm/Nxfe{gPNEf[u:v1ϛ ` ދMdE~ z02q]|@?I TQ̞ev`j.:KYuӼ# `cҟ~CRRIhQT\AR#uqJTM/PflK{"EW[EWVN8Y0ýяۏ{)##ýLHM?HZZx%w^!nl|^= {Y'"|ѢPb$0-(L۵hzu\Mt.[+ 8x+(~bq*1p7'T $SV;*Pp@Ps-3CcXi&㏇Y>KEq@|HI(CR]tMkr1Aj5͟hÆ t `|̑ )=]OwHbo/C2%˅Qa t"B9j184,&/?;E A`."= 7`z˄:~z2"@jjg܄ z/h;y~\Ξ%fXnOL@#^!sp)LJ_oe2h_.#,XX%]bгg]3n\3v5/%@-i˖ZQuKiB4k֏3we矏_I7+B+VR:ނ9 @`~Eغ }NT^矫t5A `v۷c&Ծ7\swx.O}}gFb}-t\x7n_ڇsy{BaWQs+pr}'ُQj1Kc(4g"ҥ|V;*#HHHsATe,{ج!gQod1(%Ŷ?88t!3@D?t~b4#_8rXj[j |^iӲ7o^@:$@Ãm*4T2PٲokBVuS^p:#:K`̘\NbԕҜfs6 `V2=Z7kB[GkB^A?UTHjP:_t5{U~]}5J-=%(e@0jHNNU A[1cU60]!/8۱VҩZSsAMFWܹ×R?p BE> @~T*0G.UH{x IٳW=&i8s&NMWZ2=DKzN7Ri߾-A ȁ2 -?vpժɾ[-/^BÆ8&BwjO FjQ#̻j.M3ow27G(SJxIQ9vyڸ*--7z8]TREn z#zBOwpkT:% t8[)g-JȤ ;ƍ^Kkw_LOj">yy /cNJ*1N񉯏Ui(n!-@9 ,Z{7j͛E ))ꙴ꤉^NÇCdSys7֬M  `4B+Wtu^Ne"rfDc Xqg6Ȯ>OC~NWq5ҁ ,Z䨓{rmUZNN@8D%G!>l^]9>w3i̘ܝzVr @t غD':w~^~㊒J 0PŰp:/qgXM۴)HD\#0}z[n߭I֨¤XZU>4~|3UE GPW v㵰(TE +T+tZ"%0"%"($$4#J95*Rakמp)R`>409)SZq SB.\:1cpޱc5 t7O&>@{Y!Ж7jKV(|>*Q `| 9s#iO#/nT"[!F\]{VWÜD~:@P ѷq!CcE >NԷo w2&mR(h q?,2TPh@bbkGՎ @8*NII |7Ԭ |#Y#}nE@ǎ%J fM$K3""B۳gСj%hذ^ `^fuo^~QQJ=v[ƅ 8^zEu@dx!r~EFmfVE@47GWRSh5Ҕ33*)m^G7%>szn]{睭sEALy#DZ~dp( `zuMֆыgn{r?{Ҟ?JJkmr2pa"+)fTgPw "*W.H9(\nHGH]/߾h*U D̚g6뭷v(I7c"֍ y;7ߑ GIXyz5'Y {řX1^\A5  cj]-9(tA{/%( RltJLtt"zyY)|0C+KNrE#*͛+zq@N,J-Z|8/f@UG׫ZoH&+7$t 2>62eP6U= 6-EjѰFcTuuQ\]d?& St=\E{@0Az-93{y%#Ie26vEW9VPh~"#[-AM[ʕ1V?18[/9Y x30]hݺ,nvAbtgDs+4n4gN@3W$QBS9 0?L.Q굨a44-.qxڻw*NKrPVӯb f%c/*~ em|J6/ u+Z}5%7<[ʾz>y9!2|c?#0iJj|e~ .h^t 'E l,0:Rȥı&sihV*&ۅ2yBzn4rd}N`ܸUk_QB; "c: ڞ>̜ۥp: wƢwGָVT _D 4ɓ[S>յT|;HXi?o sѨQYqq㒴yT*Sa6b B*qTf=Zl Ub^'3nס^ !}{O@ q,Է;O>RV9t#X1V,cQZLYJQ}5yg˖F&z#Т,^M$s*Vl]pxQ zȦÇ(R 3Yi(Jqc}MUNajZt?Cر"5n\wN_5d 9ݻ }C!)+@NU r8qڷ+&P˖yYI 5l]úBhI@,dC[=߶z} | M`Ѣ,8]2{ɖGd/jWALBN=ؼoGSr3w$E@ ڸou'3jD @X*G:! ?uv p,jC: 2es|Y.R2htjk*=$X@>v*[(˟1w:.ֹۛ+W"]qܓٶ3z<*VJKö utM1q7zˀm+O>E ^ǃ=ύ>mo͚%paQ"(AhgZdR")X2y_fTT +V Bt% @WcA"`fnjXL|V:QQk1|^Ƣ*q:@RRR"oB))J*ցy /:gx9چ.4@ri⋿@6GEmrV@@r#p3Zb|V%L x? LŨ]hE (SJ ܀K1o٥Kp֭[BCepMs@@r'P\AvWo*_lBjP - 9G]jz2@4=Է[ '96'U|=o/[ڄ`ApD`|K s`JLL5vT5@ H}a>144f|Z`qD%н{en&{^g-[kJ61uR\O. {Յ4h\ڸY&ЫWu.\ewAC |M8-&GB[7QB 㭗K˷Ԋ TQ,hå&C `4l{0V bB ?!пMӵ?NhSF/49)t… b@@:tr"_*ׁ 3;s[ؿ714]~v8E 9[۪…=t-d]Uj>,i˖Ja]1pP g&NleEIg" ҏ?o1! X,lMhɒ9lڪ( HKg>(Ǐܥ^Ho$$ D@EWS:"M)JҥJPJ$tHO !].vw77;3vgg~.{|g"))g9Ŀ;,8AMbˬ2p$h0kY5Ӭ;dpLˆ,$S`„k3snoA*XO6[$Ά3v>#ps}[ ۡߔ|1ѳ瞣L)~/~ٯe3?#>#] ~AEG~)3;H#:TYYM_\8հ@ (YCtN`t.bN ߑ/Ѣd=l:E/gl@~N-[Z rx][OE3ʔ_wݬI~~o}kDzr J. ]Xk^CnPS2F8y\̜h~㽷ʈhQQʥoۭܰj+=I D \w\s5RtD c7qׯ:!^! AlT9=oA&N ;;G'W (@IDAT .]?@ TUWܢ3f1ްEzl@@KԚ2K/Q 5 RP [}[ (,}@ˆ Ʈ n ֢ 8蠁&"%dd^'IR'j6PQnmO(@\|N.e'`np;T-Gj7'#D@?x>{Icc9Ws1(n2|xUJ77oU%%9Qt 5 ZKgmzN9sKAAkC `%)yP@WLxyU颋f4{.p!mjZmVw1<;#+>Gg 2%pFSՂTVt] Z6"6 \~rtM4΅#Y{䨣0Yo-S!7Ź6r9LI89l+~DL– uM ܹZ֬wRϒ" ē# H=~/W\9\'0.3>Ƹli+pjr)o/__N `-){Dk25aGjealgE-\BK&^Jkk/ @3N J;q:?%;;}K̛\Y|y8k 42|%z+@M;'v}A@}w9I;/{}K-V{2ӛop%B/ z֚z/P&O_߯&sũtj(674bʭ6&5ɔ6wR@"poYLx<'&ySȤW_hwe?@ ˲rzo}~w#/@#pOӧ7ї_,. .kp%qϖٳ2%Mn1KJ ćГի4p9½4\QfA lRc:w9~d']6xzYٷ֗(̜yTWc;o-@ -@x̗^!lR貲Eӛ$KlxݯLlkhtr*7kwٸq\p&pvͫ|ٯ,^xa-ohʧnl%}3lgS2$S]-~OLO!KK;NS~4 z

U/@z둎@ʗ{yCzĜc^#UҮ0On>򳟽DZI!|wZxtS&7m,$.yҵ9n/OJWHNN̙Gٽ{ɣ'Mak$ؾ}{SNťrٺu<z=˲ek'ڞ0l})s8-(K6nDs{kjƠpW+Vlsmo{1oQ&o0 @$Q!`:יmu02* ll;ѿΒqy睵ŞO<i#G $J@|pr>ZEeesY4b@qǍ6&p}li&(wor/@K#F";w2UJw@d̤V,YsԴ }~O_VfL\+#n5et@e¢W4Pߓ jhQ)rxW{_&7mk ĉhKY`du %fJ~ɎNNM'[X @ @<#pɣɰaz%GOl -/gm'ek.sg] |nZAc#w9߶B裻96~{sꜙ.cTVF<M)@m0ziln[|L`-la%Q? deG-_<0pKtxYp@Fd;=zʟtt$k9Re[kqn4Gfn;ٳ! +Co6ewņt3N5q>fAUǧcTBvqDBx NsO0r?yr?yؖwee^};: 3 WOT?ɯo|ٗ2dMi'?yݼ+Vl=EJdJE`ڴ^RWwys2A;iOHeܸ3}oû[oakvÆ" zom4}e~@ q_~SNի:o%;4Gי?\i 8sڂxX`ٲdnzKSxSZrD#P\{H)\z̽odʔ;f߻E/>}Oζ(֓9؇dݺ&MKK3^'VpNt7h'U-<`t{ٴ!`Ќp@K)-N)-A9#3Z&G {[rESl޼M˯W_]!TWW7?s 8AZ2FO5u%2)Et(0zJ嬳!g񤔖Joi*}q󟟟#kr^u_ FG3!mɒэqfU4xi%N.1[,;r=zVl :]"C6X.@rRDmc!SVJ'A3L$QZ9tʉIO]wf2[';s啯go> b]R-Xc=m `*I"О-3$3 0]mҎωww^Y*4k$%%p}$ [֘؂2mZobS{\.PUU/skjOة۳̚*gycOϕ_=ؗ__AaIIef'7ȷ_G7|ڽ{df&#tL9R, '\]L0 1#}vYc;);wPUap'>bʧ;R{ j랟:wR|Uxd Hv /'!(&Lj x89,ctnPnY`TR]^}k|3eA7 Gyѷ_yߝ=m ` +"бЎ#('ϗHٳٕ@.9IG, qФ@s:|$bE:WR;P>+58uĉ:h\+uQRD5рGS+O*N43?:+BYk쐙30Ҭ3cQ2$Zpzb[e ՚ !opGAV3ra ˱Q-{!,e˶J߾יBiN'w(GY"@FA v!Cʘ0v6 a @ 9yy2gW#yԟ6UE1ݥ8Q%N `.䢓ٜ׽]I׎y_O:ܷ3Nh`pr814MR]]瘖Y~We 1 b?vA'TVɩ>.l{PO7s MQ"?AOM]'ЫWia_Owg3Hڸq44$?t`m G0 M>@1A3߻RX+7LJʕ'MN91#DMŎX/@G4vaúKG8@P>zXr0OR%8)n]e2ߛo3;>*,̑q= ,F%/7rYrϿ-*?_Z,u(_`WwoncGʧ-k^h%CO'8 thS_NL|UXhG>$^`ժrOIv%r5feȿ}g,Oٗ<)*@U6"9w׳ϛ7(͈J/88#4W_Es}do{~>GQ$`J@<<ts̪L-k۝ 7 $Zύ1->(,Jn\%:DoJYf)}y/ Uq.1E}Ni{Sӟ3m)Ռ$'GggAd4~\vٳ&f3k<&s˄ibڸV) K"5|,1X|Їؼ<Q\$s}F3?(G$N?E)nxK.P71,fe]Y, $ Ad@kűtb[KrMcq HnnsE=.0|xW))^9'H}}} Q@  ?_JϞךC&]ޯZlAb|fψ6 #vÇ+PTxggo65 74U"p[M6Dw*ɫSRQQ C"euud.WL֑)%KD)hQR}c7=_8,Zt,^C9RZ}yf FQM];I ʩ6cRC`P8=9j )G5.,(6E@nnϲV㯍׎@u+G=AyL;[Ir$.<"7'>У/=h0 Oe &?: C%#5b{\I51OIIO+_}u}X`_<)0mZoS/,j`0KnңGQYd$W"@ ;/v8h xa{DV!sߖc? S!~0&2_O!iGJ@oYdUɑNNz)Z^mYnNA',{…ߓcHYYu|8Fm}QO?VG?cm3cm%q: Ҩ([n{[ʰtO?NQ1O>@ۯj1n\7y8\?lZj˗o/)heF8 q tGӲ @yu"ddd믟 `@cc@*+kK&V&wۏ{@-qĞ-7:8}ܹC0tO,>F*+u / ֗^^F d@s\h."<7 G J9`o!~o[m/[yI4ٳF @+htv8 &uQsQT*{X,tV/W/u#pr2DDł L+C\('D TW7&>SrLm53#֥n вZnd t(.:\[KPtV X&P]a:4`0(6U@Z¥J`}{Gv0(+gJ;v[S#Ƨ вRRwsZ{fkPI%QA*^/bGuJqqvG;yb7(,d{s۠܄wD/0o`wFp@+Ϟ ;nGsl];sm%)!@x;x7Lk /?fLp9ᄑN. eC$ $7gdIv6VAJtS0靫uq2=([j 8(G4ā! j^o`8W9˶JtS --U52 tVओF$~.yy|?v@M rjF.zZ? &3Z()ab="/q 8ۋr0sx2nWnksF[pF;P 0u (ӧ{fD \r [oʠAe2ujDP9\h19(OaJU@'xI/(s'tB`Μ2|xI_D9"PU^n벲<?H89"ReԨ ޽%[@ .qnB91qՙ@_UUujs UQYP&: @u  a !Ct$7o~3'j/X^nsp/W0)u#v2E@qqN >|!3 \sI-WqVxX`.Fxy y KTD VX9}$++c:$_#ɀe nV, @t iu_]Pb.h$}\wOQRU**0^ϐQ=K5@ [$:8 ߉ԛc̪s޳kǴ"ھ4e2۞3 DS$p!PFg?e Vg"~kG_JnntRl;"}TH& [*yys=F{^Zeذ ҙu* ɹњw2yrOk#I`>r=t;7JQQ<&Kj ;LG:R\' NOm͜'/4P[[W)o<w9 4h]4 vif?~) g@\wW`:Uoz•ffwWX@6oN-ws,'7ơ7Dݷwӧ}Z]]/^o],˖m1 $㻺b[a}40\[ڒa; 2䘬DU6 s 1c*! 7.31OJgqǍLpd^+_L; f4"߿DNyESM5x@22LԸXVJDV8hw\ Z '2[7g;W`͚q+99yj|{ ŘFlOK'&I/dRM)yLlS,a$>5 92 3~;]( TṕMPd~~Ն.9zg_ Ssr 2svgDhM$K@!ҳg~Vz!*O&>:(TOW.\njȇ9Qtuk:yyD9[hsg|3L+ "6 lX%Q9XY_\WdĈr3_)>rd ukTŌ;/+WnmvKaa '!u3܉K'XL|TN՚v"7#Yy?pbaWټIfg:zIOO#uc+dذH~Vk^k?g^$:9B$͘\=wV['?:o>E'ZjjN[\0M4ѫW eSi PZ,:÷t[ MtH b'_ |pҮ]B}ᄑ%;ӄfFg,\fOA"S;:s^|8)2kV_gR-W:>1xviW;nD< p e$W=Q@h޽̐sZTU՛s}޴@ (`0م: !}ӧ6W{ۗ)#6ȤKs{O󒊊|ryS͕y6Iz thyo2iVVVFhfqu/EB@#RRR̓GJe x[ԩy2 /V H=9#B^9+tN@ \y^Z\ >v1r[D\XtPyBo (QH)!"C2[@gvuÒϛeA@p"6 s.i"U~T@S ^#d;"3q' dx=   3"A9Mxҿy4Ķvb{$;vcڵ75-:@@(0jTys׬er}{3,};F_Au@@-ѣu@31V3ϜVi lZ^, :,   @Ə"4H׮'$@,E}Y`66l./fG@FG).ΎfW ȑ]-L `," \ %  !0aBO;m#Ftڹ2gN6a8E(5駗Νn׿ZT@| 0n\RYS?O~CfO%DF 33͚J0(/ KFIQQtg̺[ ʽm޳Y[{Nk\I(ȉ'j@@ r5hWO1W̪"ytOw1&ݻɃ'7pv;gp G u,DqsY_! 8 lVy%a. ͐Ls?Kz(Izܹeƌ>3Gx!7 *5/f2vҢ~j(ɱo$V,  @D@G\r yUtYrTWׇ)T3??34i`.J]$ʰa]$=G$>2̔I'})** EnjsΙ,ӧ v/G@ 1 |=U^#Jq<6U;OC{ʘ1drM8"   B 9- $W` y-_\\ 6V.x涧$@@hU }Fa#2G2\Ork=   %j}{uHP蘃G&  y$_+n)Sz&AX~MLb@@ `*i" ]B_D84 ]6s(  .aMBpMI=D((}ʜ9@1@@@JVj (hi.7ڝ@@\&@e Fq!0zt ^n*QҫWy8SK@@|(@N!peMAl,05kɭfac~$   L}F Fɀ]v&n2arC) !  ^ʏ#堃FSMkg =}Kf[ HV_^=n  #=@fT'~B;ZƎFO@@R (<匌4$A@@p %C`w3ƼUM  Xb駛QajL@3 Lcn fm\R*@@jjSޑL~,Xw A p~QBd^RTcw/4=C@puG~e"[[XJfhIk4`++eÆ*+$-@@ \sBS\H?ВR,;ةKX(3[塇>6IRC@pL^rG,'WKX&0n\w '*?w%9@@ |2n\79a2dHӊGy$@ Ad@gFjЉS:/^2n  r⋧{b(&nH@Ŗg4ڵ;-O@@@y&V 3%'+ii֍(hlD@@GpD3Pӧ(+33[  n ֣ѣ:Ռ*`*KQI @@ phP,Z-qn ʌ}<@@@mbVr}I@@?6uu@EEeu(-͓ٳ[ !  T5kv/SrqoBk (7BRx5 C*  Xr<2š…ߕt^'_S @ric[τ  \`׮:y٥roo,6)>Yw]KM'P^n-{ȴiH@@իPfcjA9SbA@@/ rR7O \xᾦ~q4@   ơh'pCdf΍իH**ˊ@@@<ЈT^:TS3f   v*pɣO.GLO`A@@ z S?O ddWh\Æi@@.@-Lrs%%%8ȸq|mD@@4~0Q~_OеVf }   et/W!@Xg?. Ʒd;"@@44DO٧\^}uUc2~NȂ  ~ V8∡k{Νh:6dgў!  &pSkQVlr˷u?ds$y@@H%M>@ G˔4]{dfm႒SD@@H 9>HII#`j G  x[n_j@L)f|xeKxgg@@p %C )]LKJd   p }   c8)(    ϖ@@@p4A@@@>ْ2       '@>[RF@@@1@@@gK   8Fc    `lI@@@pLSP@@@ `-)#   i    }%e@@@#@1MAA@@@O}   c8)(    ϖ@@@p4A@@@>ْ2       '@>[RF@@@1@@@gK   8Fc    `lI@@@pLSP@@@ `-)#   i    }%e@@@#@1MAA@@@O}   c8)(    ϖ@@@p4A@@@>ْ2       '@>[RF@@@1@@@gK   8Fc @<|A{x@@|%RYo~/y䫯vImm,X0S2  X)@JMB46䬳;xWgTO&  n ֖T`9͕ I,Yŧ"T@@ :'BO"S -Վ{_@@Z 0 [@W\{v̚Ҭ55@@ 0@+x͔N7kkj Z   К#ZSa8N+_`єa _y kFM'd]iSlR-vK}]il J 4N4I[7] wB1kh_RypW7GSKLeC|$E +RVa:PpOJȴmy啲|6˝uǎ3&2b2\W(mRR-eeҫW[$#Gv/F}[omc[iՒ.EE2dHY|q ( 'hWР'kD^ziq@+VlG4ԙԫȫWo51k9&U/x6' :SF ݦ"ޙt[;V?yV͛?tN}Y[%c6~c @@v֎zY7G鈁 @]>xuP3b@'my_Ȼﮓk+e۶ 8 @߾2iR: 54T] #'lS~O[tοS]W{^UU' ?"30K)4kQÆu e̾4Ջ !G_ˊ?p?v@ЫO<,\j]#WC`ր S;l ԕa}4~$~Yn1p'CT סbAA(fg)f轚7_C o1Ap_;j;Ys20/Ҏ{QQ \Bh.\#￿AE`pI#;?5HT*= C#08eJ^^Y5sR_ ]ӱ?9GICC۴tk_ : h_fdgJ.9f3 V1th] cXHYw믯N~Al(F$`+I"'ЉdIWOYu+{ O<<2yufzvb($ݧάb:3MGp̟?4) m?/\~{}ij#%"]G:骶I0#~Țֲ#QjO:&A;:<Աvuڵz=;ߥ1;BH/uuZK8u'])OӟzLGDmmHZ]Ӥgn)2A]Kd̘ ]2Cϴ#h:!a3ɚ㟅&}dƌ>+f'.キތxRbDYH1h6י 9mD> kM[L!-C֬N~7b޿`SSqٵ%re,M?lhvЌh5k6+s5`2`@ Tka}{Bs04= @[ 9 Hџh F%ddnСz_FYLQ5v06KyLsU"P*z51;{byX~L_ﻏu(wf qN^0I#.I8 |z3yir・JȀٳ˼yo9mx ع3V!(:;4Sc+E@ )z7V˿eӯ:tByx6qMo˟\+aA ͛ f~.=qh3@x2|-@OpN`*6lSO}} U@'[x}hb8ڿtsbӐD<̣ ,øurf+呉2).2kvht},5t~C#́I_4x䓟O/ U֣Tٴi۾ɬK? ͱ'62v [@:KqlKʶ<9Uc;@Q>` ~|33$}yS/O$wgy4(P<άZV){ Bϡ * 4 ;[r=7քFOZٌ+Wk?Zŵ'HkO>¬_>CəgN00vD H4 @w2 M\sB91f2F~">~}\^_jGhmtX>lOwWtlٲClj'T3н,yˁhNoJM`unyܪNHu"׬,$R :'V;wr i,!y!TNmʅ{b rXyNq\{E6<"~P_VSfSHXQٸqY7yODgK?P,w'޺H~K[YWd0Y W@fI7 _9<4>C_WTIi!Xop@ :GVOc]CO #\͛e ڴS4 f!p~3hRjkMskRY$h^gAA<0l53" <-L/YV,ٳ@NZ-vb( @ ^ܩdl2';wN hn"rM `D0G-_U}vKLj46m.",6TDԈ̫iFK~X24*3L%7q'Κ@ zRv5WjѮ[1Gs?YJdV'bAg AٽZmm&׵y ֮zs"T@kgNiJ |k̺^^|q̞O w3Oz~W+˻}Do6To6#) F]ֺ;ٺuYseo_dd%9&;w֚No!4_iP Νc>,/Q,hl TWIC^%3ӻ73҇M]^UWz:?GCC0c޲K p}mkl ihh ct=N 4FkzU/r"ωrۧ2o߲JJrL&5j[kd~i2Y~ӆͣ8sCSOc'Z6G^ʢ4|TtuղLq\z~Zqy?#WYPьh֯H;hHTo5滫P ࠃ'&ඦm h@Nc#zv>1M1b^X.hi~6ywh׫:t^몁)ls5m`:[κ~΂ [{oye2xp_"}'XX-y6g pvP:<-LC]wjti S =U'yۼ*ȸ^+￿jS6le&2C5ؤCwLN{zՇλA`A +Ui;̺̼ϔУssӥK\sqf9L %,a$W`޼2iR9QyLR:/ɥ,?BvɮX"b6y/eɒ-c̪*'qs+@kHXH9[t.J6a2Ozo^!TNmʅ3~#uodn x>rJ3iٔ>Z)}n3Għ3K7o6ī2YU~3f׼Vւ@'OttS}CO;wYn&,v_(W' hg^ңGt^iGB#?>3ӎ{䝣mS~SiJLևFkjRY|WudfXAH@gرCWuA%2p`{bQ5H6..qԶJԱeVLBQTmڎ,. Dd b ˅$f{8!!{{y~̓s9}7>3y5]b2X/Nm @?{wU::&75 fyOG=>y|3"4M !gL%yfٯ'^عsG|[.nqB]+ q;Nczv{σz6Æ.#ݙn[H]]9edF{^Uoj0 @_|bE?@fSxW@|?[r;||^hF;wR՛][]u P+Ϩ&>Gs5N'Ijy|VKDҠ{{.~m} n5@l /'|w jP{~?H?ә"Bsߋ\@|Ԗ=3~ҬYQ5uFqy B ?]ט}J߷Tov{pg~"0`zG[ @\O>,8"tҔ<|6Gx5b /lIt^l7oO۶պwvخWpǸ[ts5_ fs;xCEz+Ӗ-۫ܳ*7};Lw_U4.P?. w~tɇ畔4~H{h 6 d6cm'קZ~zCZ|c5Y\LcLyW׶>|<<ƖQ^y[s1>Çnc{LIcGT%f?~d.ӄ #ĉ5c{{O8/sǎX\\O|<}{u~FC mKW\mo{MzkZ]@#F_|C%n1Cƍݩ+-Ս[tڽTϾw#cǨO31`k!UHĒ|SJ'2}ӧK~W@G 7\rޢ#/Y2}cwn{2DoW |@|wuOo}kӫh&E-$"׈|?}:5yyIMiZMW+~gz=WAw.os5Cs _ =&7c hN=uZ?i{׷q |K+/_=W?}^N'Xob>V<ů~ کj3| 1DGV"~W楅ɆfU%Vw(qqK gm @v.>sw-x]@#܏Ng:r^1_dk?;TStRqٲ+\7Z1VsXpf 8YՊMQT| V'kyָO>z|ZhV:Yim#]oiiZtM^ܮ ɿ  @I 'Lnکh8Znڴ5ǫry&/LÆ[.'r P;ȳޯf*y$N'3s/3fa?6cL'ݶ,ϧZW =?zjH8psJO4d$J~7njOȱ h/ Yde?='Rww|pο @;OַmִvmW.=()w  W:VW;6ėI"SΈs/iƍ]im饗* 鉮CF&gئ\bgm՟?")0$ >\Ç;vx $|L@%zxػ~&Ь>~Gޚ|$cviwH ԮRe'_=xr@k~}/7Q;^.qx6;6h}ꉀݙoߚδiSڑ럷1,7< #)Lͫk@ H@z[Ň^> +yz%P?I__X_h~/Kq,(]yXݟGg{Z~k.$v$zR7?>|7IOs!樑ZY`ƌ1u8t_LIA4@|?{֯7]U4LybĸutM#P H/BtM8}E-%S$0ԚD 0 %O{Tޔ@o$z+"5jh>!/_(k>+- @@|ޔ˖!};NaA`0-8bl;9ut]+ 7ܓ KUF @?z/:3uGe:)ճS@?5Xx7CL7@җ5e$*4qT @`b5pK.44eaV#oLQg}ʵ~G1&EYpF _x xc$bx@$bb @bB\/_N=uj<6`:ߚ>:& .KG,~tuKҪUOc1 @@)z\6 ye3һuZ:)i„F$Z;~}a =!r˲O/Ivy  @M,jt챳 d]t\:iaM\oU#; ygϞLL^:'s__@nst?~m q @/^|j?>=qc&'eH^>a9J?~nE疦_`x@P @Iw.qLz;.Ntᅧ+8Zo̱qh1~D8pC;+rᅳӲeӗPԧG@ 0iz% @\~#w?U:qb|W Ш@r@8qF5W/Hw޹<7-YP>v})A}v @@Kԯ<߼/L>=͘16 bhKP%^UӇ+8|4gĪoK >' |(]z3o @ Ԗ:tJ?:+=<9O t> ~a)g̨#;q;ͱL @`oqㆥwt3[O@,ONW]uv5i`?jgNjcl @{ D Pu~ЯF @@f(4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B$ f @ @@Ye[k  @ @P B @ P@YZ @(T@k6 @% PV @ (4M @e Ho%@ @B6paC f @ @@|.9(l5jh:\Eu#@ @&x imMVN&RBGQ @ @!Y>Z֦[m$4E @h@/5 @@{kh`i @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT@Q9 @ @h`* @hT 'v6 @ @IÇi$ @ ИȑRȑC^ @ @@KSGDeU @hL`ڴѩc&6 @ @8cΜ -QY$@ @8 㬳f5 @ @8YcseDU @tyGvo'@ @Z@`驚0VE @ @7oz ۚ5/l'{s %@ @Z`KZ##Ƥԩӂ4uU @ثJĝ ]qF @@WڋvbW 8ę sqsYIDAT @4ghёj+|[on; @ @@+ lJ; ٳ'w{Q~=^ @ @UgQ:{TZ`G#Fuޞ )? @ @M+3 ԑzzO ݘ&n  @ @@s 9|gZC>ӦNwI Ƀ @ @@ lHvE:u>+@쳏J7|\ @hNu9~s_}M>˫ v} @ @wy!]/;@}E}*xF @ @יn /|+*P ^qcW?{]D"F @ @g~iÆqFPOpOw|gsD @8tVWq~'Q{c6lNz}Z\F @ @@ lGۘ&M:6=GJG֦7+ᇗbQ ? @hX`kss./N:-t S>Z_AY 28m0( @ Pؙob&l%&1+_oؽ*K>~iɒU^H>ۙV~)  @ @!0x4}ܥl;wRZpF:YӦ;EFp IENDB`ic11PNG  IHDR szzsRGB pHYs%%IR$TIDATX WkLTWvYXaj1(D߯6c$15$FjbF`Q^^.zWӴ{y͜o9WS]\~m:Ł`ܾ Md gss||thB8tvH#tµ  jk;ielO!u,4\2Y6)*P#NWw~~>P' l6OYn>#wt w}}q\9Âhi1oKKpcwoF;C,jp/DLLL1իNNCTQ0cF8H!pʕjƊ3A#,v9:;{  9-S(`;dҥ P; X؄ b/iq/hc?r ]]V ~o  "k"k(44d={&&o!7&Y &CCvZܽuz}e:䭭$ʌFXO&Co3Ccc73ghTu{q~Xȑ%زe6΍V[[KUT""b| 8,hv .^;p./oAZZ 6oM`©Sx,jd>X.^/{fGbb$VIZwo2"˫p됓sΟ/2vb7e 緫: k~\[,;`„q8y2=atƍ:N9uJ,]OH$Wpx:vN`pdfƺq1z?Q;VKh^ճFϕ/:PЦ$@Z;M0]I}ؽ{YI?us:5X\> >p3<˫<̘dBaBi҈dPEXc[ada0~T kOċCR^VҸ7&`( E7;x(YYn#EzۙǪUqBŒ9,)N1kYQΦ\aM漼Lm!!ؾkcj'# peHJq .vRSMyY84I\NJ(^&~iY[b}"tss PYɓ$LgҪkalSpgŵk㉦ yVbuqs;+ apb _iQE=جUUm^zvĖj}( Fv}P'eg8n.anzna%5n`u_5cwTQ2$IENDB`ic12oPNG  IHDR@@iqsRGB pHYs%%IR$ IDATx[{t3ySHDxDHB/[VU^miZJ?,GҵJJPA%W!Ad2sofyd̤w53ws9{>Fĉ?Pyt`"//=͙ӋƎM ^o$tⱱ:D'(0,KHAM=ghO &-C,M5ɫtv UzH-I"?|I&ʺ ݡaSZP*--]Tv>&3& 'අ 3hݺc̴0utLmxRv[sU!̙;h v6RÆPA-ZSͤ@aL)yH_~yˈ8HCxW.D,I" 7XǛs<̣ ȑqE'NBUTi޼ i,!~McG.K' j-Dy3|]~ZG`µktX>Cnh잼 ` LMәrs?B!N&Y0 \HH Z^~9}4]0Z6} ;;ŋI+Ըq-Z)~> ,(>ӱ{SBB$EGЫv+: UFj$RSe?p˗d1>ΟCTİEM]FRfXKq:uw*_*ף@ݺ5͛!._.;rJvEw$IIQOy !ccesӦMUM^pSlDP3{rڰ$]2^~F6zԴi]ڶWEf͆24zl+["22|++SO=D;wш߰VPJJ&WU1oDKIPIMƏ*PzM n NCzn6p?*(A='xI6:ߒzhN2`"X]~ざؕlO,>͛S۶ 8VETb֮=2"X`P9Ch̘x&H}4g!P@o1P$ܰ5@:t"e0v" 99Y|+h`dGt۵ 61mwa,QHFaa1l[2plM:uEoՉl屖r5#$fmiG%~ e ٳ{pCo3SfdPW|,Qތdұwɖ ℩$24^c[vY3%C* #ڀ)P|Xn(=| |B*,8TvV7N_H0vT%!1( OT8u?$Kbw:jnCe&3s$+Fs$XxА>Eȭ]_ Kd{ڴt%[%- v_x*8zLd~X-n48O#"jWJv` kr@NJHBNK$R@#Ybv"k˗K8P~;P$qq7^^^8aN|/G>ryW_ aO IثѣE[rIn1AҶs*^zԠ@I(Z c@m k'A3ϴcPU 3w'Ҫ:[ @x,*6I{GDIZ0P  p b&OJ?en1ȇr\ŀi'VU:!E:!G2SQQ!@3w 8w2zϔoq@2 I. nVSpv%qR Z\C'M=}qln3pXұ#FrI:QaiT)xAkP%1wOӠAtwzKvLf%~p$bjš/T`ڵYT(A.5HTr:ڽ;cA^- FP `RJ׵f__{_^<uv!Ew0Yhm jKvK3?> iӶAZqBHst8EMHh6'soz!Ln%N\JnSYY?);GE,3E~^h u6K ӊ%[j3pBB!7gH%V0dʧ/٢ %BwDݻUHmH/W>\zˮSmp!3 *b+V(A58G>Ac@\llF|U멶 h: 1? g8o=dcU:`u6vmr(J  H~`x#ھ=W3ԩWd>.q0Q(Bk{@ehV cuU*]f BߑSr/Nt7o>t;9.Ysң5$3C7LP #`Lˑ+I-9hSiW[8r g,7>׌1hwFǖ-vAvdkyyB(&pFHs8R['@:M)vae`!MgU~bk+--/Eɶ՘Z%2YNN=Ơ:6K?L\M*z-ǍH8ȓ79;㨾f\E۱t&.;E٩2@6rOM 4\ eY9׽#lWeגB@!N긂@P |  ;u C@!ajP0,0ԩ+‡AAPS: >B@!N긂@P |  ;u C@!ajP0,0ԩ+‡AAPS: >B@!N긂@P | vatԩysȑ3p|V:tN>|u ZvܡCMqe Evv)u B@!'һ_f(l۷~DȥKgV99AdxckQLF((e"R= 4S=8'{\8XLY#znU2=v,6Jk9 HZLoXUTZNt^HA!@Ð+!2Xe*RSM;C|YZ,l SL&U}w C!-XClt H+3.^Kvta!Aj EGff:$| _)QFU͚UF>YuFd w>OӱAVe K2dl/b9ACNSk-rւI/%MV ~l'āPzM)w> %#N[xoz5E~NZ5`ns[#/eSȶ)ïH3+}~%*U*%a)ZMg'0@뼄s7EJ [KWqyyQ1ijHu5-t+v ~LE`XS"%꛴l9 i 7mȭݵ n6QT:J1r&47W矿 ZtŜ9Qaz)]JW^9QVEy-gG_$O%KH޹sR?ņ9Uk1cHB#_] ت':9_6Dj ՋK.Us^6o'^|qxbɒ+>{NV 6X% N+, |kc]|$:v) \gpSQK_6ln'3A)W9rV^#ۋo"ekG)n}nWa@8nC'ڋmrΨ4dpxEq å^VTZ xN)@))rMzu( #M@rL}Q8F@zb>.v3Pe^YBnvJ]s[o!~iSagW\zGROoyB*x]e'.ȃr[GVlmP9l,2{LR2̙kPy2Uci fU=6>]{1{$1N*^)cXwԓ!)ܳd`L:'Ϛ6y15jgmqH;BEr.05rM4UMYHȓ޾I̢7~..ݍOX1P΄zYGQԐ=䵉&x093F#4F|o+GhѢ26z"f:ki.\RēO5b, n*HNҶ_o̘!CZ?=>@ݠһmWE%2u\IY3uq}rVС׷,歬'P>fX^"w? \b޼@[cNX~8-ڃ=k?J xȸ#|?ȼ{G2XeY)7/|| qyJq'q [auѧOCVt ;U{R/({s)O1_Eq;A #s%gޱd&̓ /«=fJɅJ|:?lovbK0& *W.*D^ tۀ9";2Kqp ;VX, _dqRMI¶bx,:2!U*Ex h2̢SRǻT w?_qC MMR=\~J}ubw@{iHl`6{7ET&{oVm HUf[~&̮)? Jy]Mʩ|S{Q"4 #2RJH2.M:Hy2A 6?AӁ\%<»ݒ;3#6+΀G^1F)PZquNJD^z}DfaϞ޽'!ۮ5Oj·-zg$ Ҁ]І]{v\w#8k{ 1hԣJk 5kKDݹwqTHNR)U4v:}:_RS!O=Hb\*]g>ykSe.j4)zxkҠE^1uj`5)y^)7dv 0aI$$7& HifPvQ }.%u 7< Ds@<†<"Y΃};n M 9\#ْ)]3!QLgDHYcI\U}ݲYp䔶xh'?k0pC hNi 34'6A#ߙDSm a(sew.ߩE )8O: &`#,Vt8-zXő'IF9/j, }?*)@{9m_Ы؃dV~ɜlr,Xp+()Гv_~̘KĠATF:G2%2%[Iɕ&=B] [adS $ҤSc9 zdnIb^ṞoHBAbv}NZ37"0ءDS^/RDp8E YoĈvd U|/u9M^9}v>\'};#]f_>oux$,С yuq93Cx? pa4Fjei٧w.j`Q'u||-П륓|b3њ1ux%Sdԧb)ya?֐y.`rX`& %,Hz_AFf Ő}&ڲJ r9jƤ?SRRB a=L͚UBDhn SX|M/y3JGȌuN`/[q/{{`wj wI;-5`UIJvS$ׯl@@R"+ 1C,dj) Pϓd6i竝ɓ@' 6Mj' VlTv@[޹sikMo#MLV: ҅45j]Lş&S4KHH5W3Fl=.E+En4fIH' k>dm>C4CF:;Rpz*Gꪭ;hvt : 5B{%_{)zEkT%ڽl髯Y+y ;Ò.m h L&M'Çϐ' eH|&֓"O\79-+-Yi_PVo5Gsq{M{t(hTCs=7P#7i$"N0v <FN*'c畀*f7mN^}`vJeWX//DI ygLDF߾c/AH2pAo2 z7ԈLViE.(0ëQ3lZHX]X .{ػv}b}Il, } tdRGs1zf Po}Ff{ J_f=yL{\  @N&͸Bn TtJH8P )Bx;MSu:!%/BS dŸ %G~_~KTuA&?h(pV}ֱY,34iPCH0P`ln:nf]cXuO5Ev; Njt2dsl KpW]/ZfЙFilm3fJ9s6?n\B0i׃̕2+5gǯH}A`qƭyK>J>?x58gy3KTbεWQA0ޢR}V`٪pY:OH+ ǃkwbAd*N>Pu]`1/Ŧ2e1V 1p` oKׯ)) iosd㳕]P# W3ͥVOfv:#OoM6|sl֬PRTA(7zXsQ (b},})^uU#PM oRT]< %"؉ʳʘ\[q7ҽ,JFOvyo?k${Ձof"F|bIY] W駖"%woE\a^[PX_Gu 6bTu hdhf1w-Ӷo?dg:wI3ӽ2hd{^B+t/ZN2tLց^G5PD.>WK/,z5c'{sI,w2KEL۶S&Rt1Fwb"W_dީ>),]z˸}5A3E+_F^y(08 | b*N3pUZ'N\{{構n]ε]]6}g6.b'g }n~{9~gu)I[Pf-;ꄤSTn9;IMarNj#:v+N@"w͡@DD8f&*Ce \ $ErҞ<CHD OZȻ0V##7B M81fj2O 5S+7D~Cr'>+V\;g@G*:2Td[G*Y,f8] cͅ|>h.8csnu Sn_/djc/X3 J8fܸE0^D'a9pG.`d|:2ZGxw I~d>suj OziӦ z_L{RҢ!nty]/.4Ԑë9#Zxq@)9I W:}; 3pmE31A57Z=iNM'E)ܶ0'q^w4}ċ*s:Ih:4(#sW:Ӑ71Į=]&QSyS~q4UFS܈?2~b`^cHf7iǺu,Ϧ%D-=ADE! 80p@Be] IV7yOTOE1DnxZ0@{"׏xD>tF3ţH(C/7{p@gpLB!8ƺo Un@ ٦@lG'HWF6cjP߁>Qƺ7;&^ Pd <7?5Fͯ戻b`3p.ÿFP%oz+^p}Vn(DXq7o^IuWX5H!X:*qC"LI^xa!iQ}ة9ؿPߤs37@KZ뜏)Ǧ`;wM95Oݯڏ@°aeM k`Zxn塆]<J; pH1zdgk04_~Q֐Ɔ GޜlToD( bȦ7OK}4ɖm<=Ԫ78Pn>"k.`_v-AL0?.F@L5  *$!Mڵׯ_TX/|DSeAa`qݡ"0T)}S6M!4;nw]p&Q4%!nh.Mz ^."]̓IvH&ɓ t-z& J T؅ZB6i`:Jt{.x>{+ʨmˍk`0@pQ:xMJU"y[K53ge2N7سn|Tw:_MTd;%q}gm1bx*v>^i)yͫQdh׮{"~cբhrs;o~:GJCW^ER {&4|/{nM5'a3I7 @sԋ/ jr9TVP G86ibi^R QX[V(((|I-IAW) jOgf1 uPL D?o sjP6o]ŧ`4SWJO@O7 0w~n5:^7@*Nc5_:YD^CBwObr0o,"yɄ~z}EdLō#߉0T )]ѓ3K-v!, EG=Gpr $?'ypevK׋g[xVSG7оQwYHR eF,xvA׵Mc3 )M;/c_=_8 :g!,zs ;YN'"|\" D O 1e_{Tj n޽ mkwO>nv*Spt0Mz0Yfy2XLn:d7J jp社dzBQ2fL ;39Ɩ?zٹF$k.4'nڴ*P3bwv_I3 9r&5owXp'ĂEI4LpݚQC@~2-~.x:DzVK`ma7X@3v 5 p16#{swQ315nLL0{8Ƒ7ʈ6l֮= :msihaHWVUJmYw|ޘd J= UH7dzXO\w] *r2#D> _N$ڵ3*ٮ3 *,g˭sޢs4ؒ?h7$Xѓ5z"-!'+ݩ -8)%p\,^~/aӯW^NBSH7!f,Ѣ!Cm3y ǘ'D lP"zQAfZl3twM:ߖ>x`&ҥw@d>i2 ̟ \ :*54ܙ|tW!F"n+l97HhngzxmXr331=z}Ⱦ ƾum6 |%LτEtU{]'Nt|d':gK c5G4JUުUv #N:na {+>ZDվ6ws=}-v3%"M$yMhj68T^X̬SN)͸~oi7:iUZzjlb?6 7_%ΨJneI縉ᶓH#66Z^VT[F=yNFIx^ggѸ pEAIZ{޼mns<7k&5i,bܸ>%~Etz2U$q0L1 G!A 6"Ɇ!{&ʹG&tHe6׿4 Y ={[X"tonftolYHO>3#NnQcD„+9y07Mm9D4I#;`]2[Fiz8t!W&šT(݂ELG _FqE +2OAz|H:$F[]^o7=\tZ3 IM&d4MEBX}߉Ȩ 9\_$yU# +B5GҭU׳l,Җ-Gb+Ģ0<`dڈ>|ZrXF$!^};v^pЊaN'rb A= 6%a$ð#- cX|OE.KgBi ϒ% ?W^YJQ/C'Ýk  Fe7tqMm3Kdg]sԸq%A㉒A(]0y,Q"H _/{yلf j%ᛯ AU)8Ǩ Ko=IB n8e$:aݔjqjb~f/Zx`9GtO5kf?J߆GT)QPI ^zixe9X^`z<ʢRRr7iګc[_~9W@Mw?46)HnBy٧Q[X)-_cPP[={rvǿtR"hf~+K#3O?J*1SI=r$?&Cv}.z9?Lg#9~>}%nBQ -ZCW}~]qۗ 2?Hoа%=l~C u͒?y@X ʳI@" >`NE¨cwfO nOOj•ȓhH=BkXܾ5"h>g1rH2M?eIxdu>Բk bDzOawqlfW᝾Ut(bdXkIdDjRľ}ǁD¸ճip߰w)/@ԩS IС@TCa"dO>Cw~j$zk;\oNykYDG%CvFⵑ?4,XtugXI= z_&j"x{kU$ S̢{A V^emxmrM`ԁxVx%/z\]+/"}krn m6&8/=i YQ{\^x7x GQX_:6xIqU01KrSaH hr i3E2ޥ. 2e ĮdOD:Gzұ6[d:/ >?غͿg)"-L>RRe߾ lg1L?b`ڈn^:SL.O  S+-T`҅Vt28$@G{ 4$yK#]ibٲ$44o9HD6xKkzV4W;T`Zq/f`M3|Կ/VEn q=H\we)渾E ٲ%I<ﻯqԨQ6B^ ŵտ$z|KٌH#\{tn삎T'͊7l8{IbѢ<*a ɝ{9nmbCͪ ;诽v%QtsT4%< a^}(D:cQEfUEB'Dҍ'&<7S0>ƛTM@pɫICo6*ږМP30ZntD 0 M`u*1oa`"s`< Hl߿Qg&bW5Vp~"1ܵ( 89%ҵm[M*x;ɝGn{EЏp:RݑG4n@qHYY`U?x7[48&`Oh`h\E.L^j!Wǰvrp 3lWnfte *ٽ6o.=ҖK= )n=L۶DIZg-R*(o: ;S?0%Cg @A{Zr/oޭ8Մ=UW#;6SC^GڨɁ&>w٠H%yi+W $5h82"fΈ*Uk4O+hMElp\QZL>7͜xbi~dtz4(UY^GƯСĪUŮ]'Ĝ9[ZTj=ನ__ Bjl=;FA_=Tlٲ\F4pyS,B M7rTՀHɒ7E1}IQ:tQDgh&)w߽&)~q~ܹ'H1͟ CYJ@$DA1~&= 8]$ۏ6T Z%~J-y/ "٩bn)d @< !C<u?2q+#zgЏelR+W MC |V'u ʦv&0^K6mG.(*h7@eAZkXNj+!@42_܏Z(+CuɇK{C* hWfcJ! *Q njtλs!ZNY P\ P,Y'rp~o|-=ߙ2E˖U+nh%4D^OBaҹwOEOۂz_XpxvYˉ̈4qPqu)m~Bl~TL;ޥ |cǚ:~kΜM⭷ŋ']{мs{c],v:s,~mvW_]@dL"'Ky5FcT (_fАi<~+.d3NrĢE.I[,V9wRᩙȍ5;ggHҚ$2yvKI>X6?Xj'W6 vJ:2I,TJTP7İamn\ܯp(cS.5*f&|aa֭ŁF:q"O|yu  1px O7e(^/MDDDq fQ^%q-}ꦒgA+:VC;WtzPgf;\ q`SqchQ.nL8dR1kĔ.m;Ӝ'&tLO|oX5;AYW"`XS0dqgl"-[KwcI{@*}=zNm]yj7-Yp:6:Htpf3qڗU7 .._J"1rdH %)Nw1s]ޏ#f]pC=c9snm8)b"ԾNkӇO? |v,~lѧOCu93g/@ZIuop@"{֬ w H.u{(hC80Kܬ<}&Q,zǶ$Snbߠ(UZ!ܹ6aj3\YxUqOQfŧ XA׽KXX]R_ĿTAf1 S[tWfo@y) Fs5@s7!"kL"2)#q}E];Qc 8TT^DA.)V--@_w]sy U Gdf}\97$*GsYn=GDzb>qR;z3V 33hy: 0?8@6⿸:v<|G_ !@V1雜]o|1 ϥu̸C ng߄+`hFP?W`;.o#d꿋yvH -*P#n^[->,' j3bO}WC8C˖& x'R~3})sZbbfP(3ElB )ؑ'zSšp#W}W l`J0+@lzVQ#Kw;/p2eMxQYȰJ @ o˿TeТ>-@d!tLdajS(6PL'z Pl @j5PO( DA@@!b3j P&ꉂ@Bf@TzmHoR".uwյꮮW׵W\XPQQ{eiI=w.dn$d&)0ؙA@h!(l@X1UrKoCT*:-l|PM`k  -ܰA;\jԇش頺j=D/}  Ppt>Jռy͓w߲T A@n3{A'(zdyg5m&@v&@_A@HlHO=9XhMu D" $ :̙׫n(-x9 2A@A:~  $ )o~T9صA~N$4e"  S36q?+ᬈ! @"Pιsc~B&A@ojʹsQ. $/`PP >{ef @qbW9" $?bXf( C@bA@A  PA@(0  @# @c  P aA"A@G@2CA@AD ɏ0e @1(A@Nх" '?)1dܪM:w#bswA@@sPkxc#)0xpKKf A@X"p߂؃.cĦBEˇIA@HbM#c$m  ɍ0}ev %X"A@F@便2;A@Aa,a @r# @r_  `0AA@A  NA@D@KX  Wf' X" %,rPA@HnH+A@,9( $7$  ` A@aA@KE  ɍ0}ev %X"A@F@便2;A@Aa,a @r# @r_  `0AA@A  NA@D@KX  Wf' X" %,rPA@HnH+A@,9( $7$  ` A@aA@KE  ɍ0}ev %X"A@F@便2;A@Aa,a @r# @r_  `0AA@A  NA@D@KX  Wf' X" %,rPA@HnR{z2;Adx 4866ccb zm>*3"@#he1E`ӦCWs9_iS]uVk|Z*ş}vz N?qRNY$f3ZaZ[WS[|4iE߷`'bªǛmQ Ѕm 9>1ܸzj.~}]@ @܄x^>)jLitMJUdr8oF.jժ_?)ʿI꣏$, |>pȲdaP ʘ%82FK@# @߼ho]-O+evkU-9]Q^*TȀu=𿅪R%=P=z% 3a;)EHQ>8l9sz챟T΍O #@J  Sp,ZO^;w䎛wtȑkmPď0+P~Z`6Bgۧ![hQ[5.{D>՚5aH Q2(A0W2^MjTctPURW\1x1tvi]6Q#F 5BNi˖?}{$&KB6U\jZVdd>S_LY(c{A@#C80KM~5k+RTNuUVV5o iJj۶#A7 ^XS#^u\)^㢗yxa{S%0$+geUѯ^kԨ3aF'/ϭ\.7v/R|E2tLU]/1rar"hN] },uYe˚Rt!yoV$=K*̇*!CZ ;>bYN%3QAl9&LX^|q6"_v2VP'. Fld*F㙩RSTfuՙgn8p# @-0<\`_D1XB91UI1H)I@D9*= ` իW#նm1 R4hgKn{l{E3gnֆS^Sxp×`8zsJڀ\' aJaڽA8Uo3]tQGgaОz* c8y<OnUNcjnոq5XYUc@: mT "bɚع(WdO;@$vVpz睅x1331`<R-Ə]:Za*A@p*a]FeP{ -:䁱f|6W^ .h_2X{_oS:|8ǘ)|'MZT{̆;){WUkWV- p |Sj2L'O ^;O߸Mꭷ΋HOd`Xj? Jǐǩ6l8 MVhOb@($N0:6N7ժkT "13F*㐳abpׯ1iH$;č:4l9t_ԻEFK#&=9Tݺ`ҝ)Z;mk3Llz .cQ G@<լYLØ0azyFhR;;}JR̿a1~'hfv23`(R׏>}ZA:/[.dĹٿA 40XCO|bP R>2}wG~TKpa%^{tp i喾 #6?_ F=Q 5D#A"Ի&axpQʃ$-BvjC˗Q!vJfQ:֋Y즢 7f6gb"ҸXԀos O0Wjbe5|ogDJ_Pe"RY_"GD(+S2.HRu'8x45J֮ݯS]j0)y:8W*2ٺu-խ[=' uL- -x-+p$sq\-E02.C/;.b|s ^_or?Ag;[o-+Ǹ>샄C =c@mu}gt+R+O,ZoF~Vb48t(Gu*q3zl 钪}čaj.{J}*\g5dE0,4C)ȈC{o?<v"v5{Vu\3 Dݺy#0^MaTrJPQR %э`%x!gh]`p&  R`L>ߥKwC}v42 1zm۷8;eZ=A>}(6@b 'V#7k?`'Pײ\lvo!J,mS/P,srnf&- sY̙0Tذ!u}uO2 jW3ƇyDo̭eZ㩥ʅq1H:a`11>|۝;c=HKKiJ-05ҩ Cœv TQ24rݺJ@GIYb5[KpP <VQCTժ&DUTUt+xј&ʦM3L F %D4R@X["0imQB^P`J_ڨQ`;[ԎԿ=B]uU7gz2ZuW݀(xxfwM]q ǐE)x?L,fNdB/ߍ@4~7"h[WW=z4C VO87UZw_Hvjv o&`Ix53 uw\;o89!^6ߊ0vM͛N;5QzԇmˆC$80Cm:m'&0"w 4IW *5bSuΖ\'-eaV|1a u\tKaBF㵜m7o^Ciժj޼&^]%M \*Ě蚵k-@'uM|QW^i}WS Y`L/T |NhާR:(C`)+vIt1Mfn(X$9@ʤI+_VK0?ADF:^gjdsNkԞ }C@FHԷobngw" Fn5yy;.|vAت^ziNt kA,\K{/6W @ڜ8q9 w(BȇȞ9wFpڍ XW*62fn~+VʕպuPAA ،G >*8W&tuu\J"*ȝ7UFpcp}Zޕ3!NIp) b- ֋/<0 P}.E?\\.]m6l8aY1:rH꫕P5l1>Q(q`;d>w]|6FF%vSױcnԨڊW1˾}9Wgi&{d4-LTkq|,n _ ܖIWtFBLwjtߚY u j?gYx7>a1[  A~eń>R%Kv/X>h {>tNwi5;Iʪm[(UѠ.SReذV{e0Lw]J}Ύ J,_xyyΡF[`P@UZ}F`%ޡQ 7&15?‡r>FB@ed=>A :% #G~6`0@yd-W#Gr֨_ cXc;Cgׁ]=dܻZ[TA={4xne^yUԎGkOXʑ"y.fcvTB$k)Oe0jiGf*kC5fLg4(q8pl\Ӟeڴt,` tR3Z2'BjԨ͟[KXI[ŻToꔾ*%!^&cH.) w`^`jhtM{;h<$%4ӷ6T,$l5ڕ]76k"QE1h?mx,F=.wa hvEٳ8Ta_2 nϞCz\h/}X861EK v ;%,ӧoB_;ՀEa^r>E*aԢ֫\nϼy;~5;]8R`Q=`z>Dȿg|֜x^j͚} >jTH u TV³|3h1|!2 -nEp=2{郾mEn]c}+ !I{qK?G.G0>dx>d)ďˡJX*&6lcP 0} 7tYI&hd99n,d&7Kfd:{w';cjܸZw_?ob͹S"$/ ja74IKlظIj ISoB:ÀUu M7} 33$n&! ^8}wb] 3C/mH.J `-QCf7{k6m{c@>R~0s=)`-FLDx>.mb &"@Ͼ w6~-v͓t>"c}ьGH#v]fXcɒ=<)t} k765;hK/́|jd`g8AtkkI{C#Zؐ+*XC2PZz`vlJCK {GJ bvr}ZNRH{ȓz%f͊X1Èq1|Zgm,wcVzt,FcQ ^, kCtop[b}|ZUOPnT5R˳ct"4WKIn?)􀮿Fj91f:'/l³FQi0m._?I{sX3L]0rmkZjmZO}ҘX1A,0+`{7߬E;0*aWrY"vC3ޡp_nN9Î˿AeVg.wb{`aGABj?mCg$>UAcT/N4d 7, b iGw.moPDD Zb`'yL+Ur9M#_I$O0]1O]D6#}w4E&Nʕ{B-1gɼ" SOM/G(H4"m'T=#h@*ߵ 0pT/}A>"/Un][o7-hP:@Eή"m~C[_? .'ET4_'AI]tQG ̞EV[oFxm6t/m#7bWwݨv~sx뭆ʑ#02 kg NZ,1d3CYWG➴4b2QQ KK1@Zz=UutHvZS@`f 3nI9 ae5V2/÷C(w ]FX\2lt0-=!r7갼Là:JFk'ů4\!?aUc3E StpWӜҰHbH.4 Ig)n4"iq#IDbğaO<1 @pã6wbLKײ}*ϸ.n#Oj$ILn[~pwYwKbI.5M|%¢|Gcg/_ھի%#GMþv +}T<@2W@X„Vw Dۉ {m2>!)@pW5pSaR4gkh/ji$˫Đ v`=^::MZ!}*fP،N5`X'b׋]Ϟ o)t25k脻TͩSzm|bydd4^ 1!Z AG7޹}}QcܸnDuLo'/O%oK(ʶSj׮ %ts/7H&C1V1 D9>gYj=Ja5u}B qꁣVU _*3? 抽KUU]#VߩGL4)#"\<)i3g SO댕&)[SvvN}M=ƍ pdz|ͅ< I(kBaJu2fЭLѰxlTm/ ;vs˅T}TiCtcJIK2F k jժ}\hbq<`Jc.< / QFXI8NR9ֻ^jl%O# @աS2{Z!^$F m"~Z|< "CAV]0|ڊ|WRtt3fc0 v`Z1ƍΝTәr;ի=B4zPjG5]95e@tӶmhLZA$?$tEF5hd;#m˖C;O_Z4̀iBKS(v?sy_z.EEV-[һTh6޻r\,qTaߑOoӈ\zph5 _B)Ŕg` _Uz0ܬ>oA"/w.0m`rEo\O_4e;` K4xZ`CPKX(x $*h b i#%5=zۗ 4㺫2@FgZM SXm{<\MiS[~JɁբicN=,/^ [kXN#:XE 槟6 ^rB]E/Woߦz7MީʮPLWgq29[ U}p3J|͞EP $b7\R޿`ݰ=UVگcp"Xs⑸QaFJm@x$ ֤n `iXDV$ސk2]j*"fÅ w}v@Őw#.\sr( $7z$rk+YVxvTHVA5jh&Xa^R+V*QݑRRd2+W״٢ay.bcie5YOu rYqM_q) 0q"8bH9V"N~Hԃ>"]ƀED.iRGG/qn V! ئdj*LA]-)=&Wt#dqՠwhbZB\*gL$ǎCbDܥB;1pUxou̡Z dK, Izh {K.>va[4YgUƝq HxX F4QoΟv %сkK] )¢ 5m0ȇ, K5̙81 ߉x5^i"X!@;*dD1j#ޛv׾0wEDK`r+Z7 @1_/lv=:=H; ,0-SnC ixެ}t?KI~3^@u'!+(`+wΞAύGVE= P]6(?CY`iG| Spwm#D unZ+FS1~vB: $\Zo"8i*!pF<,rme*MGA;:!.!gne4=9 H1(f Tx{yp% eV>&j:N.:i /E;NqѢ{Ho aC\R tjh'Pc3gng*yIȥ!V琹 &761z~fdUCivM~j)o-:+_<ӡ?|Pi% 7 tiTߟt)u۲Y3'A.}+^|lڨ 8`u 63ܠbE1iCY=_ygoFrYM{qpd a8:j;)uĈ꣏bBH?KƅTmibLc-:}3Zp30E2!ӦmTQe7.928dm< CF`&jG\ֵb쯸kC: o#%QW~o0!i5b!n?얈Y0ji57F+F(XD08⥧fe %'xݭv#X=QD֣jԨM9\#:y7d_cWŒW\i!3adc)z/C92G7bgLap'\;ou`dU͝Mi2M}CcfM}:,>-d>z) U9gx+1+KժIep$ڶʕkrm}?:4n*v#ZiӡAH\H_=P(:yiemzz1i|L;|CU73=w[oiE%-oP4R qPsVt9=2g6HVb [ ̳t)%3;\n"wC/4}C 6]of w[jWmz&a@*Cmݺ|O|Hy [`SEu)8Z:@V4Sg}DC1yUy,IxQ׸.:p3lccC#3f؈J$~q߿YH6mZJBpYV=cf0p<i 8\gt7`;F$I8jhה>cmX$?tW>gHA# =BY hE$WgABUnSlgN\^y\Ss7߬D6#ZkNB3 n+"sn W~zVk!Ib #_z衟_bg\9NT\UwI)i^GG9x0ͽPYA::chD03J<նmH60mSh.0VQ>ixo8=SP',6d~n'^@n _5_{mwE#*ƎWKw `9:Đ!--c~o-8{kWcٜLj<X'MZ W1k7k1;[n#> HT;7it)/%05sD;QH"@DbNg 8ZŚZbo5]U4xY=Aa{$q24bx)EIfվ}KO4ϧ4?鞓!Yǻ|* o7r^(ި/ w9@IJ W)UcN1&`eKm_Q;7IL U~;~\i$Ȁx|$}Zwpj m` 3IqN fX&x7Vݺ5Ԟ u2yTH8 ԁq A~){_koADƟ'f³̂1Ҭ7f۱OdFu_2r?v,>v#@fQٵ+9yŊ:,ӶIAӱ&nXׄM(XܮV.1 $:>ڥEMVkk);<縳`)%Goߦ M6@C^UyJ;Dp?^^g'#H!+g$,@|OPǓymBE]xjW9c "ݶ "/.qu7㳱oXs/@IDATC #FkM:^(w=T$J:Ů4@"/b?'χ!1+cծC;&H}:7tSP$'|8Բep;ኳ[[6vj֬:BuxGoOxyn/|UL0%4}VڷPNj9m<1CzNnذ1!`R5fN96v(fᄏs۱?xOu@lA5B/rg.8Nќy )J OJh&}>ߟQUh\F#dHV"8u}f܀?70@s̰ǩTM$kIx\ ը8?MFZc!vo\H9<ċn+5zt{5n\|¾Ze-g3xS3k7AQ$flm61!l7Wu g:@u'7 幤(# }-%2$4`BGxxzϏοhW<&I;(o׮t0^}wc fEDG`>ыun%rUJ&T=6v^ E!RH\d^ pBzNlYR-9T=Ow&81E4nF{eDd=ݰ'-MTS\]yXԡ~a\5k"8# n Kudrc5Ly^ceu}644 JZM343>Wx1B%lZG q>y0F;`4OVY6DY0Ԉfi &O3A>JuT/p.=.tw PmdÐ,nxt x≳!죃2ժUՌoH,{ @IEgX0QNJ׿N3Ð S4NIsoxo: 8#AGvǎ6Ԙq?}IX!uǍ\mL}P"L=;$ŌO]~%Vo7Wy['(I]IwkP:!Ksx셺kC32Fر1%NӦm@KP#4Ki4|#f~,)p?s1xa ef- ~D6{|i tQؼp]-;M(;VԈ$7Rj-h6dϑhS: G(wG:CUϞ ug. B`G9oC=-`crxUMA*?[xdH|evJի1=i2e=. E_r/jϞ6ڶ3DTWMI`K>l)FH;8'Ҿbsi}6qf Ěf7KKzk"mՉ77ж4%FZ.l$#1 EBf]썰;W`P t.4+ڭ1S!.i/6nH<3( v6^S yy0~Ԁէ^ԡC`օO(JtB]Gv6!_ q5avht_h@\pkd:pu-??b6ur1t"/*Kt,+f$PL0czz4 -*PFgO0>"u(] j>O]yeWXk1$vxĬ@3r~ U ,Us__[2}%{Cĺo&F4Df*m)FHDg+v9rG5kyy00$eE!4a̜uV 5a¥XЪz}7:0 o1 GcLhe'~Oؑ^ kv:{CW\ZfALhە2!?; g_鯙fj9s!饉?2l^t.Ȑ8>Sm& $R`T !ֹI%g#{ x98."4,A1ąu ZӾ}]kBfEjoo> @Jh׮BP?~yV̈́  ?CKph?~xe8 {j߾ꪮhxX1 Q9?#̒9fL ~/; 5pJ9D> 80NQak{K5ߘ駧0OJ  PO;q/jTiHr62+}'BuHR3i e ]a62~xЭrQ_iG=찀F~#h.{QgBFk]}lY[n]GeУGX܏yv8eS ;s*U_mT~EJI+i5QV+kd*UJ/q"n]2ؗƱC`,fxOT7ցe^FK$#ڲ@P48S v6{BPSy៱pCq?' ςTo3Z*=]J|^}M-!/j/ ћ25膶e!, zT6lYI2f꥗fE$m}MMu/}S.؃t?g>AO[2=so85>V*e\Y?~z]V؃ϮC\Ce~Z{$o-7\NE?˵jUnmS_7^Pp^jy~Աfo+F iM7O7LF}]h/s֭ + ?"\r=GE·"]v۳?1v!1CXJG; ՠA!/NXovډ%Ñf+AgdrQ.>]PǏ; /:?\ V׫(|u:5`#G@I  #aڽD`@ɝ!"e(!i2|E21˗_}wΝ^F]!]R6}.@Ou]) wh*Ӵ[Ŋʼn+4fQ"uFizÇbQ>Ik]=V#6<3L <㰲H"~z0^Uah/sҮ쇌?3aue4yU Ht\./~h,mՐ!JLM.mLG?V2Kb\}_1wf;wU3E9EU0z{kQg_@go\HhraퟁEb7nBr燄?uWw5ao '>K͟-wgR4i/7CdMAZ`TI P %d!HMym;w;auflѫ.ʭc >ʸfp+ 䆵t,[b`|x_w=(cst zQF=X}ňzAYe ^prAgǩ?t} /]¿ akhtyRpE ,U 8Ywk4.m׎i:ro׫WCxҜSlQ8ОW0Z{F;G\/QI>K HeԯoxlX$"8r-G V`-qnqkc%3|헦~ tsa0<.wMSGgriU(OChs[DzcpPbu\u% ݖ"С- q5CF VoD*VNڭM!-̆?Ë \ۃymq:Hv2`Z)`jf@B4R=j8Z i3h-jS*^;:hT"8Q @~6ǒPfmmJZG,[n ƽY6}h.NŅa_3^i>\=e_{m6ڿ kqq+B0@]rI<*Xmjd3$/Q}lmf!oҤ"8!')9s3& Eo~PkBcKzҔm b{> ";Ð|mIW뮛;nx).]ڱj9џ VU{8^Uo/d1#2nTiʈ^~8l$HgnNY*EYg5Yc )yP%Yd}i67S˅jڃj}Pe)&>+a^^>v鼎ϹZuMr.N&%JH/4BvBO~sl0סÐ&h#o…OX$P8t#vJo聘SA?\Ӣԉ 7 FDzqS9*u`eϗҿwװax0s!ϚLjq_t-cGלC':)Nl v50?`(b;w7g"FRu2.]A9Rd7~4m٢E=DAS)2e6%KvHđh`qkz#k`1#`s:e{vv-a=ciކ!lucu%ڸA23STǎ  ոqݵ'Pg L޸VWє`)W mۘNIAFv*FLriBlҲeM*FG2((pIc;!V 7tG܈QK@3gfُ3m_g"Ur"8qO<q!EuEeGzL;[`ҐHK>hizB2 *84bI[x92rd{ S^ xDeeB(~oX[U?~4_ hr#wb]iUD L&\pAGt|3? :J]w ,ܥ,)aba* S\RZjوPES__sw}dvH=s +@k-2px s_2O?3`O}| ):nW_2 kV1_*N;j:WΨQnU-n1B =̐RF@op,4?!:u*wvqbHLN&cS?O1;袎V>5ݪo!ӧ>Mu/"1F'˦kUųϞZ3yc,dDbt.?HәJoK+ςڲ:,/U+h-Z4P?F}2i[K(bfgS 8ȼQ1++P|tzw',Z,!3n.y7"zrumGગc_q n]>wS;*`~p 2z:XVG\`&עˆ=PN䲈 {D[Ӷw x]0 _w?~6l?yL*i U۶ija u"IL',7vNKV LݦLYDmْE:w֍|-Z⟢&]A'jB-=U.m%mb_HN:^($ϐL= 90E,v[(-afjN]o Ś"r&|ܤb{'@wׯLj"@=' ݤ~q=Bphny=: 08{љs"ֵO?= FkZ dn @k+DB0c[IVeW\5 &W~z3$S) IQD`ΜUaXѻkj'C|yBP(Nv̍@@_˥b ѶvS2kx/2 FNR7*^z<z=Sy")WFť%|AM&C q Yx.!h֡CLKÅY"pe 6zn\  G`dCS^ Og+K&XL%2lL<س @4jK3*rmJKE NO es_nt xI?4 !gP@9a(RsU;w2Pqu­]DOq:r8`O~ɽ䫿 S22]wݗ:it9Uz䑩E8{Wo' 3Qf/vHOO }/-H5vPp =JĤYdd`kLL&^ د, _T"!zuŝտ<{ե~ 9TU6޶b`T aɀJڋ\pVaۻ(LW3*#,q>AHOr%U--,ɑPENS'K1X'//q]G5c;0jA 9缇֖Lğ, }}2j?uu=StB9Yb/Xc:b$. r",Rĉ/au!"0Y0UWuA8 w*MFk~00YƂxPrwбљGJr wLcxz)tOqG?YuxS4k 2c'w{ر. ]![VaUaH+hτ۳ kiL ˟"Dlld[QIF yRrs c{CIFӛ5Fa˾}"<~B^1k{ݿNL&N\O{T?ޚY3nA&3fl- RT^(FiT g a*᳠41%g +T'eG"C)n}f (%1 ^:1シw6'@㤭[E@LQP7K D~R@#aNՓ㉃=ǎcFH1*dALճgQVڵ+7߼xn`[ kJ#n p~iM1~##]rr5:F:J܉<# KyvB*6:9ضE%== FϼwXQ7F49 &D E1`jj|m?eʵO~յSR5k Џ>z7gQ~z.0NsQ+WN` 0!3nKx_xEjq޽"G*NDժeRiq,W 9zY(R}UVVu-5{kմi7BG(>Z/oCcWU9Sh3H"uGN.u݅o@3؁"eQ7[YCUXF 6D@\GZ۹D7Zt7t*UO}t Fݩ/ޡmh_}uӇ@TE:aճ ZI)G B6zt$YZ[?~DВGH[%IGjRYm'_! =HdTˋ/bOW0 =LڷLF5n\w5vl[uM`4LfLAi w ku%%$@W믧H0CMp5>mHHRp#.;m 4GCepfgà[$ӥZ*tK=I+# իJK>, XVS30hsn嗫e$^So Rv9hx65Ձf u"㨁2PRQzip&Y9mO=ig.J#s?+F#n { 8X zFiYyRep7Y>}ѷoSH(^l:!ChL\G3MT R[  iK8tBK׋5Xg^7^y,w3\FyY>u(}w!T( >d@Oul7LCȑm&$uTTabB`}AA R Nl>M^ *0h ? en e8~;Nñp.աC]$)>ˏr wID:tvDU[$-Ɉy %`z*&Mj֭kb4FL9D{4kV AU.[)%Z1,IIuP?`ӥK/{|s1mߍ5ov5kb  OuJiKNP6v{#Đ! AVVU& \N贈FW Б@1oTL9\)C(Tr"w1 8 FẀiU~pU3DN3QQݻСչW')7z }%penH4|,we:V=3ƞ\#N: ګM z:{yh"EG]8t#;UMJm\<6D0/ԁJr'f={CrԤIkt~Z@xFlEcϞ1FȰ⼑\N^y^}~do`9o6?_[z#F$l؟{E˪S4;w+q@j0<dV=.ˁǍT^tٖR$%@PGjTvц?4""x#0q g_ϒ{tu3SX+NkR3RMTsgva@ #T0{˝w@ÈgtbݣbqK?p[6GkWU^V^wonCR?JDJl lTp'.":夜l&OA7$'L؍}!@5^A` $I;3lUԷ]cך1+}{f~{a׭[V&P]zxKX3R%!AKXs0j~Um/ZZN[τTTZՀ:6$R;J$-@:vxlAr-QY ( ũ'h*wEh)E}Z Q#X=z4Pq#mPN?=K[3;~Z !& ?F{FCҐ&2}fφxGxJGR"Oq[{C ibl.SÆEHGs7#_]ߴMxslI"DŽhB,(Iԯ_z};['ď,}tw`RUggf;{W@"`A,aƚ3cijh챑("v" wXȖޙyٝ{)s|A+GN1Iu+6"UvAQna_Bn,`Yڟ=r #W/J@@@RޭlcŵRƌӕZ³A  gw7] G!b+4B: Nѣօ"@ڠ1euif G=.Kֵ_a,aO>VXC2n\7#qχ`C.Er9in=x$вe~ P(CKYVe(Mzz:_sd"{99*@lG=Snҥ3V떡Ke!IpL-rTWYwC>Wkprbp/\nV[@R SN=]#]|,h:toA瑡CۋHDҲzS#YRq) !țof|ि .VŊF?il/&^モ=Lw W ^@mVB *?ԙ|4~l;\mvE}l|q"fZ)-gB87z;)FTJ2٨eX=5o[wz/r@K_%&i$Eޖrd~QWˆB'2u*(Lć)$K`ˉpPr6qU2AD24IbtK о\2KҧP{B _KSݿ+ԹJ¯Y?ٟ@eGB kق4]*s箯P ݨQvC?Pi_OdĦ# =J`rIdMyx>[4co<c'p)NRJЎW^9xm xU_nT4ٵ6lW5Mzs|ɔm+lUIZX E/;|J>!\w(r&ʫ']7~>}Zg[ j65=Oi}JBT0wEJz_tf\{h?*'"tp9ЛN_чQy? p) \fzO0Ş@ƷV7w~mTW7e&NTB5^8vt0&MgH暑 MN nZK֬ىDU`߼T+<mJBgA)HEz.(1hw?ބўC<[V/[LH`ٲlɿ哌4M֔bl:;MmۦJg”ɴiT}:Qz[HQ!DuNnѣ%V('6|jgh'׸r׮bCOj(h5*SA55mWePxHiU=nxbG9>lHSQA~ +?q+.3v5[`5ztD[6 :-(׉g&`])l߾ !d{ʉCwytQŋu Ɏ=IVO.8kíYl(2+nmK֥%}ߦMcX&tB e{Æ`$K:&[ Wb *hs/xmɁ>;=IO8S?y|U?Unj5YxCq+W8_vc NhZf 8 쳵_c8gsE\+@ ?PV Ǜ=w /LՈA1tE {)キƬ wEXa]ֳgK`~3Y3}(f[N˴<1c}mر?_=yݨQڎPVhe&,27@mWJ(Ĩ7;oc_(کntJI1jzfN{4|ݟ)>}d'.ƕ7F t_骆ն ;o .DWTN^K6Qa˔aUtR%j&~>bvHYGfڄW/[oe69{Ԩҳg^X4ɽ_4((|Q6ͲI/J@@T~cb3K4 #k(g%ؔ):sK`+z;THZ}F,sȽ:0t@9?X@,(Gag bN :-ev1ٌ c*)BjND:4-CvCqٵK-[s}W!Տ,EÕkn9^pHpF éƍ|p.Ks?` tfFS(z@bu1y:8';Рv3K^6ӱ|*$,;^M55?5$8 L rVӆ qjت&K[Lt35?@ Knh ,fL>Sy,l fh;;hu' p8>zÌ>{v,\}!eR'ePl4n۶̛w|=v+> ' ; !i*dzydR9ܙ2s9xv}?7@ۏCĺb|3#tЯ0`jvCCys(K/aFB) .T*S/PWH(4(gޥ/UT耐䂻"2yLS0;?u+@U:ڧ+/ǝpZ6mISG92n\wήs@LE6FCb4]s(A^̢6w1&ù#֦>q]j .W"aI[DfG5gdn]՛X~dARtiXl!=z4JjyÚ#KJv"=Q$c6-4ĒC'P^7 fUW^YnBB'h3)XԿ,.(z짞ZОNi߾4lCO`4,*rqrqa!2 쬳AQt*}ױ{ZT aCbH`o"^88eVQ:1i6O^{m|:;s*8ڙB 戃f)h< 4,#Ү8b}"3Kzu\s͛.GD6uIȑХ([NmrPqHykm _*ns+h=rU1@iFx}ѣ pl sʽC3ebJ$<5$eP*Kو&Va)~ߛD@͟7oRkOW_=W5U\.2&Oi(F^[G 'tyY2gZc0B8Ig:jϜb.P)2SIXY3e h]nX&LSHdJy_m+\^ UΘ½CW2`@[#ن ù4w5u?l=X$[`nSG@TgVLi._ΊK:M_<#8XꀧC0<wr,|'F:8P,0>[nY-JO`<($trnU: F³@Y<2\N|ԒzGG)($vTgջ%Knٳh2@F5gSGW!t/7ЖV:ˍ7A*oVgJffZH 7iFH{l\qkxW}ף] vƔHVu”^|q|(^sKaf *pC5%1?rU1O#>Z`NRKp1'<˘Ƽ̰ Y`c d'v-k·G] \ z_{jE0ŹbA1($r"[/Ϸ8d7@Mw&Xnݫf .<ߑni{{azI}W_TrBŀ@&pV%iX)AgRW_m{ϊvxiLxv̭}L@^?a.̡tfdC;P o8LV ̿]feWeԩ).Ub%TBf!6u:2AX'CCW?܄5fy)ҥK.Y1%0|x|KCjXk痄\FǎMpn7x(!7-N`BwR#K?*H||,|`ly0">H#*+Sg2#G&r, 꾡s@ ;;' g"7!+?;7v}Kp5 <'luᰝ;,D=E(m> \8P" 7?CŅ9٥S![7TV˖92jT(Bᆛ˸q]B7ړsBmNqG![M0x tib^ {oCcxOZ0hF2O1KJ*a94 h5;U) zːuqnCC[4P}0)ӧ76$ N?s=1ހM 1ϰվC+?DD`莒n k5vjnc+䬳c&u٘W }j ./WTc;Oq@_{e˧nxٳ%,@n\wMAF*Nߵk L('3ŠG2k2Qo$q1 x#z<)nKɣNm}()%Vh2 {1g)S&c^TxUonh$3>DZn`-\|c]w͕o~j_zECΛ7C kRvZBuw @셣2\9a *<3V}BJfxvU3. F!4%2U۷SѶjZfA9(w9WSxʁ9X;@( p M7^[ٖHpM-?pJ1c0Pj/c;Q}K P22r0Α^XjtobA?$4xb@^$<ny<xR 2|JX>mٲer'Mg: KjeXZAc>rσrl `#N/7%ܿT3XGI$^>psLi,OΎ~">鶕ۭ[*N*fA' ͈:f"4Jjw??[s^o7]Br`9/H ,UK;Wvij5^(+~ NDAn@upK;ѷ_gO m_Wςw߽[HMd3e.3 QݘI 8쫯{Hea5#^: %T]3t6c( 1գ*j 4\/xS{*22Y%@OSYN;+b ۵˃9XEE'}-7ҥ¾#QNK뿹\K`̿>ky‘"-Tg?ԧX쪪wZeȈ$!{]S L>ߒg/kF"Px| ?Kgrtғ컍Iz&lPpN@sbTo@EB6рUY3GV텇88RfN(0h?&HޡGt1˦ME0m&{MȊO^Gܯ:x8竆$̸Włd f*m'V8Q2ؘ1Pמ;X_0BYkUpi2B]y`U1%R7>ڗX.p umR9N zΝfD4-h4Y%*jJ1܍R|#[M>@ҭ[s<8`+$ٌjtEǦy AhÖ1 ƏzL/Жmasn#}v'ԩZq*Ph<'YUov 5|^$-)T1=Hܯza6ݵ·GTMt!OުĒg(jǯeW?NEڷ:?՞S =P-'Q0/G.1c:V2!p䪫Þf=rmWofu8a qӃ0@JJ*¯zD,NOY;H/HBh?(MjDpr1PVj[񹯜/cċYS:P뻷p\)[ 5HJrTϤĉݱmmm* 7nՈmUQZ| ȗ% $ W@U;N}3Ŋ@Fo7+y%++C<|xH|?YY. DJ@b|gv䚚BubG]1Р2夶T{ig[nc$|ƃjyj<3HU@r챝9^~Ү]aާ /šðPױL$@#@ q,颋KvߤӓDCC2Ŗ nWoѥKs%ЖCo/2 >s#3 FI@JO8;"K}R)^O5¢Ex嫓E_VJMv>n)$:dϞCT5&! *W7WȮ]8#[7+8Z%{<9j;kô{UקfrlQ{ +\֦B<]b,_28 ʖ{#HhX`5jȪ gXnWW_aJ!5}׮b'yKgt#%&GlۊcUTlԪ!5ы.v4AҁWlxᒎՉS*r^e侈.YiQ{UTg~-fiAa̙VvVW՚Z+tE su B,Ri"=`*GU[2aBw㣈X1:J?~]^ԩB&h] okM<gכxoq@z 6-]&ڑ3ga4!@5U;XPs\8r8<Ơ _l#V)[c:'G}SYuTPǬG}; F6WW۴Q# {c f#/~k2X`Na "":klؠx Qe[uO1eٿ*|Xm;r*f?˵ݺ5$Ծ9@5-L%V;Qry罌{цRӽ#u^k4%C 5gC^\{UN}jǪA^IW^Yf!&P}+oփ\iUXR ɓ{ȑ'pf-pkx;TY>_QȫW+ ɣjhs&ҽ;Ⱥl盧Xm:eʂ;p<K'x\8 8` &^IH\HVnx.6tX!EBg{lwyp P gyy]{#י~$M`!-f8>uHQѡHc(X"xS%pw7\v`tq 98mo?p@R_ ݗL89|cO:{н_>K a>x{<Ѹv5 4{}Оwjݪ5̜n3<Ϛr˕.^8B*&tj}"&|7lpkl) AHqL'RuWf;%L7//_vQW 벚hzKΪe1]]r( ֫vH97oNtt݌!<=0Kc֬=z?nG`Ѣ-r-!~UTV|%So{ݓ_ThY#дUUPYRP 3^WHc-0k!ᅏ\b./C w ?3^a: ݿWaW`3EQ4l[E\J1+U:HY1ȨZ^8醠"ҧclHYè.Ĺ*gO)EæMj߾HMdF{Xfԡϵ׾@+jtK@F}B+BmU 3ó#4] }vFc[8߭dXq@PiUVV)ȗjxIU[ //mٳWw X})#ܲ$.+vzꙑ2vDJuw/g_4תu>M"]h. t;.=eĐ3,RNfJd|Q'QO_if ;+uctT^}:fcL;._6o-{KdU-Ze:$)aOu<J90xIߍҭ B`(#x=fSHcߐJ}m _FczSzl-TC&t?NIh^GwDU(Q1D^1ٶM}A$pQqmĬ}!g&23 ,*g5_Ja]4C!&*^h#jF|U*/^x$g~P k~/ӦKXyD z:+#9[T_dD)V"yɈc&M2W(zKs-]ii%<啣ɺ_pC)d?W; T4: "i_`xx _>xrL?>X3rð]sp;`Z$ucWKΦ(q**+j׶5 ѣ4jokꥯ+C ,+ u*!%%%cGt/E@Hǩ+q* lu& 4fDϞ-] PUx~Ha@9^y`ۧ(XPzc& .-`L$#PRRr(tbfd,ZG]muQ&8Ψ\< Ctk[*C`^ RGuu8ؖg}0tS(]@|rn$ݝ]|+&AK:4iUts0:}<"%3f F>nT;HV{ d77T 㦷SIzL; \x@֨SX+^H)TI^9n;WQn^H~~SҺ5}z{RN=7?)qJ30mK6]UE۴ürJ&V,ݟTeجx%77[nXnYͭ +/j޲YJ>}@U4$*Mz0omYOjﷇSjUm=#ּK(>f%kuC%R kcbf8FuVu[!Æʵ׎H\Yh8RUu, _KEuxB@M 5/,9x0[.`y&PG%GU 1`<$ TqE[Bkhd;JE=._CzYl6ή\W@n蠥ob Ug.]ԞɮsQpwuBn*Bvm=ɮ]PT;x#}=v$,Lz50V$ǎ,|Z̓WwImplEJu*[o\Hs%qrlzV?`׮bW.k_=I6/Cv8T[D ;WUT?7rqtθXwаSuns/<8z뱜 ڵ{ց~RPTN폭r!NА!mw[뀯"NrimoM Z&=im6Š+6~@o瑍\7ɑ#̗SNiE1%d#Ou^=Pk";7@f ǣO E-)]ՑPmOmjğV ]_|zFFi%/_[W hp8{Ie }d~Zpj-y@]۷^[HZZC'ci?^jW߸󟟣. M:ňZN~QMY3\իU{8Ԟ9{֮ ]4P2yf 9.07Oӟ.1,(1AY.2y9e˂T!|-16ONjcp!z333T-/&W]<ѮM߯L^YBHH rVXYҀi>G$1teBRQ^5{H%@ rɜ9keփ;  UhUA.SVgz3^m)Xw¨O5 <)۶j2 } T-#hۈgV d`K(X{BۭZOv.ž>̑H"!0|gԍ8裓eĈ|ɝdk)+W* @F`M mm=p+wYO'_З؛ƾ̑H%G´j **+!ӦM4Lm:f9ҬYvPb3HR@Zӈasg,vKqq`{%99ρ@..rY}pΊ@ mi+Xu0e/!r%GHH6<|"vo !|bئ9QW@AaaSܸD* ]eJv6o?'H99|G *[rdpco[x$@$@ILJIҹ;6j3FMnD{+n<HH(شjW[5O9;G}FnYfΜ*Md& 6HH!"d~,|^AZpquk!;j &  T"@ zY,:/Z|%lrv 1 ug,Y~ Q)5#bVJ,\dժV:B$@q'v>9ge)@oеks vf)L|Y" HeoW_=O:t3&MG@KyP˪r ̟9 bI/'NT/jXJ .9V L@Ȩw]d@逸^ΔA@HZtha}Gwo!m67 !i$@$($yǟsN?9heezC%ٿ>gMIH @D|f9dH(&ML $ D@ *.*wˌb?|2#QɋI TVzС )+k4pHZSҤQdj.bQ,1v[o ˖mA2fTVeÆҵks;7uO ;uHdmǕgm>X-o@(+zrHzK7ϕnݚKdx`@PZZ)۶ɦM&fҴiVٴ^nǨWESEBJz:wyQE3>ۈl?eVʜ9kc;ߐ@m:,^]^|q2Yf/N.Yf|Wc\D|Pj,c.Ҹq187n)yyY 1CUcej.裵[+u᥿쓯^5K8L4]~ժ$kdr2СYԨ#8[""WQ6V\#%%_rcBUJqq9 3cGRZC@ka)M f  v)s}mSru#YPlU B`̙̊K1H>tt9sȧQ:I'0%3 U4f:GQ{g[dfRZZ :7PZ5{76޿*o/HVv7:q}늷F_U~:s;]6c;хƫMFz Ppرj^e<[LeȽSΝW˻ﮒիwcg:c5>Zf4AwҺu.fMWْ뛙 3O}t>u%%:ֶTO߄v=wJH L.i ٻw|&/S2aBw2}tې: '7|- :t/S & ˘1r=7IM@/ކ-hVo~̦+n} (:<8P)˗k%XO3^:C˰aS۶qNiC2w:o|/7v 'Z`mXUMqjC#ᇿZdW]57D׌O _]W "n|[A RskwZt_wVc|95y!eUVu4 Y9%SӃX:8Gg'/*zS/TTyf~~#W=U՛X}U =Iaa,X,0ᅠ`+Y5..:۫+hsӰDֽҷoqI_\Jd'bMY~4~(u *Һu>wrG+jZGE/\ǟC@#yRiI-gH( 'cb1UP7X)|~e^WrcI@l/ C`8,$`TA54uM  kݛ_JMZ_=}0ō>|}+yrG63u\?p :W_m4\$̱_ӀgDR29$>/qBs NM m#, ŝ|+PR6W]6~bXgX:>N+wo`8Y`cb'9Bp ]R72)C Hq.k֬߆# 7jf;33MN?l}NVRAY`}g{ 2{jãz۹3`od]I@}{ wʽz 't9PD.ˢD۹s3,|;2lX->n.C2|wru_**˧׀$˗2XÃ*}<}h}ZaM;(@iVQQOX{,KMK~ yk$1]8-}qz45O,O?ֽ>//+E{M=Jx [a;@M7[=a)C&Pn<&N3# -&M1B!̬>pp[kj"!/̦RmI:Ϻ.~$X6[̒-r0T&1 ACp?l:z<'\nD2p@n#;,69WijMdҤ_4B;D|߸a5F5ق%=rWY4wWq뒺.{C^ ƒ^gq ik Kq]e9YTaYк@U{ŰSn0=H _kfCI™H,./3|ܿ*]/} :hu v`p {{+} _xk4|:#ֻncP\J?t{*m_[e ċ@Ix| @xm­F?;.oᯃ3 pOtUa/2 "tpg98MФ Kh/:9ՒЊ0J@TųIkvY#xhPugVY {Dug}82 ]kڵY3L޽‰v@ Znd8G}^Xhl=fg0Y= 4Lm)Loig 7M}'o}`U [wo]矫ރ 3/FmR  tp(~cک48VPDT!pt**\l? 0@r//7tyw[?Z/5i Fyœ7guIj_wC^]j~2졇6?YM_TT~".|u!鰞P'G}epTn8JҐ5@./ DCa~D~½AV^u/Ozj !)V:]xg F]9h6~.ׇ-.=UyCH{d" C_ES_)EEԷYb+Woru=1&b& F.)߹\NĠ?J]E,C頠{l~ 6?f9L$@ P7뛠Д)d5dH7s:>F_%O=vݫjs@PNL$@$`ɕWOIE8uFJ ~^r>kJ5S^Ja{ ?]/B`O.d_vꖐUu̘Buգyťo5@ y? ĖYqņM.;Rb[s D @m-w5A~[+*p |,).Q ߓ @ lNÇ9 ~RPr]C(".h$A!ηSZ~opIH >[8?vl@8PԌ {lub#@=5 Sӯm+ӧ3]-jwYoy#WsJ! )$@)E@=VJ}c g>%/TtP75˖SYg-X"_iP'@SIUk:u̯:0POf>v믯M>@ {ct14vU۶&U*N ʞVH!"alq<Ki[? M@ݬRN;m!n>ۙSQj ѣ Wg1 ":PX{K#1 @4Y =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M PiDZ$@$@$  $@$@$`Slq6 DC@4x- ؔvM$@$@ =^K$@$@6%@j @4(DCג M $THOwM$@$@%12-ͩS>`VIHH 8"_}7/G'p8vqb"  I@WRmfbW,? $n_" N] @ PH-}jra"  {P?e2vlƅҸqќ<O=5EnxAAHHHt,/[a?_:vl&rTW  ؏ƘHaaMi w]%6iU/0?IHH#~*(h"'v_b})hӦlFyeuz> ?'  9[r `E y V[AcLIENDB`il32q՗т؀œ Ǚ4UHhc;sb #&9Xyv!L铁e!2P0J:v@]V2Z4  ؂g9.OpZIuX+ GڴeE$h  cQcۀ:рG"߀$dݘ+ ^(ށ рр:ف{U 7 qсX Nсa :~сссуOʾPdYS75%  _( #N9   m՗՗т؀œ Ǚ4UHhc;sb #&9Xyv!L铁e!2P0J:v@]V2Z4  ؂g9.OpZIuX+ GڴeE$h  cQcۀ:рG"߀$dݘ+ ^(ށ рр:ف{U 7 qсX Nсa :~сссуOʾPdYS75%  _( #N9   m՗~~~~~՗Ղ~ꂀ肀ɆL肀͚ոݿ{邀{{}|邀ỵ}~肀}ꮩ肀ςy겚}~肀览ۂ|~ z肀ڲ }|xٍɀ肀փy~~ݨ肀Ԯ华|肀u肀}~Ձ 邀|zρ{邀|w 뚁ꂀvƂx w{⃀ ۜ낀ʏ炀ܑ悀肀H肀ԧʰ肀Б肀肀ꂀ~՗Ղ~~~~~is32zzЀzVbׇ]3Ss@~-et*NamSsHXԠQ{Ϛ滜(ߵp`UM㭨zzzzЀzVbׇ]3Ss@~-et*NamSsHXԠQ{Ϛ滜(ߵp`UM㭨zzɋ}}ڀـ}}z}í}~}⹿y}}u§}}ۢ}}{ڌ~}ޏՋ}~}}}ܿ}}؀}}چؽ}ɋ}l8mks8mkqmapshack-1.5.1/MacOSX/resources/Contents/Resources/de.lproj/locversion.plist000644 001750 000144 00000000644 12572350112 030311 0ustar00oeichlerusers000000 000000 LprojCompatibleVersion 123 LprojLocale de LprojRevisionLevel 1 LprojVersion 123 qmapshack-1.5.1/MacOSX/resources/Contents/Info.plist000644 001750 000144 00000002370 12572350112 023330 0ustar00oeichlerusers000000 000000 CFBundleDevelopmentRegion English CFBundleIconFile QMapShack CFBundleIdentifier org.qlandkarte.QMapShack CFBundleExecutable QMapShack CFBundleName QMapShack CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString APP_VERSION CFBundleVersion BUNDLE_VERSION BuildTime BUNDLE_VERSION BuildHashKey BUNDLE_VERSION NSPrincipalClass NSApplication CSResourcesFileMapped LSRequiresCarbon qmapshack-1.5.1/MacOSX/build-qmapshack.sh000644 001750 000144 00000002525 12616742144 021165 0ustar00oeichlerusers000000 000000 #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $DIR/env-path.sh function installLibraies { if ! [ -x "$(command -v brew)" ]; then ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" fi brew install caskroom/cask/brew-cask brew install qt5 brew install gdal brew install proj } function updateLibraies { brew update brew upgrade caskroom/cask/brew-cask brew info qt5 brew info gdal brew info proj brew outdated # brew upgrade brew upgrade qt5 brew upgrade gdal brew upgrade proj } # mhg update # mhg pull function makeXcodePrj { if [ -d $BUILD_DIR ]; then rm -rf $BUILD_DIR fi mkdir $BUILD_DIR cd $BUILD_DIR cmake -G Xcode -D CMAKE_PREFIX_PATH=$QT_DIR -D CMAKE_OSX_DEPLOYMENT_TARGET=10.5 -D ROUTINO_DEV_PATH=$LIB_ROUTINO_DIR $SRC_QMAPSHACK_DIR } function setFormatter { cp ./clang-format /~/.clang-format } if [[ "$1" == "format" ]]; then setFormatter fi if [[ "$1" == "xcode" ]]; then makeXcodePrj fi if [[ "$1" == "pre-install" ]]; then installLibraies updateLibraies fi # -d -h -c if [[ "$1" == "run" ]]; then $BUILD_BUNDLE_APP_FILE $2 $3 $4 $5 $6 fi if [[ "$1" == "run-bare" ]]; then $BUILD_RELEASE_DIR/$APP_NAME $2 $3 $4 $5 $6 fiqmapshack-1.5.1/MacOSX/bundle.sh000755 001750 000144 00000022420 12616742144 017370 0ustar00oeichlerusers000000 000000 #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $DIR/env-path.sh APP_VERSION=0 BUILD_TIME=$(date +"%y-%m-%dT%H:%M:%S") BUILD_HASH_KEY=0 function buildIcon { rm -rf $BUILD_BIN_DIR/$APP_NAME.iconset mkdir $BUILD_BIN_DIR/$APP_NAME.iconset sips -z 16 16 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_16x16.png sips -z 32 32 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_16x16@2x.png sips -z 32 32 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_32x32.png sips -z 64 64 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_32x32@2x.png sips -z 128 128 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_128x128.png sips -z 256 256 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_128x128@2x.png sips -z 256 256 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_256x256.png sips -z 512 512 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_256x256@2x.png sips -z 512 512 $SRC_RESOURCES_DIR/$APP_NAME.png --out $BUILD_BIN_DIR/$APP_NAME.iconset/icon_512x512.png cp $SRC_RESOURCES_DIR/$APP_NAME.png $BUILD_BIN_DIR/$APP_NAME.iconset/icon_512x512@2x.png iconutil -c icns -o $BUILD_BIN_DIR/$APP_NAME.icns $BUILD_BIN_DIR/$APP_NAME.iconset # rm -r $BUILD_BIN_DIR/$APP_NAME.iconset } function buildAppStructure { # structure bundle # QmapShack.app/ # Contents/ # Info.plist # MacOS/ # QMapShack # Resources/ # QMapShack.icns # Frameworks/ # # PlugIns # if [ ! -f "$BUILD_RELEASE_DIR/$APP_NAME" ]; then cp $BUILD_BUNDLE_APP_DIR/$APP_NAME $BUILD_RELEASE_DIR fi rm -rf $BUILD_BUNDLE_DIR mkdir $BUILD_BUNDLE_DIR cp -R $SRC_RESOURCES_DIR/Contents $BUILD_BUNDLE_DIR if [ -f "$BUILD_BIN_DIR/$APP_NAME.icns" ]; then cp $BUILD_BIN_DIR/$APP_NAME.icns $BUILD_BUNDLE_RES_DIR fi cp $BUILD_RELEASE_DIR/$APP_NAME $BUILD_BUNDLE_APP_DIR mkdir $BUILD_BUNDLE_RES_QM_DIR mkdir $BUILD_BUNDLE_RES_GDAL_DIR mkdir $BUILD_BUNDLE_RES_PROJ_DIR mkdir $BUILD_BUNDLE_RES_ROUTINO_DIR cp $BUILD_DIR/src/*.qm $BUILD_BUNDLE_RES_QM_DIR } function qtDeploy { # -no-strip $QT_DIR/bin/macdeployqt $BUILD_BUNDLE_DIR -always-overwrite -verbose=3 } function printLinkingApp { printLinking $BUILD_BUNDLE_APP_FILE for F in `find $BUILD_BUNDLE_FRW_DIR -type f -type f \( -iname "*.dylib" -o -iname "*.so" \)` do printLinking $F done for F in `find $BUILD_BUNDLE_FRW_DIR/Qt*.framework/Versions/5 -type f -maxdepth 1` do printLinking $F done for F in `find $BUILD_BUNDLE_PLUGIN_DIR -type f -type f \( -iname "*.dylib" -o -iname "*.so" \)` do printLinking $F done } function adjustLinking { for F in `find $BUILD_BUNDLE_PLUGIN_DIR -type f -type f \( -iname "*.dylib" -o -iname "*.so" \)` do adjustLinkQt $F "Qt" done for F in `find $BUILD_BUNDLE_FRW_DIR/Qt*.framework/Versions/5 -type f -maxdepth 1` do adjustLinkQt $F "Qt" done for F in `find $BUILD_BUNDLE_FRW_DIR -type f -type f \( -iname "*.dylib" -o -iname "*.so" \)` do adjustLinkQt $F "Qt" adjustLinkQt $F "libroutino" done adjustLinkQt $BUILD_BUNDLE_APP_FILE "Qt" adjustLinkQt $BUILD_BUNDLE_APP_FILE "libroutino" } function adjustLinkQt { F=$1 L=$2 FREL=${F##*/} #printLinking $F for P in `otool -L $F | awk '{print $1}'` do # replace doubel slashes if [[ "$P" == *//* ]]; then PSLASH=$(echo $P | sed 's,//,/,g') sudo install_name_tool -change $P $PSLASH $F fi if [[ "$P" == *$L* ]]; then LIB=${P##*/} LIB=${LIB%%:} if [[ "$P" == *".framework"* ]]; then LIB_VERSION=Versions/5 LIB=$LIB.framework/$LIB_VERSION/$LIB fi PREL="@executable_path/../Frameworks/$LIB" if [[ "$P" == *"PlugIns"* ]]; then # subdirectory for PlugIns PREL=$(P##PlugIns/) # remove prepart PREL=$(PREL%%/) # remove slash at end LIB=$PREL/$LIB PREL="@executable_path/../PlugIns/$LIB" fi if [[ "$LIB" == *$FREL* ]]; then sudo install_name_tool -id $PREL $F else sudo install_name_tool -change $P $PREL $F fi echo "$FREL > $P - $PREL" fi done #printLinking $F } function copyAdditionalLibraries { cp $LIB_ROUTINO_LIB_DIR/libroutino.so $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtSensors.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtPositioning.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtMultimediaWidgets.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtMultimedia.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtWebKitWidgets.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtOpenGL.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtQuick.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtQml.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtWebChannel.framework $BUILD_BUNDLE_FRW_DIR cp -R $QT_DIR/lib/QtDBus.framework $BUILD_BUNDLE_FRW_DIR } function copyExternalFiles { cp $QT_DIR/translations/*_de.qm $BUILD_BUNDLE_RES_QM_DIR cp $QT_DIR/translations/*_fr.qm $BUILD_BUNDLE_RES_QM_DIR cp $QT_DIR/translations/*_cs.qm $BUILD_BUNDLE_RES_QM_DIR cp $GDAL_DIR/share/gdal/* $BUILD_BUNDLE_RES_GDAL_DIR cp $PROJ_DIR/share/proj/* $BUILD_BUNDLE_RES_PROJ_DIR cp $LIB_ROUTINO_XML_DIR/profiles.xml $BUILD_BUNDLE_RES_ROUTINO_DIR cp $LIB_ROUTINO_XML_DIR/translations.xml $BUILD_BUNDLE_RES_ROUTINO_DIR cp $LIB_ROUTINO_XML_DIR/tagging.xml $BUILD_BUNDLE_RES_ROUTINO_DIR } function printLinking { echo "--------------------" echo "otool $1" otool -L $1 echo "--------------------" } function archiveBundle { ARCHIVE=$(printf "%s/%s-MacOSX_%s.tar.gz" "$BUILD_RELEASE_DIR" "$APP_NAME" "$APP_VERSION") echo $ARCHIVE rm $ARCHIVE cd $BUILD_RELEASE_DIR tar -zcvf $ARCHIVE $APP_BUNDLE cd .. } function extractVersion { # Version CMakeList.txt # set(APPLICATION_VERSION_MAJOR "1") # set(APPLICATION_VERSION_MINOR "3") # set(APPLICATION_VERSION_PATCH "0.libroutino") MAJOR_VERSION=$(sed -n 's/.*APPLICATION_VERSION_MAJOR.*\"\(.*\)\".*/\1/p' $SRC_QMAPSHACK_DIR/CMakeLists.txt) MINOR_VERSION=$(sed -n 's/.*APPLICATION_VERSION_MINOR.*\"\(.*\)\".*/\1/p' $SRC_QMAPSHACK_DIR/CMakeLists.txt) PATCH_VERSION=$(sed -n 's/.*APPLICATION_VERSION_PATCH.*\"\(.*\)\".*/\1/p' $SRC_QMAPSHACK_DIR/CMakeLists.txt) echo "$MAJOR_VERSION $MINOR_VERSION $PATCH_VERSION" APP_VERSION="$MAJOR_VERSION.$MINOR_VERSION.$PATCH_VERSION" } function readRevisionHash { cd $SRC_DIR BUILD_HASH_KEY=$($HG_BIN --debug id -i) if [[ "$BUILD_HASH_KEY" == *"+"* ]]; then read -p "BEWARE - There are uncommited chagnes..." fi } function updateInfoPlist { /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $APP_VERSION" "$BUILD_BUNDLE_CONTENTS_DIR/Info.plist" /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $APP_VERSION" "$BUILD_BUNDLE_CONTENTS_DIR/Info.plist" /usr/libexec/PlistBuddy -c "Set :BuildHashKey $BUILD_HASH_KEY" "$BUILD_BUNDLE_CONTENTS_DIR/Info.plist" /usr/libexec/PlistBuddy -c "Set :BuildTime $BUILD_TIME" "$BUILD_BUNDLE_CONTENTS_DIR/Info.plist" } function buildBinary { rm -rf $BUILD_RELEASE_DIR/$APP_NAME mkdir $BUILD_BIN_DIR mkdir $BUILD_RELEASE_DIR xcodebuild -list -project $BUILD_DIR/$APP_NAME.xcodeproj xcodebuild -project $BUILD_DIR/$APP_NAME.xcodeproj -configuration Release build } function replaceBinary { if [ -d "$BUILD_BUNDLE_APP_DIR" ]; then cp $BUILD_RELEASE_DIR/$APP_NAME $BUILD_BUNDLE_APP_DIR adjustLinkQt $BUILD_BUNDLE_APP_FILE "Qt" adjustLinkQt $BUILD_BUNDLE_APP_FILE "libroutino" fi } if [[ "$1" == "icon" ]]; then buildIcon fi if [[ "$1" == "build" ]]; then buildBinary replaceBinary fi if [[ "$1" == "bundle" ]]; then echo "---extract version -----------------" extractVersion readRevisionHash echo "---build bundle --------------------" buildAppStructure echo "---replace version string ----------" updateInfoPlist echo "---qt deploy tool ------------------" qtDeploy echo "---copy libraries ------------------" copyAdditionalLibraries echo "---copy external files ---------------" copyExternalFiles echo "---adjust linking ------------------" adjustLinking echo "------------------------------------" # chmod a+x $BUILD_BUNDLE_DIR/Contents/Frameworks/* fi if [[ "$1" == "info" ]]; then printLinkingApp fi if [[ "$1" == "info-before" ]]; then printLinking $BUILD_RELEASE_DIR/$APP_NAME printLinking $LIB_ROUTINO_LIB_DIR/libroutino.so fi if [[ "$1" == "archive" ]]; then extractVersion archiveBundle fi qmapshack-1.5.1/MacOSX/build-routino.sh000644 001750 000144 00000007236 12616741641 020721 0ustar00oeichlerusers000000 000000 #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $DIR/env-path.sh SRC_ROUTINO_DIR_B=$ROOT_DIR/routino-lib-src SRC_ROUTINO_DIR_T=$ROOT_DIR/routino-src SRC_ROUTINO_DIR=$SRC_ROUTINO_DIR_T REPO_URL_B=http://routino.org/svn/branches/libroutino/ REPO_URL_T=http://routino.org/svn/trunk/ function updateRoutino { svn revert $SRC_ROUTINO_DIR svn update $SRC_ROUTINO_DIR } function buildRoutino { cd $SRC_ROUTINO_DIR rm $SRC_ROUTINO_DIR/src/*.o rm $SRC_ROUTINO_DIR/src/filedumper rm $SRC_ROUTINO_DIR/src/filedumper-slim rm $SRC_ROUTINO_DIR/src/filedumperx rm $SRC_ROUTINO_DIR/src/libroutino.so rm $SRC_ROUTINO_DIR/src/planetsplitter rm $SRC_ROUTINO_DIR/src/planetsplitter-slim rm $SRC_ROUTINO_DIR/src/router rm $SRC_ROUTINO_DIR/src/router-slim pimpMakefileConf make } function adjustLinking { sudo install_name_tool -id $LIB_ROUTINO_LIB_DIR/libroutino.so $LIB_ROUTINO_LIB_DIR/libroutino.so sudo install_name_tool -id $LIB_ROUTINO_LIB_DIR/routino.so $LIB_ROUTINO_LIB_DIR/routino.so sudo install_name_tool -id $LIB_ROUTINO_LIB_DIR/routino.a $LIB_ROUTINO_LIB_DIR/routino.a sudo install_name_tool -id $LIB_ROUTINO_LIB_DIR/libroutino.a $LIB_ROUTINO_LIB_DIR/libroutino.a } function pimpMakefileConf { sed 's/LDFLAGS_SONAME.*/LDFLAGS_SONAME=-dynamiclib -Wl,-headerpad_max_install_names,-undefined,dynamic_lookup,-compatibility_version,$(SOVERSION),-current_version,$(SOVERSION),-install_name,"libroutino.so" -o "libroutino.so"/' $SRC_ROUTINO_DIR/Makefile.conf> ./makefile.tmp sed 's/LDFLAGS_SLIM_SONAME.*/LDFLAGS_SLIM_SONAME=-dynamiclib -Wl,-headerpad_max_install_names,-undefined,dynamic_lookup,-compatibility_version,$(SOVERSION),-current_version,$(SOVERSION),-install_name,"libroutino-slim.so" -o "libroutino-slim.so"/' ./makefile.tmp > ./makefile2.tmp sed 's/LDFLAGS_LDSO.*/LDFLAGS_LDSO=-Wl/' ./makefile2.tmp > $SRC_ROUTINO_DIR/Makefile.conf rm ./makefile.tmp rm ./makefile2.tmp # Makefile.conf # LDFLAGS_SONAME=-dynamiclib -Wl,-headerpad_max_install_names,-undefined,dynamic_lookup,-compatibility_version,$(SOVERSION),-current_version,$(SOVERSION),-install_name,"libroutino.so" -o "libroutino.so" # LDFLAGS_SLIM_SONAME=-dynamiclib -Wl,-headerpad_max_install_names,-undefined,dynamic_lookup,-compatibility_version,$(SOVERSION),-current_version,$(SOVERSION),-install_name,"libroutino-slim.so" -o "libroutino-slim.so" # LDFLAGS_LDSO=-Wl } function releaseRoutino { rm -R $LIB_ROUTINO_DIR/* mkdir $LIB_ROUTINO_LIB_DIR mkdir $LIB_ROUTINO_H_DIR mkdir $LIB_ROUTINO_XML_DIR cp $SRC_ROUTINO_DIR/src/libroutino.so $LIB_ROUTINO_LIB_DIR cp $SRC_ROUTINO_DIR/src/routino.h $LIB_ROUTINO_H_DIR cp $SRC_ROUTINO_DIR/xml/routino-profiles.xml $LIB_ROUTINO_XML_DIR cp $SRC_ROUTINO_DIR/xml/routino-tagging.xml $LIB_ROUTINO_XML_DIR cp $SRC_ROUTINO_DIR/xml/routino-translations.xml $LIB_ROUTINO_XML_DIR cp $LIB_ROUTINO_LIB_DIR/libroutino.so $LIB_ROUTINO_LIB_DIR/routino cp $LIB_ROUTINO_LIB_DIR/libroutino.so $LIB_ROUTINO_LIB_DIR/routino.so cp $LIB_ROUTINO_LIB_DIR/libroutino.so $LIB_ROUTINO_LIB_DIR/routino.a cp $LIB_ROUTINO_LIB_DIR/libroutino.so $LIB_ROUTINO_LIB_DIR/libroutino.a cp $LIB_ROUTINO_XML_DIR/routino-profiles.xml $LIB_ROUTINO_XML_DIR/profiles.xml cp $LIB_ROUTINO_XML_DIR/routino-tagging.xml $LIB_ROUTINO_XML_DIR/tagging.xml cp $LIB_ROUTINO_XML_DIR/routino-translations.xml $LIB_ROUTINO_XML_DIR/translations.xml } if [[ "$1" == "routino-build" ]]; then updateRoutino buildRoutino releaseRoutino adjustLinking fiqmapshack-1.5.1/MacOSX/env-path.sh000644 001750 000144 00000003052 12616742144 017636 0ustar00oeichlerusers000000 000000 #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SRC_DIR=$(dirname "$DIR") ROOT_DIR=$(dirname "$SRC_DIR") set -a # Set this pathes according to your environment # --------------------------------------------- LIB_BREW_DIR=/usr/local/Cellar # should use /usr/local/opt/qt5 QT_DIR=$LIB_BREW_DIR/qt5/5.5.0 GDAL_DIR=$LIB_BREW_DIR/gdal/1.11.3 PROJ_DIR=$LIB_BREW_DIR/proj/4.9.2 HG_BIN=/Applications/Dev/MacHg.app/Contents/Resources/localhg # --------------------------------------------- APP_NAME=QMapShack APP_BUNDLE=$APP_NAME.app SRC_QMAPSHACK_DIR=$ROOT_DIR/qmapshack-osx SRC_OSX_DIR=$SRC_QMAPSHACK_DIR/MacOSX SRC_RESOURCES_DIR=$SRC_OSX_DIR/resources BUILD_DIR=$ROOT_DIR/build_xcode_osx BUILD_BIN_DIR=$BUILD_DIR/bin BUILD_RELEASE_DIR=$BUILD_BIN_DIR/Release BUILD_BUNDLE_DIR=$BUILD_RELEASE_DIR/$APP_BUNDLE BUILD_BUNDLE_CONTENTS_DIR=$BUILD_BUNDLE_DIR/Contents BUILD_BUNDLE_APP_DIR=$BUILD_BUNDLE_DIR/Contents/MacOS BUILD_BUNDLE_RES_DIR=$BUILD_BUNDLE_DIR/Contents/Resources BUILD_BUNDLE_FRW_DIR=$BUILD_BUNDLE_DIR/Contents/Frameworks BUILD_BUNDLE_PLUGIN_DIR=$BUILD_BUNDLE_DIR/Contents/PlugIns BUILD_BUNDLE_APP_FILE=$BUILD_BUNDLE_APP_DIR/$APP_NAME BUILD_BUNDLE_RES_QM_DIR=$BUILD_BUNDLE_RES_DIR/translations BUILD_BUNDLE_RES_GDAL_DIR=$BUILD_BUNDLE_RES_DIR/gdal BUILD_BUNDLE_RES_PROJ_DIR=$BUILD_BUNDLE_RES_DIR/proj BUILD_BUNDLE_RES_ROUTINO_DIR=$BUILD_BUNDLE_RES_DIR/routino LIB_ROUTINO_DIR=$ROOT_DIR/routino-lib LIB_ROUTINO_LIB_DIR=$ROOT_DIR/routino-lib/lib LIB_ROUTINO_H_DIR=$ROOT_DIR/routino-lib/include LIB_ROUTINO_XML_DIR=$ROOT_DIR/routino-lib/xml set +a qmapshack-1.5.1/MacOSX/build-cask.sh000644 001750 000144 00000003361 12616742144 020135 0ustar00oeichlerusers000000 000000 #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $DIR/env-path.sh GITHUB_USER='kribe' CASK_DIR=$(brew --prefix)/Library/Taps/caskroom/homebrew-cask CASK_SRC_DIR=/$ROOT_DIR/homebrew-cask CASK_QMS_FILE=Casks/qmapshack.rb CASK_QMS='qmapshack' function command { echo "$CASK_DIR/" echo "CREATE BRANCH:" echo "git checkout $CASK_QMS" echo "git pull" echo "git remote -v" echo "git remote add $GITHUB_USER https://github.com/$GITHUB_USER/homebrew-cask" echo "git checkout remotes/origin/$CASK_QMS -b $CASK_QMS" echo "" echo "COMMIT BRANCH:" echo "git status" echo "git commit -m 'QMapShack v $VERSION_FILE' -v" echo "git push $GITHUB_USER $CASK_QMS" echo "git checkout master" echo "" echo "PULL REQUEST ERSTELLEN:" echo "https://github.com/$GITHUB_USER/homebrew-cask" } function updateCask { cp $CASK_SRC_DIR/$CASK_QMS_FILE ./qmapshack-save-src.rb cp $CASK_DIR/$CASK_QMS_FILE ./qmapshack-save.rb cd $CASK_SRC_DIR FILE_NAME=`ls $BUILD_RELEASE_DIR/*.tar.gz | sort -n | head -1` SHASUM=`shasum -a 256 $FILE_NAME | awk '{ print $1 }'` VERSION_FILE="${FILE_NAME%.tar.gz}" VERSION_FILE="${VERSION_FILE#*MacOSX\_}" cat $CASK_SRC_DIR/$CASK_QMS_FILE echo "$FILE_NAME $SHASUM $VERSION_FILE" sed "s/version .*/version '$VERSION_FILE'/" $CASK_SRC_DIR/$CASK_QMS_FILE > ./qmapshack.tmp sed "s/sha256.*/sha256 '$SHASUM'/" ./qmapshack.tmp > $CASK_SRC_DIR/$CASK_QMS_FILE rm ./qmapshack.tmp cat $CASK_SRC_DIR/$CASK_QMS_FILE cp $CASK_SRC_DIR/$CASK_QMS_FILE $CASK_DIR/$CASK_QMS_FILE brew cask install $CASK_QMS brew cask audit $CASK_QMS --download command } if [[ "$1" == "cask" ]]; then updateCask fiqmapshack-1.5.1/Doxyfile000644 001750 000144 00000312143 12616742144 016200 0ustar00oeichlerusers000000 000000 # Doxyfile 1.8.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = MapRoom # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = /home/oeichler/Code/cpp/MapRoom/src/icons/48x48/MapRoom.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = ./doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = ./src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.idl \ *.ddl \ *.odl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.cs \ *.d \ *.php \ *.php4 \ *.php5 \ *.phtml \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.for \ *.tcl \ *.vhd \ *.vhdl \ *.ucf \ *.qsf \ *.as \ *.js # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra stylesheet files is of importance (e.g. the last # stylesheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /

   0.001   -> "%1.4f"
   0.01    -> "%1.3f"
   0.1     -> "%1.2f"
   1       -> "%1.1f"
   10      -> "%2.1f"
   >10000 scientific notation "%1.3e"
   
@param val value to calculate the string on @return a zero terminated format string */ const QString CPlotAxis::fmtdbl( qreal val ) { static QString f; qreal tmp; qreal exponent; qreal residue; if ( val != 0 ) { if ( val < 0 ) { val = -val; } tmp = qLog10( val ); exponent = ( int ) tmp; residue = tmp - exponent; } else { exponent = 0; residue = 0; } if ( abs( ( int ) exponent ) > 5 ) { f = "%1.3e"; } else { if ( exponent >= 0 ) { f = "%" + QString( "%1" ).arg( ( int ) ( exponent + 1 ) ); if ( ( exponent == 0 ) && ( residue < 0 ) ) { f += ".2f"; } else { f += ".1f"; } } else { f = "%1." + QString( "%1" ).arg( ( int ) ( -exponent + 2 ) ) + "f"; } } return f; } int CPlotAxis::getScaleWidth( const QFontMetrics& m ) { if(!valid) { return 0; } if ( scaleWidth > 0 ) { return scaleWidth * m.width( " " ); } int width = 0; int tmp; QString format_single_prec = fmtsgl( interval ); const tic_t * t = ticmark(); while ( t ) { tmp = m.width( QString().sprintf( format_single_prec.toLatin1().data(), t->val ) ); if ( tmp > width ) { width = tmp; } t = ticmark( t ); } return width; } void CPlotAxis::getLimits(qreal& limMin, qreal& limMax, qreal& useMin, qreal& useMax) { limMin = limitMin; limMax = limitMax; useMin = usedMin; useMax = usedMax; } const CPlotAxis::tic_t* CPlotAxis::ticmark( const tic_t * t ) { QString format_single_prec = fmtsgl( interval ); switch ( ticType ) { case eNoTic: return 0; break; case eTicMinMax: if ( t == NULL ) { tic.val = usedMin; firstTic = true; } else if ( firstTic == true ) { tic.val = usedMax; firstTic = false; } else { return 0; } break; case eTicNorm: if ( interval == 0 ) { //qWarning() << "CPlotAxis::ticmark() mode 'norm': interval == 0"; return 0; } if ( t == NULL ) { tic.val = ticStart; } else { tic.val += interval; if ( ( tic.val - usedMax ) > interval / 20 ) { return 0; } } break; case eTicFull: if ( t == NULL ) { tic.val = usedMin; firstTic = true; } else if ( firstTic == true ) { tic.val = ticStart; firstTic = false; } else if ( lastTic == true ) { lastTic = false; return 0; } else { tic.val += interval; if ( ( tic.val - usedMax ) > interval / 20 ) { tic.val = usedMax; lastTic = true; } } break; } tic.lbl.sprintf( format_single_prec.toLatin1(), tic.val ); return &tic; } void CPlotAxis::setScale( const unsigned int pts ) { //if ( !initialized ) //qWarning( "you try to set the scale before defining the min & max value. not very sensible." ); points = pts; scale = pts / ( usedMax - usedMin ); } void CPlotAxis::resetZoom() { setMinMax(limitMin, limitMax); } void CPlotAxis::zoom(bool in, int point) { qreal min, max, p, d, factor; if (in) { factor = 1/1.1; } else { factor = 1.1; } p = pt2val(point); min = (p - usedMin) * (1 - factor) + usedMin; d = min - usedMin * factor; max = usedMax * factor + d; if(qRound(max - min) <= qRound(limitMax - limitMin)) { setMinMax(min, max); move(0); } } void CPlotAxis::move(int delta_pt) { qreal delta_val = pt2val(delta_pt) - pt2val(0); bool f = !(usedMax - usedMin < limitMax - limitMin); if (f ^ (usedMin + delta_val < limitMin)) { delta_val = (limitMin - usedMin); } if (f ^ (usedMax + delta_val > limitMax)) { delta_val = (limitMax - usedMax); } setMinMax(usedMin + delta_val, usedMax + delta_val); } qmapshack-1.5.1/src/plot/CPlotAxis.h000644 001750 000144 00000010633 12622435722 020253 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPLOTAXIS_H #define CPLOTAXIS_H #include class QFontMetrics; class CPlotAxis : public QObject { Q_OBJECT public: CPlotAxis(QObject * parent); virtual ~CPlotAxis(); /// tic mark information structure struct tic_t { tic_t() { val=0; lbl=""; } qreal val; QString lbl; }; ///tic type enum tictype_e { eNoTic, /**< no tics are produced*/ eTicMinMax, /**< only min max tics are produced*/ eTicNorm, /**< tics by interval*/ eTicFull /**< minmax && norm*/ }; ///zoom in/out with a given point as static virtual void zoom(bool in, int point); ///set the desired minimum and maximum value equal to limit values virtual void resetZoom(); ///add delta_pt to min and max values virtual void move(int delta); ///set the desired minimum and maximum value virtual void setMinMax(qreal givenMin, qreal givenMax); ///set the limit minimum and maximum value virtual void setLimits(qreal min, qreal max); ///set the scale factor for a given size in points virtual void setScale(const unsigned int pts); ///calculate format for the given value virtual const QString fmtsgl(qreal val); ///calculate format for the given value virtual const QString fmtdbl(qreal val); ///get the maximum width of a scale with provided fontmetrics virtual int getScaleWidth(const QFontMetrics& m); ///get a new ticmark object virtual const tic_t* ticmark(const tic_t * t = NULL); /// get the total limits and the used ones virtual void getLimits(qreal& limMin, qreal& limMax, qreal& useMin, qreal& useMax); inline int val2pt( qreal val ) const { if ( scale == 0 ) { return 0; } return ( int ) ( ( val - usedMin ) * scale + 0.5 ); } inline qreal pt2val( int pt ) const { if ( scale == 0 ) { return 0; } return ( qreal ) ( ( (qreal)pt - 0.5 ) / scale + usedMin ); } void setAutoscale(bool on) { autoscale = on; } inline tictype_e getTicType() { return ticType; } inline tictype_e setTicType(tictype_e t) { tictype_e old = ticType; ticType = t; return old; } qreal min() const { return usedMin; } qreal max() const { return usedMax; } bool isValid() const { return valid; } protected: virtual void calc(); ///true if axis has been initialized bool initialized = false; ///true if autoscaling bool autoscale = false; bool valid = false; ///scalefactor qreal scale = 1.0; ///the actual applied min value qreal usedMin = 0.0; ///the actual applied max value qreal usedMax = 0.0; qreal limitMin = 0.0; qreal limitMax = 0.0; ///the interval of the ticmarks qreal interval = 0.0; ///start value of the tic marks qreal ticStart = 0; /// this is set to -1 by default /** a value > 0 will override the dynamic value in getScaleWidth(); */ qint32 scaleWidth = 0; ///the ticmark generation type tictype_e ticType = eTicNorm; ///local copy of the last ticmark object tic_t tic; /// used by ticmark() bool firstTic = false; /// used by ticmark() bool lastTic = false; ///points of dimension quint32 points = 0; }; #endif //CPLOTAXIS_H qmapshack-1.5.1/src/plot/CPlotAxisTime.h000644 001750 000144 00000002717 12622435722 021076 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPLOTAXISTIME_H #define CPLOTAXISTIME_H #include "plot/CPlotAxis.h" class CPlotAxisTime : public CPlotAxis { Q_OBJECT public: CPlotAxisTime(QObject * parent); virtual ~CPlotAxisTime(); ///calculate format for the given value const QString fmtsgl(qreal /*val*/) { return strFormat; } ///calculate format for the given value const QString fmtdbl(qreal /*val*/) { return strFormat; } const tic_t* ticmark( const tic_t * t ); protected: void calc(); QString strFormat; }; #endif //CPLOTAXISTIME_H qmapshack-1.5.1/src/plot/ITrack.cpp000644 001750 000144 00000010022 12622435716 020110 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CGisItemTrk.h" #include "helpers/CDraw.h" #include "plot/ITrack.h" #include ITrack::ITrack() { pjtar = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); } ITrack::~ITrack() { if(pjtar) { pj_free(pjtar); } if(pjsrc) { pj_free(pjsrc); } } void ITrack::save(QImage& image) { setSize(image.width(), image.height()); draw(); image = buffer; } void ITrack::setSize(int w, int h) { buffer = QImage(w, h, QImage::Format_ARGB32); updateData(); } void ITrack::setupProjection(const QRectF& boundingBox) { if(pjsrc) { pj_free(pjsrc); pjsrc = 0; } if(boundingBox.top() > (60*DEG_TO_RAD)) { pjsrc = pj_init_plus("+init=epsg:32661"); } else if(boundingBox.bottom() < (-60*DEG_TO_RAD)) { pjsrc = pj_init_plus("+init=epsg:32761"); } else { pjsrc = pj_init_plus("+init=epsg:3857"); } } void ITrack::setTrack(CGisItemTrk * track) { trk = track; setupProjection(trk->getBoundingRect()); updateData(); } void ITrack::setTrack(const QPolygonF& track) { coords = track; setupProjection(coords.boundingRect()); updateData(); } void ITrack::updateData() { if((pjsrc == 0) || (trk == 0 && coords.isEmpty())) { return; } if(trk) { coords.clear(); const CGisItemTrk::trk_t& t = trk->getTrackData(); foreach (const CGisItemTrk::trkseg_t& seg, t.segs) { foreach(const CGisItemTrk::trkpt_t& trkpt, seg.pts) { if(trkpt.flags & CGisItemTrk::trkpt_t::eHidden) { continue; } coords << QPointF(trkpt.lon * DEG_TO_RAD, trkpt.lat * DEG_TO_RAD); } } } line.clear(); foreach(const QPointF &trkpt, coords) { QPointF pt(trkpt.x(), trkpt.y()); pj_transform(pjtar, pjsrc, 1, 0, &pt.rx(), &pt.ry(), 0); line << pt; } QRectF r1 = line.boundingRect(); qreal w1 = r1.width(); qreal h1 = r1.height(); QRectF r2 = buffer.rect(); qreal w2 = r2.width(); qreal h2 = r2.height(); if(qAbs(w1) > qAbs(h1)) { scale.rx() = (w2 - 10) / w1; scale.ry() = -scale.x(); xoff = 0; yoff = -((h2 - 10)/scale.y() + h1) / 2; } else { scale.ry() = (-h2 + 10) / h1; scale.rx() = -scale.y(); xoff = -((w2 - 10)/scale.x() - w1) / 2; yoff = 0; } xoff += r1.left() - 5/scale.x(); yoff += r1.bottom() - 5/scale.y(); needsRedraw = true; } void ITrack::draw(QPainter& p) { if(needsRedraw) { draw(); needsRedraw = false; } p.drawImage(0,0,buffer); } void ITrack::draw() { buffer.fill(Qt::transparent); QPainter p(&buffer); USE_ANTI_ALIASING(p, true); p.setPen(CDraw::penBorderBlack); p.setBrush(QColor(255,255,255,255)); PAINT_ROUNDED_RECT(p,buffer.rect().adjusted(1,1,-1,-1)); p.setPen(QPen(Qt::darkBlue,2/scale.x())); p.scale(scale.x(), scale.y()); p.translate(-xoff,-yoff); p.drawPolyline(line); } qmapshack-1.5.1/src/plot/CPlotAxisTime.cpp000644 001750 000144 00000006250 12622435716 021430 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "plot/CPlotAxisTime.h" #include CPlotAxisTime::CPlotAxisTime(QObject * parent) : CPlotAxis(parent) { } CPlotAxisTime::~CPlotAxisTime() { } void CPlotAxisTime::calc() { int dSec = usedMax - usedMin; ticStart = usedMin; strFormat = "hh:mm:ss"; if(dSec < 0) { qDebug() << "ouch"; valid = false; return; } else if(dSec < 20) { interval = 1; ticStart = usedMin; } else if(dSec < 100) { interval = 5; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 200) { interval = 10; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 600) { interval = 30; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 1200) { interval = 60; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 6000) { interval = 600; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 12000) { interval = 600; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 36000) { interval = 1800; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 72000) { interval = 3600; ticStart = qCeil(usedMin / interval) * interval; } else if(dSec < 216000) { interval = 10800; ticStart = qCeil(usedMin / interval) * interval; } else { qDebug() << "ouch"; valid = false; return; } if ( autoscale ) { usedMin = qFloor( usedMin / interval ) * interval; usedMax = qCeil( usedMax / interval ) * interval; } else { usedMin = usedMin; usedMax = usedMax; } int t1 = ( int )( usedMin / interval + 0.5); ticStart = interval * t1; if ( ticStart < usedMin ) { ticStart += interval; } valid = true; } const CPlotAxis::tic_t* CPlotAxisTime::ticmark( const tic_t * t ) { const tic_t * _tic_ = CPlotAxis::ticmark(t); if(_tic_) { QDateTime time = QDateTime::fromTime_t(tic.val); time.setTimeSpec(Qt::LocalTime); tic.lbl = time.toString(strFormat); } return _tic_; } qmapshack-1.5.1/src/GeoMath.cpp000644 001750 000144 00000036752 12622435716 017323 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2009 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********************************************************************************************/ #include "GeoMath.h" #include "canvas/IDrawContext.h" #include "units/IUnit.h" #include #include #include #include #define PI M_PI #define TWOPI (2*PI) pointDP::pointDP() : used(true), idx(NOIDX) { } segment_t::segment_t() : idx11(NOIDX), idx12(NOIDX), idx21(NOIDX) { } void GPS_Math_DegMinSec_To_Deg(bool sign, const qint32 d, const qint32 m, const qreal s, qreal °) { deg = qAbs(d) + qreal(m) / 60.0 + s / 3600; if(sign) { deg = -deg; } return; } bool GPS_Math_Deg_To_DegMin(qreal v, qint32 *d, qreal *m) { bool sign = v < 0; qint32 deg = qAbs(v); qreal min = (qAbs(v) - deg) * 60.0; *d = deg; *m = min; return sign; } void GPS_Math_DegMin_To_Deg(bool sign, const qint32 d, const qreal m, qreal& deg) { deg = qAbs(d) + m / 60.0; if(sign) { deg = -deg; } return; } // from http://www.movable-type.co.uk/scripts/LatLongVincenty.html qreal GPS_Math_Distance(const qreal u1, const qreal v1, const qreal u2, const qreal v2, qreal& a1, qreal& a2) { qreal cosSigma = 0.0; qreal sigma = 0.0; qreal sinAlpha = 0.0; qreal cosSqAlpha = 0.0; qreal cos2SigmaM = 0.0; qreal sinSigma = 0.0; qreal sinLambda = 0.0; qreal cosLambda = 0.0; qreal a = 6378137.0, b = 6356752.3142, f = 1.0/298.257223563; // WGS-84 ellipsiod qreal L = u2 - u1; qreal U1 = qAtan((1-f) * qTan(v1)); qreal U2 = qAtan((1-f) * qTan(v2)); qreal sinU1 = qSin(U1), cosU1 = qCos(U1); qreal sinU2 = qSin(U2), cosU2 = qCos(U2); qreal lambda = L, lambdaP = 2*PI; unsigned iterLimit = 20; while ((qAbs(lambda - lambdaP) > 1e-12) && (--iterLimit > 0)) { sinLambda = qSin(lambda); cosLambda = qCos(lambda); sinSigma = qSqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda)); if (sinSigma==0) { return 0; // co-incident points } cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = qAtan2(sinSigma, cosSigma); sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma; cosSqAlpha = 1 - sinAlpha * sinAlpha; cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; if (qIsNaN(cos2SigmaM)) { cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6) } qreal C = f/16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); lambdaP = lambda; lambda = L + (1-C) * f * sinAlpha * (sigma + C*sinSigma*(cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); } if (iterLimit==0) { return FP_NAN; // formula failed to converge } qreal uSq = cosSqAlpha * (a*a - b*b) / (b*b); qreal A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); qreal B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); qreal deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); qreal s = b*A*(sigma-deltaSigma); a1 = qAtan2(cosU2 * sinLambda, cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * 360 / TWOPI; a2 = qAtan2(cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda) * 360 / TWOPI; return s; } qreal GPS_Math_Distance(const qreal u1, const qreal v1, const qreal u2, const qreal v2) { qreal cosSigma = 0.0; qreal sigma = 0.0; qreal sinAlpha = 0.0; qreal cosSqAlpha = 0.0; qreal cos2SigmaM = 0.0; qreal sinSigma = 0.0; qreal sinLambda = 0.0; qreal cosLambda = 0.0; qreal a = 6378137.0, b = 6356752.3142, f = 1.0/298.257223563; // WGS-84 ellipsiod qreal L = u2 - u1; qreal U1 = qAtan((1-f) * qTan(v1)); qreal U2 = qAtan((1-f) * qTan(v2)); qreal sinU1 = qSin(U1), cosU1 = qCos(U1); qreal sinU2 = qSin(U2), cosU2 = qCos(U2); qreal lambda = L, lambdaP = 2*PI; unsigned iterLimit = 20; while ((qAbs(lambda - lambdaP) > 1e-12) && (--iterLimit > 0)) { sinLambda = qSin(lambda); cosLambda = qCos(lambda); sinSigma = qSqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda)); if (sinSigma==0) { return 0; // co-incident points } cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = qAtan2(sinSigma, cosSigma); sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma; cosSqAlpha = 1 - sinAlpha * sinAlpha; cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; if (qIsNaN(cos2SigmaM)) { cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6) } qreal C = f/16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); lambdaP = lambda; lambda = L + (1-C) * f * sinAlpha * (sigma + C*sinSigma*(cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); } if (iterLimit==0) { return FP_NAN; // formula failed to converge } qreal uSq = cosSqAlpha * (a*a - b*b) / (b*b); qreal A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))); qreal B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))); qreal deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); qreal s = b*A*(sigma-deltaSigma); return s; } qreal GPS_Math_DistanceQuick(const qreal u1, const qreal v1, const qreal u2, const qreal v2) { qreal dU = u2 - u1; // lambda qreal dV = v2 - v1; // roh qreal d = 2*qAsin(qSqrt(qSin(dV/2) * qSin(dV/2) + qCos(v1) * qCos(v2) * qSin(dU/2) * qSin(dU/2))); return 6371010 * d; } void GPS_Math_Wpt_Projection(const qreal lon1, const qreal lat1, const qreal distance, const qreal bearing, qreal& lon2, qreal& lat2) { qreal d = distance / 6378130.0; lat2 = qAsin(qSin(lat1) * qCos(d) + qCos(lat1) * qSin(d) * qCos(-bearing)); lon2 = qCos(lat1) == 0 ? lon1 : fmod(lon1 - qAsin(qSin(-bearing) * qSin(d) / qCos(lat1)) + PI, TWOPI) - PI; } qreal GPS_Math_distPointLine3D(point3D& x1, point3D& x2, point3D& x0) { point3D v1, v2, v3, v1x2; qreal a1x2, a3; // (x0 - x1) v1.x = x0.x - x1.x; v1.y = x0.y - x1.y; v1.z = x0.z - x1.z; // (x0 - x2) v2.x = x0.x - x2.x; v2.y = x0.y - x2.y; v2.z = x0.z - x2.z; // (x2 - x1) v3.x = x2.x - x1.x; v3.y = x2.y - x1.y; v3.z = x2.z - x1.z; // (x0 - x1)x(x0 - x2) v1x2.x = v1.y * v2.z - v1.z * v2.y; v1x2.y = v1.z * v2.x - v1.x * v2.z; v1x2.z = v1.x * v2.y - v1.y * v2.x; // |(x0 - x1)x(x0 - x2)| a1x2 = v1x2.x*v1x2.x + v1x2.y*v1x2.y + v1x2.z*v1x2.z; // |(x2 - x1)| a3 = v3.x*v3.x + v3.y*v3.y + v3.z*v3.z; return qSqrt(a1x2/a3); } static inline qreal sqr(qreal a) { return a*a; } static inline qreal sqrlen(const QPointF &a) { return sqr(a.x()) + sqr(a.y()); } qreal GPS_Math_DistPointPolyline(const QPolygonF &points, const QPointF &q) { const qint32 count = points.size(); if(count == 0) { return NOFLOAT; } QPointF b = points[0]; QPointF dbq = b - q; qreal dist = sqrlen(dbq); for (qint32 i = 1; i1. { current_dist = sqrlen(dbq); } if (current_dist &line, qreal d) { if(line.count() < 3) { return; } QStack stack; stack << segment(0, line.size() - 1); while(!stack.isEmpty()) { qint32 idx = NOIDX; segment seg = stack.pop(); pointDP& x1 = line[seg.idx1]; pointDP& x2 = line[seg.idx2]; qreal dmax = d; for(qint32 i = seg.idx1 + 1; i < seg.idx2; i++) { qreal distance = GPS_Math_distPointLine3D(x1, x2, line[i]); if(distance > dmax) { idx = i; dmax = distance; } } if(idx > 0) { stack << segment(seg.idx1, idx); stack << segment(idx, seg.idx2); } else { for(qint32 i = seg.idx1 + 1; i < seg.idx2; i++) { line[i].used = false; } } } } QPointF GPS_Math_Wpt_Projection(const QPointF& pt1, qreal distance, qreal bearing) { QPointF pt2; qreal d = distance / 6378130.0; qreal lon1 = pt1.x(); qreal lat1 = pt1.y(); qreal lat2 = qAsin(qSin(lat1) * qCos(d) + qCos(lat1) * qSin(d) * qCos(-bearing)); qreal lon2 = qCos(lat1) == 0 ? lon1 : fmod(lon1 - qAsin(qSin(-bearing) * qSin(d) / qCos(lat1)) + M_PI, (2*M_PI)) - M_PI; pt2.rx() = lon2; pt2.ry() = lat2; return pt2; } bool GPS_Math_LineCrossesRect(const QPointF &p1, const QPointF &p2, const QRectF &rect) { // the trivial case if(rect.contains(p1) || rect.contains(p2)) { return true; } qreal slope = qreal(p2.y() - p1.y()) / (p2.x() - p1.x()); qreal offset = p1.y() - slope * p1.x(); qreal y1 = offset + slope * rect.left(); qreal y2 = offset + slope * rect.right(); if((y1 < rect.top()) && (y2 < rect.top())) { return false; } else if((y1 > rect.bottom()) && (y2 > rect.bottom())) { return false; } return true; } void GPS_Math_SubPolyline(const QPointF& pt1, const QPointF& pt2, qint32 threshold, const QPolygonF& pixel, segment_t &result) { qint32 i, len; projXY p1, p2; qreal dx,dy; // delta x and y defined by p1 and p2 qreal d_p1_p2; // distance between p1 and p2 qreal u; // ratio u the tangent point will divide d_p1_p2 qreal x,y; // coord. (x,y) of the point on line defined by [p1,p2] close to pt qreal distance; // the distance to the polyline qreal shortest1 = threshold; qreal shortest2 = threshold; qint32 idx11 = NOIDX, idx21 = NOIDX, idx12 = NOIDX; QPointF pt11; QPointF pt21; len = pixel.size(); // find points on line closest to pt1 and pt2 for(i=1; i= 0.0 && u <= 1.0) { x = p1.u + u * dx; y = p1.v + u * dy; distance = qSqrt((x - pt1.x())*(x - pt1.x()) + (y - pt1.y())*(y - pt1.y())); if(distance < shortest1) { idx11 = i - 1; idx12 = i; pt11.setX(x); pt11.setY(y); shortest1 = distance; } } // find point on line closest to pt2 u = ((pt2.x() - p1.u) * dx + (pt2.y() - p1.v) * dy) / (d_p1_p2 * d_p1_p2); if(u >= 0.0 && u <= 1.0) { x = p1.u + u * dx; y = p1.v + u * dy; distance = qSqrt((x - pt2.x())*(x - pt2.x()) + (y - pt2.y())*(y - pt2.y())); if(distance < shortest2) { idx21 = i - 1; pt21.setX(x); pt21.setY(y); shortest2 = distance; } } } // if 1st point can't be found test for distance to both ends if(idx11 == NOIDX) { QPointF px = pixel.first(); distance = qSqrt((qreal)((px.x() - pt1.x())*(px.x() - pt1.x()) + (px.y() - pt1.y())*(px.y() - pt1.y()))); if(distance < (threshold<<1)) { idx11 = 0; idx12 = 1; pt11 = px; } else { px = pixel.last(); distance = qSqrt((qreal)((px.x() - pt1.x())*(px.x() - pt1.x()) + (px.y() - pt1.y())*(px.y() - pt1.y()))); if(distance < (threshold<<1)) { idx11 = pixel.size() - 2; idx12 = pixel.size() - 1; pt11 = px; } } } // if 2nd point can't be found test for distance to both ends if(idx21 == NOIDX) { QPointF px = pixel.first(); distance = qSqrt((qreal)((px.x() - pt2.x())*(px.x() - pt2.x()) + (px.y() - pt2.y())*(px.y() - pt2.y()))); if(distance < (threshold<<1)) { idx21 = 0; pt21 = px; } else { px = pixel.last(); distance = qSqrt((qreal)((px.x() - pt2.x())*(px.x() - pt2.x()) + (px.y() - pt2.y())*(px.y() - pt2.y()))); if(distance < (threshold<<1)) { idx21 = pixel.size() - 2; pt21 = px; } } } // qDebug() << pixel.size() << idx11 << idx12 << idx21 << pt1 << pt2 << pt11 << pt21; result.idx11 = idx11; result.idx12 = idx12; result.idx21 = idx21; result.px1 = pt11; result.px2 = pt21; } void segment_t::apply(const QPolygonF& coords, const QPolygonF& pixel, QPolygonF& segCoord, QPolygonF& segPixel, IDrawContext * context) { QPointF pt1 = px1; QPointF pt2 = px2; context->convertPx2Rad(pt1); context->convertPx2Rad(pt2); if(idx11 != NOIDX && idx21 != NOIDX) { if(idx12 == idx21) { segPixel.push_back(pixel[idx12]); segCoord.push_back(coords[idx12]); } else if(idx11 < idx21) { for(int i = idx12; i <= idx21; i++) { segPixel.push_back(pixel[i]); segCoord.push_back(coords[i]); } } else if(idx11 > idx21) { for(int i = idx11; i > idx21; i--) { segPixel.push_back(pixel[i]); segCoord.push_back(coords[i]); } } } } qmapshack-1.5.1/src/CMainWindow.cpp000644 001750 000144 00000063503 12623413746 020150 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CAbout.h" #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "config.h" #include "dem/CDemDraw.h" #include "dem/CDemList.h" #include "gis/CGisWidget.h" #include "gis/IGisLine.h" #include "gis/WptIcons.h" #include "gis/db/CSetupWorkspace.h" #include "gis/prj/IGisProject.h" #include "gis/trk/CKnownExtension.h" #include "helpers/CProgressDialog.h" #include "helpers/CSettings.h" #include "map/CMapDraw.h" #include "map/CMapItem.h" #include "map/CMapList.h" #include "tool/CImportDatabase.h" #include "tool/CMapVrtBuilder.h" #include "tool/CRoutinoDatabaseBuilder.h" #include "units/CCoordFormatSetup.h" #include "units/CTimeZoneSetup.h" #include "units/CUnitsSetup.h" #include "units/IUnit.h" #include "version.h" #include #include #include #ifdef WIN32 #include "device/CDeviceWatcherWindows.h" #include #include #include #include #include #endif // WIN32 CMainWindow * CMainWindow::pSelf = 0; CMainWindow::CMainWindow() { SETTINGS; pSelf = this; setupUi(this); setWindowTitle(WHAT_STR); initWptIcons(); IUnit::self().setUnitType((IUnit::type_e)cfg.value("MainWindow/units",IUnit::eTypeMetric).toInt(), this); CKnownExtension::init(IUnit::self()); gisWidget = new CGisWidget(menuProject, this); dockGis->setWidget(gisWidget); // start ---- restore window geometry ----- if ( cfg.contains("MainWindow/geometry")) { restoreGeometry(cfg.value("MainWindow/geometry").toByteArray()); } else { setGeometry(0,0,800,600); } if ( cfg.contains("MainWindow/state")) { restoreState(cfg.value("MainWindow/state").toByteArray()); } // end ---- restore window geometry ----- connect(actionAbout, SIGNAL(triggered()), this, SLOT(slotAbout())); connect(actionHelp, SIGNAL(triggered()), this, SLOT(slotHelp())); connect(actionAddMapView, SIGNAL(triggered()), this, SLOT(slotAddCanvas())); connect(actionCloneMapView, SIGNAL(triggered()), this, SLOT(slotCloneCanvas())); connect(actionShowScale, SIGNAL(changed()), this, SLOT(slotUpdateCurrentWidget())); connect(actionShowGrid, SIGNAL(changed()), this, SLOT(update())); connect(actionPOIText, SIGNAL(changed()), this, SLOT(slotUpdateCurrentWidget())); connect(actionMapToolTip, SIGNAL(changed()), this, SLOT(slotUpdateCurrentWidget())); connect(actionNightDay, SIGNAL(changed()), this, SLOT(slotUpdateCurrentWidget())); connect(actionProfileIsWindow, SIGNAL(toggled(bool)), this, SLOT(slotSetProfileMode(bool))); connect(actionSetupMapFont, SIGNAL(triggered()), this, SLOT(slotSetupMapFont())); connect(actionSetupGrid, SIGNAL(triggered()), this, SLOT(slotSetupGrid())); connect(actionSetupMapPaths, SIGNAL(triggered()), this, SLOT(slotSetupMapPath())); connect(actionSetupDEMPaths, SIGNAL(triggered()), this, SLOT(slotSetupDemPath())); connect(actionSetupMapView, SIGNAL(triggered()), this, SLOT(slotSetupMapView())); connect(actionSetupTimeZone, SIGNAL(triggered()), this, SLOT(slotSetupTimeZone())); connect(actionSetupUnits, SIGNAL(triggered()), this, SLOT(slotSetupUnits())); connect(actionSetupWorkspace, SIGNAL(triggered()), this, SLOT(slotSetupWorkspace())); connect(actionSetupCoordFormat, SIGNAL(triggered(bool)), this, SLOT(slotSetupCoordFormat())); connect(actionImportDatabase, SIGNAL(triggered()), this, SLOT(slotImportDatabase())); connect(actionSaveGISData, SIGNAL(triggered()), gisWidget, SLOT(slotSaveAll())); connect(actionLoadGISData, SIGNAL(triggered()), this, SLOT(slotLoadGISData())); connect(actionVrtBuilder, SIGNAL(triggered()), this, SLOT(slotBuildVrt())); connect(actionStoreView, SIGNAL(triggered()), this, SLOT(slotStoreView())); connect(actionLoadView, SIGNAL(triggered()), this, SLOT(slotLoadView())); connect(actionClose, SIGNAL(triggered()), this, SLOT(close())); connect(actionCreateRoutinoDatabase, SIGNAL(triggered()), this, SLOT(slotCreateRoutinoDatabase())); connect(actionPrintMap, SIGNAL(triggered()), this, SLOT(slotPrintMap())); connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(slotTabCloseRequest(int))); connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabCanvas(int))); connect(tabMaps, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabMaps(int))); connect(tabDem, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabDem(int))); cfg.beginGroup("Canvas"); CMapDraw::loadMapPath(cfg); CDemDraw::loadDemPath(cfg); cfg.beginGroup("Views"); QStringList names = cfg.childGroups(); foreach(const QString &name, names) { CCanvas * view = new CCanvas(tabWidget, name); tabWidget->addTab(view, view->objectName()); connect(view, SIGNAL(sigMousePosition(QPointF,qreal)), this, SLOT(slotMousePosition(QPointF, qreal))); cfg.beginGroup(name); view->loadConfig(cfg); cfg.endGroup(); // name } if(names.isEmpty()) { CCanvas * view = new CCanvas(tabWidget,""); tabWidget->addTab(view, view->objectName()); connect(view, SIGNAL(sigMousePosition(QPointF, qreal)), this, SLOT(slotMousePosition(QPointF, qreal))); } cfg.endGroup(); // Views actionShowScale->setChecked(cfg.value("isScaleVisible", true).toBool()); actionShowGrid->setChecked(cfg.value("isGridVisible", true).toBool()); actionPOIText->setChecked(cfg.value("POIText", true).toBool()); actionMapToolTip->setChecked(cfg.value("MapToolTip", true).toBool()); actionNightDay->setChecked(cfg.value("isNight", false).toBool()); actionFlipMouseWheel->setChecked(cfg.value("flipMouseWheel", false).toBool()); actionProfileIsWindow->setChecked(cfg.value("profileIsWindow", false).toBool()); mapFont = cfg.value("mapFont", font()).value(); tabWidget->setCurrentIndex(cfg.value("visibleCanvas",0).toInt()); cfg.endGroup(); // Canvas QByteArray tz; IUnit::tz_mode_e tzmode; bool useShortFormat; tz = cfg.value("Units/timezone", "UTC").toByteArray(); tzmode = (IUnit::tz_mode_e)cfg.value("Units/timezone/mode", IUnit::eTZUtc).toInt(); useShortFormat = cfg.value("Units/time/useShortFormat", false).toBool(); IUnit::setTimeZoneSetup(tzmode, tz, useShortFormat); IUnit::coord_format_e coordFormat; coordFormat = (IUnit::coord_format_e)cfg.value("Units/coordFormat", IUnit::eCoordFormat1).toInt(); IUnit::setCoordFormat(coordFormat); QStatusBar * status = statusBar(); lblPosWGS84 = new QLabel(status); status->addPermanentWidget(lblPosWGS84); lblElevation = new QLabel(status); status->addPermanentWidget(lblElevation); lblPosGrid = new QLabel(status); status->addPermanentWidget(lblPosGrid); menuWindow->addAction(dockMaps->toggleViewAction()); menuWindow->addAction(dockDem->toggleViewAction()); menuWindow->addAction(dockGis->toggleViewAction()); menuWindow->addAction(dockRte->toggleViewAction()); loadGISData(qlOpts->arguments); } CMainWindow::~CMainWindow() { int cnt = 0; SETTINGS; cfg.setValue("MainWindow/state", saveState()); cfg.setValue("MainWindow/geometry", saveGeometry()); cfg.setValue("MainWindow/units", IUnit::self().type); /* The "Canvas" section will hold all settings global to all views and "Views" section containing a subsection for each view. */ cfg.beginGroup("Canvas"); QList allViews; QList allOtherTabs; // save setup of all views cfg.beginGroup("Views"); // remove all previous setups in this section first cfg.remove(""); for(int i = 0; i < tabWidget->count(); i++) { CCanvas * view = dynamic_cast(tabWidget->widget(i)); if(view == 0) { allOtherTabs << tabWidget->widget(i); continue; } cnt++; // save views cfg.beginGroup(view->objectName()); view->saveConfig(cfg); cfg.endGroup(); allViews << view; } cfg.endGroup(); // Views cfg.setValue("visibleCanvas", tabWidget->currentIndex()); cfg.setValue("isScaleVisible", actionShowScale->isChecked()); cfg.setValue("isGridVisible", actionShowGrid->isChecked()); cfg.setValue("POIText", actionPOIText->isChecked()); cfg.setValue("MapToolTip", actionMapToolTip->isChecked()); cfg.setValue("isNight", actionNightDay->isChecked()); cfg.setValue("flipMouseWheel", actionFlipMouseWheel->isChecked()); cfg.setValue("profileIsWindow",actionProfileIsWindow->isChecked()); cfg.setValue("mapFont", mapFont); CMapDraw::saveMapPath(cfg); CDemDraw::saveDemPath(cfg); cfg.endGroup(); // Canvas /* Delete all widgets in the tab widget other than views. The IPlot objects in a track detail dialog send update events to the view on destruction. So it is important that these are destroyed first. */ qDeleteAll(allOtherTabs); /* Delete all canvas objects now to make sure they are destroyed before all other objects. This allows children of the canvas to access central objects like CGisWidget safely upon their destruction. (e.g. CMouseRangeTrk to reset it's track's draw mode by key) */ qDeleteAll(allViews); QByteArray tz; IUnit::tz_mode_e tzmode; bool useShortFormat; IUnit::getTimeZoneSetup(tzmode, tz, useShortFormat); cfg.setValue("Units/timezone", tz); cfg.setValue("Units/timezone/mode", tzmode); cfg.setValue("Units/time/useShortFormat", useShortFormat); IUnit::coord_format_e coordFormat; IUnit::getCoordFormat(coordFormat); cfg.setValue("Units/coordFormat", coordFormat); } QWidget * CMainWindow::getBestWidgetForParent() { QWidget * w = CProgressDialog::self(); if(w) { return w; } w = self().getVisibleCanvas(); if(w) { return w; } return &self(); } bool CMainWindow::isScaleVisible() { return actionShowScale->isChecked(); } bool CMainWindow::isGridVisible() { return actionShowGrid->isChecked(); } bool CMainWindow::isNight() { return actionNightDay->isChecked(); } bool CMainWindow::isPOIText() { return actionPOIText->isChecked(); } bool CMainWindow::isMapToolTip() { return actionMapToolTip->isChecked(); } bool CMainWindow::flipMouseWheel() { return actionFlipMouseWheel->isChecked(); } bool CMainWindow::profileIsWindow() { return actionProfileIsWindow->isChecked(); } void CMainWindow::addMapList(CMapList * list, const QString &name) { tabMaps->addTab(list,name); } void CMainWindow::addDemList(CDemList * list, const QString &name) { tabDem->addTab(list,name); } void CMainWindow::addWidgetToTab(QWidget * w) { if(tabWidget->indexOf(w) == NOIDX) { tabWidget->addTab(w, w->objectName()); } tabWidget->setCurrentWidget(w); } CCanvas * CMainWindow::getVisibleCanvas() { return dynamic_cast(tabWidget->currentWidget()); } void CMainWindow::zoomCanvasTo(const QRectF rect) { CCanvas * canvas = getVisibleCanvas(); if(canvas) { canvas->zoomTo(rect); } } qreal CMainWindow::getEelevationAt(const QPointF& pos) { CCanvas * canvas = getVisibleCanvas(); if(canvas) { return canvas->getElevationAt(pos); } return NOFLOAT; } void CMainWindow::getEelevationAt(SGisLine &line) { CCanvas * canvas = getVisibleCanvas(); if(canvas) { canvas->getElevationAt(line); } else { for(int i = 0; i < tabWidget->count(); i++) { canvas = dynamic_cast(tabWidget->widget(i)); if(canvas) { canvas->getElevationAt(line); return; } } for(int i = 0; i < line.size(); i++) { line[i].resetElevation(); } } } void CMainWindow::getEelevationAt(const QPolygonF &pos, QPolygonF& ele) { CCanvas * canvas = getVisibleCanvas(); if(canvas) { canvas->getElevationAt(pos, ele); } else { for(int i = 0; i < tabWidget->count(); i++) { canvas = dynamic_cast(tabWidget->widget(i)); if(canvas) { canvas->getElevationAt(pos, ele); return; } } ele.clear(); } } void CMainWindow::slotAbout() { CAbout dlg(this); dlg.exec(); } void CMainWindow::slotHelp() { QDesktopServices::openUrl(QUrl("https://bitbucket.org/maproom/qmapshack/wiki/DocMain")); } void CMainWindow::slotAddCanvas() { int i, cnt = 0; for(i = 0; i < tabWidget->count(); i++) { CCanvas * canvas = dynamic_cast(tabWidget->widget(i)); if(canvas == 0) { continue; } cnt++; } CCanvas * canvas = new CCanvas(tabWidget,""); tabWidget->addTab(canvas, canvas->objectName()); connect(canvas, SIGNAL(sigMousePosition(QPointF, qreal)), this, SLOT(slotMousePosition(QPointF, qreal))); tabWidget->setCurrentWidget(canvas); } void CMainWindow::slotCloneCanvas() { CCanvas * source = getVisibleCanvas(); if(source == 0) { return; } QTemporaryFile temp; temp.open(); temp.close(); QSettings view(temp.fileName(), QSettings::IniFormat); view.clear(); source->saveConfig(view); slotAddCanvas(); CCanvas * target = getVisibleCanvas(); if(target == 0) { return; } target->loadConfig(view); target->slotTriggerCompleteUpdate(CCanvas::redraw_e::eRedrawGis); SETTINGS; cfg.beginGroup("Canvas"); cfg.beginGroup("Views"); cfg.beginGroup(target->objectName()); target->saveConfig(cfg); cfg.endGroup(); cfg.endGroup(); cfg.endGroup(); } void CMainWindow::slotTabCloseRequest(int i) { QMutexLocker lock(&CMapItem::mutexActiveMaps); QWidget * w = tabWidget->widget(i); delete w; } void CMainWindow::slotCurrentTabCanvas(int i) { QString name = tabWidget->tabText(i); for(int n = 0; n < tabMaps->count(); n++) { if(tabMaps->tabText(n) == name) { tabMaps->setCurrentIndex(n); break; } } for(int n = 0; n < tabDem->count(); n++) { if(tabDem->tabText(n) == name) { tabDem->setCurrentIndex(n); break; } } for(int n = 0; n < tabWidget->count(); n++) { CCanvas * canvas = dynamic_cast(tabWidget->widget(n)); if(canvas) { if(n == i) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); canvas->showProfile(true); } else { canvas->showProfile(false); } } } } void CMainWindow::slotCurrentTabMaps(int i) { QString name = tabMaps->tabText(i); for(int n = 0; n < tabWidget->count(); n++) { if(tabWidget->tabText(n) == name) { tabWidget->setCurrentIndex(n); break; } } for(int n = 0; n < tabDem->count(); n++) { if(tabDem->tabText(n) == name) { tabDem->setCurrentIndex(n); break; } } } void CMainWindow::slotCurrentTabDem(int i) { QString name = tabMaps->tabText(i); for(int n = 0; n < tabWidget->count(); n++) { if(tabWidget->tabText(n) == name) { tabWidget->setCurrentIndex(n); break; } } for(int n = 0; n < tabMaps->count(); n++) { if(tabMaps->tabText(n) == name) { tabMaps->setCurrentIndex(n); break; } } } void CMainWindow::slotMousePosition(const QPointF& pos, qreal ele) { QString str; IUnit::degToStr(pos.x(), pos.y(), str); lblPosWGS84->setText(str); if(ele != NOFLOAT) { QString val, unit; IUnit::self().meter2elevation(ele, val, unit); lblElevation->setText(tr("Ele: %1%2").arg(val).arg(unit)); lblElevation->show(); } else { lblElevation->hide(); } if(actionShowGrid->isChecked()) { CCanvas * canvas = getVisibleCanvas(); if(canvas) { QString str; lblPosGrid->show(); canvas->convertGridPos2Str(pos, str, false); lblPosGrid->setText(tr("[Grid: %1]").arg(str)); } } else { lblPosGrid->hide(); } } void CMainWindow::slotUpdateCurrentWidget() { CCanvas * canvas = getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawAll); return; } QWidget * w = tabWidget->currentWidget(); if(w) { w->update(); return; } } void CMainWindow::slotSetupMapFont() { bool ok = false; QFont f = QFontDialog::getFont(&ok, mapFont, this); if(ok) { mapFont = f; QWidget * w = tabWidget->currentWidget(); if(w) { w->update(); } } } void CMainWindow::slotSetupGrid() { CCanvas * canvas = getVisibleCanvas(); if(canvas == 0) { return; } canvas->setupGrid(); } void CMainWindow::slotSetupMapPath() { CMapDraw::setupMapPath(); } void CMainWindow::slotSetupDemPath() { CDemDraw::setupDemPath(); } void CMainWindow::slotSetupMapView() { CCanvas * canvas = getVisibleCanvas(); if(canvas == 0) { return; } canvas->setup(); } void CMainWindow::slotSetupTimeZone() { CTimeZoneSetup dlg(this); dlg.exec(); } void CMainWindow::slotSetupUnits() { CUnitsSetup dlg(this); dlg.exec(); if(QDialog::Accepted == dlg.result()) { CKnownExtension::init(IUnit::self()); } } void CMainWindow::slotSetupWorkspace() { CSetupWorkspace dlg(this); dlg.exec(); } void CMainWindow::slotSetupCoordFormat() { CCoordFormatSetup dlg(this); dlg.exec(); } void CMainWindow::slotImportDatabase() { CImportDatabase * widget = new CImportDatabase(this); addWidgetToTab(widget); } void CMainWindow::slotBuildVrt() { CMapVrtBuilder * widget = new CMapVrtBuilder(this); addWidgetToTab(widget); } void CMainWindow::slotCreateRoutinoDatabase() { CRoutinoDatabaseBuilder * widget = new CRoutinoDatabaseBuilder(this); addWidgetToTab(widget); } void CMainWindow::slotLoadGISData() { SETTINGS; QString path = cfg.value("Paths/lastGisPath", QDir::homePath()).toString(); QString filter = cfg.value("Paths/lastGisFilter", IGisProject::filedialogAllSupported).toString(); QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Load GIS Data..."), path, IGisProject::filedialogLoadFilters, &filter); if(filenames.isEmpty()) { return; } loadGISData(filenames); path = QFileInfo(filenames.first()).absolutePath(); cfg.setValue("Paths/lastGisPath", path); cfg.setValue("Paths/lastGisFilter", filter); } void CMainWindow::loadGISData(const QStringList& filenames) { foreach(const QString &filename, filenames) { gisWidget->loadGisProject(filename); } } void CMainWindow::slotStoreView() { CCanvas * canvas = getVisibleCanvas(); if(canvas == 0) { return; } SETTINGS; QString path = cfg.value("Paths/lastViewPath", QDir::homePath()).toString(); QString filename = QFileDialog::getSaveFileName( this, tr("Select output file"), path,"QMapShack View (*.view)"); if(filename.isEmpty()) { return; } QFileInfo fi(filename); if(fi.suffix().toLower() != "view") { filename += ".view"; } QSettings view(filename, QSettings::IniFormat); view.clear(); canvas->saveConfig(view); path = fi.absolutePath(); cfg.setValue("Paths/lastViewPath", path); } void CMainWindow::slotLoadView() { SETTINGS; QString path = cfg.value("Paths/lastViewPath", QDir::homePath()).toString(); QString filename = QFileDialog::getOpenFileName(this, tr("Select file to load"), path, "QMapShack View (*.view)"); if(filename.isEmpty()) { return; } slotAddCanvas(); CCanvas * canvas = getVisibleCanvas(); if(canvas == 0) { return; } QSettings view(filename, QSettings::IniFormat); canvas->loadConfig(view); cfg.beginGroup("Canvas"); cfg.beginGroup("Views"); cfg.beginGroup(canvas->objectName()); canvas->saveConfig(cfg); cfg.endGroup(); cfg.endGroup(); cfg.endGroup(); QFileInfo fi(filename); path = fi.absolutePath(); cfg.setValue("Paths/lastViewPath", path); } void CMainWindow::slotSetProfileMode(bool on) { for(int i = 0; i < tabWidget->count(); i++) { CCanvas * view = dynamic_cast(tabWidget->widget(i)); if(view == 0) { continue; } view->showProfileAsWindow(on); } } void CMainWindow::slotPrintMap() { CCanvas * canvas = getVisibleCanvas(); if(canvas == 0) { return; } canvas->setMousePrint(); } #ifdef WIN32 static void sendDeviceEvent(DWORD unitmask, bool add) { for (char i = 0; i < 26; ++i) { if (unitmask & 0x1) { QString path = QString(i + 'A') + ":/"; qDebug() << "sendDeviceEvent" << path << add; CEventDevice * event = new CEventDevice(path, add); QCoreApplication::postEvent(CDeviceWatcherWindows::self(), event); //qDebug() << "postEvent"; } unitmask = unitmask >> 1; } } bool CMainWindow::nativeEvent(const QByteArray & eventType, void * message, long * result) { MSG* msg = (MSG*)message; //qDebug() << "nativeEvent" << eventType << msg->message << msg->lParam << msg->wParam; if (WM_DEVICECHANGE == msg->message) { //qDebug() << "WM_DEVICECHANGE"; PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)msg->lParam; switch (msg->wParam) { case DBT_DEVICEARRIVAL: { qDebug() << "DBT_DEVICEARRIVAL"<< pHdr->dbch_devicetype; if (pHdr->dbch_devicetype == DBT_DEVTYP_VOLUME) { PDEV_BROADCAST_VOLUME pHdrv = (PDEV_BROADCAST_VOLUME)pHdr; sendDeviceEvent(pHdrv->dbcv_unitmask, true); } break; } case DBT_DEVICEREMOVECOMPLETE: { qDebug() << "DBT_DEVICEREMOVECOMPLETE" << pHdr->dbch_devicetype; if (pHdr->dbch_devicetype == DBT_DEVTYP_VOLUME) { PDEV_BROADCAST_VOLUME pHdrv = (PDEV_BROADCAST_VOLUME)pHdr; sendDeviceEvent(pHdrv->dbcv_unitmask, false); } break; } default: { break; } } } return QWidget::nativeEvent(eventType, message, result); } #endif // WIN32 void CMainWindow::dragEnterEvent(QDragEnterEvent *event) { if(event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); QFileInfo fi(urls[0].path()); QString ext = fi.suffix().toUpper(); if ( (ext == "QMS") || (ext == "GPX")) { event->acceptProposedAction(); } } } void CMainWindow::dropEvent(QDropEvent *event) { QList urls = event->mimeData()->urls(); QUrl url; QStringList filenames; foreach(url, urls) { filenames << url.toLocalFile(); } loadGISData(filenames); event->acceptProposedAction(); } qmapshack-1.5.1/src/IMainWindow.ui000644 001750 000144 00000047772 12616741641 020023 0ustar00oeichlerusers000000 000000 IMainWindow 0 0 800 600 true QMapShack :/icons/48x48/QMapShack.png:/icons/48x48/QMapShack.png 0 0 0 0 0 -1 true 0 0 800 21 File View Window ? Project Tool 0 0 QDockWidget::DockWidgetFeatureMask Maps 1 0 0 0 0 0 -1 0 0 QDockWidget::DockWidgetFeatureMask Dig. Elev. Model (DEM) 1 0 0 0 0 0 0 0 QDockWidget::DockWidgetFeatureMask Data 2 0 0 QDockWidget::DockWidgetFeatureMask Route 2 0 0 0 0 0 :/icons/32x32/AddMapWorkspace.png:/icons/32x32/AddMapWorkspace.png Add Map View Add Map View Ctrl+T true :/icons/32x32/Scale.png:/icons/32x32/Scale.png Show Scale :/icons/32x32/Font.png:/icons/32x32/Font.png Setup Map Font true :/icons/32x32/Grid.png:/icons/32x32/Grid.png Show Grid Ctrl+G :/icons/32x32/GridSetup.png:/icons/32x32/GridSetup.png Setup Grid Ctrl+Alt+G true :/icons/32x32/MouseWheel.png:/icons/32x32/MouseWheel.png Flip Mouse Wheel :/icons/32x32/FolderMap.png:/icons/32x32/FolderMap.png Setup Map Paths Setup Map Paths true :/icons/32x32/POIText.png:/icons/32x32/POIText.png POI Text true :/icons/32x32/NightDay.png:/icons/32x32/NightDay.png Night / Day true :/icons/32x32/ToolTip.png:/icons/32x32/ToolTip.png Map Tool Tip Ctrl+I :/icons/32x32/FolderDEM.png:/icons/32x32/FolderDEM.png Setup DEM Paths :/icons/32x32/Info.png:/icons/32x32/Info.png About :/icons/32x32/Help.png:/icons/32x32/Help.png Help :/icons/32x32/SetupMapWorkspace.png:/icons/32x32/SetupMapWorkspace.png Setup Map View Setup Map View :/icons/32x32/LoadGIS.png:/icons/32x32/LoadGIS.png Load GIS Data Load projects from file Ctrl+L :/icons/32x32/SaveAllGIS.png:/icons/32x32/SaveAllGIS.png Save All GIS Data Save all projects in the workspace Ctrl+S :/icons/32x32/TimeZoneSetup.png:/icons/32x32/TimeZoneSetup.png Setup Time Zone :/icons/32x32/AddProject.png:/icons/32x32/AddProject.png Add empty project true :/icons/32x32/SearchGoogle.png:/icons/32x32/SearchGoogle.png Search Google :/icons/32x32/Close.png:/icons/32x32/Close.png Close all projects F8 :/icons/32x32/UnitSetup.png:/icons/32x32/UnitSetup.png Setup Units :/icons/32x32/DatabaseSetup.png:/icons/32x32/DatabaseSetup.png Setup Workspace Setup save on exit. :/icons/32x32/DatabaseConvert.png:/icons/32x32/DatabaseConvert.png Import Database from QLandkarte Import QLandkarte GT database :/icons/32x32/VrtBuilder.png:/icons/32x32/VrtBuilder.png VRT Builder GUI front end to gdalbuildvrt :/icons/32x32/SaveView.png:/icons/32x32/SaveView.png Store Map View Write current active map and DEM list including the properties to a file :/icons/32x32/LoadView.png:/icons/32x32/LoadView.png Load Map View Restore view with active map and DEM list including the properties from a file true :/icons/32x32/ProfileToWindow.png:/icons/32x32/ProfileToWindow.png Ext. Profile Ctrl+E :/icons/32x32/Off.png:/icons/32x32/Off.png Close Ctrl+Q :/icons/32x32/CloneMapWorkspace.png:/icons/32x32/CloneMapWorkspace.png Clone Map View Ctrl+Shift+T :/icons/32x32/RouteSetup.png:/icons/32x32/RouteSetup.png Create Routino Database :/icons/32x32/PrintSave.png:/icons/32x32/PrintSave.png Save(Print) Map Screenshot Print a selected area of the map Ctrl+P :/icons/32x32/SetupCoordFormat.png:/icons/32x32/SetupCoordFormat.png Setup Coord. Format Change the format coordinates are displayed CRouterSetup QWidget
gis/rte/router/CRouterSetup.h
1
qmapshack-1.5.1/src/grid/CProjWizard.cpp000644 001750 000144 00000015615 12622435716 021115 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "CProjWizard.h" #include "grid/mitab.h" #include #include struct mitab_entry_t { QString name; int idx; }; static bool mitabLessThan(const mitab_entry_t &s1, const mitab_entry_t &s2) { return s1.name < s2.name; } CProjWizard::CProjWizard(QLineEdit &line) : QDialog(CMainWindow::getBestWidgetForParent()) , line(line) { setupUi(this); mitab_entry_t entry; QList list; int idx = 0; const MapInfoDatumInfo * di = asDatumInfoListQL; while(di->nMapInfoDatumID != -1) { entry.name = di->pszOGCDatumName; entry.idx = idx; list << entry; ++di; ++idx; } qSort(list.begin(), list.end(), mitabLessThan); foreach(entry, list) { comboDatum->addItem(entry.name, entry.idx); } comboHemisphere->addItem(tr("north"), ""); comboHemisphere->addItem(tr("south"), "+south"); connect(radioMercator, SIGNAL(clicked()), this, SLOT(slotChange())); connect(radioWorldMercator, SIGNAL(clicked()), this, SLOT(slotChange())); connect(radioUPSNorth, SIGNAL(clicked()), this, SLOT(slotChange())); connect(radioUPSSouth, SIGNAL(clicked()), this, SLOT(slotChange())); connect(radioUTM, SIGNAL(clicked()), this, SLOT(slotChange())); connect(radioUserDef, SIGNAL(clicked()), this, SLOT(slotChange())); connect(comboDatum, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChange())); connect(comboHemisphere, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChange())); connect(lineUserDef, SIGNAL(textChanged(const QString &)), this, SLOT(slotChange())); connect(spinUTMZone, SIGNAL(valueChanged(int)), this, SLOT(slotChange())); QString projstr = line.text(); QRegExp re2("\\s*\\+proj=merc \\+a=6378137 \\+b=6378137 \\+lat_ts=0.001 \\+lon_0=0.0 \\+x_0=0.0 \\+y_0=0 \\+k=1.0 \\+units=m \\+nadgrids=@null \\+no_defs"); QRegExp re3("\\s*\\+proj=merc\\s(.*)"); QRegExp re4("\\s*\\+proj=utm \\+zone=([0-9]+)\\s(.*)"); if(re2.exactMatch(projstr)) { radioWorldMercator->setChecked(true); } else if(re3.exactMatch(projstr)) { radioMercator->setChecked(true); findDatum(re3.cap(1)); } else if(re4.exactMatch(projstr)) { radioUTM->setChecked(true); spinUTMZone->setValue(re4.cap(1).toInt()); QString datum = re4.cap(2); if(datum.startsWith("+south ")) { datum = datum.mid(7); comboHemisphere->setCurrentIndex(1); } findDatum(datum); } slotChange(); } CProjWizard::~CProjWizard() { } void CProjWizard::findDatum(const QString& str) { QString cmp; int idx = 0; const MapInfoDatumInfo * di = asDatumInfoListQL; while(di->nMapInfoDatumID != -1) { cmp.clear(); if(di->pszOGCDatumName != QString("")) { const MapInfoSpheroidInfo * si = asSpheroidInfoList; while(si->nMapInfoId != -1) { if(si->nMapInfoId == di->nEllipsoid) { break; } ++si; } cmp += QString("+a=%1 +b=%2 ").arg(si->dfA,0,'f',4).arg(si->dfA * (1.0 - (1.0/si->dfInvFlattening)),0,'f',4); cmp += QString("+towgs84=%1,%2,%3,%4,%5,%6,%7,%8 ").arg(di->dfShiftX).arg(di->dfShiftY).arg(di->dfShiftZ).arg(di->dfDatumParm0).arg(di->dfDatumParm1).arg(di->dfDatumParm2).arg(di->dfDatumParm3).arg(di->dfDatumParm4); cmp += "+units=m +no_defs"; } if(cmp == str) { comboDatum->setCurrentIndex(comboDatum->findText(di->pszOGCDatumName)); break; } ++di; ++idx; } } void CProjWizard::slotChange() { QString str; if(radioMercator->isChecked()) { str += "+proj=merc "; } else if(radioWorldMercator->isChecked()) { str += "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.001 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"; labelResult->setText(str); return; } else if(radioUPSNorth->isChecked()) { str += "+init=epsg:32661"; } else if(radioUPSSouth->isChecked()) { str += "+init=epsg:32761"; } else if(radioUTM->isChecked()) { str += QString("+proj=utm +zone=%1 %2 ").arg(spinUTMZone->value()).arg(comboHemisphere->itemData(comboHemisphere->currentIndex()).toString()); } else if(radioUserDef->isChecked()) { str += lineUserDef->text() + " "; } int idx = comboDatum->itemData(comboDatum->currentIndex()).toInt(); const MapInfoDatumInfo di = asDatumInfoListQL[idx]; if(di.pszOGCDatumName != QString("")) { const MapInfoSpheroidInfo * si = asSpheroidInfoList; while(si->nMapInfoId != -1) { if(si->nMapInfoId == di.nEllipsoid) { break; } ++si; } str += QString("+a=%1 +b=%2 ").arg(si->dfA,0,'f',4).arg(si->dfA * (1.0 - (1.0/si->dfInvFlattening)),0,'f',4); str += QString("+towgs84=%1,%2,%3,%4,%5,%6,%7,%8 ").arg(di.dfShiftX).arg(di.dfShiftY).arg(di.dfShiftZ).arg(di.dfDatumParm0).arg(di.dfDatumParm1).arg(di.dfDatumParm2).arg(di.dfDatumParm3).arg(di.dfDatumParm4); str += "+units=m +no_defs"; } labelResult->setText(str); } void CProjWizard::accept() { if (CProjWizard::validProjStr(labelResult->text())) { line.setText(labelResult->text()); line.setCursorPosition(0); QDialog::accept(); } } bool CProjWizard::validProjStr(const QString projStr) { projPJ projCheck = pj_init_plus(projStr.toUtf8().data()); if (!projCheck) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."),tr("The value\n'%1'\nis not a valid coordinate system definition:\n%2").arg(projStr).arg(pj_strerrno(pj_errno)),QMessageBox::Abort,QMessageBox::Abort); return false; } else { pj_free(projCheck); return true; } } qmapshack-1.5.1/src/grid/CGridSetup.h000644 001750 000144 00000002617 12622435722 020370 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGRIDSETUP_H #define CGRIDSETUP_H #include "ui_IGridSetup.h" #include class CGrid; class CMapDraw; class CGridSetup : public QDialog, private Ui::IGridSetup { Q_OBJECT public: CGridSetup(CGrid * grid, CMapDraw *map); virtual ~CGridSetup(); public slots: void accept(); private slots: void slotProjWizard(); void slotSelectGridColor(); void slotRestoreDefault(); void slotProjFromMap(); private: CGrid * grid; CMapDraw * map; }; #endif //CGRIDSETUP_H qmapshack-1.5.1/src/grid/IGridSetup.ui000644 001750 000144 00000014156 12527654570 020575 0ustar00oeichlerusers000000 000000 IGridSetup 0 0 446 140 Setup Grid... Projection 32 32 restore default ... :/icons/32x32/Reset.png:/icons/32x32/Reset.png 22 22 32 32 Get projection from current map. ... :/icons/32x32/FromMap.png:/icons/32x32/FromMap.png 22 22 32 32 projection wizzard ... :/icons/32x32/GridWizzard.png:/icons/32x32/GridWizzard.png 22 22 Qt::Horizontal 40 20 75 true Grid color 32 32 setup grid color ... :/icons/32x32/SelectColor.png:/icons/32x32/SelectColor.png 22 22 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IGridSetup accept() 248 254 157 274 buttonBox rejected() IGridSetup reject() 316 260 286 274 qmapshack-1.5.1/src/grid/IProjWizard.ui000644 001750 000144 00000012555 12527654570 020763 0ustar00oeichlerusers000000 000000 IProjWizard 0 0 580 348 Proj4 Wizzard QFrame::StyledPanel QFrame::Raised Mercator UTM zone 1 60 Qt::Horizontal 40 20 user defined Datum World Mercator (OSM) Qt::Vertical 20 40 Result: UPS North (North Pole) UPS South (South Pole) Projection Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IProjWizard accept() 248 254 157 274 buttonBox rejected() IProjWizard reject() 316 260 286 274 qmapshack-1.5.1/src/grid/mitab.cpp000644 001750 000144 00000045134 12622435717 020013 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "mitab.h" const MapInfoDatumInfo asDatumInfoListQL[] = { { // Datum ignore 0, "", 29, 0, 0, 0, 0, 0, 0, 0, 0 }, {74, "North_American_Datum_1983", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, "Adindan", 6, -162, -12, 206, 0, 0, 0, 0, 0}, {2, "Afgooye", 3, -43, -163, 45, 0, 0, 0, 0, 0}, {3, "Ain_el_Abd_1970", 4, -150, -251, -2, 0, 0, 0, 0, 0}, {4, "Anna_1_Astro_1965", 2, -491, -22, 435, 0, 0, 0, 0, 0}, {5, "Arc_1950", 15,-143, -90, -294,0, 0, 0, 0, 0}, {6, "Arc_1960", 6, -160, -8, -300,0, 0, 0, 0, 0}, {7, "Ascension_Islands", 4, -207, 107, 52, 0, 0, 0, 0, 0}, {8, "Astro_Beacon_E", 4, 145, 75, -272,0, 0, 0, 0, 0}, {9, "Astro_B4_Sorol_Atoll", 4, 114, -116, -333,0, 0, 0, 0, 0}, {10, "Astro_Dos_71_4", 4, -320, 550, -494,0, 0, 0, 0, 0}, {11, "Astronomic_Station_1952", 4, 124, -234, -25, 0, 0, 0, 0, 0}, {12, "Australian_Geodetic_Datum_66",2, -133, -48, 148, 0, 0, 0, 0, 0}, {13, "Australian_Geodetic_Datum_84",2, -134, -48, 149, 0, 0, 0, 0, 0}, {14, "Bellevue_Ign", 4, -127, -769, 472, 0, 0, 0, 0, 0}, {15, "Bermuda_1957", 7, -73, 213, 296, 0, 0, 0, 0, 0}, {16, "Bogota", 4, 307, 304, -318,0, 0, 0, 0, 0}, {17, "Campo_Inchauspe", 4, -148, 136, 90, 0, 0, 0, 0, 0}, {18, "Canton_Astro_1966", 4, 298, -304, -375,0, 0, 0, 0, 0}, {19, "Cape", 6, -136, -108, -292,0, 0, 0, 0, 0}, {20, "Cape_Canaveral", 7, -2, 150, 181, 0, 0, 0, 0, 0}, {21, "Carthage", 6, -263, 6, 431, 0, 0, 0, 0, 0}, {22, "Chatham_1971", 4, 175, -38, 113, 0, 0, 0, 0, 0}, {23, "Chua", 4, -134, 229, -29, 0, 0, 0, 0, 0}, {24, "Corrego_Alegre", 4, -206, 172, -6, 0, 0, 0, 0, 0}, {25, "Batavia", 10,-377,681, -50, 0, 0, 0, 0, 0}, {26, "Dos_1968", 4, 230, -199, -752,0, 0, 0, 0, 0}, {27, "Easter_Island_1967", 4, 211, 147, 111, 0, 0, 0, 0, 0}, {28, "European_Datum_1950", 4, -87, -98, -121,0, 0, 0, 0, 0}, {29, "European_Datum_1979", 4, -86, -98, -119,0, 0, 0, 0, 0}, {30, "Gandajika_1970", 4, -133, -321, 50, 0, 0, 0, 0, 0}, {31, "New_Zealand_GD49", 4, 84, -22, 209, 0, 0, 0, 0, 0}, {32, "GRS_67", 21,0, 0, 0, 0, 0, 0, 0, 0}, {33, "GRS_80", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {34, "Guam_1963", 7, -100, -248, 259, 0, 0, 0, 0, 0}, {35, "Gux_1_Astro", 4, 252, -209, -751,0, 0, 0, 0, 0}, {36, "Hito_XVIII_1963", 4, 16, 196, 93, 0, 0, 0, 0, 0}, {37, "Hjorsey_1955", 4, -73, 46, -86, 0, 0, 0, 0, 0}, {38, "Hong_Kong_1963", 4, -156, -271, -189,0, 0, 0, 0, 0}, {39, "Hu_Tzu_Shan", 4, -634, -549, -201,0, 0, 0, 0, 0}, {40, "Indian_Thailand_Vietnam", 11,214, 836, 303, 0, 0, 0, 0, 0}, {41, "Indian_Bangladesh", 11,289, 734, 257, 0, 0, 0, 0, 0}, {42, "Ireland_1965", 13,506, -122, 611, 0, 0, 0, 0, 0}, {43, "ISTS_073_Astro_1969", 4, 208, -435, -229,0, 0, 0, 0, 0}, {44, "Johnston_Island_1961", 4, 191, -77, -204,0, 0, 0, 0, 0}, {45, "Kandawala", 11,-97, 787, 86, 0, 0, 0, 0, 0}, {46, "Kerguyelen_Island", 4, 145, -187, 103, 0, 0, 0, 0, 0}, {47, "Kertau", 17,-11, 851, 5, 0, 0, 0, 0, 0}, {48, "L_C_5_Astro", 7, 42, 124, 147, 0, 0, 0, 0, 0}, {49, "Liberia_1964", 6, -90, 40, 88, 0, 0, 0, 0, 0}, {50, "Luzon_Phillippines", 7, -133, -77, -51, 0, 0, 0, 0, 0}, {51, "Luzon_Mindanao_Island", 7, -133, -79, -72, 0, 0, 0, 0, 0}, {52, "Mahe_1971", 6, 41, -220, -134,0, 0, 0, 0, 0}, {53, "Marco_Astro", 4, -289, -124, 60, 0, 0, 0, 0, 0}, {54, "Massawa", 10,639, 405, 60, 0, 0, 0, 0, 0}, {55, "Merchich", 16,31, 146, 47, 0, 0, 0, 0, 0}, {56, "Midway_Astro_1961", 4, 912, -58, 1227,0, 0, 0, 0, 0}, {57, "Minna", 6, -92, -93, 122, 0, 0, 0, 0, 0}, {58, "Nahrwan_Masirah_Island", 6, -247, -148, 369, 0, 0, 0, 0, 0}, {59, "Nahrwan_Un_Arab_Emirates", 6, -249, -156, 381, 0, 0, 0, 0, 0}, {60, "Nahrwan_Saudi_Arabia", 6, -231, -196, 482, 0, 0, 0, 0, 0}, {61, "Naparima_1972", 4, -2, 374, 172, 0, 0, 0, 0, 0}, {62, "NAD_1927", 7, -8, 160, 176, 0, 0, 0, 0, 0}, {62, "North_American_Datum_1927", 7, -8, 160, 176, 0, 0, 0, 0, 0}, {63, "NAD_27_Alaska", 7, -5, 135, 172, 0, 0, 0, 0, 0}, {64, "NAD_27_Bahamas", 7, -4, 154, 178, 0, 0, 0, 0, 0}, {65, "NAD_27_San_Salvador", 7, 1, 140, 165, 0, 0, 0, 0, 0}, {66, "NAD_27_Canada", 7, -10, 158, 187, 0, 0, 0, 0, 0}, {67, "NAD_27_Canal_Zone", 7, 0, 125, 201, 0, 0, 0, 0, 0}, {68, "NAD_27_Caribbean", 7, -7, 152, 178, 0, 0, 0, 0, 0}, {69, "NAD_27_Central_America", 7, 0, 125, 194, 0, 0, 0, 0, 0}, {70, "NAD_27_Cuba", 7, -9, 152, 178, 0, 0, 0, 0, 0}, {71, "NAD_27_Greenland", 7, 11, 114, 195, 0, 0, 0, 0, 0}, {72, "NAD_27_Mexico", 7, -12, 130, 190, 0, 0, 0, 0, 0}, {73, "NAD_27_Michigan", 8, -8, 160, 176, 0, 0, 0, 0, 0}, {75, "Observatorio_1966", 4, -425, -169, 81, 0, 0, 0, 0, 0}, {76, "Old_Egyptian", 22,-130, 110, -13, 0, 0, 0, 0, 0}, {77, "Old_Hawaiian", 7, 61, -285, -181,0, 0, 0, 0, 0}, {78, "Oman", 6, -346, -1, 224, 0, 0, 0, 0, 0}, {79, "OSGB_1936", 9, 375, -111, 431, 0, 0, 0, 0, 0}, {80, "Pico_De_Las_Nieves", 4, -307, -92, 127, 0, 0, 0, 0, 0}, {81, "Pitcairn_Astro_1967", 4, 185, 165, 42, 0, 0, 0, 0, 0}, {82, "Provisional_South_American", 4, -288, 175, -376,0, 0, 0, 0, 0}, {83, "Puerto_Rico", 7, 11, 72, -101,0, 0, 0, 0, 0}, {84, "Qatar_National", 4, -128, -283, 22, 0, 0, 0, 0, 0}, {85, "Qornoq", 4, 164, 138, -189, 0, 0, 0, 0, 0}, {86, "Reunion", 4, 94, -948,-1262,0, 0, 0, 0, 0}, {87, "Monte_Mario", 4, -225, -65, 9, 0, 0, 0, 0, 0}, {88, "Santo_Dos", 4, 170, 42, 84, 0, 0, 0, 0, 0}, {89, "Sao_Braz", 4, -203, 141, 53, 0, 0, 0, 0, 0}, {90, "Sapper_Hill_1943", 4, -355, 16, 74, 0, 0, 0, 0, 0}, {91, "Schwarzeck", 14,616, 97, -251, 0, 0, 0, 0, 0}, {92, "South_American_Datum_1969", 24,-57, 1, -41, 0, 0, 0, 0, 0}, {93, "South_Asia", 19,7, -10, -26, 0, 0, 0, 0, 0}, {94, "Southeast_Base", 4, -499, -249,314, 0, 0, 0, 0, 0}, {95, "Southwest_Base", 4, -104, 167, -38, 0, 0, 0, 0, 0}, {96, "Timbalai_1948", 11,-689, 691, -46, 0, 0, 0, 0, 0}, {97, "Tokyo", 10,-128, 481, 664, 0, 0, 0, 0, 0}, {98, "Tristan_Astro_1968", 4, -632, 438, -609, 0, 0, 0, 0, 0}, {99, "Viti_Levu_1916", 6, 51, 391, -36, 0, 0, 0, 0, 0}, {100, "Wake_Entiwetok_1960", 23,101, 52, -39, 0, 0, 0, 0, 0}, {101, "WGS_60", 26,0, 0, 0, 0, 0, 0, 0, 0}, {102, "WGS_66", 27,0, 0, 0, 0, 0, 0, 0, 0}, {103, "WGS_1972", 1, 0, 8, 10, 0, 0, 0, 0, 0}, {104, "WGS_1984", 28,0, 0, 0, 0, 0, 0, 0, 0}, {105, "Yacare", 4, -155, 171, 37, 0, 0, 0, 0, 0}, {106, "Zanderij", 4, -265, 120, -358, 0, 0, 0, 0, 0}, {107, "NTF", 30,-168, -60, 320, 0, 0, 0, 0, 0}, {108, "European_Datum_1987", 4, -83, -96, -113, 0, 0, 0, 0, 0}, {109, "Netherlands_Bessel", 10,593, 26, 478, 0, 0, 0, 0, 0}, {110, "Belgium_Hayford", 4, 81, 120, 129, 0, 0, 0, 0, 0}, {111, "NWGL_10", 1, -1, 15, 1, 0, 0, 0, 0, 0}, {112, "Rikets_koordinatsystem_1990",10,498, -36, 568, 0, 0, 0, 0, 0}, {113, "Lisboa_DLX", 4, -303, -62, 105, 0, 0, 0, 0, 0}, {114, "Melrica_1973_D73", 4, -223, 110, 37, 0, 0, 0, 0, 0}, {115, "Euref_98", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {116, "GDA94", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {117, "NZGD2000", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {118, "America_Samoa", 7, -115, 118, 426, 0, 0, 0, 0, 0}, {119, "Antigua_Astro_1965", 6, -270, 13, 62, 0, 0, 0, 0, 0}, {120, "Ayabelle_Lighthouse", 6, -79, -129, 145, 0, 0, 0, 0, 0}, {121, "Bukit_Rimpah", 10,-384, 664, -48, 0, 0, 0, 0, 0}, {122, "Estonia_1937", 10,374, 150, 588, 0, 0, 0, 0, 0}, {123, "Dabola", 6, -83, 37, 124, 0, 0, 0, 0, 0}, {124, "Deception_Island", 6, 260, 12, -147, 0, 0, 0, 0, 0}, {125, "Fort_Thomas_1955", 6, -7, 215, 225, 0, 0, 0, 0, 0}, {126, "Graciosa_base_1948", 4, -104, 167, -38, 0, 0, 0, 0, 0}, {127, "Herat_North", 4, -333, -222,114, 0, 0, 0, 0, 0}, {128, "Hermanns_Kogel", 10,682, -203, 480, 0, 0, 0, 0, 0}, {129, "Indian", 50,283, 682, 231, 0, 0, 0, 0, 0}, {130, "Indian_1954", 11,217, 823, 299, 0, 0, 0, 0, 0}, {131, "Indian_1960", 11,198, 881, 317, 0, 0, 0, 0, 0}, {132, "Indian_1975", 11,210, 814, 289, 0, 0, 0, 0, 0}, {133, "Indonesian_Datum_1974", 4, -24, -15, 5, 0, 0, 0, 0, 0}, {134, "ISTS061_Astro_1968", 4, -794, 119, -298, 0, 0, 0, 0, 0}, {135, "Kusaie_Astro_1951", 4, 647, 1777, -1124,0, 0, 0, 0, 0}, {136, "Leigon", 6, -130, 29, 364, 0, 0, 0, 0, 0}, {137, "Montserrat_Astro_1958", 6, 174, 359, 365, 0, 0, 0, 0, 0}, {138, "Mporaloko", 6, -74, -130, 42, 0, 0, 0, 0, 0}, {139, "North_Sahara_1959", 6, -186, -93, 310, 0, 0, 0, 0, 0}, {140, "Observatorio_Met_1939", 4, -425, -169,81, 0, 0, 0, 0, 0}, {141, "Point_58", 6, -106, -129,165, 0, 0, 0, 0, 0}, {142, "Pointe_Noire", 6, -148, 51, -291, 0, 0, 0, 0, 0}, {143, "Porto_Santo_1936", 4, -499, -249,314, 0, 0, 0, 0, 0}, {144, "Selvagem_Grande_1938", 4, -289, -124,60, 0, 0, 0, 0, 0}, {145, "Sierra_Leone_1960", 6, -88, 4, 101, 0, 0, 0, 0, 0}, {146, "S_JTSK_Ferro", 10, 589, 76, 480, 0, 0, 0, 0, 0}, {147, "Tananarive_1925", 4, -189, -242,-91, 0, 0, 0, 0, 0}, {148, "Voirol_1874", 6, -73, -247,227, 0, 0, 0, 0, 0}, {149, "Virol_1960", 6, -123, -206,219, 0, 0, 0, 0, 0}, {150, "Hartebeesthoek94", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {151, "ATS77", 51, 0, 0, 0, 0, 0, 0, 0, 0}, {152, "JGD2000", 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1000,"DHDN_Potsdam_Rauenberg", 10,582, 105, 414, -1.04, -0.35, 3.08, 8.3, 0}, {1001,"Pulkovo_1942", 3, 24, -123, -94, -0.02, 0.25, 0.13, 1.1, 0}, {1002,"NTF_Paris_Meridian", 30,-168, -60, 320, 0, 0, 0, 0, 2.337229166667}, {1003,"Switzerland_CH_1903", 10,660.077,13.551, 369.344, 0.804816, 0.577692, 0.952236, 5.66,0}, {1004,"Hungarian_Datum_1972", 21,-56, 75.77, 15.31, -0.37, -0.2, -0.21, -1.01, 0}, {1005,"Cape_7_Parameter", 28,-134.73,-110.92, -292.66, 0, 0, 0, 1, 0}, {1006,"AGD84_7_Param_Aust", 2, -117.763,-51.51, 139.061, -0.292, -0.443, -0.277, -0.191, 0}, {1007,"AGD66_7_Param_ACT", 2, -129.193,-41.212, 130.73, -0.246, -0.374, -0.329, -2.955, 0}, {1008,"AGD66_7_Param_TAS", 2, -120.271,-64.543, 161.632, -0.2175, 0.0672, 0.1291, 2.4985, 0}, {1009,"AGD66_7_Param_VIC_NSW", 2, -119.353,-48.301, 139.484, -0.415, -0.26, -0.437, -0.613, 0}, {1010,"NZGD_7_Param_49", 4, 59.47, -5.04, 187.44, -0.47, 0.1, -1.024, -4.5993, 0}, {1011,"Rikets_Tri_7_Param_1990", 10,419.3836, 99.3335, 591.3451, -0.850389, -1.817277, 7.862238, -0.99496, 0}, {1012,"Russia_PZ90", 52, -1.08,-0.27,-0.9,0, 0, -0.16,-0.12, 0}, {1013,"Russia_SK42", 52, 23.92,-141.27,-80.9, 0, -0.35,-0.82, -0.12, 0}, {1014,"Russia_SK95", 52, 24.82,-131.21,-82.66,0,0,-0.16,-0.12, 0}, {1015,"Tokyo", 10, -146.414, 507.337, 680.507,0,0,0,0,0}, {1016,"Finnish_KKJ", 4, -96.062, -82.428, -121.754, -4.801, -0.345, 1.376, 1.496, 0}, {-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* -------------------------------------------------------------------- */ /* This table was hand entered from Appendix I of the mapinfo 6 */ /* manuals. */ /* -------------------------------------------------------------------- */ const MapInfoSpheroidInfo asSpheroidInfoList[] = { { 9,"Airy 1930", 6377563.396, 299.3249646}, {13,"Airy 1930 (modified for Ireland 1965", 6377340.189, 299.3249646}, {51,"ATS77 (Average Terrestrial System 1977)", 6378135, 298.257}, { 2,"Australian", 6378160.0, 298.25}, {10,"Bessel 1841", 6377397.155, 299.1528128}, {35,"Bessel 1841 (modified for NGO 1948)", 6377492.0176, 299.15281}, {14,"Bessel 1841 (modified for Schwarzeck)", 6377483.865, 299.1528128}, {36,"Clarke 1858", 6378293.639, 294.26068}, { 7,"Clarke 1866", 6378206.4, 294.9786982}, { 8,"Clarke 1866 (modified for Michigan)", 6378450.047484481,294.9786982}, { 6,"Clarke 1880", 6378249.145, 293.465}, {15,"Clarke 1880 (modified for Arc 1950)", 6378249.145326, 293.4663076}, {30,"Clarke 1880 (modified for IGN)", 6378249.2, 293.4660213}, {37,"Clarke 1880 (modified for Jamaica)", 6378249.136, 293.46631}, {16,"Clarke 1880 (modified for Merchich)", 6378249.2, 293.46598}, {38,"Clarke 1880 (modified for Palestine)", 6378300.79, 293.46623}, {39,"Everest (Brunei and East Malaysia)", 6377298.556, 300.8017}, {11,"Everest (India 1830)", 6377276.345, 300.8017}, {40,"Everest (India 1956)", 6377301.243, 300.80174}, {50,"Everest (Pakistan)", 6377309.613, 300.8017}, {17,"Everest (W. Malaysia and Singapore 1948)", 6377304.063, 300.8017}, {48,"Everest (West Malaysia 1969)", 6377304.063, 300.8017}, {18,"Fischer 1960", 6378166.0, 298.3}, {19,"Fischer 1960 (modified for South Asia)", 6378155.0, 298.3}, {20,"Fischer 1968", 6378150.0, 298.3}, {21,"GRS 67", 6378160.0, 298.247167427}, { 0,"GRS 80", 6378137.0, 298.257222101}, { 5,"Hayford", 6378388.0, 297.0}, {22,"Helmert 1906", 6378200.0, 298.3}, {23,"Hough", 6378270.0, 297.0}, {31,"IAG 75", 6378140.0, 298.257222}, {41,"Indonesian", 6378160.0, 298.247}, { 4,"International 1924", 6378388.0, 297.0}, {49,"Irish (WOFO)", 6377542.178, 299.325}, { 3,"Krassovsky", 6378245.0, 298.3}, {32,"MERIT 83", 6378137.0, 298.257}, {33,"New International 1967", 6378157.5, 298.25}, {42,"NWL 9D", 6378145.0, 298.25}, {43,"NWL 10D", 6378135.0, 298.26}, {44,"OSU86F", 6378136.2, 298.25722}, {45,"OSU91A", 6378136.3, 298.25722}, {46,"Plessis 1817", 6376523.0, 308.64}, {52,"PZ90", 6378136.0, 298.257839303}, {24,"South American", 6378160.0, 298.25}, {12,"Sphere", 6370997.0, 0.0}, {47,"Struve 1860", 6378297.0, 294.73}, {34,"Walbeck", 6376896.0, 302.78}, {25,"War Office", 6378300.583, 296.0}, {26,"WGS 60", 6378165.0, 298.3}, {27,"WGS 66", 6378145.0, 298.25}, { 1,"WGS 72", 6378135.0, 298.26}, {28,"WGS 84", 6378137.0, 298.257223563}, {29,"WGS 84 (MAPINFO Datum 0)", 6378137.01, 298.257223563}, {-1,0, 0.0, 0.0} }; qmapshack-1.5.1/src/grid/CGrid.h000644 001750 000144 00000003457 12622435722 017352 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGRID_H #define CGRID_H #include #include #include class QPainter; class QSettings; class CMapDraw; class CGrid : public QObject { Q_OBJECT public: CGrid(CMapDraw * map); virtual ~CGrid(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); void draw(QPainter& p, const QRect &rect); void setProjAndColor(const QString& proj, const QColor& c); void convertPos2Str(const QPointF& pos, QString& info, bool simple); private: friend class CGridSetup; void findGridSpace(qreal min, qreal max, qreal& xSpace, qreal& ySpace); bool calcIntersection(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, qreal& x, qreal& y); CMapDraw * map; projPJ pjWGS84 = 0; projPJ pjGrid = 0; QString projstr = "+proj=longlat +datum=WGS84 +no_defs"; QColor color = Qt::magenta; }; #endif //CGRID_H qmapshack-1.5.1/src/grid/CProjWizard.h000644 001750 000144 00000002513 12622435722 020550 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPROJWIZARD_H #define CPROJWIZARD_H #include "ui_IProjWizard.h" #include class CProjWizard : public QDialog, private Ui::IProjWizard { Q_OBJECT public: CProjWizard(QLineEdit& line); virtual ~CProjWizard(); static bool validProjStr(const QString projStr); public slots: void accept(); void slotChange(); private: void findDatum(const QString& str); QLineEdit& line; }; #endif //CPROJWIZARD_H qmapshack-1.5.1/src/grid/CGrid.cpp000644 001750 000144 00000027062 12622435717 017707 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "grid/CGrid.h" #include "helpers/CDraw.h" #include "helpers/CSettings.h" #include "map/CMapDraw.h" #include CGrid::CGrid(CMapDraw *map) : QObject(map) , map(map) { pjWGS84 = pj_init_plus("+proj=longlat +datum=WGS84 +no_defs"); setProjAndColor(projstr, color); } CGrid::~CGrid() { pj_free(pjWGS84); pj_free(pjGrid); } void CGrid::convertPos2Str(const QPointF& pos, QString& info, bool simple) { if(pjGrid == 0) { return; } QPointF pt = pos * DEG_TO_RAD; pj_transform(pjWGS84, pjGrid, 1, 0, &pt.rx(), &pt.ry(), 0); if(pj_is_latlong(pjGrid)) { QString lat,lng; pt *= RAD_TO_DEG; lat = pt.y() < 0 ? "S" : "N"; lng = pt.x() < 0 ? "W" : "E"; if(simple) { info += tr("%1 %2 ").arg(pt.y(), 0, 'f', 6).arg(pt.x(), 0, 'f', 6); } else { info += tr("%1%2%5 %3%4%5 ").arg(lat).arg(qAbs(pt.y()), 0, 'f', 6).arg(lng).arg(qAbs(pt.x()), 0, 'f', 6).arg(QChar('\260')); } } else { if(simple) { info += tr("%1m, %2m ").arg(pt.y(),0,'f',0).arg(pt.x(),0,'f',0); } else { info += tr("N %1m, E %2m ").arg(pt.y(),0,'f',0).arg(pt.x(),0,'f',0); } } } void CGrid::saveConfig(QSettings& cfg) { cfg.setValue("grid/color", color.name()); cfg.setValue("grid/proj", projstr); } void CGrid::loadConfig(QSettings& cfg) { color = QColor(cfg.value("grid/color", color.name()).toString()); projstr = cfg.value("grid/proj", projstr).toString(); setProjAndColor(projstr, color); } void CGrid::setProjAndColor(const QString& proj, const QColor& c) { projstr = proj; color = c; if(pjGrid) { pj_free(pjGrid); } pjGrid = pj_init_plus(projstr.toLatin1()); } void CGrid::findGridSpace(qreal min, qreal max, qreal& xSpace, qreal& ySpace) { qreal dX = qAbs(min - max) / 10; if(dX < M_PI/180000) { xSpace = 5*M_PI/1800000; ySpace = 5*M_PI/1800000; } else if(dX < M_PI/18000) { xSpace = 5*M_PI/180000; ySpace = 5*M_PI/180000; } else if(dX < M_PI/1800) { xSpace = 5*M_PI/18000; ySpace = 5*M_PI/18000; } else if(dX < M_PI/180) { xSpace = 5*M_PI/1800; ySpace = 5*M_PI/1800; } else if(dX < M_PI/18) { xSpace = 5*M_PI/180; ySpace = 5*M_PI/180; } else if(dX < M_PI/1.8) { xSpace = 5*M_PI/180; ySpace = 5*M_PI/180; } else if(dX < 3000) { xSpace = 1000; ySpace = 1000; } else if(dX < 7000) { xSpace = 5000; ySpace = 5000; } else if(dX < 30000) { xSpace = 10000; ySpace = 10000; } else if(dX < 70000) { xSpace = 50000; ySpace = 50000; } else if(dX < 300000) { xSpace = 100000; ySpace = 100000; } else if(dX < 700000) { xSpace = 500000; ySpace = 500000; } else if(dX < 3000000) { xSpace = 1000000; ySpace = 1000000; } else if(dX < 7000000) { xSpace = 5000000; ySpace = 5000000; } else if(dX < 30000000) { xSpace = 10000000; ySpace = 10000000; } else if(dX < 70000000) { xSpace = 50000000; ySpace = 50000000; } } bool CGrid::calcIntersection(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4, qreal& x, qreal& y) { qreal ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3))/((y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1)); x = x1 + ua * (x2 - x1); y = y1 + ua * (y2 - y1); qreal d12 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); qreal d1x = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y); qreal d2x = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y); qreal d34 = (x4 - x3) * (x4 - x3) + (y4 - y3) * (y4 - y3); qreal d3x = (x3 - x) * (x3 - x) + (y3 - y) * (y3 - y); qreal d4x = (x4 - x) * (x4 - x) + (y4 - y) * (y4 - y); return (d12 >= d1x) && (d12 >= d2x) && (d34 >= d3x) && (d34 >= d4x); } struct val_t { val_t(qint32 pos, qreal val) : pos(pos), val(val) { } qint32 pos; qreal val; }; void CGrid::draw(QPainter& p, const QRect& rect) { if(pjWGS84 == 0 || pjGrid == 0 || !CMainWindow::self().isGridVisible()) { return; } QPointF topLeft = rect.topLeft(); QPointF topRight = rect.topRight(); QPointF btmLeft = rect.bottomLeft(); QPointF btmRight = rect.bottomRight(); map->convertPx2Rad(topLeft); map->convertPx2Rad(topRight); map->convertPx2Rad(btmLeft); map->convertPx2Rad(btmRight); pj_transform(pjWGS84, pjGrid, 1, 0, &topLeft.rx(), &topLeft.ry(), 0); pj_transform(pjWGS84, pjGrid, 1, 0, &topRight.rx(), &topRight.ry(), 0); pj_transform(pjWGS84, pjGrid, 1, 0, &btmLeft.rx(), &btmLeft.ry(), 0); pj_transform(pjWGS84, pjGrid, 1, 0, &btmRight.rx(), &btmRight.ry(), 0); // qDebug() << "---"; // qDebug() << "topLeft " << topLeft.u << topLeft.v; // qDebug() << "topRight" << topRight.u << topRight.v; // qDebug() << "btmLeft " << btmLeft.u << btmLeft.v; // qDebug() << "btmRight" << btmRight.u << btmRight.v; // qDebug() << topLeft.u - topRight.u; // qDebug() << btmLeft.u - btmRight.u; // qDebug() << topLeft.v - btmLeft.v; // qDebug() << topRight.v - btmRight.v; qreal topMax = topLeft.y() > topRight.y() ? topLeft.y() : topRight.y(); qreal btmMin = btmLeft.y() < btmRight.y() ? btmLeft.y() : btmRight.y(); qreal leftMin = topLeft.x() < btmLeft.x() ? topLeft.x() : btmLeft.x(); qreal rightMax = topRight.x() > btmRight.x() ? topRight.x() : btmRight.x(); qreal xGridSpace = 1000; qreal yGridSpace = 1000; findGridSpace(leftMin, rightMax, xGridSpace, yGridSpace); qreal xStart = qFloor(leftMin / xGridSpace) * xGridSpace; qreal yStart = qCeil(topMax / yGridSpace) * yGridSpace; qreal x = xStart - xGridSpace; qreal y = yStart + yGridSpace; if(pj_is_latlong(pjGrid)) { if(y > (85*DEG_TO_RAD)) { y = (85*DEG_TO_RAD); } if(btmMin < -(85*DEG_TO_RAD - yGridSpace)) { btmMin = -(85*DEG_TO_RAD - yGridSpace); } if(x > rightMax) { if(qAbs(x) > qAbs(rightMax)) { xStart = x = -180 * DEG_TO_RAD; } if(qAbs(x) < qAbs(rightMax)) { rightMax = 180 * DEG_TO_RAD; } } } QList< val_t > horzTopTicks; QList< val_t > horzBtmTicks; QList< val_t > vertLftTicks; QList< val_t > vertRgtTicks; p.save(); p.setBrush(Qt::NoBrush); p.setPen(QPen(color,1)); USE_ANTI_ALIASING(p,false); qreal h = rect.height(); qreal w = rect.width(); while(y > btmMin) { while(x < rightMax) { QPointF p1(x, y); QPointF p2(x + xGridSpace, y); QPointF p3(x + xGridSpace, y - yGridSpace); QPointF p4(x, y - yGridSpace); qreal xVal = p1.x(); qreal yVal = p1.y(); pj_transform(pjGrid, pjWGS84, 1, 0, &p1.rx(), &p1.ry(), 0); pj_transform(pjGrid, pjWGS84, 1, 0, &p2.rx(), &p2.ry(), 0); pj_transform(pjGrid, pjWGS84, 1, 0, &p3.rx(), &p3.ry(), 0); pj_transform(pjGrid, pjWGS84, 1, 0, &p4.rx(), &p4.ry(), 0); // qDebug() << (p1 * RAD_TO_DEG) << (p2 * RAD_TO_DEG) << (p3 * RAD_TO_DEG) << (p4 * RAD_TO_DEG); map->convertRad2Px(p1); map->convertRad2Px(p2); map->convertRad2Px(p3); map->convertRad2Px(p4); qreal xx,yy; if(calcIntersection(0,0,w,0, p1.x(), p1.y(), p4.x(), p4.y(), xx, yy)) { horzTopTicks << val_t(xx, xVal); } if(calcIntersection(0,h,w,h, p1.x(), p1.y(), p4.x(), p4.y(), xx, yy)) { horzBtmTicks << val_t(xx, xVal); } if(calcIntersection(0,0,0,h, p1.x(), p1.y(), p2.x(), p2.y(), xx, yy)) { vertLftTicks << val_t(yy, yVal); } if(calcIntersection(w,0,w,h, p1.x(), p1.y(), p2.x(), p2.y(), xx, yy)) { vertRgtTicks << val_t(yy, yVal); } p.drawLine(p1, p2); p.drawLine(p2, p3); p.drawLine(p3, p4); p.drawLine(p4, p1); x += xGridSpace; } x = xStart; y -= yGridSpace; } USE_ANTI_ALIASING(p,true); p.restore(); QColor textColor; textColor.setHsv(color.hslHue(), color.hsvSaturation(), (color.value()>128 ? color.value()-128 : 0)); if(pj_is_latlong(pjGrid)) { QFontMetrics fm(CMainWindow::self().getMapFont()); int yoff = fm.height() + fm.ascent(); int xoff = fm.width("XX.XXXX")>>1; foreach(const val_t &val, horzTopTicks) { CDraw::text(qAbs(val.val)<1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(val.pos, yoff), textColor); } foreach(const val_t &val, horzBtmTicks) { CDraw::text(qAbs(val.val)<1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(val.pos, h), textColor); } foreach(const val_t &val, vertLftTicks) { CDraw::text(qAbs(val.val)<1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(xoff, val.pos), textColor); } foreach(const val_t &val, vertRgtTicks) { CDraw::text(qAbs(val.val)<1.e-5 ? "0" : QString("%1%2").arg(val.val * RAD_TO_DEG).arg(QChar(0260)), p, QPoint(w - xoff, val.pos), textColor); } } else { QFontMetrics fm(CMainWindow::self().getMapFont()); int yoff = fm.height() + fm.ascent(); int xoff = fm.width("XXXX")>>1; foreach(const val_t &val, horzTopTicks) { CDraw::text(QString("%1").arg(qint32(val.val/1000)), p, QPoint(val.pos, yoff), textColor); } foreach(const val_t &val, horzBtmTicks) { CDraw::text(QString("%1").arg(qint32(val.val/1000)), p, QPoint(val.pos, h), textColor); } foreach(const val_t &val, vertLftTicks) { CDraw::text(QString("%1").arg(qint32(val.val/1000)), p, QPoint(xoff, val.pos), textColor); } foreach(const val_t &val, vertRgtTicks) { CDraw::text(QString("%1").arg(qint32(val.val/1000)), p, QPoint(w - xoff, val.pos), textColor); } } } qmapshack-1.5.1/src/grid/CGridSetup.cpp000644 001750 000144 00000005445 12622435717 020731 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "grid/CGrid.h" #include "grid/CGridSetup.h" #include "grid/CProjWizard.h" #include "map/CMapDraw.h" #include CGridSetup::CGridSetup(CGrid *grid, CMapDraw * map) : QDialog(CMainWindow::getBestWidgetForParent()) , grid(grid) , map(map) { setupUi(this); lineProjection->setText(grid->projstr); lineProjection->setCursorPosition(0); QPalette palette = labelGridColor->palette(); palette.setColor(labelGridColor->foregroundRole(), grid->color); labelGridColor->setPalette(palette); connect(toolRestoreDefault, SIGNAL(clicked()), this, SLOT(slotRestoreDefault())); connect(toolFromMap, SIGNAL(clicked()), this, SLOT(slotProjFromMap())); connect(toolProjWizzard, SIGNAL(clicked()), this, SLOT(slotProjWizard())); connect(toolGridColor,SIGNAL(clicked()),this,SLOT(slotSelectGridColor())); } CGridSetup::~CGridSetup() { } void CGridSetup::accept() { if (CProjWizard::validProjStr(lineProjection->text())) { QPalette palette = labelGridColor->palette(); grid->setProjAndColor(lineProjection->text(), palette.color(labelGridColor->foregroundRole())); QDialog::accept(); } } void CGridSetup::slotProjWizard() { CProjWizard dlg(*lineProjection); dlg.exec(); } void CGridSetup::slotSelectGridColor() { QPalette palette = labelGridColor->palette(); QColor color = palette.color(labelGridColor->foregroundRole()); color = QColorDialog::getColor(color, this); if(color.isValid()) { palette.setColor(labelGridColor->foregroundRole(), color); labelGridColor->setPalette(palette); } } void CGridSetup::slotRestoreDefault() { lineProjection->setText("+proj=longlat +datum=WGS84 +no_defs"); lineProjection->setCursorPosition(0); } void CGridSetup::slotProjFromMap() { lineProjection->setText(map->getProjection()); lineProjection->setCursorPosition(0); } qmapshack-1.5.1/src/grid/mitab.h000644 001750 000144 00000003252 12622435722 017447 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef MITAB_H #define MITAB_H struct MapInfoDatumInfo { int nMapInfoDatumID; const char *pszOGCDatumName; int nEllipsoid; double dfShiftX; double dfShiftY; double dfShiftZ; double dfDatumParm0; /* RotX */ double dfDatumParm1; /* RotY */ double dfDatumParm2; /* RotZ */ double dfDatumParm3; /* Scale Factor */ double dfDatumParm4; /* Prime Meridian */ }; struct MapInfoSpheroidInfo { int nMapInfoId; const char *pszMapinfoName; double dfA; /* semi major axis in meters */ double dfInvFlattening; /* Inverse flattening */ }; extern const MapInfoDatumInfo asDatumInfoListQL[]; extern const MapInfoSpheroidInfo asSpheroidInfoList[]; #endif //MITAB_H qmapshack-1.5.1/src/IAbout.ui000644 001750 000144 00000026170 12623676470 017013 0ustar00oeichlerusers000000 000000 IAbout 0 0 550 596 About.... 150 0 150 580 :/pics/about.png true 0 0 <b>QMapShack</b>, Version TextLabel 0 0 :/icons/48x48/QMapShack.png Qt::Horizontal QFormLayout::AllNonFixedFieldsGrow Qt TextLabel GDAL TextLabel Proj4 TextLabel Routino TextLabel Qt::Horizontal Rainer Unseld French Czech Pavel Fric German <b>Translation:</b> Josef Latt Spanish Jose Luis Domingo Lopez Dutch Harrie Klomp Qt::Horizontal Ivo Kronenberg Helmut Schmidt Win64 OS X ...and thanks to all Linux binary maintainers for doing a great job. Special thanks to Dan Horák and Bas Couwenberg for showing presence on the mailing list to discuss distribution related topics. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter true <b>Binaries:</b> Qt::Horizontal <b>Contributors:</b> Christian Eichler (qms@christian-eichler.de) Qt::Horizontal This software is licensed under GPL3 or any later version © 2014 Oliver Eichler (oliver.eichler@gmx.de) Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() IAbout accept() 248 254 157 274 buttonBox rejected() IAbout reject() 316 260 286 274 qmapshack-1.5.1/src/units/CUnitsSetup.h000644 001750 000144 00000002253 12622435722 021016 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CUNITSSETUP_H #define CUNITSSETUP_H #include "ui_IUnitsSetup.h" #include class CUnitsSetup : public QDialog, private Ui::IUnitsSetup { public: CUnitsSetup(QWidget * parent); virtual ~CUnitsSetup(); public slots: void accept(); }; #endif //CUNITSSETUP_H qmapshack-1.5.1/src/units/IUnit.cpp000644 001750 000144 00000046547 12622435717 020175 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "units/CUnitImperial.h" #include "units/CUnitMetric.h" #include "units/CUnitNautic.h" #include #include IUnit * IUnit::m_self = 0; const QPointF NOPOINTF(NOFLOAT, NOFLOAT); const QPoint NOPOINT (NOINT, NOINT); IUnit::tz_mode_e IUnit::timeZoneMode = IUnit::eTZUtc; IUnit::coord_format_e IUnit::coordFormat = IUnit::eCoordFormat1; QByteArray IUnit::timeZone = "UTC"; bool IUnit::useShortFormat = false; const char * IUnit::tblTimezone[] = { "Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers", "Africa/Asmara", "Africa/Bamako", "Africa/Bangui", "Africa/Banjul", "Africa/Bissau", "Africa/Blantyre", "Africa/Brazzaville", "Africa/Bujumbura", "Africa/Cairo", "Africa/Casablanca", "Africa/Conakry", "Africa/Dakar", "Africa/Dar_es_Salaam", "Africa/Djibouti", "Africa/Douala", "Africa/El_Aaiun", "Africa/Freetown", "Africa/Gaborone", "Africa/Harare", "Africa/Johannesburg", "Africa/Kampala", "Africa/Khartoum", "Africa/Kigali", "Africa/Kinshasa", "Africa/Lagos", "Africa/Libreville", "Africa/Lome", "Africa/Luanda", "Africa/Lubumbashi", "Africa/Lusaka", "Africa/Malabo", "Africa/Maputo", "Africa/Maseru", "Africa/Mbabane", "Africa/Mogadishu", "Africa/Monrovia", "Africa/Nairobi", "Africa/Ndjamena", "Africa/Niamey", "Africa/Nouakchott", "Africa/Ouagadougou", "Africa/Porto-Novo", "Africa/Sao_Tome", "Africa/Tripoli", "Africa/Tunis", "Africa/Windhoek", "America/Adak", "America/Anguilla", "America/Antigua", "America/Araguaina", "America/Argentina/Buenos_Aires", "America/Argentina/Catamarca", "America/Argentina/Cordoba", "America/Argentina/Jujuy", "America/Argentina/La_Rioja", "America/Argentina/Mendoza", "America/Argentina/Rio_Gallegos", "America/Argentina/San_Juan", "America/Argentina/San_Luis", "America/Argentina/Tucuman", "America/Argentina/Ushuaia", "America/Aruba", "America/Asuncion", "America/Atikokan", "America/Bahia", "America/Barbados", "America/Belem", "America/Belize", "America/Blanc-Sablon", "America/Boa_Vista", "America/Bogota", "America/Boise", "America/Cambridge_Bay", "America/Campo_Grande", "America/Cancun", "America/Caracas", "America/Cayenne", "America/Cayman", "America/Chicago", "America/Chihuahua", "America/Coral_Harbour", "America/Costa_Rica", "America/Cuiaba", "America/Curacao", "America/Dawson", "America/Dawson_Creek", "America/Denver", "America/Dominica", "America/Edmonton", "America/Eirunepe", "America/El_Salvador", "America/Fortaleza", "America/Glace_Bay", "America/Goose_Bay", "America/Grand_Turk", "America/Grenada", "America/Guadeloupe", "America/Guatemala", "America/Guayaquil", "America/Guyana", "America/Halifax", "America/Havana", "America/Hermosillo", "America/Indiana/Indianapolis", "America/Indiana/Knox", "America/Indiana/Marengo", "America/Indiana/Petersburg", "America/Indiana/Vevay", "America/Indiana/Vincennes", "America/Indiana/Winamac", "America/Inuvik", "America/Iqaluit", "America/Jamaica", "America/Juneau", "America/Kentucky/Louisville", "America/Kentucky/Monticello", "America/La_Paz", "America/Lima", "America/Los_Angeles", "America/Maceio", "America/Managua", "America/Manaus", "America/Marigot", "America/Martinique", "America/Mazatlan", "America/Menominee", "America/Merida", "America/Mexico_City", "America/Miquelon", "America/Moncton", "America/Monterrey", "America/Montevideo", "America/Montreal", "America/Montserrat", "America/Nassau", "America/New_York", "America/Nipigon", "America/Noronha", "America/North_Dakota/Center", "America/North_Dakota/Salem", "America/Panama", "America/Pangnirtung", "America/Paramaribo", "America/Phoenix", "America/Port-au-Prince", "America/Port_of_Spain", "America/Porto_Velho", "America/Puerto_Rico", "America/Rainy_River", "America/Rankin_Inlet", "America/Recife", "America/Regina", "America/Resolute", "America/Rio_Branco", "America/Santarem", "America/Santiago", "America/Santo_Domingo", "America/Sao_Paulo", "America/St_Barthelemy", "America/St_Johns", "America/St_Kitts", "America/St_Lucia", "America/St_Thomas", "America/St_Vincent", "America/Tegucigalpa", "America/Thunder_Bay", "America/Tijuana", "America/Toronto", "America/Tortola", "America/Vancouver", "America/Whitehorse", "America/Winnipeg", "America/Yellowknife", "Ameriica/Swift_Current", "Arctic/Longyearbyen", "Asia/Aden", "Asia/Almaty", "Asia/Amman", "Asia/Anadyr", "Asia/Aqtau", "Asia/Aqtobe", "Asia/Ashgabat", "Asia/Baghdad", "Asia/Bahrain", "Asia/Baku", "Asia/Bangkok", "Asia/Beirut", "Asia/Bishkek", "Asia/Brunei", "Asia/Choibalsan", "Asia/Chongqing", "Asia/Colombo", "Asia/Damascus", "Asia/Dhaka", "Asia/Dili", "Asia/Dubai", "Asia/Dushanbe", "Asia/Gaza", "Asia/Harbin", "Asia/Ho_Chi_Minh", "Asia/Hong_Kong", "Asia/Hovd", "Asia/Irkutsk", "Asia/Jakarta", "Asia/Jayapura", "Asia/Jerusalem", "Asia/Kabul", "Asia/Kamchatka", "Asia/Karachi", "Asia/Kashgar", "Asia/Katmandu", "Asia/Kolkata", "Asia/Krasnoyarsk", "Asia/Kuala_Lumpur", "Asia/Kuching", "Asia/Kuwait", "Asia/Macau", "Asia/Magadan", "Asia/Makassar", "Asia/Manila", "Asia/Muscat", "Asia/Nicosia", "Asia/Novosibirsk", "Asia/Omsk", "Asia/Oral", "Asia/Phnom_Penh", "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", "Asia/Qyzylorda", "Asia/Rangoon", "Asia/Riyadh", "Asia/Sakhalin", "Asia/Samarkand", "Asia/Seoul", "Asia/Shanghai", "Asia/Singapore", "Asia/Taipei", "Asia/Tashkent", "Asia/Tbilisi", "Asia/Tehran", "Asia/Thimphu", "Asia/Tokyo", "Asia/Ulaanbaatar", "Asia/Urumqi", "Asia/Vientiane", "Asia/Vladivostok", "Asia/Yakutsk", "Asia/Yekaterinburg", "Asia/Yerevan", "Atlantic/Azores", "Atlantic/Bermuda", "Atlantic/Canary", "Atlantic/Cape_Verde", "Atlantic/Faroe", "Atlantic/Madeira", "Atlantic/Reykjavik", "Atlantic/South_Georgia", "Atlantic/St_Helena", "Atlantic/Stanley", "Australia/Adelaide", "Australia/Brisbane", "Australia/Broken_Hill", "Australia/Currie", "Australia/Darwin", "Australia/Eucla", "Australia/Hobart", "Australia/Lindeman", "Australia/Lord_Howe", "Australia/Melbourne", "Australia/Perth", "Australia/Sydney", "Europe/Amsterdam", "Europe/Andorra", "Europe/Athens", "Europe/Belgrade", "Europe/Berlin", "Europe/Bratislava", "Europe/Brussels", "Europe/Bucharest", "Europe/Budapest", "Europe/Chisinau", "Europe/Copenhagen", "Europe/Dublin", "Europe/Gibraltar", "Europe/Guernsey", "Europe/Helsinki", "Europe/Isle_of_Man", "Europe/Istanbul", "Europe/Jersey", "Europe/Kaliningrad", "Europe/Kiev", "Europe/Lisbon", "Europe/Ljubljana", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", "Europe/Malta", "Europe/Marienhamn", "Europe/Minsk", "Europe/Monaco", "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Podgorica", "Europe/Prague", "Europe/Riga", "Europe/Rome", "Europe/Samara", "Europe/San_Marino", "Europe/Sarajevo", "Europe/Simferopol", "Europe/Skopje", "Europe/Sofia", "Europe/Stockholm", "Europe/Tallinn", "Europe/Tirane", "Europe/Uzhgorod", "Europe/Vaduz", "Europe/Vatican", "Europe/Vienna", "Europe/Vilnius", "Europe/Volgograd", "Europe/Warsaw", "Europe/Zagreb", "Europe/Zaporozhye", "Europe/Zurich", "Indian/Antananarivo", "Indian/Chagos", "Indian/Christmas", "Indian/Cocos", "Indian/Comoro", "Indian/Kerguelen", "Indian/Mahe", "Indian/Maldives", "Indian/Mauritius", "Indian/Mayotte", "Indian/Reunion", "Pacific/Apia", "Pacific/Auckland", "Pacific/Chatham", "Pacific/Easter", "Pacific/Efate", "Pacific/Enderbury", "Pacific/Fakaofo", "Pacific/Fiji", "Pacific/Funafuti", "Pacific/Galapagos", "Pacific/Gambier", "Pacific/Guadalcanal", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", "Pacific/Majuro", "Pacific/Marquesas", "Pacific/Midway", "Pacific/Nauru", "Pacific/Niue", "Pacific/Norfolk", "Pacific/Noumea", "Pacific/Pago_Pago", "Pacific/Palau", "Pacific/Pitcairn", "Pacific/Ponape", "Pacific/Port_Moresby", "Pacific/Rarotonga", "Pacific/Saipan", "Pacific/Tahiti", "Pacific/Tarawa", "Pacific/Tongatapu", "Pacific/Truk", "Pacific/Wake", "Pacific/Wallis", 0 }; const int N_TIMEZONES = sizeof(IUnit::tblTimezone)/sizeof(const char*); QRegExp IUnit::reCoord1("^\\s*([N|S]){1}\\W*([0-9]+)\\W*([0-9]+\\.[0-9]+)\\s+([E|W|O]){1}\\W*([0-9]+)\\W*([0-9]+\\.[0-9]+)\\s*$"); QRegExp IUnit::reCoord2("^\\s*([N|S]){1}\\s*([0-9]+\\.[0-9]+)\\W*\\s+([E|W|O]){1}\\s*([0-9]+\\.[0-9]+)\\W*\\s*$"); QRegExp IUnit::reCoord3("^\\s*([-0-9]+\\.[0-9]+)\\s+([-0-9]+\\.[0-9]+)\\s*$"); QRegExp IUnit::reCoord4("^\\s*([N|S]){1}\\s*([0-9]+)\\W+([0-9]+)\\W+([0-9]+\\.[0-9]+)\\W*([E|W|O]){1}\\W*([0-9]+)\\W+([0-9]+)\\W+([0-9]+\\.[0-9]+)\\W*\\s*$"); QRegExp IUnit::reCoord5("^\\s*([-0-9]+\\.[0-9]+)([N|S])\\s+([-0-9]+\\.[0-9]+)([W|E])\\s*$"); IUnit::IUnit(const type_e &type, const QString& baseunit, const qreal basefactor, const QString& speedunit, const qreal speedfactor, QObject * parent) : QObject(parent) , type(type) , baseunit(baseunit) , basefactor(basefactor) , speedunit(speedunit) , speedfactor(speedfactor) { //there can be only one... if(m_self) { delete m_self; } m_self = this; } IUnit::~IUnit() { } void IUnit::setUnitType(type_e t, QObject * parent) { switch(t) { case eTypeMetric: new CUnitMetric(parent); break; case eTypeImperial: new CUnitImperial(parent); break; case eTypeNautic: new CUnitNautic(parent); break; } QSettings cfg; cfg.setValue("Units/type",t); } void IUnit::meter2speed(qreal meter, QString& val, QString& unit) { val.sprintf("%2.2f",meter * speedfactor); unit = speedunit; } void IUnit::seconds2time(quint32 ttime, QString& val, QString& unit) { QTime time(0,0,0); quint32 days = ttime / 86400; time = time.addSecs(ttime); if(days) { val = QString("%1:").arg(days) + time.toString("HH:mm:ss"); unit = "d"; } else { val = time.toString("HH:mm:ss"); unit = "h"; } } bool IUnit::parseTimestamp(const QString &time, QDateTime &datetime) { int tzoffset; datetime = parseTimestamp(time, tzoffset); if (!datetime.isValid()) { return false; } return true; } QDateTime IUnit::parseTimestamp(const QString &timetext, int& tzoffset) { const QRegExp tzRE("[-+]\\d\\d:\\d\\d$"); int i; tzoffset = 0; QString format = "yyyy-MM-dd'T'hh:mm:ss"; i = timetext.indexOf("."); if (i != NOIDX) { if(timetext[i+1] == '0') { format += ".zzz"; } else { format += ".z"; } } // trailing "Z" explicitly declares the timestamp to be UTC if (timetext.indexOf("Z") != NOIDX) { format += "'Z'"; } else if ((i = tzRE.indexIn(timetext)) != NOIDX) { // trailing timezone offset [-+]HH:MM present // This does not match the original intentions of the GPX // file format but appears to be found occasionally in // the wild. Try our best parsing it. // add the literal string to the format so fromString() // will succeed format += "'"; format += timetext.right(6); format += "'"; // calculate the offset int offsetHours(timetext.mid(i + 1, 2).toUInt()); int offsetMinutes(timetext.mid(i + 4, 2).toUInt()); if (timetext[i] == '-') { tzoffset = -(60 * offsetHours + offsetMinutes); } else { tzoffset = 60 * offsetHours + offsetMinutes; } tzoffset *= 60; // seconds } QDateTime datetime = QDateTime::fromString(timetext, format); datetime.setOffsetFromUtc(tzoffset); return datetime; } QString IUnit::datetime2string(const QDateTime& time, bool shortDate, const QPointF& pos) { QTimeZone tz; tz_mode_e tmpMode = (pos != NOPOINTF) ? timeZoneMode : eTZLocal; switch(tmpMode) { case eTZUtc: tz = QTimeZone("UTC"); break; case eTZLocal: tz = QTimeZone(QTimeZone::systemTimeZoneId()); break; case eTZAuto: tz = QTimeZone(pos2timezone(pos)); break; case eTZSelected: tz = QTimeZone(timeZone); break; } QDateTime tmp = time.toTimeZone(tz); return tmp.toString((shortDate|useShortFormat) ? Qt::ISODate : Qt::SystemLocaleLongDate); } QByteArray IUnit::pos2timezone(const QPointF& pos) { static QImage imgTimezone = QPixmap(":/pics/timezones.png").toImage(); int x = qRound(2048.0 / 360.0 * (180.0 + pos.x() * RAD_TO_DEG)); int y = qRound(1024.0 / 180.0 * (90.0 - pos.y() * RAD_TO_DEG)); QRgb rgb = imgTimezone.pixel(x,y); if(qRed(rgb) == 0 && qGreen(rgb) == 0) { return "UTC"; } int tz = ((qRed(rgb) & 248) << 1) + ((qGreen(rgb) >> 4) & 15); if(tz >= N_TIMEZONES) { return 0; } return tblTimezone[tz]; } void IUnit::degToStr(const qreal& x, const qreal& y, QString& str) { switch(coordFormat) { case eCoordFormat1: { qint32 degN,degE; qreal minN,minE; bool signLat = GPS_Math_Deg_To_DegMin(y, °N, &minN); bool signLon = GPS_Math_Deg_To_DegMin(x, °E, &minE); QString lat,lng; lat = signLat ? "S" : "N"; lng = signLon ? "W" : "E"; str.sprintf("%s%02d° %06.3f %s%03d° %06.3f",lat.toUtf8().data(),qAbs(degN),minN,lng.toUtf8().data(),qAbs(degE),minE); break; } case eCoordFormat2: { bool signLat = y < 0; bool signLon = x < 0; QString lat,lng; lat = signLat ? "S" : "N"; lng = signLon ? "W" : "E"; str.sprintf("%s%02.6f° %s%03.6f°",lat.toUtf8().data(),qAbs(y),lng.toUtf8().data(),qAbs(x)); break; } case eCoordFormat3: { qint32 degN,degE; qreal minN,minE; bool signLat = GPS_Math_Deg_To_DegMin(y, °N, &minN); bool signLon = GPS_Math_Deg_To_DegMin(x, °E, &minE); qreal secN = (minN - qFloor(minN)) * 60; qreal secE = (minE - qFloor(minE)) * 60; QString lat,lng; lat = signLat ? "S" : "N"; lng = signLon ? "W" : "E"; str.sprintf("%s%02d° %02d' %02.2f'' %s%03d° %02d' %02.2f''",lat.toUtf8().data(),qAbs(degN),qFloor(minN),secN,lng.toUtf8().data(),qAbs(degE),qFloor(minE),secE); break; } } } bool IUnit::strToDeg(const QString& str, qreal& lon, qreal& lat) { if(reCoord2.exactMatch(str)) { bool signLat = reCoord2.cap(1) == "S"; qreal absLat = reCoord2.cap(2).toDouble(); lat = signLat ? -absLat : absLat; bool signLon = reCoord2.cap(3) == "W"; qreal absLon = reCoord2.cap(4).toDouble(); lon = signLon ? -absLon : absLon; } else if(reCoord1.exactMatch(str)) { bool signLat = reCoord1.cap(1) == "S"; int degLat = reCoord1.cap(2).toInt(); qreal minLat = reCoord1.cap(3).toDouble(); GPS_Math_DegMin_To_Deg(signLat, degLat, minLat, lat); bool signLon = reCoord1.cap(4) == "W"; int degLon = reCoord1.cap(5).toInt(); qreal minLon = reCoord1.cap(6).toDouble(); GPS_Math_DegMin_To_Deg(signLon, degLon, minLon, lon); } else if(reCoord3.exactMatch(str)) { lat = reCoord3.cap(1).toDouble(); lon = reCoord3.cap(2).toDouble(); } else if(reCoord4.exactMatch(str)) { bool signLat = reCoord4.cap(1) == "S"; int degLat = reCoord4.cap(2).toInt(); int minLat = reCoord4.cap(3).toInt(); qreal secLat = reCoord4.cap(4).toFloat(); GPS_Math_DegMinSec_To_Deg(signLat, degLat, minLat, secLat, lat); bool signLon = reCoord4.cap(5) == "W"; int degLon = reCoord4.cap(6).toInt(); int minLon = reCoord4.cap(7).toInt(); qreal secLon = reCoord4.cap(8).toFloat(); GPS_Math_DegMinSec_To_Deg(signLon, degLon, minLon, secLon, lon); } else if(reCoord5.exactMatch(str)) { bool signLon = reCoord4.cap(4) == "W"; bool signLat = reCoord4.cap(2) == "S"; lat = reCoord5.cap(1).toDouble(); lon = reCoord5.cap(3).toDouble(); if(signLon) { lon = -lon; } if(signLat) { lat = -lat; } } else { QMessageBox::warning(CMainWindow::getBestWidgetForParent(),QObject::tr("Error"),QObject::tr("Bad position format. Must be: \"[N|S] ddd mm.sss [W|E] ddd mm.sss\" or \"[N|S] ddd.ddd [W|E] ddd.ddd\""),QMessageBox::Ok,QMessageBox::NoButton); return false; } if(fabs(lon) > 180.0 || fabs(lat) > 90.0) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(),QObject::tr("Error"),QObject::tr("Position values out of bounds. "),QMessageBox::Ok,QMessageBox::NoButton); return false; } return true; } bool IUnit::isValidCoordString(const QString& str) { if(reCoord1.exactMatch(str)) { return true; } else if(reCoord2.exactMatch(str)) { return true; } else if(reCoord3.exactMatch(str)) { return true; } else if(reCoord4.exactMatch(str)) { return true; } else if(reCoord5.exactMatch(str)) { return true; } else { return false; } } qmapshack-1.5.1/src/units/CUnitImperial.h000644 001750 000144 00000002667 12622435722 021306 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #ifndef CUNITIMPERIAL_H #define CUNITIMPERIAL_H #include "IUnit.h" class CUnitImperial : public IUnit { Q_OBJECT; public: CUnitImperial(QObject * parent); virtual ~CUnitImperial(); void meter2elevation(qreal meter, QString& val, QString& unit); void meter2distance(qreal meter, QString& val, QString& unit); void meter2area(qreal meter, QString& val, QString& unit); qreal elevation2meter(const QString& val); }; #endif //CUNITIMPERIAL_H qmapshack-1.5.1/src/units/ICoordFormatSetup.ui000644 001750 000144 00000004623 12616741641 022335 0ustar00oeichlerusers000000 000000 ICoordFormatSetup 0 0 287 126 Coordinate Format... N48° 53' 39.6" E13° 31' 6.78" N48.8943° E013.51855° N48° 53.660 E013° 31.113 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ICoordFormatSetup accept() 248 254 157 274 buttonBox rejected() ICoordFormatSetup reject() 316 260 286 274 qmapshack-1.5.1/src/units/CTimeZoneSetup.h000644 001750 000144 00000002303 12622435722 021442 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CTIMEZONESETUP_H #define CTIMEZONESETUP_H #include "ui_ITimeZoneSetup.h" #include class CTimeZoneSetup : public QDialog, private Ui::ITimeZoneSetup { public: CTimeZoneSetup(QWidget * parent); virtual ~CTimeZoneSetup(); public slots: void accept(); }; #endif //CTIMEZONESETUP_H qmapshack-1.5.1/src/units/CUnitImperial.cpp000644 001750 000144 00000004646 12622435717 021644 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #include "units/CUnitImperial.h" CUnitImperial::CUnitImperial(QObject * parent) : IUnit(eTypeImperial, "ft", 3.28084, "ml/h", 2.23693164, parent) { } CUnitImperial::~CUnitImperial() { } void CUnitImperial::meter2elevation(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.0f", meter * 3.28084); unit = "ft"; } } void CUnitImperial::meter2distance(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else if(meter < 10) { val.sprintf("%1.1f", meter * 3.28084); unit = "ft"; } else if(meter < 1600) { val.sprintf("%1.0f", meter * 3.28084); unit = "ft"; } else if(meter < 16000) { val.sprintf("%1.2f", meter * 0.6213699E-3); unit = "ml"; } else if(meter < 32000) { val.sprintf("%1.1f", meter * 0.6213699E-3); unit = "ml"; } else { val.sprintf("%1.0f", meter * 0.6213699E-3); unit = "ml"; } } void CUnitImperial::meter2area(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.2f", meter / (1/0.6213699E-3 * 1/0.6213699E-3)); unit = "ml²"; } } qreal CUnitImperial::elevation2meter(const QString& val) { return val.toDouble() / 3.28084; } qmapshack-1.5.1/src/units/CUnitMetric.cpp000644 001750 000144 00000005434 12622435717 021321 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #include "units/CUnitMetric.h" CUnitMetric::CUnitMetric(QObject * parent) : IUnit(eTypeMetric, "m", 1.0, "km/h", 3.6, parent) { } CUnitMetric::~CUnitMetric() { } void CUnitMetric::meter2elevation(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT || meter == NOINT) { val = "-"; unit = ""; } else { val.sprintf("%1.0f", meter); unit = "m"; } } void CUnitMetric::meter2distance(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else if(meter < 10) { val.sprintf("%1.1f", meter); unit = "m"; } else if(meter < 1000) { val.sprintf("%1.0f", meter); unit = "m"; } else if(meter < 10000) { val.sprintf("%1.2f", meter / 1000); unit = "km"; } else if(meter < 20000) { val.sprintf("%1.1f", meter / 1000); unit = "km"; } else { val.sprintf("%1.0f", meter / 1000); unit = "km"; } } void CUnitMetric::meter2speed(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else if (meter < 0.27) { val.sprintf("%1.0f",meter * speedfactor * 1000); unit = "m/h"; } else if (meter < 10.0) { val.sprintf("%1.1f",meter * speedfactor); unit = speedunit; } else { val.sprintf("%1.0f",meter * speedfactor); unit = speedunit; } } void CUnitMetric::meter2area(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.2f", meter / 1000000); unit = "km²"; } } qreal CUnitMetric::elevation2meter(const QString& val) { return val.toDouble(); } qmapshack-1.5.1/src/units/CUnitNautic.h000644 001750 000144 00000002754 12622435722 020764 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #ifndef CUNITNAUTIC_H #define CUNITNAUTIC_H #include "IUnit.h" class CUnitNautic : public IUnit { Q_OBJECT; public: CUnitNautic(QObject * parent); virtual ~CUnitNautic(); void meter2elevation(qreal meter, QString& val, QString& unit); void meter2distance(qreal meter, QString& val, QString& unit); void meter2speed(qreal meter, QString& val, QString& unit); void meter2area(qreal meter, QString& val, QString& unit); qreal elevation2meter(const QString& val); }; #endif //CUNITNAUTIC_H qmapshack-1.5.1/src/units/CCoordFormatSetup.h000644 001750 000144 00000002355 12622435722 022136 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCOORDFORMATSETUP_H #define CCOORDFORMATSETUP_H #include "ui_ICoordFormatSetup.h" #include class CCoordFormatSetup : public QDialog, private Ui::ICoordFormatSetup { Q_OBJECT public: CCoordFormatSetup(QWidget * parent); virtual ~CCoordFormatSetup(); public slots: void accept(); }; #endif //CCOORDFORMATSETUP_H qmapshack-1.5.1/src/units/CUnitMetric.h000644 001750 000144 00000002753 12622435722 020763 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #ifndef CUNITMETRIC_H #define CUNITMETRIC_H #include "IUnit.h" class CUnitMetric : public IUnit { Q_OBJECT; public: CUnitMetric(QObject * parent); virtual ~CUnitMetric(); void meter2elevation(qreal meter, QString& val, QString& unit); void meter2distance(qreal meter, QString& val, QString& unit); void meter2speed(qreal meter, QString& val, QString& unit); void meter2area(qreal meter, QString& val, QString& unit); qreal elevation2meter(const QString& val); }; #endif //CUNITMETRIC_H qmapshack-1.5.1/src/units/IUnit.h000644 001750 000144 00000011236 12622435722 017621 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #ifndef IUNIT_H #define IUNIT_H #include #include #define NOFLOAT 1000000000000.0 #define NOINT 0x7FFFFFFF #define NOTIME 0xFFFFFFFF #define NOIDX (-1) extern const QPointF NOPOINTF; extern const QPoint NOPOINT; class IUnit : public QObject { Q_OBJECT public: virtual ~IUnit(); static IUnit& self() { return *m_self; } /// convert meter of elevation into a value and unit string virtual void meter2elevation(qreal meter, QString& val, QString& unit) = 0; /// convert meter of distance into a value and unit string virtual void meter2distance(qreal meter, QString& val, QString& unit) = 0; /// convert meter per second to a speed value string and unit label virtual void meter2speed(qreal meter, QString& val, QString& unit); /// convert square meter to string and unit label virtual void meter2area(qreal meter, QString& val, QString& unit) = 0; /// convert seconds to a timespan of days, hours, minutes and seconds virtual void seconds2time(quint32 ttime, QString& val, QString& unit); /// convert an elevation string to a float virtual qreal elevation2meter(const QString& val) = 0; enum type_e {eTypeMetric, eTypeImperial, eTypeNautic}; /// instantiate the correct unit object static void setUnitType(type_e t, QObject *parent); /// parse a string for a timestamp static bool parseTimestamp(const QString &time, QDateTime &datetime); /** @brief Convert date time object to string using the current timezone configuration @param time the date/time object @param shortDate set true to get a short ISO time string @param pos optional a position attached to the date/time object [rad] @return A time string. */ static QString datetime2string(const QDateTime& time, bool shortDate, const QPointF& pos = NOPOINTF); /// find the timezone setup by position static QByteArray pos2timezone(const QPointF& pos); const type_e type; const QString baseunit; const qreal basefactor; const QString speedunit; const qreal speedfactor; static const char * tblTimezone[]; enum tz_mode_e { eTZUtc ,eTZLocal ,eTZAuto ,eTZSelected }; static void getTimeZoneSetup(tz_mode_e& mode, QByteArray& zone, bool& format) { mode = timeZoneMode; zone = timeZone; format = useShortFormat; } static void setTimeZoneSetup(tz_mode_e mode, const QByteArray& zone, bool format) { timeZoneMode = mode; timeZone = zone; useShortFormat = format; } enum coord_format_e { eCoordFormat1 ,eCoordFormat2 ,eCoordFormat3 }; static void getCoordFormat(coord_format_e& format) { format = coordFormat; } static void setCoordFormat(const coord_format_e format) { coordFormat = format; } static void degToStr(const qreal& x, const qreal& y, QString& str); static bool strToDeg(const QString& str, qreal& lon, qreal& lat); static bool isValidCoordString(const QString& str); static QRegExp reCoord1; static QRegExp reCoord2; static QRegExp reCoord3; static QRegExp reCoord4; static QRegExp reCoord5; protected: IUnit(const type_e& type, const QString& baseunit, const qreal basefactor, const QString& speedunit, const qreal speedfactor, QObject * parent); static QDateTime parseTimestamp(const QString &timetext, int& tzoffset); static tz_mode_e timeZoneMode; static QByteArray timeZone; static bool useShortFormat; static coord_format_e coordFormat; private: static IUnit * m_self; }; #endif //IUNIT_H qmapshack-1.5.1/src/units/CUnitsSetup.cpp000644 001750 000144 00000003436 12622435717 021361 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "units/CUnitsSetup.h" #include "units/IUnit.h" CUnitsSetup::CUnitsSetup(QWidget *parent) : QDialog(parent) { setupUi(this); switch(IUnit::self().type) { case IUnit::eTypeMetric: radioMetric->setChecked(true); break; case IUnit::eTypeImperial: radioImperial->setChecked(true); break; case IUnit::eTypeNautic: radioNautic->setChecked(true); break; } } CUnitsSetup::~CUnitsSetup() { } void CUnitsSetup::accept() { if(radioMetric->isChecked()) { IUnit::setUnitType(IUnit::eTypeMetric, &CMainWindow::self()); } else if(radioImperial->isChecked()) { IUnit::setUnitType(IUnit::eTypeImperial, &CMainWindow::self()); } else if(radioNautic->isChecked()) { IUnit::setUnitType(IUnit::eTypeNautic, &CMainWindow::self()); } QDialog::accept(); } qmapshack-1.5.1/src/units/CTimeZoneSetup.cpp000644 001750 000144 00000005036 12622435717 022007 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "units/CTimeZoneSetup.h" #include "units/IUnit.h" CTimeZoneSetup::CTimeZoneSetup(QWidget *parent) : QDialog(parent) { setupUi(this); QByteArray zone; IUnit::tz_mode_e mode; bool useShortFormat; IUnit::getTimeZoneSetup(mode, zone, useShortFormat); switch(mode) { case IUnit::eTZUtc: radioUtc->setChecked(true); break; case IUnit::eTZLocal: radioLocal->setChecked(true); break; case IUnit::eTZAuto: radioAutomatic->setChecked(true); break; case IUnit::eTZSelected: radioSelected->setChecked(true); break; } const char ** tz = IUnit::tblTimezone; while(*tz) { comboTimeZone->addItem(*tz); tz++; } if(useShortFormat) { radioShortFormat->setChecked(true); } else { radioLongFormat->setChecked(true); } comboTimeZone->setCurrentIndex(comboTimeZone->findText(QString(zone))); } CTimeZoneSetup::~CTimeZoneSetup() { } void CTimeZoneSetup::accept() { QByteArray zone = comboTimeZone->currentText().toLatin1(); IUnit::tz_mode_e mode = IUnit::eTZUtc; bool useShortFormat = false; if(radioUtc->isChecked()) { mode = IUnit::eTZUtc; } else if(radioLocal->isChecked()) { mode = IUnit::eTZLocal; } else if(radioAutomatic->isChecked()) { mode = IUnit::eTZAuto; } else if(radioSelected->isChecked()) { mode = IUnit::eTZSelected; } if(radioShortFormat->isChecked()) { useShortFormat = true; } IUnit::setTimeZoneSetup(mode, zone, useShortFormat); QDialog::accept(); } qmapshack-1.5.1/src/units/CUnitNautic.cpp000644 001750 000144 00000004266 12622435717 021323 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA **********************************************************************************************/ #include "units/CUnitNautic.h" CUnitNautic::CUnitNautic(QObject * parent) : IUnit(eTypeNautic, "nm", 0.00053989, "nm/h", 1.94361780, parent) { } CUnitNautic::~CUnitNautic() { } void CUnitNautic::meter2elevation(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.0f", meter); unit = "m"; } } void CUnitNautic::meter2distance(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.2f", meter * basefactor); unit = baseunit; } } void CUnitNautic::meter2speed(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.2f",meter * speedfactor); unit = speedunit; } } void CUnitNautic::meter2area(qreal meter, QString& val, QString& unit) { if(meter == NOFLOAT) { val = "-"; unit = ""; } else { val.sprintf("%1.2f", meter / (1852 * 1852)); unit = "nm²"; } } qreal CUnitNautic::elevation2meter(const QString& val) { return val.toDouble(); } qmapshack-1.5.1/src/units/CCoordFormatSetup.cpp000644 001750 000144 00000003625 12622435717 022476 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "units/CCoordFormatSetup.h" #include "units/IUnit.h" CCoordFormatSetup::CCoordFormatSetup(QWidget * parent) : QDialog(parent) { setupUi(this); IUnit::coord_format_e coordFormat; IUnit::getCoordFormat(coordFormat); switch(coordFormat) { case IUnit::eCoordFormat1: radioFormat1->setChecked(true); break; case IUnit::eCoordFormat2: radioFormat2->setChecked(true); break; case IUnit::eCoordFormat3: radioFormat3->setChecked(true); break; } } CCoordFormatSetup::~CCoordFormatSetup() { } void CCoordFormatSetup::accept() { IUnit::coord_format_e coordFormat = IUnit::eCoordFormat1; if(radioFormat1->isChecked()) { coordFormat = IUnit::eCoordFormat1; } else if(radioFormat2->isChecked()) { coordFormat = IUnit::eCoordFormat2; } else if(radioFormat3->isChecked()) { coordFormat = IUnit::eCoordFormat3; } IUnit::setCoordFormat(coordFormat); QDialog::accept(); } qmapshack-1.5.1/src/units/ITimeZoneSetup.ui000644 001750 000144 00000011056 12527654570 021653 0ustar00oeichlerusers000000 000000 ITimeZoneSetup 0 0 398 106 Setup Time Zone ... UTC buttonGroup Local buttonGroup Automatic buttonGroup 0 0 buttonGroup Print date/time in long format, or buttonGroup_2 short format buttonGroup_2 Qt::Horizontal 40 20 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ITimeZoneSetup accept() 248 254 157 274 buttonBox rejected() ITimeZoneSetup reject() 316 260 286 274 qmapshack-1.5.1/src/units/IUnitsSetup.ui000644 001750 000144 00000005603 12623160161 021205 0ustar00oeichlerusers000000 000000 IUnitsSetup 0 0 281 147 Setup units... Nautic Imperial Metric <b>Note:</b> For some GUI elements changing the units will not take effect until you restart QMapShack. Qt::AlignJustify|Qt::AlignTop true Qt::Vertical 20 7 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IUnitsSetup accept() 248 254 157 274 buttonBox rejected() IUnitsSetup reject() 316 260 286 274 qmapshack-1.5.1/src/GeoMath.h000644 001750 000144 00000005603 12622435722 016754 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2009 Oliver Eichler oliver.eichler@gmx.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********************************************************************************************/ #ifndef GEOMATH_H #define GEOMATH_H #include #include #include #include class QPolygonF; class IDrawContext; struct point3D { point3D() : x(0), y(0), z(0) { } qreal x; qreal y; qreal z; }; struct pointDP : public point3D { pointDP(); bool used; qint32 idx; }; struct segment_t { segment_t(); void apply(const QPolygonF& coords, const QPolygonF& pixel, QPolygonF& segCoord, QPolygonF& segPixel, IDrawContext * context); qint32 idx11; qint32 idx12; qint32 idx21; QPointF px1; QPointF px2; }; extern void GPS_Math_DegMin_To_Deg(bool sign, const qint32 d, const qreal m, qreal& deg); extern void GPS_Math_DegMinSec_To_Deg(bool sign, const qint32 d, const qint32 m, const qreal s, qreal& deg); extern bool GPS_Math_Deg_To_DegMin(qreal v, qint32 *d, qreal *m); /// use for long distances extern qreal GPS_Math_Distance(const qreal u1, const qreal v1, const qreal u2, const qreal v2, qreal& a1, qreal& a2); extern qreal GPS_Math_Distance(const qreal u1, const qreal v1, const qreal u2, const qreal v2); /// use for short distances, much quicker processing extern qreal GPS_Math_DistanceQuick(const qreal u1, const qreal v1, const qreal u2, const qreal v2); extern void GPS_Math_Wpt_Projection(const qreal lon1, const qreal lat1, const qreal distance, const qreal bearing, qreal& lon2, qreal& lat2); extern void GPS_Math_DouglasPeucker(QVector& line, qreal d); extern QPointF GPS_Math_Wpt_Projection(const QPointF& pt1, qreal distance, qreal bearing); extern bool GPS_Math_LineCrossesRect(const QPointF& p1, const QPointF& p2, const QRectF& rect); extern void GPS_Math_SubPolyline(const QPointF& pt1, const QPointF& pt2, qint32 threshold, const QPolygonF& pixel, segment_t& result); extern qreal GPS_Math_DistPointPolyline(const QPolygonF &points, const QPointF &q); #endif //GEOMATH_H qmapshack-1.5.1/src/version.h000644 001750 000144 00000002264 12622435722 017115 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef VERSION_H #define VERSION_H #ifndef _MKSTR_1 #define _MKSTR_1(x) #x #define _MKSTR(x) _MKSTR_1(x) #endif #define VER_STR _MKSTR(VER_MAJOR) "." _MKSTR (VER_MINOR) "." _MKSTR (VER_STEP) #define WHAT_STR _MKSTR(APPLICATION_NAME) ", Version " VER_STR #endif //VERSION_H qmapshack-1.5.1/src/widgets/CTextEditWidget.h000644 001750 000144 00000005767 12622435722 022112 0ustar00oeichlerusers000000 000000 /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the demonstration applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef CTEXTEDITWIDGET_H #define CTEXTEDITWIDGET_H #include "ui_ITextEditWidget.h" #include #include #include #include class CTextEditWidget : public QDialog, private Ui::ITextEditWidget { Q_OBJECT public: CTextEditWidget(QWidget * parent); virtual ~CTextEditWidget(); QString getHtml(); void setHtml(const QString& text) { textEdit->clear(); textEdit->insertHtml(text); } private slots: void textBold(); void textUnderline(); void textItalic(); void textStyle(int styleIndex); void textColor(); void textAlign(QAction *a); void currentCharFormatChanged(const QTextCharFormat &format); void cursorPositionChanged(); void clipboardDataChanged(); private: void mergeFormatOnWordOrSelection(const QTextCharFormat &format); void fontChanged(const QFont &f); void colorChanged(const QColor &c); void alignmentChanged(Qt::Alignment a); QAction * actionTextColor; }; #endif //CTEXTEDITWIDGET_H qmapshack-1.5.1/src/widgets/CFadingIcon.h000644 001750 000144 00000002364 12622435722 021203 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFADINGICON_H #define CFADINGICON_H #include #include class CFadingIcon : public QLabel { Q_OBJECT public: CFadingIcon(const QPoint &pt, const QString& resource, QWidget * parent); virtual ~CFadingIcon(); private slots: void slotTimeout(); private: qreal o = 1.0; QPixmap icon; }; #endif //CFADINGICON_H qmapshack-1.5.1/src/widgets/CDoubleSpinBox.h000644 001750 000144 00000002467 12622435722 021723 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDOUBLESPINBOX_H #define CDOUBLESPINBOX_H #include class CDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT public slots: void slotSelectAll() { selectAll(); } signals: void valueChangedByStep(double val); public: CDoubleSpinBox(QWidget * parent = 0); void stepBy(int steps); protected: void focusInEvent(QFocusEvent *event); }; #endif // CDOUBLESPINBOX_H qmapshack-1.5.1/src/widgets/IPhotoAlbum.ui000644 001750 000144 00000004513 12616742144 021450 0ustar00oeichlerusers000000 000000 IPhotoAlbum 0 0 400 150 0 0 Form 0 0 0 0 0 false 0 0 ... :/icons/32x32/Left.png:/icons/32x32/Left.png false 0 0 ... :/icons/32x32/Right.png:/icons/32x32/Right.png qmapshack-1.5.1/src/widgets/CPhotoAlbum.h000644 001750 000144 00000003263 12622435722 021253 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPHOTOALBUM_H #define CPHOTOALBUM_H #include "ui_IPhotoAlbum.h" #include #include class CPhotoAlbum : public QWidget, private Ui::IPhotoAlbum { Q_OBJECT public: CPhotoAlbum(QWidget * parent); virtual ~CPhotoAlbum(); void reload(const QList& imgs); signals: void sigChanged(const QList& imgs); public slots: void slotAddImage(); void slotDelImage(); protected: void resizeEvent(QResizeEvent * e); void mouseReleaseEvent(QMouseEvent *e); private slots: void slotRight(); void slotLeft(); private: void updateView(); QList images; QList rects; qint32 idx1stVisible = 0; qint32 idxSelected = 0; }; #endif //CPHOTOALBUM_H qmapshack-1.5.1/src/widgets/CHistoryListWidget.h000644 001750 000144 00000002711 12622435722 022637 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CHISTORYLISTWIDGET_H #define CHISTORYLISTWIDGET_H #include "gis/IGisItem.h" #include class CHistoryListWidget : public QListWidget { Q_OBJECT public: CHistoryListWidget(QWidget * parent); virtual ~CHistoryListWidget(); void setupHistory(IGisItem &gisItem); signals: void sigChanged(); private slots: void slotSelectionChanged(); void slotContextMenu(const QPoint& point); void slotCutHistory(); private: IGisItem::key_t key; QMenu * menu; QAction * actionCutHistory; }; #endif //CHISTORYLISTWIDGET_H qmapshack-1.5.1/src/widgets/CFadingIcon.cpp000644 001750 000144 00000003245 12622435717 021541 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CFadingIcon.h" #include CFadingIcon::CFadingIcon(const QPoint& pt, const QString &resource, QWidget *parent) : QLabel(parent) , icon(resource) { setPixmap(icon); QTimer * timer = new QTimer(this); timer->setSingleShot(false); timer->setInterval(100); timer->start(); connect(timer, SIGNAL(timeout()), this, SLOT(slotTimeout())); move(pt.x() - icon.width()/2, pt.y() - icon.height()/2); show(); } CFadingIcon::~CFadingIcon() { } void CFadingIcon::slotTimeout() { o -= 0.1; if(o <= 0) { deleteLater(); } else { QPixmap tmp(icon.size()); tmp.fill(Qt::transparent); QPainter p(&tmp); p.setOpacity(o); p.drawPixmap(0,0,icon); setPixmap(tmp); } } qmapshack-1.5.1/src/widgets/ITextEditWidget.ui000644 001750 000144 00000026376 12616742144 022307 0ustar00oeichlerusers000000 000000 ITextEditWidget 0 0 427 341 Edit text... 2 ... 24 24 ... 24 24 ... 24 24 ... 24 24 ... 24 24 Qt::Vertical ... 24 24 ... 24 24 ... 24 24 ... 24 24 Qt::Horizontal 40 20 2 ... 24 24 ... 24 24 ... 24 24 ... 24 24 Qt::Horizontal 40 20 QDialogButtonBox::Cancel|QDialogButtonBox::Ok :/icons/32x32/Undo.png:/icons/32x32/Undo.png Undo Ctrl+Z :/icons/32x32/Redo.png:/icons/32x32/Redo.png Redo Ctrl+Shift+Z :/icons/32x32/Cut.png:/icons/32x32/Cut.png Cut Ctrl+X :/icons/32x32/Copy.png:/icons/32x32/Copy.png Copy Ctrl+C :/icons/32x32/Paste.png:/icons/32x32/Paste.png Paste Ctrl+V true :/icons/32x32/TextLeft.png:/icons/32x32/TextLeft.png Align Left Ctrl+L true :/icons/32x32/TextRight.png:/icons/32x32/TextRight.png Align Right Ctrl+R true :/icons/32x32/TextCenter.png:/icons/32x32/TextCenter.png Align Center Ctrl+E true :/icons/32x32/TextJustified.png:/icons/32x32/TextJustified.png Align Block Ctrl+J true :/icons/32x32/TextUnderlined.png:/icons/32x32/TextUnderlined.png Underline Ctrl+U true :/icons/32x32/TextBold.png:/icons/32x32/TextBold.png Bold Ctrl+B true :/icons/32x32/TextItalic.png:/icons/32x32/TextItalic.png Italic Ctrl+I buttonBox accepted() ITextEditWidget accept() 454 479 454 250 buttonBox rejected() ITextEditWidget reject() 454 479 454 250 qmapshack-1.5.1/src/widgets/CPhotoAlbum.cpp000644 001750 000144 00000012037 12622435717 021611 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CPhotoViewer.h" #include "helpers/CSettings.h" #include "widgets/CPhotoAlbum.h" #include CPhotoAlbum::CPhotoAlbum(QWidget *parent) : QWidget(parent) { setupUi(this); setFocusPolicy(Qt::WheelFocus); connect(toolLeft, SIGNAL(clicked()), this, SLOT(slotLeft())); connect(toolRight, SIGNAL(clicked()), this, SLOT(slotRight())); } CPhotoAlbum::~CPhotoAlbum() { } void CPhotoAlbum::resizeEvent(QResizeEvent * e) { QWidget::resizeEvent(e); updateView(); } void CPhotoAlbum::mouseReleaseEvent(QMouseEvent * e) { CPhotoViewer dlg(images, 0,this); dlg.exec(); e->accept(); } void CPhotoAlbum::reload(const QList& imgs) { images = imgs; if(idxSelected >= images.size()) { idx1stVisible = 0; idxSelected = 0; } updateView(); } void CPhotoAlbum::slotAddImage() { SETTINGS; QString path = cfg.value("Paths/lastWptImagePath", QDir::homePath()).toString(); QString filters = "All Files (*);; All Images (*.png *.jpg);; PNG Image (*.png);; JPEG Image (*.jpg)"; QString defaultFilter = "All Images (*.png *.jpg)"; QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Select images..."), path, filters, &defaultFilter); if(filenames.isEmpty()) { return; } foreach(const QString &filename, filenames) { CGisItemWpt::image_t image; image.fileName = filename; if(image.pixmap.load(filename)) { int w = image.pixmap.width(); int h = image.pixmap.height(); if(w < h) { h *= 400.0 / w; w = 400; } else { h *= 600.0 / w; w = 600; } image.pixmap = image.pixmap.scaled(w,h,Qt::KeepAspectRatio, Qt::SmoothTransformation); images << image; } else { qDebug() << "Cannot load image from file " << filename; } } QFileInfo fi(filenames.first()); path = fi.absolutePath(); cfg.setValue("Paths/lastWptImagePath", path); emit sigChanged(images); } void CPhotoAlbum::slotDelImage() { images.removeAt(idxSelected); emit sigChanged(images); } void CPhotoAlbum::slotRight() { idxSelected++; QRect r1 = rects[idxSelected]; QRect r2 = label->rect(); while(!r2.contains(r1)) { int w = rects[idx1stVisible].width(); r1.moveLeft(r1.left() - w); idx1stVisible++; } updateView(); } void CPhotoAlbum::slotLeft() { idxSelected--; QRect r1 = rects[idxSelected]; QRect r2 = label->rect(); while(!r2.contains(r1)) { idx1stVisible--; int w = rects[idx1stVisible].width(); r1.moveLeft(r1.left() + w); } updateView(); } void CPhotoAlbum::updateView() { toolLeft->setEnabled(idxSelected != 0); toolRight->setEnabled(idxSelected != (images.size() - 1)); if(images.isEmpty()) { hide(); return; } setEnabled(true); show(); QPixmap img(label->size()); img.fill(Qt::black); QPainter p(&img); int xoff = 0; for(int i = 0; i < rects.size() && i < idx1stVisible; i++) { xoff -= rects[i].width(); } rects.clear(); for(int i = 0; i < images.size(); i++) { CGisItemWpt::image_t& image = images[i]; QImage tmp = image.pixmap.scaledToHeight(label->height(), Qt::SmoothTransformation); if(tmp.width() > label->width()) { tmp = image.pixmap.scaledToWidth(label->width(), Qt::SmoothTransformation); } QRect r = tmp.rect(); int yoff = (height()- r.height()) / 2; p.save(); p.translate(xoff,yoff); p.drawImage(0,0,tmp); p.setPen(QPen(Qt::black, 3)); p.setBrush(Qt::NoBrush); p.drawRect(r); p.restore(); r.moveTopLeft(QPoint(xoff, yoff)); rects << r; xoff += tmp.width(); } if(idxSelected < rects.size()) { p.setPen(QPen(Qt::yellow, 5)); p.drawRect(rects[idxSelected]); } label->setPixmap(img); } qmapshack-1.5.1/src/widgets/CColorLegend.h000644 001750 000144 00000003323 12622435722 021373 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCOLORLEGEND_H #define CCOLORLEGEND_H #include "gis/trk/CGisItemTrk.h" #include "helpers/INotifiable.h" #include class CColorLegend : public QWidget, public INotifiable { Q_OBJECT public: CColorLegend(QWidget *parent, CGisItemTrk *trk = nullptr); ~CColorLegend(); void setMinimum(qreal min); void setMaximum(qreal max); void setUnit(const QString &unit); void notify(); protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); private: int paintLabel(QPainter &p, qreal value); const int colorWidth = 18; const int colorHeight = 256; QRect colorRect; QString unit; qreal minimum; qreal maximum; bool background = false; int xOffset = 1; CGisItemTrk *trk = nullptr; }; #endif // CCOLORLEGEND_H qmapshack-1.5.1/src/widgets/CHistoryListWidget.cpp000644 001750 000144 00000006547 12622435717 023211 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisWidget.h" #include "gis/prj/IGisProject.h" #include "widgets/CHistoryListWidget.h" #include CHistoryListWidget::CHistoryListWidget(QWidget *parent) : QListWidget(parent) { setIconSize(QSize(32,32)); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint))); menu = new QMenu(this); actionCutHistory = menu->addAction(QIcon("://icons/32x32/CutHistory.png"),tr("Cut history"), this, SLOT(slotCutHistory())); } CHistoryListWidget::~CHistoryListWidget() { } void CHistoryListWidget::setupHistory(IGisItem& gisItem) { blockSignals(true); clear(); key = gisItem.getKey(); const IGisItem::history_t& history = gisItem.getHistory(); //foreach(const IGisItem::history_event_t& event, history.events) for(int i = 0; i < history.events.size(); i++) { const IGisItem::history_event_t& event = history.events[i]; QString str; QListWidgetItem * item = new QListWidgetItem(this); str = event.time.toString(); str += "\n"; str += event.comment; item->setText(str); item->setIcon(QIcon(event.icon)); if(event.data.isEmpty()) { item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } } if(history.histIdxCurrent < count()) { setCurrentItem(item(history.histIdxCurrent)); } blockSignals(false); } void CHistoryListWidget::slotSelectionChanged() { IGisItem * item = CGisWidget::self().getItemByKey(key); if(item == 0) { return; } item->loadHistory(currentRow()); item->updateDecoration(IGisItem::eMarkChanged, IGisItem::eMarkNone); emit sigChanged(); } void CHistoryListWidget::slotContextMenu(const QPoint& point) { if(currentRow() == (count() - 1) || (count() == 0)) { return; } QPoint p = mapToGlobal(point); menu->exec(p); } void CHistoryListWidget::slotCutHistory() { if(currentRow() == (count() - 1)) { return; } IGisItem * item = CGisWidget::self().getItemByKey(key); if(item == 0) { return; } item->cutHistory(); item->setText(CGisListWks::eColumnDecoration,"*"); IGisProject * project = dynamic_cast(item->parent()); if(project) { project->setChanged(); } emit sigChanged(); } qmapshack-1.5.1/src/widgets/CTextEditWidget.cpp000644 001750 000144 00000023225 12622435717 022436 0ustar00oeichlerusers000000 000000 /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the demonstration applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "CTextEditWidget.h" #include CTextEditWidget::CTextEditWidget(QWidget * parent) : QDialog(parent) { setupUi(this); connect(actionTextBold, SIGNAL(triggered()), this, SLOT(textBold())); toolBold->setDefaultAction(actionTextBold); connect(actionTextItalic, SIGNAL(triggered()), this, SLOT(textItalic())); toolItalic->setDefaultAction(actionTextItalic); connect(actionTextUnderline, SIGNAL(triggered()), this, SLOT(textUnderline())); toolUnder->setDefaultAction(actionTextUnderline); QActionGroup *grp = new QActionGroup(this); grp->addAction(actionAlignLeft); grp->addAction(actionAlignRight); grp->addAction(actionAlignCenter); grp->addAction(actionAlignJustify); connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(textAlign(QAction *))); toolLeft->setDefaultAction(actionAlignLeft); toolCenter->setDefaultAction(actionAlignCenter); toolRight->setDefaultAction(actionAlignRight); toolBlock->setDefaultAction(actionAlignJustify); QPixmap pix(24, 24); pix.fill(Qt::black); actionTextColor = new QAction(pix, tr("&Color..."), this); connect(actionTextColor, SIGNAL(triggered()), this, SLOT(textColor())); toolColor->setDefaultAction(actionTextColor); comboStyle->addItem("standard"); comboStyle->addItem("Bullet List (Disc)"); comboStyle->addItem("Bullet List (Circle)"); comboStyle->addItem("Bullet List (Square)"); comboStyle->addItem("Ordered List (Decimal)"); comboStyle->addItem("Ordered List (Alpha lower)"); comboStyle->addItem("Ordered List (Alpha upper)"); connect(comboStyle, SIGNAL(activated(int)), this, SLOT(textStyle(int))); connect(textEdit, SIGNAL(currentCharFormatChanged(const QTextCharFormat &)), this, SLOT(currentCharFormatChanged(const QTextCharFormat &))); connect(textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged())); textEdit->setFocus(); fontChanged(textEdit->font()); colorChanged(textEdit->textColor()); alignmentChanged(textEdit->alignment()); toolUndo->setDefaultAction(actionUndo); toolRedo->setDefaultAction(actionRedo); toolCut->setDefaultAction(actionCut); toolCopy->setDefaultAction(actionCopy); toolPaste->setDefaultAction(actionPaste); actionPaste->setEnabled(!QApplication::clipboard()->text().isEmpty()); actionUndo->setEnabled(textEdit->document()->isUndoAvailable()); actionRedo->setEnabled(textEdit->document()->isRedoAvailable()); connect(textEdit->document(), SIGNAL(undoAvailable(bool)), actionUndo, SLOT(setEnabled(bool))); connect(textEdit->document(), SIGNAL(redoAvailable(bool)), actionRedo, SLOT(setEnabled(bool))); connect(actionUndo, SIGNAL(triggered()), textEdit, SLOT(undo())); connect(actionRedo, SIGNAL(triggered()), textEdit, SLOT(redo())); actionCut->setEnabled(false); actionCopy->setEnabled(false); connect(actionCut, SIGNAL(triggered()), textEdit, SLOT(cut())); connect(actionCopy, SIGNAL(triggered()), textEdit, SLOT(copy())); connect(actionPaste, SIGNAL(triggered()), textEdit, SLOT(paste())); connect(textEdit, SIGNAL(copyAvailable(bool)), actionCut, SLOT(setEnabled(bool))); connect(textEdit, SIGNAL(copyAvailable(bool)), actionCopy, SLOT(setEnabled(bool))); connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged())); } CTextEditWidget::~CTextEditWidget() { } QString CTextEditWidget::getHtml() { QString str = textEdit->toHtml(); QRegExp re(".*(\\).*"); if(re.exactMatch(str)) { str = re.cap(1); QRegExp re1(""); re1.setMinimal(true); str = str.replace("body>","div>").replace(re1,"
"); } return str; } void CTextEditWidget::textBold() { QTextCharFormat fmt; fmt.setFontWeight(actionTextBold->isChecked() ? QFont::Bold : QFont::Normal); mergeFormatOnWordOrSelection(fmt); } void CTextEditWidget::textUnderline() { QTextCharFormat fmt; fmt.setFontUnderline(actionTextUnderline->isChecked()); mergeFormatOnWordOrSelection(fmt); } void CTextEditWidget::textItalic() { QTextCharFormat fmt; fmt.setFontItalic(actionTextItalic->isChecked()); mergeFormatOnWordOrSelection(fmt); } void CTextEditWidget::textAlign(QAction *a) { if (a == actionAlignLeft) { textEdit->setAlignment(Qt::AlignLeft); } else if (a == actionAlignCenter) { textEdit->setAlignment(Qt::AlignHCenter); } else if (a == actionAlignRight) { textEdit->setAlignment(Qt::AlignRight); } else if (a == actionAlignJustify) { textEdit->setAlignment(Qt::AlignJustify); } } void CTextEditWidget::textStyle(int styleIndex) { QTextCursor cursor = textEdit->textCursor(); if (styleIndex != 0) { QTextListFormat::Style style = QTextListFormat::ListDisc; switch (styleIndex) { default: case 1: style = QTextListFormat::ListDisc; break; case 2: style = QTextListFormat::ListCircle; break; case 3: style = QTextListFormat::ListSquare; break; case 4: style = QTextListFormat::ListDecimal; break; case 5: style = QTextListFormat::ListLowerAlpha; break; case 6: style = QTextListFormat::ListUpperAlpha; break; } cursor.beginEditBlock(); QTextBlockFormat blockFmt = cursor.blockFormat(); QTextListFormat listFmt; if (cursor.currentList()) { listFmt = cursor.currentList()->format(); } else { listFmt.setIndent(blockFmt.indent() + 1); blockFmt.setIndent(0); cursor.setBlockFormat(blockFmt); } listFmt.setStyle(style); cursor.createList(listFmt); cursor.endEditBlock(); } else { // #### QTextBlockFormat bfmt; bfmt.setObjectIndex(-1); cursor.mergeBlockFormat(bfmt); } } void CTextEditWidget::textColor() { QColor col = QColorDialog::getColor(textEdit->textColor(), this); if (!col.isValid()) { return; } QTextCharFormat fmt; fmt.setForeground(col); mergeFormatOnWordOrSelection(fmt); colorChanged(col); } void CTextEditWidget::mergeFormatOnWordOrSelection(const QTextCharFormat &format) { QTextCursor cursor = textEdit->textCursor(); if (!cursor.hasSelection()) { cursor.select(QTextCursor::WordUnderCursor); } cursor.mergeCharFormat(format); textEdit->mergeCurrentCharFormat(format); } void CTextEditWidget::fontChanged(const QFont &f) { actionTextBold->setChecked(f.bold()); actionTextItalic->setChecked(f.italic()); actionTextUnderline->setChecked(f.underline()); } void CTextEditWidget::colorChanged(const QColor &c) { QPixmap pix(16, 16); pix.fill(c); actionTextColor->setIcon(pix); } void CTextEditWidget::alignmentChanged(Qt::Alignment a) { if (a & Qt::AlignLeft) { actionAlignLeft->setChecked(true); } else if (a & Qt::AlignHCenter) { actionAlignCenter->setChecked(true); } else if (a & Qt::AlignRight) { actionAlignRight->setChecked(true); } else if (a & Qt::AlignJustify) { actionAlignJustify->setChecked(true); } } void CTextEditWidget::currentCharFormatChanged(const QTextCharFormat &format) { fontChanged(format.font()); colorChanged(format.foreground().color()); } void CTextEditWidget::cursorPositionChanged() { alignmentChanged(textEdit->alignment()); } void CTextEditWidget::clipboardDataChanged() { actionPaste->setEnabled(!QApplication::clipboard()->text().isEmpty()); } qmapshack-1.5.1/src/widgets/CTinySpinBox.h000644 001750 000144 00000004006 12622435722 021423 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ /** @brief A QWidget derived from QSpinBox, aiming to be tiny with regard to spaced consumed on screen. By default, a readonly CTinySpinBox looks like an ordinary label, whereas a non-readonly CTinySpinBox contains blue, underlined text. As soon as the non-readonly widget receives the focus, the color is changed to black, the underline disappears and the modifiable text is selected. */ #ifndef CTINYSPINBOX_H #define CTINYSPINBOX_H #include #include #include class CTinySpinBox : public QSpinBox { Q_OBJECT private: bool initialized; QPalette paletteEdit; QPalette paletteRO; QPalette paletteRW; QFont fontNoUnderline; QFont fontUnderline; void initialize(); void updateStyle(); public slots: void slotSelectAll() { selectAll(); } signals: void valueChangedByStep(int val); public: CTinySpinBox(QWidget * parent = 0); void setReadOnly(bool r); void stepBy(int steps); protected: void focusInEvent(QFocusEvent *event); void focusOutEvent(QFocusEvent *event); }; #endif // CTINYSPINBOX_H qmapshack-1.5.1/src/widgets/CDoubleSpinBox.cpp000644 001750 000144 00000002530 12622435717 022251 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "widgets/CDoubleSpinBox.h" #include CDoubleSpinBox::CDoubleSpinBox(QWidget * parent) : QDoubleSpinBox(parent) { } void CDoubleSpinBox::stepBy(int steps) { QDoubleSpinBox::stepBy(steps); emit valueChangedByStep(value()); } void CDoubleSpinBox::focusInEvent(QFocusEvent *event) { if(!isReadOnly()) { QTimer::singleShot(0, this, SLOT(slotSelectAll())); } QDoubleSpinBox::focusInEvent(event); } qmapshack-1.5.1/src/widgets/CTinySpinBox.cpp000644 001750 000144 00000004567 12622435717 021776 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "widgets/CTinySpinBox.h" #include void CTinySpinBox::initialize() { initialized = true; paletteEdit = QPalette(palette()); paletteRO = QPalette(palette()); paletteRW = QPalette(palette()); paletteRW.setColor(QPalette::Text, QColor(0, 0, 255)); fontNoUnderline = QFont(font()); fontUnderline = QFont(font()); fontUnderline.setUnderline(true); } void CTinySpinBox::updateStyle() { if(!initialized) { initialize(); } if(isReadOnly()) { setPalette(paletteRO); setFont(fontNoUnderline); } else if(hasFocus()) { setPalette(paletteEdit); setFont(fontNoUnderline); } else { setPalette(paletteRW); setFont(fontUnderline); } } CTinySpinBox::CTinySpinBox(QWidget * parent) : QSpinBox(parent) { // initialization has to be done deferred, // as the correct palette is set after construction initialized = false; } void CTinySpinBox::stepBy(int steps) { QSpinBox::stepBy(steps); emit valueChangedByStep(value()); } void CTinySpinBox::setReadOnly(bool r) { QSpinBox::setReadOnly(r); updateStyle(); } void CTinySpinBox::focusInEvent(QFocusEvent *event) { updateStyle(); if(!isReadOnly()) { QTimer::singleShot(0, this, SLOT(slotSelectAll())); } QSpinBox::focusInEvent(event); } void CTinySpinBox::focusOutEvent(QFocusEvent *event) { updateStyle(); QSpinBox::focusOutEvent(event); } qmapshack-1.5.1/src/widgets/CColorLegend.cpp000644 001750 000144 00000012053 12622435717 021732 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "widgets/CColorLegend.h" #include CColorLegend::CColorLegend(QWidget *parent, CGisItemTrk *trk) : QWidget(parent), trk(trk) { colorRect = QRect(0, 0, colorWidth, colorHeight); colorRect.moveCenter(QPoint(xOffset + colorWidth / 2, height() / 2)); if(nullptr != trk) { background = true; xOffset = 5; trk->registerNotification(this); // read data from trk notify(); } } CColorLegend::~CColorLegend() { if(trk) { trk->unregisterNotification(this); } } void CColorLegend::notify() { if(!trk->getColorizeSource().isEmpty()) { unit = trk->getColorizeUnit(); minimum = trk->getColorizeLimitLow(); maximum = trk->getColorizeLimitHigh(); update(); show(); } else { hide(); } } void CColorLegend::setMinimum(qreal min) { minimum = min; update(); } void CColorLegend::setMaximum(qreal max) { maximum = max; update(); } void CColorLegend::setUnit(const QString &unit) { this->unit = unit; update(); } int CColorLegend::paintLabel(QPainter &p, qreal value) { const int fontHeight = QFontMetrics(p.font()).ascent() + 1; const qreal relativePos = (value - minimum) / (maximum - minimum); const int posY = colorRect.bottom() + fontHeight / 2 - (2 + colorRect.height()) * relativePos + 1; int posX = xOffset + colorWidth + 3; p.setPen( QPen(QBrush(Qt::black), 2.) ); p.drawLine(posX, posY - fontHeight / 2 + 1, posX + 2, posY - fontHeight / 2 + 1); if(value == minimum || value == maximum || (posY > colorRect.top() + 3*fontHeight / 2 && posY < colorRect.bottom() - fontHeight / 2)) { posX += 5; const QString &labelText = QString("%1%2").arg(value).arg(unit); p.drawText(posX, posY, labelText); posX += QFontMetrics(p.font()).width(labelText); } return posX; } void CColorLegend::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); colorRect.setHeight(height() - 20); colorRect.moveCenter(QPoint(xOffset + colorWidth / 2, height() / 2)); updateGeometry(); } static qreal legendRound(qreal value, int powOffset) { int l10 = (int) (value > 0) ? log10(value) : log10(-value); qreal div = pow(10, l10 + powOffset); return ceil(value / div) * div; } void CColorLegend::paintEvent(QPaintEvent *event) { const QFont &font = CMainWindow::self().getMapFont(); if(isEnabled()) { QPainter p(this); p.setFont(font); if(background) { p.setRenderHint(QPainter::Antialiasing); p.setOpacity(0.6); p.setPen( QPen(QBrush(Qt::darkGray), 2.) ); p.setBrush(Qt::white); p.drawRoundedRect(1, 1, width() - 2, height() - 2, 5.f, 5.f); p.setOpacity(1.f); p.setRenderHint(QPainter::Antialiasing, false); } // draw the black frame QRect borderRect(colorRect); borderRect += QMargins(1, 1, 1, 1); p.setPen( QPen(QBrush(Qt::SolidPattern), 2., Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin) ); p.drawRect(borderRect); // draw the gradient QLinearGradient grad(colorRect.topLeft(), colorRect.bottomLeft()); grad.setColorAt(1.00, QColor( 0, 0, 255)); // blue grad.setColorAt(0.60, QColor( 0, 255, 0)); // green grad.setColorAt(0.40, QColor(255, 255, 0)); // yellow grad.setColorAt(0.00, QColor(255, 0, 0)); // red p.fillRect(colorRect, grad); int reqWidth = paintLabel(p, minimum); reqWidth = qMax(paintLabel(p, maximum), reqWidth); // draw values inbetween min/max const qreal delta = maximum - minimum; qreal step = legendRound(delta / 8, 0); qreal roundedMinimum = legendRound(minimum, delta > 60 ? -1 : 0); for(qreal v = roundedMinimum; v < maximum; v+= step) { reqWidth = qMax(paintLabel(p, v), reqWidth); } if(reqWidth + 5 != width()) { setMinimumWidth(reqWidth + 5); resize(reqWidth + 5, height()); } p.end(); } } qmapshack-1.5.1/src/CAbout.h000644 001750 000144 00000002141 12622435722 016577 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CABOUT_H #define CABOUT_H #include "ui_IAbout.h" #include class CAbout : public QDialog, private Ui::IAbout { public: CAbout(QWidget * parent); virtual ~CAbout(); }; #endif //CABOUT_H qmapshack-1.5.1/src/mouse/CMouseMoveWpt.cpp000644 001750 000144 00000007506 12622435717 021640 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "GeoMath.h" #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/WptIcons.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CDraw.h" #include "mouse/CMouseMoveWpt.h" #include "units/IUnit.h" #include #include CMouseMoveWpt::CMouseMoveWpt(CGisItemWpt &wpt, CGisDraw * gis, CCanvas *parent) : IMouse(gis, parent) { cursor = QCursor(QPixmap(":/cursors/cursorMoveWpt.png"),0,0); key = wpt.getKey(); icon = getWptIconByName(wpt.getIconName(), focus); origPos = wpt.getPosition() * DEG_TO_RAD; } CMouseMoveWpt::~CMouseMoveWpt() { } void CMouseMoveWpt::draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) { QString val, unit, str; qreal d, a1 = 0, a2 = 0; QPointF p1 = origPos; QPointF p2 = newPos; d = GPS_Math_Distance(p1.x(), p1.y(), p2.x(), p2.y(), a1, a2); IUnit::self().meter2distance(d, val, unit); str = QString("%1 %2, %3").arg(val).arg(unit).arg(a2, 0, 'f', 1); gis->convertRad2Px(p1); gis->convertRad2Px(p2); QPointF p11 = p1 + QPoint(17 * qCos((a1 - 90) * DEG_TO_RAD), 17 * qSin((a1 - 90) * DEG_TO_RAD)); QPointF p22 = p2 + QPoint(21 * qCos((a2 + 90) * DEG_TO_RAD), 21 * qSin((a2 + 90) * DEG_TO_RAD)); QPen pen(Qt::darkBlue, 3); pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::MiterJoin); p.setPen(pen); p.setBrush(Qt::NoBrush); p.drawEllipse(p1, 16, 16); p.drawEllipse(p2, 16, 16); p.drawLine(p11,p22); p.save(); p.translate(p22); p.rotate(a2 + 180); QPolygonF arrow; arrow << QPointF(0,0) << QPointF(5, -20) << QPointF(0, -10) << QPointF(-5, -20); p.setBrush(Qt::NoBrush); p.drawPolygon(arrow); p.restore(); CDraw::text(str, p, (p2 + QPoint(0, -30)).toPoint(), Qt::darkBlue); p.drawPixmap(p1 - focus, icon); p.drawPixmap(p2 - focus, icon); } void CMouseMoveWpt::slotPanCanvas() { IMouse::slotPanCanvas(); newPos = point; gis->convertPx2Rad(newPos); } void CMouseMoveWpt::mousePressEvent(QMouseEvent * e) { point = e->pos(); if(e->button() == Qt::RightButton) { canvas->resetMouse(); canvas->update(); } else if(e->button() == Qt::LeftButton) { QMutexLocker lock(&IGisItem::mutexItems); QPointF pos = e->pos(); gis->convertPx2Rad(pos); CGisItemWpt * wpt = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(wpt != 0) { wpt->setPosition(pos * RAD_TO_DEG); } canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } } void CMouseMoveWpt::mouseMoveEvent(QMouseEvent * e) { point = e->pos(); newPos = point; gis->convertPx2Rad(newPos); panCanvas(point); } void CMouseMoveWpt::mouseReleaseEvent(QMouseEvent *e) { point = e->pos(); } void CMouseMoveWpt::wheelEvent(QWheelEvent * e) { canvas->update(); } qmapshack-1.5.1/src/mouse/CScrOptUnclutter.cpp000644 001750 000144 00000012231 12622435717 022335 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/IGisItem.h" #include "gis/trk/CGisItemTrk.h" #include "helpers/CDraw.h" #include "mouse/CScrOptUnclutter.h" #include const QPoint CScrOptUnclutter::positions[9][8] = { { }, { QPoint(-50,-23) }, { QPoint(-30,0) , QPoint( 30,0) }, { QPoint( 0,-30) , QPoint( 30, 30) , QPoint(-30, 30) }, { QPoint(-30,-30) , QPoint( 30,-30) , QPoint(-30, 30) , QPoint( 30, 30) }, { QPoint(-25, 40) , QPoint( 25, 40) , QPoint(-40, -5) , QPoint( 40, -5) , QPoint( 0,-40) }, { QPoint(-40,-22) , QPoint( 40,-22) , QPoint(-40, 22) , QPoint( 40, 22) , QPoint( 0,-55) , QPoint( 0, 55) }, { QPoint(-50,-23) , QPoint( 50,-23) , QPoint(-45, 21) , QPoint( 45, 21) , QPoint(-22,-55) , QPoint( 22,-55) , QPoint( 0, 50) }, { QPoint(-50,-23) , QPoint( 50,-23) , QPoint(-50, 23) , QPoint( 50, 23) , QPoint(-22,-55) , QPoint( 22,-55) , QPoint(-22, 55) , QPoint( 22, 55) } }; CScrOptUnclutter::CScrOptUnclutter(IMouse *mouse) : IScrOpt(mouse) { } CScrOptUnclutter::~CScrOptUnclutter() { } void CScrOptUnclutter::clear() { if(doSpecialCursor) { CCanvas::restoreOverrideCursor("CScrOptUnclutter::clear()"); doSpecialCursor = false; } items.clear(); } void CScrOptUnclutter::mouseMoveEvent(QMouseEvent * e) { IScrOpt::mouseMoveEvent(e); foreach(const item_t &item, items) { if(item.active.contains(mousePos) || item.text.contains(mousePos)) { if(!doSpecialCursor) { CCanvas::setOverrideCursor(Qt::PointingHandCursor,"CScrOptUnclutter::mouseMoveEvent"); doSpecialCursor = true; } return; } } if(doSpecialCursor) { CCanvas::restoreOverrideCursor("CScrOptUnclutter::mouseMoveEvent"); doSpecialCursor = false; return; } } void CScrOptUnclutter::addItem(IGisItem * gisItem) { items << item_t(); item_t& item = items.last(); item.hasUserFocus = gisItem->hasUserFocus(); item.name = gisItem->getNameEx(); item.key = gisItem->getKey(); item.icon = gisItem->getIcon(); item.area = item.icon.rect(); item.active = item.area.adjusted(-10,-10,10,10); } IGisItem::key_t CScrOptUnclutter::getItemKey(int index) { if(index < items.size()) { return items[index].key; } return IGisItem::key_t(); } const CScrOptUnclutter::item_t * CScrOptUnclutter::selectItem(const QPoint& point) { foreach(const item_t &item, items) { if(item.active.contains(point) || item.text.contains(point)) { return &item; } } return 0; } void CScrOptUnclutter::draw(QPainter& p) { const int N = items.size(); QFontMetrics fm(CMainWindow::self().getMapFont()); for(int cnt = 0; cnt < N; cnt++) { item_t& item = items[cnt]; if(item.text.isNull()) { item.area.moveCenter(origin + positions[N][cnt]); item.active.moveCenter(item.area.center()); item.text = fm.boundingRect(item.name); if(cnt & 0x01) { item.text.moveTopLeft(item.area.topRight() + QPoint( 17, fm.height()/2)); } else { item.text.moveTopRight(item.area.topLeft() + QPoint(-17, fm.height()/2)); } item.text.adjust(-4, -3, 4, 3); } } foreach(const item_t &item, items) { p.setPen(Qt::NoPen); p.setBrush(QColor(255,255,255,255)); p.drawEllipse(item.area.center(), 20,20); p.drawRoundedRect(item.text, 3, 3); p.setPen(QPen(item.hasUserFocus ? Qt::red : Qt::lightGray,2)); p.setBrush(Qt::NoBrush); p.drawRoundedRect(item.text, 3, 3); p.drawEllipse(item.area.center(), 18,18); p.drawPixmap(item.area, item.icon); CDraw::text(item.name, p, item.text, Qt::darkBlue); } } qmapshack-1.5.1/src/mouse/CMouseNormal.h000644 001750 000144 00000006016 12622435722 021123 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSENORMAL_H #define CMOUSENORMAL_H #include "IMouse.h" #include #include #include #include class CCanvas; class IScrOpt; class CScrOptUnclutter; class QMenu; class CMouseNormal : public IMouse { Q_OBJECT public: CMouseNormal(CGisDraw * gis, CCanvas *canvas); virtual ~CMouseNormal(); void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent *e); void wheelEvent(QWheelEvent * e); void keyPressEvent(QKeyEvent * e); private slots: void slotAddWpt(); void slotAddTrk(); void slotAddRte(); void slotAddArea(); void slotCopyPosition(); void slotCopyPositionGrid(); private: bool setScreenOption(const QPoint& pt, IGisItem * item); protected: void stopTracking(); void resetState(); /// the flag is true if the map moving is in progress bool mapMove = false; /// the flag is true if the map has been moved actually bool mapDidMove = false; /// always the last seen mouse cursor position QPoint lastPos; enum item_selection_states_e { eStateIdle = 0 /// there is only a single item close to the cursor , eStateHooverSingle = 1 /// there are multiple items close to the cursor , eStateHooverMultiple = 2 /// not a real state, but at this value and above no map move is allowed to take place , eStateNoMapMovePossible = 3 /// the user clicked on multiple cluttered items and gets now an uncluttered representation , eStateUnclutterMultiple = 3 /// the user has selected a single item, show options how to proceed , eStateShowItemOptions = 4 }; item_selection_states_e stateItemSel = eStateIdle; CScrOptUnclutter * screenUnclutter; QPointer screenItemOption; QMenu * menu; }; #endif //CMOUSENORMAL_H qmapshack-1.5.1/src/mouse/IScrOpt.cpp000644 001750 000144 00000003217 12622435717 020441 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CDraw.h" #include "mouse/IMouse.h" #include "mouse/IScrOpt.h" #include "units/IUnit.h" #include IScrOpt::IScrOpt(IMouse *mouse) : QWidget(mouse->getCanvas()) , mouse(mouse) { setFocusPolicy(Qt::WheelFocus); } IScrOpt::~IScrOpt() { if(hasFocus() && !mouse.isNull()) { CCanvas::setOverrideCursor(*mouse,"IScrOpt::~IScrOpt"); } } void IScrOpt::mouseMoveEvent(QMouseEvent * e) { mousePos = e->pos(); } void IScrOpt::enterEvent(QEvent * e) { QWidget::enterEvent(e); CCanvas::restoreOverrideCursor("IScrOpt::enterEvent"); } void IScrOpt::leaveEvent(QEvent * e) { QWidget::leaveEvent(e); if(!mouse.isNull()) { CCanvas::setOverrideCursor(*mouse,"IScrOpt::leaveEvent"); } } qmapshack-1.5.1/src/mouse/CMousePrint.h000644 001750 000144 00000004175 12622435722 020773 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEPRINT_H #define CMOUSEPRINT_H #include "mouse/IMouse.h" class CGisDraw; class CCanvas; class CMousePrint : public IMouse { Q_OBJECT public: CMousePrint(CGisDraw * gis, CCanvas * parent); virtual ~CMousePrint(); void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent *e); void wheelEvent(QWheelEvent * e); private: QPoint lastPos; QPointF offset; QPointF posInitial; QRectF rectSelection; QRectF rectTopLeft {0,0,20,20}; QRectF rectTopRight {0,0,20,20}; QRectF rectBottomLeft {0,0,20,20}; QRectF rectBottomRight {0,0,20,20}; QRectF rectPrintButton {0,0,48,48}; QRectF rectImageButton {0,0,48,48}; enum state_e { eStateIdle ,eStateInitial ,eStateMap ,eStateMapMoving ,eStateResize }; state_e state = eStateIdle; enum corner_e { eCornerNone , eCornerTopLeft , eCornerTopRight , eCornerBottomLeft , eCornerBottomRight , eCornerPrint , eCornerImage }; corner_e corner = eCornerNone; }; #endif //CMOUSEPRINT_H qmapshack-1.5.1/src/mouse/CMouseWptBubble.h000644 001750 000144 00000003101 12622435722 021551 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEWPTBUBBLE_H #define CMOUSEWPTBUBBLE_H #include "gis/IGisItem.h" #include "mouse/IMouse.h" class CGisItemWpt; class CGisDraw; class CCanvas; class CMouseWptBubble : public IMouse { Q_OBJECT public: CMouseWptBubble(const IGisItem::key_t& key, CGisDraw * gis, CCanvas * parent); virtual ~CMouseWptBubble(); virtual void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect); virtual void mousePressEvent(QMouseEvent * e); virtual void mouseMoveEvent(QMouseEvent * e); virtual void mouseReleaseEvent(QMouseEvent *e); virtual void wheelEvent(QWheelEvent * e); private: const IGisItem::key_t& key; }; #endif //CMOUSEWPTBUBBLE_H qmapshack-1.5.1/src/mouse/CMousePrint.cpp000644 001750 000144 00000020137 12622435717 021326 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisDraw.h" #include "mouse/CMousePrint.h" #include "print/CPrintDialog.h" #include CMousePrint::CMousePrint(CGisDraw *gis, CCanvas *parent) : IMouse(gis, parent) { cursor = QCursor(QPixmap("://cursors/cursorSave.png"),0,0); canvas->reportStatus("CMousePrint", tr("Save(Print) Map
Select a rectangular area on the map. Use the left mouse button and move the mouse. Abort with a right click. Adjust the selection by point-click-move on the corners. Save/print the selection by a left click on the disc/printer icon in the center of the selection.")); } CMousePrint::~CMousePrint() { canvas->reportStatus("CMousePrint", ""); } void CMousePrint::draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) { if(rectSelection.isNull()) { return; } QPointF pt1 = rectSelection.topLeft(); QPointF pt2 = rectSelection.bottomRight(); gis->convertRad2Px(pt1); gis->convertRad2Px(pt2); QRectF rectSel(pt1,pt2); QRectF rectScr = canvas->rect(); rectTopLeft.moveTopLeft(rectSel.topLeft()); rectTopRight.moveTopRight(rectSel.topRight()); rectBottomLeft.moveBottomLeft(rectSel.bottomLeft()); rectBottomRight.moveBottomRight(rectSel.bottomRight()); QPainterPath path; path.addRect(rectScr); path.addRect(rectSel); p.setPen(Qt::black); p.setBrush(QColor(0,0,0,128)); p.drawPath(path); p.setBrush(Qt::lightGray); p.drawRect(rectTopLeft); p.drawRect(rectTopRight); p.drawRect(rectBottomLeft); p.drawRect(rectBottomRight); p.setBrush(Qt::red); switch(corner) { case eCornerTopLeft: p.drawRect(rectTopLeft); break; case eCornerTopRight: p.drawRect(rectTopRight); break; case eCornerBottomLeft: p.drawRect(rectBottomLeft); break; case eCornerBottomRight: p.drawRect(rectBottomRight); break; } if(rectSel.width() > 100 && rectSel.height() > 50) { rectPrintButton.moveCenter(rectSel.center() + QPointF(30,0)); p.setPen(corner == eCornerPrint ? QPen(Qt::red,3) : QPen(Qt::darkBlue,2)); p.setBrush(Qt::white); p.drawRect(rectPrintButton.adjusted(-3,-3,3,3)); p.drawPixmap(rectPrintButton.topLeft(), QPixmap("://icons/48x48/Print.png")); rectImageButton.moveCenter(rectSel.center() - QPointF(30,0)); p.setPen(corner == eCornerImage ? QPen(Qt::red,3) : QPen(Qt::darkBlue,2)); p.setBrush(Qt::white); p.drawRect(rectImageButton.adjusted(-3,-3,3,3)); p.drawPixmap(rectImageButton.topLeft(), QPixmap("://icons/48x48/Save.png")); } } void CMousePrint::mousePressEvent(QMouseEvent * e) { e->accept(); canvas->reportStatus("CMousePrint", ""); if(e->button() == Qt::RightButton) { canvas->resetMouse(); canvas->update(); } else if(e->button() == Qt::LeftButton) { switch(state) { case eStateIdle: { QPointF pos = e->pos(); gis->convertPx2Rad(pos); rectSelection.setTopLeft(pos); rectSelection.setBottomRight(pos); posInitial = pos; state = eStateInitial; break; } case eStateMap: { if(corner != eCornerNone) { if(corner == eCornerPrint) { CPrintDialog dlg(CPrintDialog::eTypePrint, rectSelection, canvas); dlg.exec(); canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawAll); } else if(corner == eCornerImage) { CPrintDialog dlg(CPrintDialog::eTypeImage, rectSelection, canvas); dlg.exec(); canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawAll); } else { state = eStateResize; } } else { lastPos = e->pos(); state = eStateMapMoving; } break; } } } } void CMousePrint::mouseMoveEvent(QMouseEvent * e) { e->accept(); switch(state) { case eStateInitial: { QPointF pos = e->pos(); gis->convertPx2Rad(pos); if(pos.x() < posInitial.x()) { rectSelection.setLeft(pos.x()); } else { rectSelection.setRight(pos.x()); } if(pos.y() < posInitial.y()) { rectSelection.setBottom(pos.y()); } else { rectSelection.setTop(pos.y()); } canvas->update(); break; } case eStateMap: { corner_e _corner = corner; QPoint pos = e->pos(); if(rectTopLeft.contains(pos)) { offset = pos - rectTopLeft.topLeft(); corner = eCornerTopLeft; } else if(rectTopRight.contains(pos)) { offset = pos - rectTopRight.topRight(); corner = eCornerTopRight; } else if(rectBottomLeft.contains(pos)) { offset = pos - rectBottomLeft.bottomLeft(); corner = eCornerBottomLeft; } else if(rectBottomRight.contains(pos)) { offset = pos - rectBottomRight.bottomRight(); corner = eCornerBottomRight; } else if(rectPrintButton.contains(pos)) { corner = eCornerPrint; } else if(rectImageButton.contains(pos)) { corner = eCornerImage; } else { corner = eCornerNone; } if(corner != _corner) { canvas->update(); } break; } case eStateMapMoving: { QPoint pos = e->pos(); if(pos != lastPos) { QPoint delta = pos - lastPos; canvas->moveMap(delta); lastPos = pos; } break; } case eStateResize: { QPointF pos = e->pos() - offset; gis->convertPx2Rad(pos); switch(corner) { case eCornerTopLeft: rectSelection.setTopLeft(pos); break; case eCornerTopRight: rectSelection.setTopRight(pos); break; case eCornerBottomLeft: rectSelection.setBottomLeft(pos); break; case eCornerBottomRight: rectSelection.setBottomRight(pos); break; } canvas->update(); break; } } } void CMousePrint::mouseReleaseEvent(QMouseEvent *e) { e->accept(); if(!rectSelection.isNull()) { QPointF pt1 = rectSelection.topLeft(); QPointF pt2 = rectSelection.bottomRight(); gis->convertRad2Px(pt1); gis->convertRad2Px(pt2); QRectF rectSel(pt1,pt2); if(rectSel.width() < 40 || rectSel.height() < 40) { rectSelection = QRectF(); } } state = rectSelection.isNull() ? eStateIdle : eStateMap; canvas->update(); } void CMousePrint::wheelEvent(QWheelEvent * e) { } qmapshack-1.5.1/src/mouse/CMouseEditTrk.h000644 001750 000144 00000002727 12622435722 021246 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEEDITTRK_H #define CMOUSEEDITTRK_H #include "mouse/line/IMouseEditLine.h" class CGisItemTrk; class CMouseEditTrk : public IMouseEditLine { Q_OBJECT public: CMouseEditTrk(const QPointF& point, CGisDraw * gis, CCanvas * parent); CMouseEditTrk(CGisItemTrk &trk, CGisDraw * gis, CCanvas * parent); virtual ~CMouseEditTrk(); void mousePressEvent(QMouseEvent * e); protected slots: void slotAbort(); void slotCopyToNew(); void slotCopyToOrig(); protected: IGisLine * getGisLine(); bool isNewLine = true; }; #endif //CMOUSEEDITTRK_H qmapshack-1.5.1/src/mouse/CMouseEditRte.cpp000644 001750 000144 00000007050 12624371122 021560 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/rte/CGisItemRte.h" #include "mouse/CMouseEditRte.h" #include "mouse/line/CScrOptEditLine.h" #include CMouseEditRte::CMouseEditRte(const QPointF &point, CGisDraw *gis, CCanvas *parent) : IMouseEditLine(IGisItem::key_t(), point, true, tr("Route"), gis, parent) { startNewLine(point); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } CMouseEditRte::CMouseEditRte(CGisItemRte &rte, CGisDraw * gis, CCanvas * parent) : IMouseEditLine(rte.getKey(), rte, true, tr("Route"), gis, parent) { canvas->reportStatus(key.item, tr("Edit Route Points
Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.
")); if(!points.first().subpts.isEmpty()) { scrOptEditLine->toolAutoRoute->setChecked(true); } else { scrOptEditLine->toolNoRoute->setChecked(true); } /* trigger complete update of GIS components to make sure all changes to the originating object are reflected on the canvas */ canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } CMouseEditRte::~CMouseEditRte() { } void CMouseEditRte::mousePressEvent(QMouseEvent * e) { canvas->reportStatus(key.item, ""); IMouseEditLine::mousePressEvent(e); } IGisLine * CMouseEditRte::getGisLine() { return dynamic_cast(CGisWidget::self().getItemByKey(key)); } void CMouseEditRte::slotAbort() { canvas->reportStatus(key.item,""); IMouseEditLine::slotAbortEx(false); } void CMouseEditRte::slotCopyToOrig() { canvas->reportStatus(key.item,""); IMouseEditLine::slotCopyToOrig(); } void CMouseEditRte::slotCopyToNew() { QMutexLocker lock(&IGisItem::mutexItems); canvas->reportStatus(key.item,""); if(points.size() < 2) { return; } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } /// @todo make this independent from track QString name; CGisItemRte * rte = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(rte != 0) { name = rte->getName(); } name = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new route name."), QLineEdit::Normal, name); if(name.isEmpty()) { return; } new CGisItemRte(points,name, project, NOIDX); canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } qmapshack-1.5.1/src/mouse/CScrOptRangeTrk.cpp000644 001750 000144 00000003577 12622435717 022102 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/trk/CGisItemTrk.h" #include "helpers/CDraw.h" #include "mouse/CScrOptRangeTrk.h" #include CScrOptRangeTrk::CScrOptRangeTrk(const QPointF &point, CGisItemTrk * trk, IMouse *mouse, QWidget *parent) : IScrOpt(mouse) { if(parent != 0) { setParent(parent); } setupUi(this); label->setFont(CMainWindow::self().getMapFont()); label->setText(trk->getInfoRange()); adjustSize(); setOrigin(point.toPoint()); move(point.toPoint() + QPoint(-width()/2,SCR_OPT_OFFSET)); show(); connect(toolHidePoints, SIGNAL(clicked()), this, SLOT(hide())); connect(toolShowPoints, SIGNAL(clicked()), this, SLOT(hide())); connect(toolActivity, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(hide())); } CScrOptRangeTrk::~CScrOptRangeTrk() { } void CScrOptRangeTrk::draw(QPainter& p) { if(isVisible()) { CDraw::bubble(p, geometry(), origin); } } qmapshack-1.5.1/src/mouse/CMouseRangeTrk.cpp000644 001750 000144 00000017661 12622435717 021757 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/trk/CGisItemTrk.h" #include "mouse/CMouseRangeTrk.h" #include "mouse/CScrOptRangeTrk.h" #include CMouseRangeTrk::CMouseRangeTrk(CGisItemTrk &trk, CGisDraw *gis, CCanvas *parent) : IMouse(gis, parent) { cursor = QCursor(QPixmap("://cursors/cursorSelectRange.png"),0,0); key = trk.getKey(); // switch to full mode to show deleted (hidden) track points, too trk.setMode(CGisItemTrk::eModeRange, "CMouseRangeTrk"); // reset user focus if the track has it trk.setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseMove, "CMouseRangeTrk"); trk.setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseClick, "CMouseRangeTrk"); canvas->reportStatus(key.item, tr("Select Range
Select first track point. And then a second one.
")); /* trigger complete update of GIS components to make sure all changes to the originating object are reflected on the canvas */ canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } CMouseRangeTrk::~CMouseRangeTrk() { canvas->reportStatus(key.item, ""); CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk) { trk->setMode(CGisItemTrk::eModeNormal, "CMouseRangeTrk"); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } delete scrOptRange; } void CMouseRangeTrk::draw(QPainter& p, CCanvas::redraw_e, const QRect &) { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk) { trk->drawRange(p); if(anchor != NOPOINTF) { p.setPen(Qt::darkBlue); p.setBrush(QColor(255,255,255,200)); p.drawEllipse(anchor, 6, 6); QPixmap bullet("://icons/8x8/bullet_magenta.png"); p.drawPixmap(anchor.x() - 3, anchor.y() - 3, bullet); } } if(!scrOptRange.isNull()) { scrOptRange->draw(p); } } void CMouseRangeTrk::mousePressEvent(QMouseEvent * e) { canvas->reportStatus(key.item, ""); point = e->pos(); if(e->button() == Qt::RightButton) { canvas->resetMouse(); canvas->update(); } else if(e->button() == Qt::LeftButton) { switch(state) { case eStateIdle: { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0 && anchor != NOPOINTF) { anchor = trk->setMouseFocusByPoint(point, CGisItemTrk::eFocusMouseClick, "CMouseRangeTrk"); state = eStateSelectRange; canvas->update(); } else { state = eStateMoveMap; } break; } case eStateSelectRange: { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0 && anchor != NOPOINTF) { QPointF pt = trk->setMouseFocusByPoint(point, CGisItemTrk::eFocusMouseClick, "CMouseRangeTrk"); scrOptRange = new CScrOptRangeTrk(pt, trk, this); connect(scrOptRange->toolHidePoints, SIGNAL(clicked()), this, SLOT(slotHidePoints())); connect(scrOptRange->toolShowPoints, SIGNAL(clicked()), this, SLOT(slotShowPoints())); connect(scrOptRange->toolActivity, SIGNAL(clicked()), this, SLOT(slotActivity())); connect(scrOptRange->toolCopy, SIGNAL(clicked()), this, SLOT(slotCopy())); state = eStateRangeSelected; canvas->update(); } break; } case eStateRangeSelected: { resetState(); canvas->update(); break; } default: ; } } } void CMouseRangeTrk::mouseMoveEvent(QMouseEvent * e) { point = e->pos(); switch(state) { case eStateIdle: { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { anchor = trk->setMouseFocusByPoint(point, CGisItemTrk::eFocusMouseMove, "CMouseRangeTrk"); canvas->update(); } break; } case eStateMoveMap: { if(point != lastPoint) { QPoint delta = point - lastPoint; canvas->moveMap(delta); } break; } case eStateSelectRange: { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { anchor = trk->setMouseFocusByPoint(point, CGisItemTrk::eFocusMouseMove, "CMouseRangeTrk"); canvas->update(); } panCanvas(point); break; } default: ; } lastPoint = point; } void CMouseRangeTrk::mouseReleaseEvent(QMouseEvent *e) { if(e->button() == Qt::LeftButton) { if(state == eStateMoveMap) { state = eStateIdle; } } } void CMouseRangeTrk::wheelEvent(QWheelEvent * e) { resetState(); } void CMouseRangeTrk::keyPressEvent(QKeyEvent * e) { resetState(); } void CMouseRangeTrk::resetState() { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { trk->setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseMove, "CMouseRangeTrk"); trk->setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseClick, "CMouseRangeTrk"); } if(!scrOptRange.isNull()) { scrOptRange->deleteLater(); } state = eStateIdle; } void CMouseRangeTrk::slotHidePoints() { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { trk->hideSelectedPoints(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } scrOptRange->deleteLater(); canvas->resetMouse(); } void CMouseRangeTrk::slotShowPoints() { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { trk->showSelectedPoints(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } scrOptRange->deleteLater(); canvas->resetMouse(); } void CMouseRangeTrk::slotActivity() { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { trk->setActivity(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } scrOptRange->deleteLater(); canvas->resetMouse(); } void CMouseRangeTrk::slotCopy() { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { trk->copySelectedPoints(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } scrOptRange->deleteLater(); canvas->resetMouse(); } qmapshack-1.5.1/src/mouse/CMouseEditArea.cpp000644 001750 000144 00000006677 12624371123 021715 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/ovl/CGisItemOvlArea.h" #include "mouse/CMouseEditArea.h" #include CMouseEditArea::CMouseEditArea(const QPointF& point, CGisDraw * gis, CCanvas * parent) : IMouseEditLine(IGisItem::key_t(), point, false, tr("Area"), gis, parent) { startNewLine(point); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } CMouseEditArea::CMouseEditArea(CGisItemOvlArea &area, CGisDraw * gis, CCanvas * parent) : IMouseEditLine(area.getKey(), area, false, tr("Area"), gis, parent) { canvas->reportStatus(key.item, tr("Edit Area
Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.
")); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } CMouseEditArea::~CMouseEditArea() { canvas->reportStatus(key.item, ""); } void CMouseEditArea::mousePressEvent(QMouseEvent * e) { canvas->reportStatus(key.item, ""); IMouseEditLine::mousePressEvent(e); } void CMouseEditArea::drawLine(const QPolygonF &l, const QColor color, int width, QPainter& p) { p.setPen(QPen(color, width, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.setBrush(QBrush(Qt::magenta, Qt::BDiagPattern)); p.drawPolygon(l); } IGisLine * CMouseEditArea::getGisLine() { return dynamic_cast(CGisWidget::self().getItemByKey(key)); } void CMouseEditArea::slotAbort() { canvas->reportStatus(key.item,""); IMouseEditLine::slotAbortEx(false); } void CMouseEditArea::slotCopyToOrig() { canvas->reportStatus(key.item,""); IMouseEditLine::slotCopyToOrig(); } void CMouseEditArea::slotCopyToNew() { QMutexLocker lock(&IGisItem::mutexItems); canvas->reportStatus(key.item,""); if(points.size() < 3) { return; } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } QString name; CGisItemOvlArea * area = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(area != 0) { name = area->getName(); } name = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new area name."), QLineEdit::Normal, name); if(name.isEmpty()) { return; } new CGisItemOvlArea(points, name, project, NOIDX); canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } qmapshack-1.5.1/src/mouse/CScrOptRangeTrk.h000644 001750 000144 00000002434 12622435722 021532 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTRANGETRK_H #define CSCROPTRANGETRK_H #include "mouse/IScrOpt.h" #include "ui_IScrOptRangeTrk.h" class CGisItemTrk; class CScrOptRangeTrk : public IScrOpt, public Ui::IScrOptRangeTrk { public: CScrOptRangeTrk(const QPointF& point, CGisItemTrk *trk, IMouse *mouse, QWidget * parent = 0); virtual ~CScrOptRangeTrk(); void draw(QPainter& p); }; #endif //CSCROPTRANGETRK_H qmapshack-1.5.1/src/mouse/CScrOptUnclutter.h000644 001750 000144 00000003356 12622435722 022006 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTUNCLUTTER_H #define CSCROPTUNCLUTTER_H #include "gis/IGisItem.h" #include "mouse/IScrOpt.h" class CScrOptUnclutter : public IScrOpt { public: CScrOptUnclutter(IMouse *mouse); virtual ~CScrOptUnclutter(); struct item_t { bool hasUserFocus; QString name; IGisItem::key_t key; QPixmap icon; QRect area; QRect text; QRect active; }; virtual void clear(); virtual int size() { return items.size(); } void addItem(IGisItem * gisItem); IGisItem::key_t getItemKey(int index = 0); const item_t *selectItem(const QPoint& point); void draw(QPainter& p); void mouseMoveEvent(QMouseEvent * e); private: static const QPoint positions[9][8]; QList items; bool doSpecialCursor = false; }; #endif //CSCROPTUNCLUTTER_H qmapshack-1.5.1/src/mouse/IScrOptRangeTrk.ui000644 001750 000144 00000007156 12616741641 021737 0ustar00oeichlerusers000000 000000 IScrOptRangeTrk 0 0 116 100 Form 3 3 3 3 3 3 Hide all points. ... :/icons/32x32/PointHide.png:/icons/32x32/PointHide.png Show all points. ... :/icons/32x32/PointShow.png:/icons/32x32/PointShow.png Select an activity for the selected range. ... :/icons/32x32/Activity.png:/icons/32x32/Activity.png Copy track points as new track. ... :/icons/32x32/Copy.png:/icons/32x32/Copy.png Qt::Horizontal 40 20 0 0 TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop qmapshack-1.5.1/src/mouse/CMouseRangeTrk.h000644 001750 000144 00000003723 12622435722 021412 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSERANGETRK_H #define CMOUSERANGETRK_H #include "gis/IGisItem.h" #include "mouse/IMouse.h" #include class CGisItemTrk; class CGisDraw; class CCanvas; class CScrOptRangeTrk; class CMouseRangeTrk : public IMouse { Q_OBJECT public: CMouseRangeTrk(CGisItemTrk& trk, CGisDraw * gis, CCanvas * parent); virtual ~CMouseRangeTrk(); void draw(QPainter& p, CCanvas::redraw_e, const QRect &); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent *e); void wheelEvent(QWheelEvent * e); void keyPressEvent(QKeyEvent * e); private slots: void slotHidePoints(); void slotShowPoints(); void slotActivity(); void slotCopy(); private: void resetState(); IGisItem::key_t key; enum state_e { eStateIdle ,eStateMoveMap ,eStateSelectRange ,eStateRangeSelected }; state_e state = eStateIdle; QPointF anchor = NOPOINTF; QPoint lastPoint; QPointer scrOptRange; }; #endif //CMOUSERANGETRK_H qmapshack-1.5.1/src/mouse/CMouseDummy.h000644 001750 000144 00000002605 12622435722 020766 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEDUMMY_H #define CMOUSEDUMMY_H #include class CMouseDummy : public IMouse { public: CMouseDummy(); virtual ~CMouseDummy(); void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) { } void mousePressEvent(QMouseEvent * e) { } void mouseMoveEvent(QMouseEvent * e) { } void mouseReleaseEvent(QMouseEvent *e) { } virtual void wheelEvent(QWheelEvent * e) { } }; #endif //CMOUSEDUMMY_H qmapshack-1.5.1/src/mouse/IMouse.h000644 001750 000144 00000004551 12622435722 017762 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IMOUSE_H #define IMOUSE_H #include #include #include #include class QMouseEvent; class QWheelEvent; class CCanvas; class QTimer; class CGisDraw; class IMouse : public QObject { Q_OBJECT public: IMouse(CGisDraw * gis, CCanvas * canvas); virtual ~IMouse(); enum type_e { eNormal }; virtual void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) = 0; virtual void mousePressEvent(QMouseEvent * e) = 0; virtual void mouseMoveEvent(QMouseEvent * e) = 0; virtual void mouseReleaseEvent(QMouseEvent *e) = 0; virtual void mouseDoubleClickEvent(QMouseEvent *e) { } virtual void wheelEvent(QWheelEvent * e) = 0; virtual void keyPressEvent(QKeyEvent * e) { } /// the current mouse cursor /** Each mouse function is represented by a special cursor. The main widget uses this method to query the current cursor. */ operator const QCursor&() { return cursor; } CCanvas * getCanvas() { return canvas; } void panCanvas(const QPoint& pos); virtual void setMouseTracking(bool enabled); protected slots: virtual void slotPanCanvas(); protected: /// the functions mouse icon QCursor cursor; QPointer gis; QPointer canvas; // the current point reported by the mouse events QPoint point; QTimer * timer; }; #endif //IMOUSE_H qmapshack-1.5.1/src/mouse/CMouseEditRte.h000644 001750 000144 00000002733 12622435722 021235 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEEDITRTE_H #define CMOUSEEDITRTE_H #include "gis/IGisItem.h" #include "mouse/line/IMouseEditLine.h" class CGisItemRte; class CMouseEditRte : public IMouseEditLine { Q_OBJECT public: CMouseEditRte(const QPointF& point, CGisDraw * gis, CCanvas * parent); CMouseEditRte(CGisItemRte &rte, CGisDraw * gis, CCanvas * parent); virtual ~CMouseEditRte(); void mousePressEvent(QMouseEvent * e); protected slots: void slotAbort(); void slotCopyToNew(); void slotCopyToOrig(); protected: IGisLine * getGisLine(); }; #endif //CMOUSEEDITRTE_H qmapshack-1.5.1/src/mouse/CMouseNormal.cpp000644 001750 000144 00000027123 12622716133 021456 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "mouse/CMouseNormal.h" #include "mouse/CScrOptUnclutter.h" #include "widgets/CFadingIcon.h" #include CMouseNormal::CMouseNormal(CGisDraw *gis, CCanvas *canvas) : IMouse(gis, canvas) { cursor = QCursor(QPixmap(":/cursors/cursorMoveMap.png"),0,0); screenUnclutter = new CScrOptUnclutter(this); menu = new QMenu(canvas); menu->addAction(QIcon("://icons/32x32/AddWpt.png"), tr("Add Waypoint"), this, SLOT(slotAddWpt())); menu->addAction(QIcon("://icons/32x32/AddTrk.png"), tr("Add Track"), this, SLOT(slotAddTrk())); menu->addAction(QIcon("://icons/32x32/AddRte.png"), tr("Add Route"), this, SLOT(slotAddRte())); menu->addAction(QIcon("://icons/32x32/AddArea.png"), tr("Add Area"), this, SLOT(slotAddArea())); menu->addSeparator(); menu->addAction(QIcon("://icons/32x32/Copy.png"), tr("Copy position"), this, SLOT(slotCopyPosition())); menu->addAction(QIcon("://icons/32x32/Copy.png"), tr("Copy position (Grid)"), this, SLOT(slotCopyPositionGrid())); } CMouseNormal::~CMouseNormal() { } void CMouseNormal::stopTracking() { const IGisItem::key_t& key = CGisItemTrk::getKeyUserFocus(); if(!key.item.isEmpty()) { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { trk->setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseMove, "CMouseNormal"); } } } void CMouseNormal::mousePressEvent(QMouseEvent * e) { point = e->pos(); if(e->button() == Qt::LeftButton) { lastPos = e->pos(); // start to block map moving when a previous click // has triggered a selection of any kind mapMove = (stateItemSel < eStateNoMapMovePossible); mapDidMove = false; } else if(e->button() == Qt::RightButton) { QPoint p = canvas->mapToGlobal(point); menu->exec(p); } } void CMouseNormal::mouseMoveEvent(QMouseEvent * e) { screenUnclutter->mouseMoveEvent(e); if(!screenItemOption.isNull()) { screenItemOption->mouseMoveEvent(e); } point = e->pos(); if(mapMove) { if(point != lastPos) { QPoint delta = point - lastPos; canvas->moveMap(delta); lastPos = point; mapDidMove = true; } } else { switch(stateItemSel) { case eStateIdle: CGisWidget::self().mouseMove(point); case eStateHooverSingle: case eStateHooverMultiple: { const IGisItem::key_t& keyTrk = CGisItemTrk::getKeyUserFocus(); if(!keyTrk.item.isEmpty()) { CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(keyTrk)); if(trk != 0) { trk->setMouseFocusByPoint(point, CGisItemTrk::eFocusMouseMove, "CMouseNormal"); } } const IGisItem::key_t& keyRte = CGisItemRte::getKeyUserFocus(); if(!keyRte.item.isEmpty()) { CGisItemRte * rte = dynamic_cast(CGisWidget::self().getItemByKey(keyRte)); if(rte != 0) { rte->setMouseFocusByPoint(point, CGisItemRte::eFocusMouseMove, "CMouseNormal"); } } break; } default: ; } canvas->displayInfo(point); canvas->update(); } } void CMouseNormal::mouseReleaseEvent(QMouseEvent *e) { point = e->pos(); if(e->button() == Qt::LeftButton) { if(!mapDidMove) { switch(stateItemSel) { case eStateHooverSingle: { stateItemSel = eStateIdle; IGisItem * item = CGisWidget::self().getItemByKey(screenUnclutter->getItemKey()); if(item) { item->treeWidget()->collapseAll(); item->treeWidget()->setCurrentItem(item); item->treeWidget()->scrollToItem(item, QAbstractItemView::PositionAtCenter); if(setScreenOption(point, item)) { stateItemSel = eStateShowItemOptions; } stopTracking(); } break; } case eStateHooverMultiple: { screenUnclutter->setOrigin(e->pos()); stateItemSel = eStateUnclutterMultiple; stopTracking(); break; } case eStateUnclutterMultiple: { const CScrOptUnclutter::item_t * scrOpt = screenUnclutter->selectItem(point); if(scrOpt != 0) { IGisItem * item = CGisWidget::self().getItemByKey(scrOpt->key); screenUnclutter->clear(); // CAUTION!! this will delete the object scrOpt is pointing to. scrOpt = 0; if(item) { item->treeWidget()->collapseAll(); item->treeWidget()->setCurrentItem(item); item->treeWidget()->scrollToItem(item, QAbstractItemView::PositionAtCenter); if(setScreenOption(screenUnclutter->getOrigin(), item)) { stateItemSel = eStateShowItemOptions; break; } } } resetState(); break; } case eStateShowItemOptions: { resetState(); break; } default: ; } canvas->update(); } mapMove = false; mapDidMove = false; } } void CMouseNormal::mouseDoubleClickEvent(QMouseEvent *e) { if(stateItemSel == eStateIdle) { const IGisItem::key_t& keyTrk = CGisItemTrk::getKeyUserFocus(); CGisWidget::self().focusTrkByKey(false, keyTrk); const IGisItem::key_t& keyRte = CGisItemRte::getKeyUserFocus(); CGisWidget::self().focusRteByKey(false, keyRte); } } void CMouseNormal::wheelEvent(QWheelEvent * e) { resetState(); } void CMouseNormal::keyPressEvent(QKeyEvent * e) { resetState(); } void CMouseNormal::resetState() { screenUnclutter->clear(); if(!screenItemOption.isNull()) { screenItemOption->deleteLater(); } stateItemSel = eStateIdle; } bool CMouseNormal::setScreenOption(const QPoint& pt, IGisItem * item) { CGisItemTrk * trk = dynamic_cast(item); if(trk && trk->setMouseFocusByPoint(pt, CGisItemTrk::eFocusMouseClick, "CMouseNormal") == NOPOINTF) { new CFadingIcon(pt, "://icons/48x48/NoGo.png", canvas); return false; } delete screenItemOption; screenItemOption = item->getScreenOptions(pt, this); return !screenItemOption.isNull(); } void CMouseNormal::draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) { // no mouse interaction while gis thread is running if(gis->isRunning()) { return; } switch(stateItemSel) { case eStateIdle: case eStateHooverSingle: case eStateHooverMultiple: { /* Collect and draw items close to the last mouse position in the draw method. This might be a bit odd but there are two reasons: 1) Multiple update events are combined by the event loop. Thus multiple mouse move events are reduced a single paint event. As getItemsByPos() is quite cycle intense this seems like a good idea. 2) The list of items passed back by getItemsByPos() must not be stored. That is why the list has to be generated within the draw handler to access the item's drawHighlight() method. */ screenUnclutter->clear(); QList items; CGisWidget::self().getItemsByPos(point, items); if(items.empty() || items.size() > 8) { stateItemSel = eStateIdle; break; } foreach(IGisItem * item, items) { item->drawHighlight(p); screenUnclutter->addItem(item); } stateItemSel = (screenUnclutter->size() == 1) ? eStateHooverSingle : eStateHooverMultiple; break; } case eStateUnclutterMultiple: { screenUnclutter->draw(p); break; } case eStateShowItemOptions: { if(screenItemOption.isNull()) { stateItemSel = eStateIdle; break; } // the screen option might not be destroyed yet, but already hidden if(screenItemOption->isVisible()) { screenItemOption->draw(p); } break; } default: ; } } void CMouseNormal::slotAddWpt() { QString name; QString icon; QPointF pt = point; gis->convertPx2Rad(pt); pt *= RAD_TO_DEG; if(!CGisItemWpt::getNewWptData(pt, icon, name)) { return; } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } QMutexLocker lock(&IGisItem::mutexItems); CGisItemWpt * wpt = new CGisItemWpt(pt, name, icon, project); wpt->edit(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } void CMouseNormal::slotAddTrk() { QPointF pt = point; gis->convertPx2Rad(pt); canvas->setMouseEditTrk(pt); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } void CMouseNormal::slotAddRte() { QPointF pt = point; gis->convertPx2Rad(pt); canvas->setMouseEditRte(pt); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } void CMouseNormal::slotAddArea() { QPointF pt = point; gis->convertPx2Rad(pt); canvas->setMouseEditArea(pt); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } void CMouseNormal::slotCopyPosition() { QPointF pt = point; gis->convertPx2Rad(pt); QString position; IUnit::degToStr(pt.x() * RAD_TO_DEG, pt.y() * RAD_TO_DEG, position); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(position); } void CMouseNormal::slotCopyPositionGrid() { QString position; QPointF pt = point; gis->convertPx2Rad(pt); canvas->convertGridPos2Str(pt * RAD_TO_DEG, position, true); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(position); } qmapshack-1.5.1/src/mouse/CMouseWptBubble.cpp000644 001750 000144 00000004621 12622435717 022120 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/wpt/CGisItemWpt.h" #include "mouse/CMouseWptBubble.h" #include CMouseWptBubble::CMouseWptBubble(const IGisItem::key_t &key, CGisDraw * gis, CCanvas * parent) : IMouse(gis, parent) , key(key) { cursor = QCursor(QPixmap("://cursors/cursorArrow.png"),0,0); } CMouseWptBubble::~CMouseWptBubble() { } void CMouseWptBubble::draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) { } void CMouseWptBubble::mousePressEvent(QMouseEvent * e) { QMutexLocker lock(&IGisItem::mutexItems); QPointF pos = e->pos(); CGisItemWpt * wpt = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(wpt) { wpt->mousePress(pos); } else { canvas->resetMouse(); } } void CMouseWptBubble::mouseMoveEvent(QMouseEvent * e) { QMutexLocker lock(&IGisItem::mutexItems); QPointF pos = e->pos(); CGisItemWpt * wpt = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(wpt) { wpt->mouseMove(pos); } else { canvas->resetMouse(); } } void CMouseWptBubble::mouseReleaseEvent(QMouseEvent *e) { QMutexLocker lock(&IGisItem::mutexItems); QPointF pos = e->pos(); CGisItemWpt * wpt = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(wpt) { wpt->mouseRelease(pos); } else { canvas->resetMouse(); } } void CMouseWptBubble::wheelEvent(QWheelEvent * e) { } qmapshack-1.5.1/src/mouse/line/CLineOpAddPoint.h000644 001750 000144 00000002767 12622435722 022433 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CLINEOPADDPOINT_H #define CLINEOPADDPOINT_H #include "mouse/line/ILineOp.h" class CLineOpAddPoint : public ILineOp { public: CLineOpAddPoint(SGisLine& points, CGisDraw *gis, CCanvas *canvas, IMouseEditLine *parent); virtual ~CLineOpAddPoint(); void mousePressEventEx(QMouseEvent * e); void mouseMoveEventEx(QMouseEvent * e); void mouseReleaseEventEx(QMouseEvent *e) { } void drawFg(QPainter& p); void canvasPanned(QPointF pos); void append(); bool abortStep(); private: bool addPoint = false; bool isPoint = false; }; #endif //CLINEOPADDPOINT_H qmapshack-1.5.1/src/mouse/line/CScrOptEditLine.cpp000644 001750 000144 00000002176 12622435717 023003 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "mouse/line/CScrOptEditLine.h" #include CScrOptEditLine::CScrOptEditLine(IMouse *mouse) : IScrOpt(mouse) { setupUi(this); move(0,0); adjustSize(); show(); } CScrOptEditLine::~CScrOptEditLine() { } qmapshack-1.5.1/src/mouse/line/CScrOptRangeLine.cpp000644 001750 000144 00000002774 12622435717 023156 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CDraw.h" #include "mouse/line/CScrOptRangeLine.h" CScrOptRangeLine::CScrOptRangeLine(const QPointF &point, IMouse *mouse, QWidget * parent) : IScrOpt(mouse) { if(parent != 0) { setParent(parent); } setupUi(this); // label->setFont(CMainWindow::self().getMapFont()); // label->setText(trk->getInfoRange()); adjustSize(); setOrigin(point.toPoint()); move(point.toPoint() + QPoint(-width()/2,SCR_OPT_OFFSET)); show(); } CScrOptRangeLine::~CScrOptRangeLine() { } void CScrOptRangeLine::draw(QPainter& p) { CDraw::bubble(p, geometry(), origin); } qmapshack-1.5.1/src/mouse/line/CLineOpDeletePoint.h000644 001750 000144 00000002604 12622435722 023133 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CLINEOPDELETEPOINT_H #define CLINEOPDELETEPOINT_H #include "mouse/line/ILineOp.h" class CLineOpDeletePoint : public ILineOp { public: CLineOpDeletePoint(SGisLine& points, CGisDraw *gis, CCanvas *canvas, IMouseEditLine *parent); virtual ~CLineOpDeletePoint(); void mousePressEventEx(QMouseEvent * e) { } void mouseMoveEventEx(QMouseEvent * e); void mouseReleaseEventEx(QMouseEvent *e); void drawFg(QPainter& p); private: }; #endif //CLINEOPDELETEPOINT_H qmapshack-1.5.1/src/mouse/line/CLineOpAddPoint.cpp000644 001750 000144 00000016135 12622435717 022764 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "mouse/line/CLineOpAddPoint.h" #include "mouse/line/IMouseEditLine.h" #include CLineOpAddPoint::CLineOpAddPoint(SGisLine& points, CGisDraw *gis, CCanvas * canvas, IMouseEditLine * parent) : ILineOp(points, gis, canvas, parent) { cursor = QCursor(QPixmap(":/cursors/cursorAdd.png"),0,0); } CLineOpAddPoint::~CLineOpAddPoint() { } void CLineOpAddPoint::append() { // this is called on construction when creating a complete new line // A new point is appended to what ever line already exists, // and add point mode is entered immediately. idxFocus = points.size(); points.insert(idxFocus, IGisLine::point_t(points.last())); addPoint = true; isPoint = true; parentHandler->setCanvasPanning(addPoint); } void CLineOpAddPoint::mousePressEventEx(QMouseEvent * e) { if(e->button() == Qt::LeftButton) { if(addPoint) { // drop the new point at current position // update subpoints of previous and this point slotTimeoutRouting(); // if isPoint is true the line has been appended/prepended // in this case go on with adding another point if(isPoint) { if(idxFocus == (points.size() - 1)) { idxFocus++; } // store current state of line to undo/redo history parentHandler->storeToHistory(points); QPointF coord = e->pos(); gis->convertPx2Rad(coord); points.insert(idxFocus, IGisLine::point_t(coord)); } else { // store current state of line to undo/redo history parentHandler->storeToHistory(points); // terminate operation if the new point was inbetween a line segment. addPoint = false; idxFocus = NOIDX; } } else if(isPoint) { // as isPoint is set, add a new point either at the start or end of the line if(idxFocus == (points.size() - 1)) { idxFocus++; } QPointF coord = e->pos(); gis->convertPx2Rad(coord); points.insert(idxFocus, IGisLine::point_t(coord)); addPoint = true; } else { // clear current line segment points[idxFocus].subpts.clear(); // add a new point to line segment QPointF coord = e->pos(); gis->convertPx2Rad(coord); idxFocus++; points.insert(idxFocus, IGisLine::point_t(coord)); addPoint = true; } } else if(e->button() == Qt::RightButton) { abortStep(); idxFocus = NOIDX; } parentHandler->setCanvasPanning(addPoint); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } bool CLineOpAddPoint::abortStep() { if(addPoint) { // cancel action and restore last state of line cancelDelayedRouting(); parentHandler->restoreFromHistory(points); addPoint = false; idxFocus = NOIDX; parentHandler->setCanvasPanning(addPoint); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); return true; } return false; } void CLineOpAddPoint::mouseMoveEventEx(QMouseEvent * e) { if(addPoint) { QPointF coord = e->pos(); gis->convertPx2Rad(coord); IGisLine::point_t& pt = points[idxFocus]; // update position of point pt.coord = coord; // clear subpoints, as they have to be recalculated // by the routing, if any pt.subpts.clear(); if(idxFocus > 0) { points[idxFocus - 1].subpts.clear(); } // retrigger delayed routing startDelayedRouting(); } else { isPoint = false; // find line segment close to cursor idxFocus = isCloseToLine(e->pos()); // if none is found try to find point if(idxFocus == NOIDX) { // if no line segment is found but a point // it is either first or the last point in the line idxFocus = isCloseTo(e->pos()); if((idxFocus == 0) || (idxFocus == (points.size() - 1))) { isPoint = true; } } } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpAddPoint::canvasPanned(QPointF pos) { if(addPoint) { gis->convertPx2Rad(pos); points[idxFocus].coord = pos; } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpAddPoint::drawFg(QPainter& p) { if(idxFocus == NOIDX) { return; } if(addPoint) { const IGisLine::point_t& pt = points[idxFocus]; drawSinglePointSmall(pt.pixel, p); } else if(isPoint) { const IGisLine::point_t& pt = points[idxFocus]; drawSinglePointLarge(pt.pixel, p); } else if(idxFocus < (points.size() - 1)) { QPolygonF line; const IGisLine::point_t& pt1 = points[idxFocus]; const IGisLine::point_t& pt2 = points[idxFocus + 1]; if(pt1.subpts.isEmpty()) { line << pt1.pixel << pt2.pixel; } else { line << pt1.pixel; foreach(const IGisLine::subpt_t& pt, pt1.subpts) { line << pt.pixel; } line << pt2.pixel; } p.setPen(penBgPoint); p.setBrush(brushBgPoint); rectPoint.moveCenter(pt1.pixel.toPoint()); p.drawRect(rectPoint); rectPoint.moveCenter(pt2.pixel.toPoint()); p.drawRect(rectPoint); p.setPen(QPen(Qt::white, 7, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(line); p.setPen(penFgPoint); p.setBrush(brushFgPoint); rectPoint.moveCenter(pt1.pixel.toPoint()); p.drawRect(rectPoint); rectPoint.moveCenter(pt2.pixel.toPoint()); p.drawRect(rectPoint); p.setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(line); } } qmapshack-1.5.1/src/mouse/line/ILineOp.cpp000644 001750 000144 00000020054 12622435717 021342 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/rte/router/CRouterSetup.h" #include "mouse/line/ILineOp.h" #include "mouse/line/IMouseEditLine.h" #include ILineOp::ILineOp(SGisLine& points, CGisDraw *gis, CCanvas *canvas, IMouseEditLine *parent) : QObject(parent) , parentHandler(parent) , points(points) , canvas(canvas) , gis(gis) { timerRouting = new QTimer(this); timerRouting->setSingleShot(true); timerRouting->setInterval(400); connect(timerRouting, SIGNAL(timeout()), this, SLOT(slotTimeoutRouting())); } ILineOp::~ILineOp() { } void ILineOp::cancelDelayedRouting() { timerRouting->stop(); } void ILineOp::startDelayedRouting() { if(parentHandler->useAutoRouting()) { timerRouting->start(); } else if(parentHandler->useVectorRouting()) { slotTimeoutRouting(); } } void ILineOp::slotTimeoutRouting() { timerRouting->stop(); finalizeOperation(idxFocus); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void ILineOp::drawBg(QPainter& p) { drawLeadLine(leadLinePixel1,p); drawLeadLine(leadLinePixel2,p); } void ILineOp::drawSinglePointSmall(const QPointF& pt, QPainter& p) { QRect r(0,0,3,3); r.moveCenter(pt.toPoint()); p.setPen(QPen(Qt::white, 2)); p.setBrush(Qt::white); p.drawRect(r); p.setPen(Qt::black); p.setBrush(Qt::black); p.drawRect(r); } void ILineOp::drawSinglePointLarge(const QPointF &pt, QPainter& p) { rectPoint.moveCenter(pt.toPoint()); p.setPen(penBgPoint); p.setBrush(brushBgPoint); p.drawRect(rectPoint); p.setPen(penFgPoint); p.setBrush(brushFgPoint); p.drawRect(rectPoint); } void ILineOp::drawLeadLine(const QPolygonF& line, QPainter& p) { p.setPen(QPen(Qt::yellow, 7, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(line); } void ILineOp::mousePressEvent(QMouseEvent * e) { if(idxFocus != NOIDX) { mousePressEventEx(e); } else { const QPoint& pos = e->pos(); if(e->button() == Qt::LeftButton) { lastPos = pos; mapMove = true; mapDidMove = false; } } } void ILineOp::mouseMoveEvent(QMouseEvent * e) { const QPoint& pos = e->pos(); if(mapMove) { if(pos != lastPos) { QPoint delta = pos - lastPos; canvas->moveMap(delta); mapDidMove = true; } } else { updateLeadLines(idxFocus); mouseMoveEventEx(e); } lastPos = pos; } void ILineOp::mouseReleaseEvent(QMouseEvent *e) { mouseReleaseEventEx(e); mapMove = false; mapDidMove = false; } void ILineOp::updateLeadLines(qint32 idx) { leadLinePixel1.clear(); leadLinePixel2.clear(); subLinePixel1.clear(); subLinePixel2.clear(); if(parentHandler->useVectorRouting() && (idx != NOIDX)) { leadLineCoord1.clear(); leadLineCoord2.clear(); subLineCoord1.clear(); subLineCoord2.clear(); if(idx > 0) { const IGisLine::point_t& pt1 = points[idx - 1]; const IGisLine::point_t& pt2 = points[idx]; if(canvas->findPolylineCloseBy(pt2.pixel, pt2.pixel, 10, leadLineCoord1)) { leadLinePixel1 = leadLineCoord1; gis->convertRad2Px(leadLinePixel1); segment_t result; GPS_Math_SubPolyline(pt1.pixel, pt2.pixel, 10, leadLinePixel1, result); result.apply(leadLineCoord1, leadLinePixel1, subLineCoord1, subLinePixel1, gis); } } if(idx < points.size() - 1) { const IGisLine::point_t& pt1 = points[idx]; const IGisLine::point_t& pt2 = points[idx + 1]; if(canvas->findPolylineCloseBy(pt1.pixel, pt1.pixel, 10, leadLineCoord2)) { leadLinePixel2 = leadLineCoord2; gis->convertRad2Px(leadLinePixel2); segment_t result; GPS_Math_SubPolyline(pt1.pixel, pt2.pixel, 10, leadLinePixel2, result); result.apply(leadLineCoord2, leadLinePixel2, subLineCoord2, subLinePixel2, gis); } } } } void ILineOp::finalizeOperation(qint32 idx) { if(idx == NOIDX) { return; } if(parentHandler->useAutoRouting()) { CCanvas::setOverrideCursor(Qt::WaitCursor,"ILineOp::finalizeOperation"); if(idx > 0) { QPolygonF subs; IGisLine::point_t& pt1 = points[idx - 1]; IGisLine::point_t& pt2 = points[idx]; if(CRouterSetup::self().calcRoute(pt1.coord, pt2.coord, subs) >= 0) { pt1.subpts.clear(); foreach(const QPointF &sub, subs) { pt1.subpts << IGisLine::subpt_t(sub); } } } if(idx < (points.size() - 1)) { QPolygonF subs; IGisLine::point_t& pt1 = points[idx]; IGisLine::point_t& pt2 = points[idx + 1]; if(CRouterSetup::self().calcRoute(pt1.coord, pt2.coord, subs) >= 0) { pt1.subpts.clear(); foreach(const QPointF &sub, subs) { pt1.subpts << IGisLine::subpt_t(sub); } } } CCanvas::restoreOverrideCursor("ILineOp::finalizeOperation"); } else if(parentHandler->useVectorRouting()) { if(idx > 0) { IGisLine::point_t& pt1 = points[idx - 1]; pt1.subpts.clear(); foreach(const QPointF &pt, subLineCoord1) { pt1.subpts << IGisLine::subpt_t(pt); } } if(idx < (points.size() - 1)) { IGisLine::point_t& pt1 = points[idx]; pt1.subpts.clear(); foreach(const QPointF &pt, subLineCoord2) { pt1.subpts << IGisLine::subpt_t(pt); } } } parentHandler->updateStatus(); } qint32 ILineOp::isCloseTo(const QPoint& pos) { qint32 min = 30; qint32 idx = NOIDX; const int N = points.size(); for(int i = 0; i < N; i++) { const IGisLine::point_t& pt = points[i]; qint32 d = (pos - pt.pixel).manhattanLength(); if(d < min) { min = d; idx = i; } } return idx; } qint32 ILineOp::isCloseToLine(const QPoint& pos) { qint32 idx = NOIDX; qreal dist = 60; for(int i = 0; i < points.size() - 1; i++) { QPolygonF line; const IGisLine::point_t& pt1 = points[i]; const IGisLine::point_t& pt2 = points[i + 1]; if(pt1.subpts.isEmpty()) { line << pt1.pixel << pt2.pixel; } else { foreach(const IGisLine::subpt_t& pt, pt1.subpts) { line << pt.pixel; } line << pt2.pixel; } qreal d = GPS_Math_DistPointPolyline(line, pos); if(d < dist) { dist = d; idx = i; } } return idx; } qmapshack-1.5.1/src/mouse/line/CLineOpSelectRange.cpp000644 001750 000144 00000014247 12622435717 023460 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "mouse/line/CLineOpSelectRange.h" #include "mouse/line/CScrOptRangeLine.h" #include "mouse/line/IMouseEditLine.h" #include CLineOpSelectRange::CLineOpSelectRange(SGisLine& points, CGisDraw *gis, CCanvas * canvas, IMouseEditLine * parent) : ILineOp(points, gis, canvas, parent) { cursor = QCursor(QPixmap(":/cursors/cursorSelectRange.png"),0,0); } CLineOpSelectRange::~CLineOpSelectRange() { } void CLineOpSelectRange::mousePressEventEx(QMouseEvent * e) { if(e->button() == Qt::LeftButton) { switch(state) { case eStateIdle: { if(idxFocus != NOIDX) { state = eState1st; } break; } case eState1st: { if(idx2nd < 0 || points.size() <= idx2nd) { break; } qint32 d = qAbs(idxFocus - idx2nd); if(d < 1) { resetState(); return; } scrOptRangeLine = new CScrOptRangeLine(points[idx2nd].pixel, parentHandler, canvas); connect(scrOptRangeLine->toolDelete, SIGNAL(clicked()), this, SLOT(slotDelete())); connect(scrOptRangeLine->toolCalcRoute, SIGNAL(clicked()), this, SLOT(slotCalc())); connect(scrOptRangeLine->toolDelete, SIGNAL(clicked()), scrOptRangeLine, SLOT(hide())); connect(scrOptRangeLine->toolCalcRoute, SIGNAL(clicked()), scrOptRangeLine, SLOT(hide())); if(d < 2) { scrOptRangeLine->toolDelete->setEnabled(false); } state = eState2nd; break; } } } else if(e->button() == Qt::RightButton) { resetState(); } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } bool CLineOpSelectRange::abortStep() { if(state != eStateIdle) { resetState(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); return true; } return false; } void CLineOpSelectRange::mouseMoveEventEx(QMouseEvent * e) { switch(state) { case eStateIdle: { // no point selected yet, find point to highlight idxFocus = isCloseTo(e->pos()); break; } case eState1st: { idx2nd = isCloseTo(e->pos()); if(idx2nd == NOIDX) { idx2nd = isCloseToLine(e->pos()); if((idx2nd != NOIDX) && ((idx2nd + 1) < points.size())) { idx2nd++; } } break; } } // switch on map panning if move operation is in progress parentHandler->setCanvasPanning(state != eStateIdle); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpSelectRange::wheelEvent(QWheelEvent * e) { resetState(); } void CLineOpSelectRange::keyPressEvent(QKeyEvent * e) { resetState(); } void CLineOpSelectRange::drawFg(QPainter& p) { if(idxFocus == NOIDX) { return; } switch(state) { case eStateIdle: { const IGisLine::point_t& pt = points[idxFocus]; drawSinglePointLarge(pt.pixel, p); break; } case eState2nd: { if(!scrOptRangeLine.isNull()) { scrOptRangeLine->draw(p); } } case eState1st: { if(idx2nd != NOIDX) { qint32 idx1 = qMin(idxFocus, idx2nd); qint32 idx2 = qMax(idxFocus, idx2nd); QPolygonF seg; for(int i = idx1; i < idx2; i++) { const IGisLine::point_t& point = points[i]; seg << point.pixel; foreach(const IGisLine::subpt_t& subpt, point.subpts) { seg << subpt.pixel; } } seg << points[idx2].pixel; p.setPen(QPen(Qt::darkGreen, 11, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(seg); p.setPen(QPen(Qt::green, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(seg); QRectF r(0,0,7,7); p.setPen(QPen(Qt::darkGreen,2)); p.setBrush(Qt::darkGreen); foreach(const QPointF &pt, seg) { r.moveCenter(pt); p.drawRect(r); } p.setPen(Qt::NoPen); p.setBrush(Qt::black); foreach(const QPointF &pt, seg) { r.moveCenter(pt); p.drawRect(r); } } break; } } } void CLineOpSelectRange::resetState() { if(!scrOptRangeLine.isNull()) { scrOptRangeLine->deleteLater(); } idxFocus = NOIDX; idx2nd = NOIDX; state = eStateIdle; } void CLineOpSelectRange::slotDelete() { qint32 idx = qMin(idxFocus, idx2nd); qint32 N = qAbs(idxFocus - idx2nd) - 1; points.remove(idx + 1, N); parentHandler->storeToHistory(points); resetState(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpSelectRange::slotCalc() { qint32 idx = qMin(idxFocus, idx2nd); qint32 N = qAbs(idxFocus - idx2nd) - 1; points.remove(idx + 1, N); finalizeOperation(idx); parentHandler->storeToHistory(points); resetState(); } qmapshack-1.5.1/src/mouse/line/CScrOptEditLine.h000644 001750 000144 00000002342 12622435722 022437 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTEDITLINE_H #define CSCROPTEDITLINE_H #include "mouse/IScrOpt.h" #include "ui_IScrOptEditLine.h" class CScrOptEditLine : public IScrOpt, public Ui::IScrOptEditLine { Q_OBJECT public: CScrOptEditLine(IMouse *mouse); virtual ~CScrOptEditLine(); void draw(QPainter& p) { } }; #endif //CSCROPTEDITLINE_H qmapshack-1.5.1/src/mouse/line/CLineOpMovePoint.h000644 001750 000144 00000002721 12622435722 022637 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CLINEOPMOVEPOINT_H #define CLINEOPMOVEPOINT_H #include "mouse/line/ILineOp.h" class CLineOpMovePoint : public ILineOp { public: CLineOpMovePoint(SGisLine& points, CGisDraw *gis, CCanvas *canvas, IMouseEditLine *parent); virtual ~CLineOpMovePoint(); void mousePressEventEx(QMouseEvent * e); void mouseMoveEventEx(QMouseEvent * e); void mouseReleaseEventEx(QMouseEvent * e) { } void drawFg(QPainter& p); void canvasPanned(QPointF pos); bool abortStep(); private: bool movePoint = false; }; #endif //CLINEOPMOVEPOINT_H qmapshack-1.5.1/src/mouse/line/IMouseEditLine.h000644 001750 000144 00000010567 12624371303 022327 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IMOUSEEDITLINE_H #define IMOUSEEDITLINE_H #include "gis/IGisItem.h" #include "gis/IGisLine.h" #include "mouse/IMouse.h" #include #include #include class CGisDraw; class CCanvas; class IGisLine; class CScrOptEditLine; class ILineOp; class IMouseEditLine : public IMouse { Q_OBJECT public: enum features_e { eFeatureSnapToLines = 0x01 ,eFeatureRouting = 0x02 }; /** @brief Start to create a new track with given point as first track point @param point the starting point @param gis the draw context to use @param parent the canvas to use */ IMouseEditLine(const IGisItem::key_t& key, const QPointF& point, bool enableStatus, const QString& type, CGisDraw * gis, CCanvas * parent); /** @brief Edit an existing track @param trk the track to edit @param gis the draw context to use @param parent the canvas to use */ IMouseEditLine(const IGisItem::key_t &key, IGisLine &src, bool enableStatus, const QString& type, CGisDraw * gis, CCanvas * parent); virtual ~IMouseEditLine(); void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent *e); void wheelEvent(QWheelEvent * e); void keyPressEvent(QKeyEvent * e); void abortStep(); bool useAutoRouting(); bool useVectorRouting(); void setCanvasPanning(bool enable) { doCanvasPanning = enable; } void storeToHistory(const SGisLine& line); void restoreFromHistory(SGisLine& line); virtual void updateStatus(); protected slots: /** @brief Delete the selected point */ void slotDeletePoint(); /** @brief Start to select a range of points */ void slotSelectRange(); /** @brief Move selected point */ void slotMovePoint(); /** @brief Add points in direction start of track (eStateAddPointBwd) */ void slotAddPoint(); void slotNoRouting(); void slotAutoRouting(); void slotVectorRouting(); virtual void slotAbort() = 0; void slotAbortEx(bool showMB); virtual void slotCopyToOrig(); virtual void slotCopyToNew() = 0; void slotUndo(); void slotRedo(); void slotPanCanvas(); protected: virtual void drawLine(const QPolygonF& l, const QColor color, int width, QPainter& p); /** @brief Get access to the IGisLine object a subclass of IMouseEditLine is handling. @return A valid pointer or 0. */ virtual IGisLine * getGisLine() = 0; virtual void startNewLine(const QPointF &point); /// shadow cursor needed to restore cursor after some actions providing their own cursor. QCursor cursor1; /// the abstract line object to edit SGisLine points; /// undo/redo history QList history; qint32 idxHistory = NOIDX; /// the on screen buttons CScrOptEditLine * scrOptEditLine; /// the key of the GIS item to edit IGisItem::key_t key; private: void commonSetup(); void changeCursor(); /// flag to enable/disable canvas/map panning bool doCanvasPanning = false; QPolygonF pixelLine; QPolygonF pixelPts; QPolygonF pixelSubs; /// the current active line operation (move, add, delete...) ILineOp * lineOp = 0; bool enableStatus; QString type; }; #endif //IMOUSEEDITLINE_H qmapshack-1.5.1/src/mouse/line/IScrOptRangeLine.ui000644 001750 000144 00000003015 12624270515 022777 0ustar00oeichlerusers000000 000000 IScrOptRangeLine 0 0 92 43 Form Delete all points between the first and last one. ... :/icons/32x32/DeleteMultiple.png:/icons/32x32/DeleteMultiple.png <html><head/><body><p>Calculate a route between the first and last selected point.</p></body></html> ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png qmapshack-1.5.1/src/mouse/line/IScrOptEditLine.ui000644 001750 000144 00000025043 12570062516 022635 0ustar00oeichlerusers000000 000000 IScrOptEditLine 0 0 653 46 0 0 0 0 16777215 100 Form Qt::Horizontal 40 20 Save to orignal Save as new Abort Qt::Vertical QFrame::NoFrame QFrame::Raised 0 0 0 0 Move points. (Ctrl+M) ... :/icons/32x32/PointMove.png:/icons/32x32/PointMove.png Ctrl+M true true true Add new points. (Ctrl++) ... :/icons/32x32/Add.png:/icons/32x32/Add.png Ctrl++ true true Select a range of points. (Ctrl+R) ... :/icons/32x32/SelectRange.png:/icons/32x32/SelectRange.png Ctrl+R true true Delete a point. (Ctrl+D) ... :/icons/32x32/DeleteOne.png:/icons/32x32/DeleteOne.png Ctrl+D true true Qt::Vertical QFrame::NoFrame QFrame::Raised 0 0 0 0 No auto-routing or line snapping (Ctrl+O) 0 :/icons/32x32/O.png:/icons/32x32/O.png Ctrl+O true true Use auto-routing to between points. (Ctrl+A) A :/icons/32x32/A.png:/icons/32x32/A.png Ctrl+A true true Snap line along lines of a vector map. (Ctrl+V) V :/icons/32x32/V.png:/icons/32x32/V.png Ctrl+V true true Qt::Vertical false Undo last change ... :/icons/32x32/Undo.png:/icons/32x32/Undo.png false Redo last change ... :/icons/32x32/Redo.png:/icons/32x32/Redo.png Qt::Horizontal 40 20 qmapshack-1.5.1/src/mouse/line/CLineOpMovePoint.cpp000644 001750 000144 00000010446 12622435717 023201 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "mouse/line/CLineOpMovePoint.h" #include "mouse/line/IMouseEditLine.h" #include "units/IUnit.h" #include CLineOpMovePoint::CLineOpMovePoint(SGisLine &points, CGisDraw *gis, CCanvas * canvas, IMouseEditLine *parent) : ILineOp(points, gis, canvas, parent) { cursor = QCursor(QPixmap(":/cursors/cursorMovePoint.png"),0,0); } CLineOpMovePoint::~CLineOpMovePoint() { } void CLineOpMovePoint::mousePressEventEx(QMouseEvent * e) { if(e->button() == Qt::LeftButton) { if(movePoint) { // update subpoints by triggering the routing, if any. slotTimeoutRouting(); // terminate moving the point movePoint = false; // store new state of line to undo/redo history parentHandler->storeToHistory(points); } else { QPointF coord = e->pos(); gis->convertPx2Rad(coord); // start moving the point IGisLine::point_t& pt = points[idxFocus]; pt.coord = coord; // clear the subpoints from this point to the next pt.subpts.clear(); // clear the subpoints from the previous point to this point if(idxFocus != 0) { points[idxFocus - 1].subpts.clear(); } movePoint = true; } } else if(e->button() == Qt::RightButton) { abortStep(); } // switch on map panning if move operation is in progress parentHandler->setCanvasPanning(movePoint); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } bool CLineOpMovePoint::abortStep() { if(movePoint) { // cancel action and restore last state of line cancelDelayedRouting(); parentHandler->restoreFromHistory(points); movePoint = false; idxFocus = NOIDX; // switch on map panning if move operation is in progress parentHandler->setCanvasPanning(movePoint); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); return true; } return false; } void CLineOpMovePoint::mouseMoveEventEx(QMouseEvent * e) { if(movePoint) { QPointF coord = e->pos(); gis->convertPx2Rad(coord); IGisLine::point_t& pt = points[idxFocus]; // update position of point pt.coord = coord; // clear subpoints, as they have to be recalculated // by the routing, if any pt.subpts.clear(); if(idxFocus > 0) { points[idxFocus - 1].subpts.clear(); } // retrigger delayed routing startDelayedRouting(); } else { // no point selected yet, find point to highlight idxFocus = isCloseTo(e->pos()); } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpMovePoint::canvasPanned(QPointF pos) { // update point position after canvas/map panning if(movePoint) { gis->convertPx2Rad(pos); points[idxFocus].coord = pos; } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpMovePoint::drawFg(QPainter& p) { if(idxFocus == NOIDX) { return; } const IGisLine::point_t& pt = points[idxFocus]; if(movePoint) { drawSinglePointSmall(pt.pixel, p); } else { drawSinglePointLarge(pt.pixel, p); } } qmapshack-1.5.1/src/mouse/line/ILineOp.h000644 001750 000144 00000007135 12622435722 021010 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef ILINEOP_H #define ILINEOP_H #include "gis/IGisLine.h" #include #include #include #include #include class QMouseEvent; class CCanvas; class QPainter; class IMouseEditLine; class ILineOp : public QObject { Q_OBJECT public: ILineOp(SGisLine &points, CGisDraw * gis, CCanvas * canvas, IMouseEditLine * parent); virtual ~ILineOp(); virtual void mousePressEvent(QMouseEvent * e); virtual void mouseMoveEvent(QMouseEvent * e); virtual void mouseReleaseEvent(QMouseEvent *e); virtual void mousePressEventEx(QMouseEvent * e) = 0; virtual void mouseMoveEventEx(QMouseEvent * e) = 0; virtual void mouseReleaseEventEx(QMouseEvent *e) = 0; virtual void wheelEvent(QWheelEvent * e) { } virtual void keyPressEvent(QKeyEvent * e) { } virtual void drawFg(QPainter& p) = 0; virtual void drawBg(QPainter& p); const QCursor& getCursor() { return cursor; } virtual void canvasPanned(QPointF pos) { } /** @brief (try to) abort a step in the current operation Method called, if a step in the current operation should be aborted, such as adding or moving a(n already) selected waypoint. This dummy implementation does not do anything and therefore always returns `false`. @return `true` if a step in the current operation was successfully, `false` otherwise */ virtual bool abortStep() { return false; } void updateStatus(); protected slots: void slotTimeoutRouting(); protected: virtual void cancelDelayedRouting(); virtual void startDelayedRouting(); virtual void finalizeOperation(qint32 idx); qint32 isCloseTo(const QPoint& pos); qint32 isCloseToLine(const QPoint& pos); void drawSinglePointSmall(const QPointF& pt, QPainter& p); void drawSinglePointLarge(const QPointF &pt, QPainter& p); void drawLeadLine(const QPolygonF& line, QPainter& p); void updateLeadLines(qint32 idx); IMouseEditLine * parentHandler; SGisLine& points; CCanvas * canvas; CGisDraw * gis; QCursor cursor; qint32 idxFocus = NOIDX; bool mapMove = false; bool mapDidMove = false; QPoint lastPos; QRect rectPoint {0,0,9,9}; const QPen penBgPoint {Qt::white, 4}; const QPen penFgPoint {Qt::red, 2}; const QBrush brushBgPoint {Qt::white}; const QBrush brushFgPoint {Qt::red}; QPolygonF leadLineCoord1; QPolygonF leadLineCoord2; QPolygonF leadLinePixel1; QPolygonF leadLinePixel2; QPolygonF subLineCoord1; QPolygonF subLineCoord2; QPolygonF subLinePixel1; QPolygonF subLinePixel2; private: QTimer * timerRouting; }; #endif //ILINEOP_H qmapshack-1.5.1/src/mouse/line/CScrOptRangeLine.h000644 001750 000144 00000002413 12622435722 022605 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTRANGELINE_H #define CSCROPTRANGELINE_H #include "mouse/IScrOpt.h" #include "ui_IScrOptRangeLine.h" class CScrOptRangeLine : public IScrOpt, public Ui::IScrOptRangeLine { Q_OBJECT public: CScrOptRangeLine(const QPointF &point, IMouse *mouse, QWidget *parent); virtual ~CScrOptRangeLine(); void draw(QPainter& p); }; #endif //CSCROPTRANGELINE_H qmapshack-1.5.1/src/mouse/line/IMouseEditLine.cpp000644 001750 000144 00000035153 12624371122 022657 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/IGisLine.h" #include "gis/rte/router/CRouterSetup.h" #include "gis/trk/CGisItemTrk.h" #include "helpers/CDraw.h" #include "helpers/CSettings.h" #include "mouse/line/CLineOpAddPoint.h" #include "mouse/line/CLineOpDeletePoint.h" #include "mouse/line/CLineOpMovePoint.h" #include "mouse/line/CLineOpSelectRange.h" #include "mouse/line/CScrOptEditLine.h" #include "mouse/line/IMouseEditLine.h" #include "units/IUnit.h" #include IMouseEditLine::IMouseEditLine(const IGisItem::key_t &key, const QPointF& point, bool enableStatus, const QString &type, CGisDraw * gis, CCanvas * parent) : IMouse(gis, parent) , key(key) , enableStatus(enableStatus) , type(type) { commonSetup(); scrOptEditLine->pushSaveOrig->hide(); // hide as there is no original points << IGisLine::point_t(point); points.updatePixel(gis); storeToHistory(points); } IMouseEditLine::IMouseEditLine(const IGisItem::key_t &key, IGisLine &src, bool enableStatus, const QString &type, CGisDraw *gis, CCanvas *parent) : IMouse(gis, parent) , idxHistory(NOIDX) , key(key) , doCanvasPanning(false) , lineOp(0) , enableStatus(enableStatus) , type(type) { commonSetup(); src.getPolylineFromData(points); points.updatePixel(gis); storeToHistory(points); } IMouseEditLine::~IMouseEditLine() { canvas->reportStatus("IMouseEditLine",""); canvas->reportStatus(key.item,""); int mode = 0; if(scrOptEditLine->toolNoRoute->isChecked()) { mode = 0; } else if(scrOptEditLine->toolAutoRoute->isChecked()) { mode = 1; } else if(scrOptEditLine->toolVectorRoute->isChecked()) { mode = 2; } SETTINGS; cfg.setValue("Route/drawMode", mode); delete scrOptEditLine; } void IMouseEditLine::commonSetup() { // create permanent line edit on screen options scrOptEditLine = new CScrOptEditLine(this); connect(scrOptEditLine->pushSaveOrig, SIGNAL(clicked()), this, SLOT(slotCopyToOrig() )); connect(scrOptEditLine->pushSaveNew, SIGNAL(clicked()), this, SLOT(slotCopyToNew() )); connect(scrOptEditLine->pushAbort, SIGNAL(clicked()), this, SLOT(slotAbort() )); connect(scrOptEditLine->toolMovePoint, SIGNAL(clicked()), this, SLOT(slotMovePoint() )); connect(scrOptEditLine->toolSelectRange, SIGNAL(clicked()), this, SLOT(slotSelectRange() )); connect(scrOptEditLine->toolAddPoint, SIGNAL(clicked()), this, SLOT(slotAddPoint() )); connect(scrOptEditLine->toolDeletePoint, SIGNAL(clicked()), this, SLOT(slotDeletePoint() )); connect(scrOptEditLine->toolNoRoute, SIGNAL(clicked()), this, SLOT(slotNoRouting() )); connect(scrOptEditLine->toolAutoRoute, SIGNAL(clicked()), this, SLOT(slotAutoRouting() )); connect(scrOptEditLine->toolVectorRoute, SIGNAL(clicked()), this, SLOT(slotVectorRouting())); connect(scrOptEditLine->toolUndo, SIGNAL(clicked()), this, SLOT(slotUndo() )); connect(scrOptEditLine->toolRedo, SIGNAL(clicked()), this, SLOT(slotRedo() )); SETTINGS; int mode = cfg.value("Route/drawMode",0).toInt(); switch(mode) { case 0: scrOptEditLine->toolNoRoute->setChecked(true); break; case 1: scrOptEditLine->toolAutoRoute->setChecked(true); break; case 2: scrOptEditLine->toolVectorRoute->setChecked(true); break; } slotMovePoint(); } void IMouseEditLine::abortStep() { // at first try to abort a step within the current operation (px. stop adding a new waypoint) if(!lineOp->abortStep()) { // if within operation nothing can be aborted, then abort the whole operation // this equals clicking the `abort` button slotAbortEx(true); } } bool IMouseEditLine::useAutoRouting() { return scrOptEditLine->toolAutoRoute->isChecked(); } bool IMouseEditLine::useVectorRouting() { return scrOptEditLine->toolVectorRoute->isChecked(); } void IMouseEditLine::drawLine(const QPolygonF &l, const QColor color, int width, QPainter& p) { p.setPen(QPen(color, width, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(l); } void IMouseEditLine::draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect) { if(needsRedraw & (CCanvas::eRedrawMouse|CCanvas::eRedrawGis)) { points.updatePixel(gis); pixelLine.clear(); pixelPts.clear(); pixelSubs.clear(); for(int i = 0; i < points.size(); i++) { IGisLine::point_t& pt = points[i]; pixelLine << pt.pixel; pixelPts << pt.pixel; for(int n = 0; n < pt.subpts.size(); n++) { IGisLine::subpt_t& sub = pt.subpts[n]; pixelLine << sub.pixel; pixelSubs << sub.pixel; } } } if(pixelPts.isEmpty()) { return; } lineOp->drawBg(p); drawLine(pixelLine, Qt::white, 7, p); // draw magenta arrows (with white background) p.setBrush(Qt::magenta); CDraw::arrows(pixelLine, QRectF(), p, 10, 80); p.setPen(Qt::NoPen); p.setBrush(Qt::white); QRect r1(0,0,9,9); foreach(const QPointF &pt, pixelPts) { r1.moveCenter(pt.toPoint()); p.drawRect(r1); } drawLine(pixelLine, Qt::magenta, 5, p); p.setPen(Qt::NoPen); p.setBrush(Qt::black); QRect r2(0,0,7,7); foreach(const QPointF &pt, pixelPts) { r2.moveCenter(pt.toPoint()); p.drawRect(r2); } foreach(const QPointF &pt, pixelSubs) { p.drawEllipse(pt, 2, 2); } QRect r3(0,0,9,9); p.setBrush(Qt::NoBrush); p.setPen(QPen(Qt::yellow,2)); r3.moveCenter(pixelPts.first().toPoint()); p.drawRect(r3); p.setPen(QPen(Qt::green,2)); r3.moveCenter(pixelPts.last().toPoint()); p.drawRect(r3); lineOp->drawFg(p); } void IMouseEditLine::startNewLine(const QPointF& point) { scrOptEditLine->toolAddPoint->setChecked(true); slotAddPoint(); CLineOpAddPoint * lineOpAddPoint = dynamic_cast(lineOp); if(lineOpAddPoint) { lineOpAddPoint->append(); } canvas->reportStatus(key.item, tr("New Line
Move the mouse and use the left mouse button to drop points. When done use the right mouse button to stop.
")); } void IMouseEditLine::mousePressEvent(QMouseEvent * e) { point = e->pos(); lineOp->mousePressEvent(e); } void IMouseEditLine::mouseMoveEvent(QMouseEvent * e) { point = e->pos(); if(doCanvasPanning) { panCanvas(e->pos()); } lineOp->mouseMoveEvent(e); } void IMouseEditLine::mouseReleaseEvent(QMouseEvent *e) { point = e->pos(); lineOp->mouseReleaseEvent(e); } void IMouseEditLine::wheelEvent(QWheelEvent * e) { canvas->update(); lineOp->wheelEvent(e); } void IMouseEditLine::keyPressEvent(QKeyEvent * e) { canvas->update(); lineOp->keyPressEvent(e); } void IMouseEditLine::slotPanCanvas() { IMouse::slotPanCanvas(); lineOp->canvasPanned(point); } void IMouseEditLine::slotDeletePoint() { canvas->reportStatus(key.item, tr("Delete Point
Move the mouse close to a point and press the left button to delete it.
")); delete lineOp; lineOp = new CLineOpDeletePoint(points, gis, canvas, this); changeCursor(); doCanvasPanning = false; } void IMouseEditLine::slotSelectRange() { canvas->reportStatus(key.item, tr("Select Range of Points
Left click on first point to start selection. Left click second point to complete selection and choose from options. Use the right mouse button to cancel.
")); delete lineOp; lineOp = new CLineOpSelectRange(points, gis, canvas, this); changeCursor(); doCanvasPanning = false; } void IMouseEditLine::slotMovePoint() { canvas->reportStatus(key.item, tr("Move Point
Move the mouse close to a point and press the left button to make it stick to the cursor. Move the mouse to move the point. Drop the point by a left click. Use the right mouse button to cancel.
")); delete lineOp; lineOp = new CLineOpMovePoint(points, gis, canvas, this); changeCursor(); doCanvasPanning = false; } void IMouseEditLine::slotAddPoint() { canvas->reportStatus(key.item, tr("Add Point
Move the mouse close to a line segment and press the left button to add a point. The point will stick to the cursor and you can move it. Drop the point by a left click. Use the right mouse button to cancel.
")); delete lineOp; lineOp = new CLineOpAddPoint(points, gis, canvas, this); changeCursor(); doCanvasPanning = false; } void IMouseEditLine::slotNoRouting() { canvas->reportStatus(key.item, tr("No Routing
All points will be connected with a straight line.
")); } void IMouseEditLine::slotAutoRouting() { canvas->reportStatus(key.item, tr("Auto Routing
The current router setup is used to derive a route between points. Note: The selected router must be able to route on-the-fly. Offline routers usually can do, online routers can't.
")); } void IMouseEditLine::slotVectorRouting() { canvas->reportStatus(key.item, tr("Vector Routing
Connect points with a line from a loaded vector map if possible.
")); } void IMouseEditLine::changeCursor() { cursor = lineOp->getCursor(); if(QApplication::overrideCursor() != 0) { CCanvas::changeOverrideCursor(cursor,"IMouseEditLine::changeCursor"); } } void IMouseEditLine::slotAbortEx(bool showMB) { // ask if the current operation should be aborted (only in case there are things to be saved) bool doAbort = ( idxHistory == 0 ) || !showMB; if(!doAbort) { doAbort = (QMessageBox::Yes == QMessageBox::question(NULL, "Abort", "Do you really want to abort?\nAny modifications done will be discarded.", QMessageBox::Yes | QMessageBox::No)); } if(doAbort) { canvas->resetMouse(); canvas->update(); } } void IMouseEditLine::slotCopyToOrig() { QMutexLocker lock(&IGisItem::mutexItems); IGisLine * l = getGisLine(); if(l != 0) { CMainWindow::self().getEelevationAt(points); l->setDataFromPolyline(points); } canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } void IMouseEditLine::restoreFromHistory(SGisLine& line) { line = history[idxHistory]; canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); updateStatus(); } void IMouseEditLine::storeToHistory(const SGisLine& line) { // crop history if necessary if(idxHistory != NOIDX) { while(history.size() > (idxHistory + 1)) { history.pop_back(); } } history << line; idxHistory = history.size() - 1; scrOptEditLine->toolRedo->setEnabled(false); scrOptEditLine->toolUndo->setEnabled(idxHistory > 0); updateStatus(); } void IMouseEditLine::slotUndo() { if(idxHistory > 0) { idxHistory--; } points = history[idxHistory]; scrOptEditLine->toolRedo->setEnabled(true); scrOptEditLine->toolUndo->setEnabled(idxHistory > 0); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void IMouseEditLine::slotRedo() { if(idxHistory < (history.size() - 1)) { idxHistory++; } points = history[idxHistory]; scrOptEditLine->toolRedo->setEnabled(idxHistory < (history.size() - 1)); scrOptEditLine->toolUndo->setEnabled(true); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void IMouseEditLine::updateStatus() { if(!enableStatus || points.isEmpty()) { canvas->reportStatus("IMouseEditLine",""); return; } canvas->getElevationAt(points); qreal asc = 0; qreal dsc = 0; qreal dist = 0; qreal lastEle = points[0].ele; qreal absDelta; qreal delta; QPointF lastPos = points[0].coord; for(int i = 0; i < points.size(); i++) { const IGisLine::point_t& pt1 = points[i]; delta = pt1.ele - lastEle; absDelta = qAbs(delta); if(absDelta > ASCEND_THRESHOLD) { if(delta > 0) { asc += delta; } if(delta < 0) { dsc -= delta; } lastEle = pt1.ele; } dist += GPS_Math_Distance(lastPos.x(), lastPos.y(), pt1.coord.x(), pt1.coord.y()); lastPos = pt1.coord; foreach(const IGisLine::subpt_t& pt, pt1.subpts) { delta = pt.ele - lastEle; absDelta = qAbs(delta); if(absDelta > ASCEND_THRESHOLD) { if(delta > 0) { asc += delta; } if(delta < 0) { dsc -= delta; } lastEle = pt.ele; } dist += GPS_Math_Distance(lastPos.x(), lastPos.y(), pt.coord.x(), pt.coord.y()); lastPos = pt.coord; } } QString msg, val, unit; msg += tr("%1 Metrics").arg(type); msg += ""; IUnit::self().meter2distance(dist, val, unit); msg += ""; IUnit::self().meter2elevation(asc, val, unit); msg += ""; IUnit::self().meter2elevation(dsc, val, unit); msg += ""; msg += "
" + tr("Distance:") + "" + QString(" %1 %2").arg(val).arg(unit) + "
" + tr("Ascend:") + "" + QString(" %1 %2").arg(val).arg(unit) + "
" + tr("Descend:") + "" + QString(" %1 %2").arg(val).arg(unit) + "
"; canvas->reportStatus("IMouseEditLine",msg); } qmapshack-1.5.1/src/mouse/line/CLineOpDeletePoint.cpp000644 001750 000144 00000004220 12622435717 023466 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "mouse/line/CLineOpDeletePoint.h" #include "mouse/line/IMouseEditLine.h" #include "units/IUnit.h" #include CLineOpDeletePoint::CLineOpDeletePoint(SGisLine& points, CGisDraw *gis, CCanvas * canvas, IMouseEditLine * parent) : ILineOp(points, gis, canvas, parent) { cursor = QCursor(QPixmap(":/cursors/cursorDelete.png"),0,0); } CLineOpDeletePoint::~CLineOpDeletePoint() { } void CLineOpDeletePoint::mouseMoveEventEx(QMouseEvent * e) { idxFocus = isCloseTo(e->pos()); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpDeletePoint::mouseReleaseEventEx(QMouseEvent *e) { if(!mapDidMove && idxFocus != NOIDX) { if(idxFocus > 0) { points[idxFocus - 1].subpts.clear(); } points.remove(idxFocus--); updateLeadLines(idxFocus); slotTimeoutRouting(); // store to undo/redo history parentHandler->storeToHistory(points); } idxFocus = NOIDX; canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } void CLineOpDeletePoint::drawFg(QPainter& p) { if(idxFocus == NOIDX) { return; } const IGisLine::point_t& pt = points[idxFocus]; drawSinglePointLarge(pt.pixel, p); } qmapshack-1.5.1/src/mouse/line/CLineOpSelectRange.h000644 001750 000144 00000003476 12622435722 023123 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CLINEOPSELECTRANGE_H #define CLINEOPSELECTRANGE_H #include "mouse/line/ILineOp.h" #include class CScrOptRangeLine; class CLineOpSelectRange : public ILineOp { Q_OBJECT public: CLineOpSelectRange(SGisLine& points, CGisDraw *gis, CCanvas *canvas, IMouseEditLine *parent); virtual ~CLineOpSelectRange(); void mousePressEventEx(QMouseEvent * e); void mouseMoveEventEx(QMouseEvent * e); void mouseReleaseEventEx(QMouseEvent *e) { } void wheelEvent(QWheelEvent * e); void keyPressEvent(QKeyEvent * e); void drawFg(QPainter& p); bool abortStep(); private slots: void slotDelete(); void slotCalc(); private: void resetState(); enum state_e { eStateIdle , eState1st , eState2nd }; state_e state = eStateIdle; qint32 idx2nd = NOIDX; QPointer scrOptRangeLine; }; #endif //CLINEOPSELECTRANGE_H qmapshack-1.5.1/src/mouse/CMouseDummy.cpp000644 001750 000144 00000002075 12622435717 021326 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMouseDummy.h" CMouseDummy::CMouseDummy() : IMouse(0,0) { cursor = QCursor(QPixmap(":/cursors/cursorArrow.png"),0,0); } CMouseDummy::~CMouseDummy() { } qmapshack-1.5.1/src/mouse/CMouseMoveWpt.h000644 001750 000144 00000003205 12622435722 021271 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEMOVEWPT_H #define CMOUSEMOVEWPT_H #include "gis/IGisItem.h" #include "mouse/IMouse.h" #include class CCanvas; class CGisItemWpt; class CGisDraw; class CMouseMoveWpt : public IMouse { public: CMouseMoveWpt(CGisItemWpt& wpt, CGisDraw * gis, CCanvas * parent); virtual ~CMouseMoveWpt(); void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QRect &rect); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent *e); void wheelEvent(QWheelEvent * e); protected slots: virtual void slotPanCanvas(); private: IGisItem::key_t key; QPointF origPos; QPointF newPos; QPointF focus; QPixmap icon; }; #endif //CMOUSEMOVEWPT_H qmapshack-1.5.1/src/mouse/CMouseEditArea.h000644 001750 000144 00000003071 12622435722 021347 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMOUSEEDITAREA_H #define CMOUSEEDITAREA_H #include "gis/IGisItem.h" #include "mouse/line/IMouseEditLine.h" class CGisItemOvlArea; class CMouseEditArea : public IMouseEditLine { Q_OBJECT public: CMouseEditArea(const QPointF& point, CGisDraw * gis, CCanvas * parent); CMouseEditArea(CGisItemOvlArea &area, CGisDraw * gis, CCanvas * parent); virtual ~CMouseEditArea(); void mousePressEvent(QMouseEvent * e); protected slots: void slotAbort(); void slotCopyToNew(); void slotCopyToOrig(); protected: void drawLine(const QPolygonF &l, const QColor color, int width, QPainter& p); IGisLine * getGisLine(); }; #endif //CMOUSEEDITAREA_H qmapshack-1.5.1/src/mouse/IScrOpt.h000644 001750 000144 00000003036 12622435722 020101 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef ISCROPT_H #define ISCROPT_H #include #include #include #include class QMouseEvent; class IMouse; #define SCR_OPT_OFFSET 15 class IScrOpt : public QWidget { public: IScrOpt(IMouse *mouse); virtual ~IScrOpt(); void setOrigin(const QPoint& pos) { origin = pos; } const QPoint& getOrigin() { return origin; } virtual void draw(QPainter& p) = 0; void mouseMoveEvent(QMouseEvent *); protected: void enterEvent(QEvent * e); void leaveEvent(QEvent * e); QPoint origin; QPoint mousePos; QPointer mouse; }; #endif //ISCROPT_H qmapshack-1.5.1/src/mouse/CMouseEditTrk.cpp000644 001750 000144 00000007777 12624371123 021607 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/trk/CGisItemTrk.h" #include "mouse/CMouseEditTrk.h" #include CMouseEditTrk::CMouseEditTrk(const QPointF& point, CGisDraw * gis, CCanvas * parent) : IMouseEditLine(IGisItem::key_t(), point, true, tr("Track"), gis, parent) { startNewLine(point); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } CMouseEditTrk::CMouseEditTrk(CGisItemTrk &trk, CGisDraw * gis, CCanvas * parent) : IMouseEditLine(trk.getKey(), trk, true, tr("Track"), gis, parent) , isNewLine(false) { canvas->reportStatus(key.item, tr("Edit Track Points
Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.
")); // reset any focus the track might have. trk.setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseMove, "CMouseEditTrk"); trk.setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseClick, "CMouseEditTrk"); trk.looseUserFocus(); /* trigger complete update of GIS components to make sure all changes to the originating object are reflected on the canvas */ canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawMouse); } CMouseEditTrk::~CMouseEditTrk() { // canvas->reportStatus(key,""); } void CMouseEditTrk::mousePressEvent(QMouseEvent * e) { canvas->reportStatus(key.item, ""); IMouseEditLine::mousePressEvent(e); } IGisLine * CMouseEditTrk::getGisLine() { return dynamic_cast(CGisWidget::self().getItemByKey(key)); } void CMouseEditTrk::slotAbort() { canvas->reportStatus(key.item,""); IMouseEditLine::slotAbortEx(false); } void CMouseEditTrk::slotCopyToOrig() { canvas->reportStatus(key.item,""); if(!isNewLine) { int res = QMessageBox::warning(canvas, tr("Warning!"), tr("This will replace all data of the original by a simple line of coordinates. All other data will be lost permanently."), QMessageBox::Ok|QMessageBox::Abort, QMessageBox::Ok); if(res != QMessageBox::Ok) { return; } } IMouseEditLine::slotCopyToOrig(); } void CMouseEditTrk::slotCopyToNew() { QMutexLocker lock(&IGisItem::mutexItems); canvas->reportStatus(key.item,""); if(points.size() < 2) { return; } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } /// @todo make this independent from track QString name; CGisItemTrk * trk = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk != 0) { name = trk->getName(); } name = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new track name."), QLineEdit::Normal, name); if(name.isEmpty()) { return; } CMainWindow::self().getEelevationAt(points); new CGisItemTrk(points,name, project, NOIDX); canvas->resetMouse(); canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } qmapshack-1.5.1/src/mouse/IMouse.cpp000644 001750 000144 00000004625 12622435717 020323 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "mouse/IMouse.h" #include #include IMouse::IMouse(CGisDraw *gis, CCanvas *canvas) : QObject(canvas) , gis(gis) , canvas(canvas) { timer = new QTimer(this); timer->setSingleShot(true); timer->setInterval(50); connect(timer, SIGNAL(timeout()), this, SLOT(slotPanCanvas())); } IMouse::~IMouse() { } void IMouse::setMouseTracking(bool enabled) { canvas->setMouseTracking(enabled); if(!enabled) { timer->stop(); } } void IMouse::slotPanCanvas() { panCanvas(point); } #define SENSITIVE_FRAME 100 #define DAMPING_FACTOR 0.25 void IMouse::panCanvas(const QPoint& pos) { if(pos.x() < SENSITIVE_FRAME) { int d = (SENSITIVE_FRAME - pos.x()) * DAMPING_FACTOR; canvas->moveMap(QPointF(d/2, 0)); timer->start(); } else if(pos.x() > canvas->width() - SENSITIVE_FRAME) { int d = (canvas->width() - SENSITIVE_FRAME - pos.x()) * DAMPING_FACTOR; canvas->moveMap(QPointF(d/2, 0)); timer->start(); } else if(pos.y() < SENSITIVE_FRAME) { int d = (SENSITIVE_FRAME - pos.y()) * DAMPING_FACTOR; canvas->moveMap(QPointF(0, d/2)); timer->start(); } else if(pos.y() > canvas->height() - SENSITIVE_FRAME) { int d = (canvas->height() - SENSITIVE_FRAME - pos.y()) * DAMPING_FACTOR; canvas->moveMap(QPointF(0, d/2)); timer->start(); } else { timer->stop(); } canvas->update(); } qmapshack-1.5.1/src/dem/IDem.h000644 001750 000144 00000010137 12622435722 017011 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDEM_H #define IDEM_H #include "canvas/IDrawObject.h" #include #include #include #define CUSTOM_SLOPE_COLORTABLE ( -1 ) class CDemDraw; class IDemProp; class QSettings; struct SlopePresets { const char *name; const qreal steps[5]; }; class IDem : public IDrawObject { Q_OBJECT public: IDem(CDemDraw * parent); virtual ~IDem(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); virtual void draw(IDrawContext::buffer_t& buf) = 0; virtual qreal getElevationAt(const QPointF& pos) = 0; bool activated() { return isActivated; } /** @brief Get the dem's setup widget. As default an instance of CDemPropSetup is used. For other setups you have to override this method. @return A pointer to the widget. Use a smart pointer to store as the widget can be destroyed at any time */ virtual IDemProp * getSetup(); bool doHillshading() { return bHillshading; } int getFactorHillshading(); bool doSlopeColor() { return bSlopeColor; } const QVector getSlopeColorTable() { return slopetable; } static const struct SlopePresets slopePresets[7]; static const size_t slopePresetCount = sizeof(IDem::slopePresets) / sizeof(IDem::slopePresets[0]); const qreal* getCurrentSlopeStepTable(); int getSlopeStepTableIndex() { return gradeSlopeColor; } void setSlopeStepTable(int idx); void setSlopeStepTableCustomValue(int idx, int val); public slots: void slotSetHillshading(bool yes) { bHillshading = yes; } void slotSetFactorHillshade(int f); void slotSetSlopeColor(bool yes) { bSlopeColor = yes; } protected: void hillshading(QVector& data, qreal w, qreal h, QImage &img); void slopecolor(QVector& data, qreal w, qreal h, QImage &img); /** @brief Reproject (translate, rotate, scale) tile before drawing it. @param img the tile as QImage @param l a 4 point polygon to fit the tile in @param p the QPainter used to paint the tile */ void drawTile(QImage& img, QPolygonF& l, QPainter& p); CDemDraw * dem; /// source projection of the current map file /** Has to be set by subclass. Destruction has to be handled by subclass. */ projPJ pjsrc = 0; /// target projection /** Is set by IMap() to WGS84. Will be freed by ~IMap() */ projPJ pjtar = 0; /// width in number of px quint32 xsize_px = 0; /// height in number of px quint32 ysize_px = 0; /// scale [px/m] qreal xscale = 1.0; /// scale [px/m] qreal yscale = 1.0; /** @brief True if map was loaded successfully */ bool isActivated = false; /// the setup dialog. Use getSetup() for access QPointer setup; QVector graytable; QVector slopetable; int hasNoData = 0; double noData = 0; private: bool bHillshading = false; qreal factorHillshading = 1.0; bool bSlopeColor = false; int gradeSlopeColor = 0; qreal slopeCustomStepTable[5]; }; #endif //IDEM_H qmapshack-1.5.1/src/dem/CDemPropSetup.cpp000644 001750 000144 00000021220 12622435717 021217 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "dem/CDemDraw.h" #include "dem/CDemPropSetup.h" #include "dem/IDem.h" #include "units/IUnit.h" #include QPointF CDemPropSetup::scale; CDemPropSetup::CDemPropSetup(IDem * demfile, CDemDraw *dem) : IDemProp(demfile, dem) { setupUi(this); slopeSpins[0] = spinSlope0; slopeSpins[1] = spinSlope1; slopeSpins[2] = spinSlope2; slopeSpins[3] = spinSlope3; slopeSpins[4] = spinSlope4; slotPropertiesChanged(); connect(sliderOpacity, SIGNAL(valueChanged(int)), demfile, SLOT(slotSetOpacity(int))); connect(sliderOpacity, SIGNAL(valueChanged(int)), dem, SLOT(emitSigCanvasUpdate())); connect(dem, SIGNAL(sigScaleChanged(QPointF)), this, SLOT(slotScaleChanged(QPointF))); connect(toolSetMinScale, SIGNAL(toggled(bool)), this, SLOT(slotSetMinScale(bool))); connect(toolSetMaxScale, SIGNAL(toggled(bool)), this, SLOT(slotSetMaxScale(bool))); connect(checkHillshading, SIGNAL(toggled(bool)), demfile, SLOT(slotSetHillshading(bool))); connect(checkHillshading, SIGNAL(clicked()), dem, SLOT(emitSigCanvasUpdate())); connect(sliderHillshading, SIGNAL(valueChanged(int)), demfile, SLOT(slotSetFactorHillshade(int))); connect(sliderHillshading, SIGNAL(valueChanged(int)), dem, SLOT(emitSigCanvasUpdate())); connect(checkSlopeColor, SIGNAL(toggled(bool)), demfile, SLOT(slotSetSlopeColor(bool))); connect(checkSlopeColor, SIGNAL(clicked()), dem, SLOT(emitSigCanvasUpdate())); connect(comboGrades, SIGNAL(currentIndexChanged(int)), this, SLOT(slotGradeIndex(int))); for(size_t i = 0; i < SLOPE_LEVELS; i++) { slopeSpins[i]->setProperty("level", (uint) i); connect(slopeSpins[i], SIGNAL(editingFinished()), this, SLOT(slotSlopeValiddateAfterEdit())); connect(slopeSpins[i], SIGNAL(valueChangedByStep(int)), this, SLOT(slotSlopeValiddateAfterEdit())); } comboGrades->blockSignals(true); for(size_t i = 0; i < IDem::slopePresetCount; i++) { comboGrades->addItem(IDem::slopePresets[i].name); } comboGrades->addItem("custom"); comboGrades->blockSignals(false); const QVector& colortable = demfile->getSlopeColorTable(); QPixmap pixmap(20, 10); pixmap.fill(colortable[5]); labelColor5->setPixmap(pixmap); pixmap.fill(colortable[4]); labelColor4->setPixmap(pixmap); pixmap.fill(colortable[3]); labelColor3->setPixmap(pixmap); pixmap.fill(colortable[2]); labelColor2->setPixmap(pixmap); pixmap.fill(colortable[1]); labelColor1->setPixmap(pixmap); } CDemPropSetup::~CDemPropSetup() { } void CDemPropSetup::resizeEvent(QResizeEvent * e) { IDemProp::resizeEvent(e); updateScaleLabel(); } void CDemPropSetup::slotPropertiesChanged() { sliderOpacity->blockSignals(true); toolSetMaxScale->blockSignals(true); toolSetMinScale->blockSignals(true); checkHillshading->blockSignals(true); sliderHillshading->blockSignals(true); checkSlopeColor->blockSignals(true); comboGrades->blockSignals(true); sliderOpacity->setValue(demfile->getOpacity()); toolSetMinScale->setChecked( demfile->getMinScale() != NOFLOAT ); toolSetMaxScale->setChecked( demfile->getMaxScale() != NOFLOAT ); updateScaleLabel(); checkHillshading->setChecked(demfile->doHillshading()); sliderHillshading->setValue(demfile->getFactorHillshading()); checkSlopeColor->setChecked(demfile->doSlopeColor()); bool spinsReadonly = true; // calculate the index of the element, that should be selected in comboGrades // -1 indicates the `custom` entry (this allows adding DEM presets migration // of configuration during update int expectedComboGradesIndex = demfile->getSlopeStepTableIndex(); if(-1 == expectedComboGradesIndex) { spinsReadonly = false; expectedComboGradesIndex = comboGrades->count() - 1; } comboGrades->setCurrentIndex(expectedComboGradesIndex); for(size_t i = 0; i < SLOPE_LEVELS; i++) { slopeSpins[i]->blockSignals(true); slopeSpins[i]->setReadOnly(spinsReadonly); slopeSpins[i]->setValue(demfile->getCurrentSlopeStepTable()[i]); slopeSpins[i]->blockSignals(false); } dem->emitSigCanvasUpdate(); comboGrades->blockSignals(false); sliderOpacity->blockSignals(false); toolSetMaxScale->blockSignals(false); toolSetMinScale->blockSignals(false); checkHillshading->blockSignals(false); sliderHillshading->blockSignals(false); checkSlopeColor->blockSignals(false); } void CDemPropSetup::slotScaleChanged(const QPointF& s) { scale = s; slotPropertiesChanged(); } void CDemPropSetup::slotSetMinScale(bool checked) { demfile->setMinScale(checked ? scale.x() : NOFLOAT); slotPropertiesChanged(); } void CDemPropSetup::slotSetMaxScale(bool checked) { demfile->setMaxScale(checked ? scale.x() : NOFLOAT); slotPropertiesChanged(); } #define BAR_HEIGHT 6 #define HOR_MARGIN 3 void CDemPropSetup::updateScaleLabel() { int w = labelScale->width(); int h = labelScale->height(); QPixmap pix(w,h); if(pix.isNull()) { return; } pix.fill(Qt::transparent); QPainter p(&pix); // draw bar background int xBar = HOR_MARGIN; int yBar = (h - BAR_HEIGHT) / 2; QRect bar(xBar, yBar, w-2*HOR_MARGIN, BAR_HEIGHT); p.setPen(Qt::darkBlue); p.setBrush(Qt::white); p.drawRect(bar); // draw current scale range qreal minScale = demfile->getMinScale(); qreal maxScale = demfile->getMaxScale(); if((minScale != NOFLOAT) || (maxScale != NOFLOAT)) { int x1Range = minScale != NOFLOAT ? HOR_MARGIN + qRound(bar.width() * (1 + log10(minScale)) / 5) : bar.left(); int x2Range = maxScale != NOFLOAT ? HOR_MARGIN + qRound(bar.width() * (1 + log10(maxScale)) / 5) : bar.right(); int yRange = yBar; QRect range(x1Range, yRange, x2Range - x1Range, BAR_HEIGHT); p.setPen(Qt::NoPen); p.setBrush(Qt::darkGreen); p.drawRect(range); } // draw scale indicator int xInd = HOR_MARGIN + qRound(bar.width() * (1 + log10(scale.x())) / 5) - 3; int yInd = yBar - 1; QRect ind(xInd, yInd, 5, BAR_HEIGHT + 2); p.setPen(Qt::darkBlue); p.setBrush(Qt::NoBrush); p.drawRect(ind); labelScale->setPixmap(pix); } void CDemPropSetup::slotSlopeChanged(int val) { uint level = QObject::sender()->property("level").toUInt(); demfile->setSlopeStepTableCustomValue(level, val); } void CDemPropSetup::slotSlopeValiddateAfterEdit() { uint level = QObject::sender()->property("level").toUInt(); demfile->setSlopeStepTableCustomValue(level, slopeSpins[level]->value()); if(comboGrades->count() - 1 == comboGrades->currentIndex()) { // ensure the levels below/above the current level are // less/greater than the current level for(uint l = level; l < SLOPE_LEVELS - 1; l++) { int val = slopeSpins[l]->value(); if(slopeSpins[l + 1]->value() <= val) { slopeSpins[l + 1]->setValue(val + 1); demfile->setSlopeStepTableCustomValue(l + 1, val + 1); } } for(uint l = level; l >= 1; l--) { int val = slopeSpins[l]->value(); if(slopeSpins[l - 1]->value() >= val) { slopeSpins[l - 1]->setValue(val - 1); demfile->setSlopeStepTableCustomValue(l - 1, val - 1); } } } dem->emitSigCanvasUpdate(); } void CDemPropSetup::slotGradeIndex(int idx) { // enable the spins if the selected entry is `custom` demfile->setSlopeStepTable(idx == IDem::slopePresetCount ? -1 : idx); slotPropertiesChanged(); } qmapshack-1.5.1/src/dem/CDemVRT.h000644 001750 000144 00000003024 12622435722 017374 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEMVRT_H #define CDEMVRT_H #include "dem/IDem.h" #include class CDemDraw; class GDALDataset; class CDemVRT : public IDem { Q_OBJECT public: CDemVRT(const QString& filename, CDemDraw *parent); virtual ~CDemVRT(); void draw(IDrawContext::buffer_t& buf); qreal getElevationAt(const QPointF& pos); private: QMutex mutex; QString filename; /// instance of GDAL dataset GDALDataset * dataset; QPointF ref1; QPointF ref2; QPointF ref3; QPointF ref4; QTransform trFwd; QTransform trInv; bool hasOverviews = false; QRectF boundingBox; }; #endif //CDEMVRT_H qmapshack-1.5.1/src/dem/IDem.cpp000644 001750 000144 00000021212 12622435717 017344 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "dem/CDemDraw.h" #include "dem/CDemPropSetup.h" #include "dem/IDem.h" #include inline qint16 getValue(QVector& data, int x, int y, int dx) { return data[x + y * dx]; } inline void fillWindow(QVector& data, int x, int y, int dx, qint16 * w) { w[0] = getValue(data, x - 1, y - 1, dx); w[1] = getValue(data, x, y - 1, dx); w[2] = getValue(data, x + 1, y - 1, dx); w[3] = getValue(data, x - 1, y, dx); w[4] = getValue(data, x, y, dx); w[5] = getValue(data, x + 1, y, dx); w[6] = getValue(data, x - 1, y + 1, dx); w[7] = getValue(data, x, y + 1, dx); w[8] = getValue(data, x + 1, y + 1, dx); } const struct SlopePresets IDem::slopePresets[7] { /* http://www.alpenverein.de/bergsport/sicherheit/skitouren-schneeschuh-sicher-im-schnee/dav-snowcard_aid_10619.html */ { "Grade 1 (DAV Snowcard)", {27.0, 31.0, 34.0, 39.0, 50.0}}, { "Grade 2 (DAV Snowcard)", {27.0, 30.0, 32.0, 35.0, 39.0}}, { "Grade 3 (DAV Snowcard)", {27.0, 29.0, 30.0, 31.0, 34.0}}, { "Grade 4 (DAV Snowcard)", {23.0, 25.0, 27.0, 28.0, 30.0}}, { "level country", { 3.0, 6.0, 8.0, 12.0, 15.0}}, { "secondary mountain", { 4.0, 7.0, 10.0, 15.0, 20.0}}, { "lofty mountain", {10.0, 15.0, 20.0, 30.0, 50.0}} }; IDem::IDem(CDemDraw *parent) : IDrawObject(parent) , dem(parent) { slotSetOpacity(50); pjtar = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); graytable.resize(256); for(int i = 0; i < 255; i++) { graytable[i] = qRgba(i,i,i,255); } graytable[255] = qRgba(0,0,0,0); slopetable << qRgba(0,0,0,0); slopetable << qRgba(0,128,0,100); slopetable << qRgba(0,255,0,100); slopetable << qRgba(255,255,0,100); slopetable << qRgba(255,128,0,100); slopetable << qRgba(255,0,0,100); } IDem::~IDem() { pj_free(pjtar); pj_free(pjsrc); } void IDem::saveConfig(QSettings& cfg) { IDrawObject::saveConfig(cfg); cfg.setValue("doHillshading", bHillshading); cfg.setValue("factorHillshading", factorHillshading); cfg.setValue("doSlopeColor", bSlopeColor); cfg.setValue("gradeSlopeColor", gradeSlopeColor); cfg.setValue("slopeCustomValue0", slopeCustomStepTable[0]); cfg.setValue("slopeCustomValue1", slopeCustomStepTable[1]); cfg.setValue("slopeCustomValue2", slopeCustomStepTable[2]); cfg.setValue("slopeCustomValue3", slopeCustomStepTable[3]); cfg.setValue("slopeCustomValue4", slopeCustomStepTable[4]); } void IDem::loadConfig(QSettings& cfg) { IDrawObject::loadConfig(cfg); bHillshading = cfg.value("doHillshading", bHillshading ).toBool(); factorHillshading = cfg.value("factorHillshading", factorHillshading).toFloat(); bSlopeColor = cfg.value("doSlopeColor", bSlopeColor ).toBool(); gradeSlopeColor = cfg.value("gradeSlopeColor", gradeSlopeColor ).toInt(); slopeCustomStepTable[0] = cfg.value("slopeCustomValue0", 5.).toFloat(); slopeCustomStepTable[1] = cfg.value("slopeCustomValue1", 10.).toFloat(); slopeCustomStepTable[2] = cfg.value("slopeCustomValue2", 15.).toFloat(); slopeCustomStepTable[3] = cfg.value("slopeCustomValue3", 20.).toFloat(); slopeCustomStepTable[4] = cfg.value("slopeCustomValue4", 25.).toFloat(); } IDemProp * IDem::getSetup() { if(setup.isNull()) { setup = new CDemPropSetup(this, dem); } return setup; } void IDem::slotSetFactorHillshade(int f) { if(f == 0) { factorHillshading = 1.0; } else if(f < 0) { factorHillshading = -1.0/f; } else { factorHillshading = f; } } void IDem::setSlopeStepTableCustomValue(int idx, int val) { slopeCustomStepTable[idx] = (qreal) val; } void IDem::setSlopeStepTable(int idx) { gradeSlopeColor = idx; dem->emitSigCanvasUpdate(); } const qreal* IDem::getCurrentSlopeStepTable() { if(CUSTOM_SLOPE_COLORTABLE == gradeSlopeColor) { return slopeCustomStepTable; } else { return slopePresets[gradeSlopeColor].steps; } } int IDem::getFactorHillshading() { if(factorHillshading == 1.0) { return 0; } else if(factorHillshading < 1) { return -1.0/factorHillshading; } else { return factorHillshading; } } void IDem::hillshading(QVector& data, qreal w, qreal h, QImage& img) { int wp2 = w + 2; qint16 win[9]; qreal dx, dy, aspect, xx_plus_yy, cang; #define ZFACT 0.125 #define ZFACT_BY_ZFACT (ZFACT*ZFACT) #define SIN_ALT (qSin(45*DEG_TO_RAD)) #define ZFACT_COS_ALT (ZFACT*qCos(45*DEG_TO_RAD)) #define AZ (315 * DEG_TO_RAD) for(int m = 1; m <= h; m++) { for(int n = 1; n <= w; n++) { fillWindow(data, n, m, wp2, win); if(hasNoData && win[4] == noData) { img.setPixel(n - 1, m - 1, 255); continue; } dx = ((win[0] + win[3] + win[3] + win[6]) - (win[2] + win[5] + win[5] + win[8])) / (xscale*factorHillshading); dy = ((win[6] + win[7] + win[7] + win[8]) - (win[0] + win[1] + win[1] + win[2])) / (yscale*factorHillshading); aspect = qAtan2(dy, dx); xx_plus_yy = dx * dx + dy * dy; cang = (SIN_ALT - ZFACT_COS_ALT * qSqrt(xx_plus_yy) * qSin(aspect - AZ)) / qSqrt(1+ZFACT_BY_ZFACT*xx_plus_yy); if (cang <= 0.0) { cang = 1.0; } else { cang = 1.0 + (254.0 * cang); } img.setPixel(n - 1, m - 1, cang); } } } void IDem::slopecolor(QVector& data, qreal w, qreal h, QImage &img) { int wp2 = w + 2; qint16 win[9]; qreal dx, dy, k, slope; for(int m = 1; m <= h; m++) { for(int n = 1; n <= w; n++) { fillWindow(data, n, m, wp2, win); dx = ((win[0] + win[3] + win[3] + win[6]) - (win[2] + win[5] + win[5] + win[8])) / (xscale); dy = ((win[6] + win[7] + win[7] + win[8]) - (win[0] + win[1] + win[1] + win[2])) / (yscale); k = (dx * dx + dy * dy); slope = qAtan(qSqrt(k) / (8 * 1.0)) * 180.0 / M_PI; const qreal *currentSlopeStepTable = getCurrentSlopeStepTable(); if(slope > currentSlopeStepTable[4]) { img.setPixel(n - 1, m - 1, 5); } else if(slope > currentSlopeStepTable[3]) { img.setPixel(n - 1, m - 1, 4); } else if(slope > currentSlopeStepTable[2]) { img.setPixel(n - 1, m - 1, 3); } else if(slope > currentSlopeStepTable[1]) { img.setPixel(n - 1, m - 1, 2); } else if(slope > currentSlopeStepTable[0]) { img.setPixel(n - 1, m - 1, 1); } else { img.setPixel(n - 1, m - 1, 0); } } } } void IDem::drawTile(QImage& img, QPolygonF& l, QPainter& p) { dem->convertRad2Px(l); // adjust the tiles width and height to fit the buffer's scale qreal dx1 = l[0].x() - l[1].x(); qreal dy1 = l[0].y() - l[1].y(); qreal dx2 = l[0].x() - l[3].x(); qreal dy2 = l[0].y() - l[3].y(); qreal w = qCeil( qSqrt(dx1*dx1 + dy1*dy1)); qreal h = qCeil( qSqrt(dx2*dx2 + dy2*dy2)); // calculate rotation. This is not really a reprojection but might be good enough for close zoom levels qreal a = qAtan(dy1/dx1) * RAD_TO_DEG; // finally translate, scale, rotate and draw tile p.save(); p.translate(l[0]); p.scale(w/img.width(), h/img.height()); p.rotate(a); p.drawImage(0,0,img); p.restore(); } qmapshack-1.5.1/src/dem/CDemVRT.cpp000644 001750 000144 00000023413 12622435717 017737 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "dem/CDemDraw.h" #include "dem/CDemVRT.h" #include "helpers/CDraw.h" #include "units/IUnit.h" #include #include #include #define TILELIMIT 30000 #define TILESIZEX 64 #define TILESIZEY 64 CDemVRT::CDemVRT(const QString &filename, CDemDraw *parent) : IDem(parent) , filename(filename) { qDebug() << "------------------------------"; qDebug() << "VRT: try to open" << filename; dataset = (GDALDataset*)GDALOpen(filename.toUtf8(),GA_ReadOnly); if(dataset == 0) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to load file: %1").arg(filename)); return; } if(dataset->GetRasterCount() != 1) { delete dataset; dataset = 0; QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("DEM must have one band with 16bit or 32bit data.")); return; } GDALRasterBand * pBand; pBand = dataset->GetRasterBand(1); if(pBand == 0) { delete dataset; dataset = 0; QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("DEM must have one band with 16bit or 32bit data.")); return; } hasOverviews = pBand->GetOverviewCount() != 0; qDebug() << "has overviews" << hasOverviews; noData = pBand->GetNoDataValue(&hasNoData); qDebug() << "no data:" << hasNoData << noData; // ------- setup projection --------------- char str[1024] = {0}; if(dataset->GetProjectionRef()) { strncpy(str,dataset->GetProjectionRef(),sizeof(str)); } char * ptr = str; OGRSpatialReference oSRS; oSRS.importFromWkt(&ptr); oSRS.exportToProj4(&ptr); qDebug() << ptr; pjsrc = pj_init_plus(ptr); free(ptr); if(pjsrc == 0) { delete dataset; dataset = 0; QMessageBox::warning(0, tr("Error..."), tr("No georeference information found.")); return; } xsize_px = dataset->GetRasterXSize(); ysize_px = dataset->GetRasterYSize(); qreal adfGeoTransform[6]; dataset->GetGeoTransform( adfGeoTransform ); xscale = adfGeoTransform[1]; yscale = adfGeoTransform[5]; trFwd.translate(adfGeoTransform[0], adfGeoTransform[3]); trFwd.scale(adfGeoTransform[1],adfGeoTransform[5]); if(adfGeoTransform[4] != 0.0) { trFwd.rotate(qAtan(adfGeoTransform[2]/adfGeoTransform[4])); } if(pj_is_latlong(pjsrc)) { xscale *= 111120; yscale *= 111120; // convert to RAD to match internal notations trFwd = trFwd * DEG_TO_RAD; } trInv = trFwd.inverted(); ref1 = trFwd.map(QPointF(0,0)); ref2 = trFwd.map(QPointF(xsize_px,0)); ref3 = trFwd.map(QPointF(xsize_px,ysize_px)); ref4 = trFwd.map(QPointF(0,ysize_px)); qDebug() << ref1 << ref2 << ref3 << ref4; boundingBox = QRectF(ref1, ref3); qDebug() << "FF" << trFwd; qDebug() << "RR" << trInv; isActivated = true; } CDemVRT::~CDemVRT() { delete dataset; } qreal CDemVRT::getElevationAt(const QPointF& pos) { if(pjsrc == 0) { return NOFLOAT; } qint16 e[4]; QPointF pt = pos; pj_transform(pjtar, pjsrc, 1, 0, &pt.rx(), &pt.ry(), 0); if(!boundingBox.contains(pt)) { return NOFLOAT; } pt = trInv.map(pt); qreal x = pt.x() - qFloor(pt.x()); qreal y = pt.y() - qFloor(pt.y()); mutex.lock(); CPLErr err = dataset->RasterIO(GF_Read, qFloor(pt.x()), qFloor(pt.y()), 2, 2, &e, 2, 2, GDT_Int16, 1, 0, 0, 0, 0); mutex.unlock(); if(err == CE_Failure) { return NOFLOAT; } if(hasNoData && e[0] == noData) { return NOFLOAT; } qreal b1 = e[0]; qreal b2 = e[1] - e[0]; qreal b3 = e[2] - e[0]; qreal b4 = e[0] - e[1] - e[2] + e[3]; qreal ele = b1 + b2 * x + b3 * y + b4 * x * y; return ele; } void CDemVRT::draw(IDrawContext::buffer_t& buf) { if(dem->needsRedraw()) { return; } if(!doHillshading() && !doSlopeColor()) { QThread::msleep(100); return; } QPointF bufferScale = buf.scale * buf.zoomFactor; // get pixel offset of top left buffer corner QPointF pp = buf.ref1; dem->convertRad2Px(pp); // calculate area to read from file QPointF pt1 = buf.ref1; QPointF pt2 = buf.ref2; QPointF pt3 = buf.ref3; QPointF pt4 = buf.ref4; pj_transform(pjtar,pjsrc, 1, 0, &pt1.rx(), &pt1.ry(), 0); pj_transform(pjtar,pjsrc, 1, 0, &pt2.rx(), &pt2.ry(), 0); pj_transform(pjtar,pjsrc, 1, 0, &pt3.rx(), &pt3.ry(), 0); pj_transform(pjtar,pjsrc, 1, 0, &pt4.rx(), &pt4.ry(), 0); pt1 = trInv.map(pt1); pt2 = trInv.map(pt2); pt3 = trInv.map(pt3); pt4 = trInv.map(pt4); qreal left, right, top, bottom; left = qRound(pt1.x() < pt4.x() ? pt1.x() : pt4.x()); right = qRound(pt2.x() > pt3.x() ? pt2.x() : pt3.x()); top = qRound(pt1.y() < pt2.y() ? pt1.y() : pt2.y()); bottom = qRound(pt4.y() > pt3.y() ? pt4.y() : pt3.y()); if(left <= 0) { left = 1; } if(left >= xsize_px) { left = xsize_px - 1; } if(top <= 0) { top = 1; } if(top >= ysize_px) { top = ysize_px - 1; } if(right >= xsize_px) { right = xsize_px - 1; } if(right <= 0) { right = 1; } if(bottom >= ysize_px) { bottom = ysize_px - 1; } if(bottom <= 0) { bottom = 1; } qreal imgw = TILESIZEX; qreal imgh = TILESIZEY; qreal w = imgw; qreal h = imgh; /* As the 3x3 window will create a border of one pixel more data is read than displayed to compensate. */ int wp2 = w + 2; int hp2 = h + 2; // start to draw the map QPainter p(&buf.image); USE_ANTI_ALIASING(p,true); p.translate(-pp); qreal o1 = getOpacity()/100.0; qreal o2 = ((o1 + 0.4) >= 1.0) ? o1 : (o1 + 0.4); p.setOpacity(o1); qreal nTiles = ((right - left) * (bottom - top) / (w * h)); qDebug() << "DEM> tiles:" << nTiles; if(!isOutOfScale(bufferScale) && (nTiles < TILELIMIT)) { for(qreal y = top - 1; y < bottom; y += h) { if(dem->needsRedraw()) { break; } for(qreal x = left - 1; x < right; x += w) { if(dem->needsRedraw()) { break; } CPLErr err = CE_Failure; qreal wp2_used = wp2; qreal hp2_used = hp2; qreal w_used = w; qreal h_used = h; if((x + wp2) > xsize_px) { wp2_used = xsize_px - x; w_used = wp2_used - 2; if(w_used < 2) { continue; } } if((y + hp2) > ysize_px) { hp2_used = ysize_px - y; h_used = hp2_used - 2; if(h_used < 2) { continue; } } QVector data(wp2_used * hp2_used); mutex.lock(); err = dataset->RasterIO(GF_Read, x, y, wp2_used, hp2_used, data.data(), wp2_used, hp2_used, GDT_Int16, 1, 0, 0, 0, 0); mutex.unlock(); if(err) { continue; } QPolygonF l(4); l[0] = QPointF(x + 1, y + 1); l[1] = QPointF(x + 1 + w_used, y + 1); l[2] = QPointF(x + 1 + w_used, y + 1 + h_used); l[3] = QPointF(x + 1, y + 1 + h_used); l = trFwd.map(l); pj_transform(pjsrc,pjtar, 1, 0, &l[0].rx(), &l[0].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[1].rx(), &l[1].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[2].rx(), &l[2].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[3].rx(), &l[3].ry(), 0); if(doHillshading()) { QPolygonF r = l; QImage img(w_used,h_used,QImage::Format_Indexed8); img.setColorTable(graytable); hillshading(data, w_used, h_used, img); drawTile(img, r, p); } if(doSlopeColor()) { QPolygonF r = l; QImage img(w_used,h_used,QImage::Format_Indexed8); img.setColorTable(slopetable); slopecolor(data, w_used, h_used, img); p.setOpacity(o2); drawTile(img, r, p); p.setOpacity(o1); } } } } } qmapshack-1.5.1/src/dem/CDemItem.h000644 001750 000144 00000005303 12622435722 017621 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEMITEM_H #define CDEMITEM_H #include #include #include #include class IDem; class CDemDraw; class QSettings; class CDemItem : public QTreeWidgetItem { public: CDemItem(QTreeWidget *parent, CDemDraw *dem); virtual ~CDemItem(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); /** @brief As the drawing thread is using the list widget to iterate of all maps to draw, all access has to be synchronized. */ static QMutex mutexActiveDems; /** @brief Query if dem objects are loaded @return True if the internal list of dem objects is not empty. */ bool isActivated(); /** @brief Either loads or destroys internal map objects @return True if the internal list of maps is not empty after the operation. */ bool toggleActivate(); /** * @brief Load all internal map objects * @return Return true on success. */ bool activate(); /** @brief Delete all internal map objects */ void deactivate(); /** @brief Move item to top of list widget */ void moveToTop(); void moveToBottom(); void updateIcon(); /** @brief Show or hide child treewidget items @param yes set true to add children, false will remove all children and delete the attached widgets */ void showChildren(bool yes); private: friend class CDemDraw; CDemDraw * dem; /** @brief A MD5 hash over the first 1024 bytes of the map file, to identify the map */ QString key; /** @brief List of map files forming that particular map */ QString filename; /** @brief List of loaded map objects when map is activated. */ QPointer demfile; }; #endif //CDEMITEM_H qmapshack-1.5.1/src/dem/CDemPropSetup.h000644 001750 000144 00000003246 12622435722 020670 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEMPROPSETUP_H #define CDEMPROPSETUP_H #define SLOPE_LEVELS 5 #include "dem/IDemProp.h" #include "ui_IDemPropSetup.h" class CDemPropSetup : public IDemProp, private Ui::IDemPropSetup { Q_OBJECT public: CDemPropSetup(IDem *demfile, CDemDraw *dem); virtual ~CDemPropSetup(); protected slots: void slotPropertiesChanged(); protected: void resizeEvent(QResizeEvent * e); private slots: void slotScaleChanged(const QPointF& s); void slotSetMinScale(bool checked); void slotSetMaxScale(bool checked); void slotGradeIndex(int idx); void slotSlopeValiddateAfterEdit(); void slotSlopeChanged(int val); private: void updateScaleLabel(); CTinySpinBox* slopeSpins[SLOPE_LEVELS]; static QPointF scale; }; #endif //CDEMPROPSETUP_H qmapshack-1.5.1/src/dem/CDemList.h000644 001750 000144 00000003366 12622435722 017645 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEMLIST_H #define CDEMLIST_H #include #include class CDemItem; class CDemTreeWidget : public QTreeWidget { Q_OBJECT public: CDemTreeWidget(QWidget * parent) : QTreeWidget(parent) { } signals: void sigChanged(); protected: void dragMoveEvent ( QDragMoveEvent * event ); void dropEvent ( QDropEvent * event ); }; #include "ui_IDemList.h" class CDemList : public QWidget, private Ui::IDemsList { Q_OBJECT public: CDemList(QWidget * parent); virtual ~CDemList(); void clear(); int count(); CDemItem * item(int i); operator QTreeWidget*() { return treeWidget; } void updateHelpText(); signals: void sigChanged(); private slots: void slotActivate(); void slotContextMenu(const QPoint &point); private: QMenu * menu; }; #endif //CDEMLIST_H qmapshack-1.5.1/src/dem/CDemItem.cpp000644 001750 000144 00000011507 12622435717 020163 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "dem/CDemDraw.h" #include "dem/CDemItem.h" #include "dem/CDemVRT.h" #include "dem/IDemProp.h" #include QMutex CDemItem::mutexActiveDems(QMutex::Recursive); CDemItem::CDemItem(QTreeWidget * parent, CDemDraw *dem) : QTreeWidgetItem(parent) , dem(dem) { setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); } CDemItem::~CDemItem() { } void CDemItem::saveConfig(QSettings& cfg) { if(demfile.isNull()) { return; } cfg.beginGroup(key); demfile->saveConfig(cfg); cfg.endGroup(); } void CDemItem::loadConfig(QSettings& cfg) { if(demfile.isNull()) { return; } cfg.beginGroup(key); demfile->loadConfig(cfg); cfg.endGroup(); } void CDemItem::showChildren(bool yes) { if(yes && !demfile.isNull()) { QTreeWidget * tw = treeWidget(); QTreeWidgetItem * item = new QTreeWidgetItem(this); item->setFlags(Qt::ItemIsEnabled); tw->setItemWidget(item, 0, demfile->getSetup()); } else { QList items = takeChildren(); qDeleteAll(items); delete demfile->getSetup(); } } void CDemItem::updateIcon() { if(filename.isEmpty()) { return; } QPixmap img("://icons/32x32/Map.png"); QFileInfo fi(filename); if(fi.suffix().toLower() == "vrt") { img = QPixmap("://icons/32x32/MimeDemVRT.png"); } setIcon(0,QIcon(img)); } bool CDemItem::isActivated() { QMutexLocker lock(&mutexActiveDems); return !demfile.isNull(); } bool CDemItem::toggleActivate() { QMutexLocker lock(&mutexActiveDems); if(demfile.isNull()) { return activate(); } else { deactivate(); return false; } } void CDemItem::deactivate() { QMutexLocker lock(&mutexActiveDems); // remove demfile setup dialog as child of this item showChildren(false); // remove demfile object delete demfile; // maybe used to reflect changes in the icon updateIcon(); // move to bottom of the active dem list moveToBottom(); // deny drag-n-drop again setFlags(flags() & ~Qt::ItemIsDragEnabled); } bool CDemItem::activate() { QMutexLocker lock(&mutexActiveDems); // remove demfile object delete demfile; // load map by suffix QFileInfo fi(filename); if(fi.suffix().toLower() == "vrt") { demfile = new CDemVRT(filename, dem); } updateIcon(); // no mapfiles loaded? Bad. if(demfile.isNull()) { return false; } // if map is activated successfully add to the list of map files // else delete all previous loaded maps and abort if(!demfile->activated()) { delete demfile; return false; } moveToBottom(); setFlags(flags() | Qt::ItemIsDragEnabled); /* As the map file setup is stored in the context of the CMapDraw object the configuration has to be loaded via the CMapDraw object to select the correct group context in the QSetting object. This call will result into a call of loadConfig() of this CMapItem object. */ dem->loadConfigForDemItem(this); // Add the demfile setup dialog as child of this item showChildren(true); return true; } void CDemItem::moveToTop() { QTreeWidget * w = treeWidget(); QMutexLocker lock(&mutexActiveDems); w->takeTopLevelItem(w->indexOfTopLevelItem(this)); w->insertTopLevelItem(0, this); dem->emitSigCanvasUpdate(); } void CDemItem::moveToBottom() { int row; QTreeWidget * w = treeWidget(); QMutexLocker lock(&mutexActiveDems); w->takeTopLevelItem(w->indexOfTopLevelItem(this)); for(row = 0; row < w->topLevelItemCount(); row++) { CDemItem * item = dynamic_cast(w->topLevelItem(row)); if(item && item->demfile.isNull()) { break; } } w->insertTopLevelItem(row, this); dem->emitSigCanvasUpdate(); } qmapshack-1.5.1/src/dem/CDemList.cpp000644 001750 000144 00000006754 12622435717 020210 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "dem/CDemItem.h" #include "dem/CDemList.h" #include void CDemTreeWidget::dragMoveEvent ( QDragMoveEvent * event ) { CDemItem * item = dynamic_cast(itemAt(event->pos())); if(item && item->isActivated()) { event->setDropAction(Qt::MoveAction); QTreeWidget::dragMoveEvent(event); } else { event->setDropAction(Qt::IgnoreAction); } } void CDemTreeWidget::dropEvent ( QDropEvent * event ) { CDemItem * item = dynamic_cast(currentItem()); if(item) { item->showChildren(false); } QTreeWidget::dropEvent(event); if(item) { item->showChildren(true); } emit sigChanged(); } CDemList::CDemList(QWidget *parent) : QWidget(parent) { setupUi(this); connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint))); connect(actionActivate, SIGNAL(triggered()), this, SLOT(slotActivate())); connect(treeWidget, SIGNAL(sigChanged()), SIGNAL(sigChanged())); menu = new QMenu(this); menu->addAction(actionActivate); } CDemList::~CDemList() { } void CDemList::clear() { treeWidget->clear(); } int CDemList::count() { return treeWidget->topLevelItemCount(); } CDemItem * CDemList::item(int i) { return dynamic_cast(treeWidget->topLevelItem(i)); } void CDemList::updateHelpText() { if(treeWidget->topLevelItemCount() == 0) { labelIcon->show(); labelHelpFillMapList->show(); labelHelpActivateMap->hide(); } else { labelHelpFillMapList->hide(); CDemItem * item = dynamic_cast(treeWidget->topLevelItem(0)); if(item && item->isActivated()) { labelIcon->hide(); labelHelpActivateMap->hide(); } else { labelIcon->show(); labelHelpActivateMap->show(); } } } void CDemList::slotActivate() { CDemItem * item = dynamic_cast(treeWidget->currentItem()); if(item == 0) { return; } bool activated = item->toggleActivate(); if(!activated) { treeWidget->setCurrentItem(0); } updateHelpText(); } void CDemList::slotContextMenu(const QPoint& point) { CDemItem * item = dynamic_cast(treeWidget->currentItem()); if(item == 0) { return; } bool activated = item->isActivated(); actionActivate->setChecked(activated); actionActivate->setText(activated ? tr("Deactivate") : tr("Activate")); QPoint p = treeWidget->mapToGlobal(point); menu->exec(p); } qmapshack-1.5.1/src/dem/CDemPathSetup.h000644 001750 000144 00000002522 12622435722 020640 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEMPATHSETUP_H #define CDEMPATHSETUP_H #include "ui_IDemPathSetup.h" #include class CDemPathSetup : public QDialog, private Ui::IDemPathSetup { Q_OBJECT public: CDemPathSetup(QStringList& paths); virtual ~CDemPathSetup(); public slots: void accept(); private slots: void slotAddPath(); void slotDelPath(); void slotItemSelectionChanged(); private: QStringList& paths; }; #endif //CDEMPATHSETUP_H qmapshack-1.5.1/src/dem/CDemDraw.cpp000644 001750 000144 00000020022 12622435717 020152 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "dem/CDemDraw.h" #include "dem/CDemItem.h" #include "dem/CDemList.h" #include "dem/CDemPathSetup.h" #include "dem/IDem.h" #include "gis/IGisLine.h" #include "helpers/CSettings.h" #include "units/IUnit.h" #include QList CDemDraw::dems; QStringList CDemDraw::demPaths; QStringList CDemDraw::supportedFormats = QString("*.vrt").split('|'); CDemDraw::CDemDraw(CCanvas *canvas) : IDrawContext("dem", CCanvas::eRedrawDem, canvas) { demList = new CDemList(canvas); CMainWindow::self().addDemList(demList, canvas->objectName()); connect(canvas, SIGNAL(destroyed()), demList, SLOT(deleteLater())); connect(demList, SIGNAL(sigChanged()), this, SLOT(emitSigCanvasUpdate())); buildMapList(); dems << this; } CDemDraw::~CDemDraw() { dems.removeOne(this); } void CDemDraw::setProjection(const QString& proj) { // --- save the active maps QStringList keys; saveActiveMapsList(keys); // --- now set the new projection IDrawContext::setProjection(proj); // --- now build the map list from scratch. This will deactivate -> activate all maps // By that everything is restored with the new projection buildMapList(); restoreActiveMapsList(keys); } void CDemDraw::setupDemPath() { CDemPathSetup dlg(demPaths); if(dlg.exec() != QDialog::Accepted) { return; } foreach(CDemDraw * dem, dems) { QStringList keys; dem->saveActiveMapsList(keys); dem->buildMapList(); dem->restoreActiveMapsList(keys); } } void CDemDraw::saveDemPath(QSettings& cfg) { cfg.setValue("demPaths", demPaths); } void CDemDraw::loadDemPath(QSettings& cfg) { demPaths = cfg.value("demPaths", demPaths).toStringList(); } void CDemDraw::saveConfig(QSettings& cfg) { // store group context for later use cfgGroup = cfg.group(); // ------------------- QStringList keys; cfg.beginGroup("dem"); saveActiveMapsList(keys, cfg); cfg.setValue("active", keys); cfg.endGroup(); } void CDemDraw::loadConfig(QSettings& cfg) { // store group context for later use cfgGroup = cfg.group(); // ------------------- cfg.beginGroup("dem"); if(cfgGroup.isEmpty()) { restoreActiveMapsList(cfg.value("active", "").toStringList(), cfg); } else { restoreActiveMapsList(cfg.value("active", "").toStringList()); } cfg.endGroup(); } void CDemDraw::buildMapList() { QCryptographicHash md5(QCryptographicHash::Md5); QMutexLocker lock(&CDemItem::mutexActiveDems); demList->clear(); foreach(const QString &path, demPaths) { QDir dir(path); // find available maps foreach(const QString &filename, dir.entryList(supportedFormats, QDir::Files|QDir::Readable, QDir::Name)) { QFileInfo fi(filename); CDemItem * item = new CDemItem(*demList, this); item->setText(0,fi.baseName().replace("_", " ")); item->filename = dir.absoluteFilePath(filename); item->updateIcon(); // calculate MD5 hash from the file's first 1024 bytes QFile f(dir.absoluteFilePath(filename)); f.open(QIODevice::ReadOnly); md5.reset(); md5.addData(f.read(1024)); item->key = md5.result().toHex(); f.close(); } } demList->updateHelpText(); } void CDemDraw::saveActiveMapsList(QStringList& keys) { SETTINGS; cfg.beginGroup(cfgGroup); cfg.beginGroup("dem"); saveActiveMapsList(keys, cfg); cfg.endGroup(); cfg.endGroup(); } void CDemDraw::saveActiveMapsList(QStringList& keys, QSettings& cfg) { QMutexLocker lock(&CDemItem::mutexActiveDems); for(int i = 0; i < demList->count(); i++) { CDemItem * item = demList->item(i); if(item && !item->demfile.isNull()) { item->saveConfig(cfg); keys << item->key; } } } void CDemDraw::loadConfigForDemItem(CDemItem * item) { if(cfgGroup.isEmpty()) { return; } SETTINGS; cfg.beginGroup(cfgGroup); cfg.beginGroup("dem"); item->loadConfig(cfg); cfg.endGroup(); cfg.endGroup(); } void CDemDraw::restoreActiveMapsList(const QStringList& keys) { QMutexLocker lock(&CDemItem::mutexActiveDems); foreach(const QString &key, keys) { for(int i = 0; i < demList->count(); i++) { CDemItem * item = demList->item(i); if(item && item->key == key) { /** @Note the item will load it's configuration upon successful activation by calling loadConfigForDemItem(). */ item->activate(); break; } } } demList->updateHelpText(); } void CDemDraw::restoreActiveMapsList(const QStringList& keys, QSettings& cfg) { QMutexLocker lock(&CDemItem::mutexActiveDems); foreach(const QString &key, keys) { for(int i = 0; i < demList->count(); i++) { CDemItem * item = demList->item(i); if(item && item->key == key) { if(item->activate()) { item->loadConfig(cfg); } break; } } } demList->updateHelpText(); } qreal CDemDraw::getElevationAt(const QPointF& pos) { qreal ele = NOFLOAT; if(CDemItem::mutexActiveDems.tryLock()) { if(demList) { for(int i = 0; i < demList->count(); i++) { CDemItem * item = demList->item(i); if(!item || item->demfile.isNull()) { // as all active maps have to be at the top of the list // it is ok to break ass soon as the first map with no // active files is hit. break; } ele = item->demfile->getElevationAt(pos); if(ele != NOFLOAT) { break; } } } CDemItem::mutexActiveDems.unlock(); } return ele; } void CDemDraw::getElevationAt(const QPolygonF& pos, QPolygonF& ele) { qreal basefactor = IUnit::self().basefactor; for(int i = 0; i < pos.size(); i++) { ele[i].ry() = getElevationAt(pos[i]) * basefactor; } } void CDemDraw::getElevationAt(SGisLine& line) { line.updateElevation(this); } void CDemDraw::drawt(buffer_t& currentBuffer) { // iterate over all active maps and call the draw method CDemItem::mutexActiveDems.lock(); if(demList) { for(int i = 0; i < demList->count(); i++) { CDemItem * item = demList->item(i); if(!item || item->demfile.isNull()) { // as all active maps have to be at the top of the list // it is ok to break ass soon as the first map with no // active files is hit. break; } item->demfile->draw(currentBuffer); } } CDemItem::mutexActiveDems.unlock(); } qmapshack-1.5.1/src/dem/IDemPathSetup.ui000644 001750 000144 00000011321 12616742144 021033 0ustar00oeichlerusers000000 000000 IDemPathSetup 0 0 450 200 Setup DEM file paths QAbstractItemView::ExtendedSelection ... :/icons/32x32/Add.png:/icons/32x32/Add.png 22 22 false ... :/icons/32x32/DeleteMultiple.png:/icons/32x32/DeleteMultiple.png 22 22 Qt::Vertical 20 40 64 16777215 :/icons/48x48/Help.png Qt::AlignCenter 0 0 - Qt::AlignJustify|Qt::AlignVCenter true 0 0 Qt::Vertical QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IDemPathSetup accept() 248 254 157 274 buttonBox rejected() IDemPathSetup reject() 316 260 286 274 qmapshack-1.5.1/src/dem/IDemProp.cpp000644 001750 000144 00000002250 12622435717 020206 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "dem/CDemDraw.h" #include "dem/IDem.h" #include "dem/IDemProp.h" IDemProp::IDemProp(IDem *demfile, CDemDraw *dem) : demfile(demfile) , dem(dem) { connect(demfile, SIGNAL(sigPropertiesChanged()), this, SLOT(slotPropertiesChanged())); } IDemProp::~IDemProp() { } qmapshack-1.5.1/src/dem/CDemPathSetup.cpp000644 001750 000144 00000005127 12622435717 021203 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "dem/CDemDraw.h" #include "dem/CDemPathSetup.h" #include CDemPathSetup::CDemPathSetup(QStringList &paths) : QDialog(CMainWindow::getBestWidgetForParent()) , paths(paths) { setupUi(this); connect(toolAdd, SIGNAL(clicked()), this, SLOT(slotAddPath())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelPath())); connect(listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged())); foreach(const QString &path, paths) { QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(path); } labelHelp->setText(tr("Add or remove paths containing DEM data. There can be multiple files in a path but no sub-path is parsed. Supported formats are: %1").arg(CDemDraw::getSupportedFormats().join(", "))); } CDemPathSetup::~CDemPathSetup() { } void CDemPathSetup::slotItemSelectionChanged() { QList items = listWidget->selectedItems(); toolDelete->setEnabled(!items.isEmpty()); } void CDemPathSetup::slotAddPath() { QString path = QFileDialog::getExistingDirectory(this, tr("Select DEM file path..."), QDir::homePath(), 0); if(!path.isEmpty()) { if(!paths.contains(path)) { QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(path); } } } void CDemPathSetup::slotDelPath() { QList items = listWidget->selectedItems(); qDeleteAll(items); } void CDemPathSetup::accept() { paths.clear(); for(int i = 0; i < listWidget->count(); i++) { QListWidgetItem *item = listWidget->item(i); paths << item->text(); } QDialog::accept(); } qmapshack-1.5.1/src/dem/IDemProp.h000644 001750 000144 00000002353 12622435722 017653 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDEMPROP_H #define IDEMPROP_H #include class IDem; class CDemDraw; class IDemProp : public QWidget { Q_OBJECT public: IDemProp(IDem *demfile, CDemDraw *dem); virtual ~IDemProp(); protected slots: virtual void slotPropertiesChanged()= 0; protected: IDem * demfile; CDemDraw * dem; }; #endif //IDEMPROP_H qmapshack-1.5.1/src/dem/IDemPropSetup.ui000644 001750 000144 00000041261 12623413746 021066 0ustar00oeichlerusers000000 000000 IDemPropSetup 0 0 400 163 Form 3 3 3 3 3 <html><head/><body><p>Change opacity of map</p></body></html> Qt::Horizontal 3 <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> ... :/icons/8x8/bullet_green.png :/icons/8x8/bullet_red.png:/icons/8x8/bullet_green.png 8 8 true <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> true <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> ... :/icons/8x8/bullet_green.png :/icons/8x8/bullet_red.png:/icons/8x8/bullet_green.png 8 8 true 3 Hillshading -8 4 2 2 Qt::Horizontal Slope QComboBox::NoInsert 3 0 0 16777215 20 255 255 255 255 255 255 239 235 231 false false QAbstractSpinBox::NoButtons false ° > 4 0 0 20 10 TextLabel 0 0 20 10 TextLabel 16777215 20 255 255 255 255 255 255 239 235 231 false QAbstractSpinBox::NoButtons ° > 3 98 0 0 20 10 TextLabel 16777215 20 255 255 255 255 255 255 239 235 231 false QAbstractSpinBox::NoButtons ° > 1 96 0 0 20 10 TextLabel 16777215 20 255 255 255 255 255 255 239 235 231 false QAbstractSpinBox::NoButtons ° > 2 97 0 0 20 10 TextLabel 16777215 20 255 255 255 255 255 255 239 235 231 false QAbstractSpinBox::NoButtons ° > 95 CTinySpinBox QSpinBox
widgets/CTinySpinBox.h
qmapshack-1.5.1/src/dem/IDemList.ui000644 001750 000144 00000011170 12527654570 020041 0ustar00oeichlerusers000000 000000 IDemsList 0 0 400 622 Form 0 0 0 0 0 Qt::CustomContextMenu QAbstractItemView::InternalMove 32 32 false 1 3 3 3 3 3 0 0 64 16777215 :/icons/48x48/Help.png Qt::AlignCenter 8 0 0 0 0 To add files with elevation data use File->Setup DEM Paths. Qt::AlignJustify|Qt::AlignVCenter true Use the context menu (right mouse button click on entry) to activate a file. Use drag-n-drop to move the activated file in the process order. Qt::AlignJustify|Qt::AlignVCenter true true :/icons/32x32/Check.png :/icons/32x32/Cancel.png:/icons/32x32/Check.png Activate CDemTreeWidget QTreeWidget
dem/CDemList.h
qmapshack-1.5.1/src/dem/CDemDraw.h000644 001750 000144 00000006013 12622435722 017617 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEMDRAW_H #define CDEMDRAW_H #include "canvas/IDrawContext.h" class QPainter; class CDemList; class CCanvas; class QSettings; class CDemItem; class CDemDraw : public IDrawContext { public: CDemDraw(CCanvas * canvas); virtual ~CDemDraw(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); /** @brief This is called most likely from the item itself to call it's loadConfig() method. As the setup of a map is stored in the context of the view the correct groups have to be set prior to call the item's loadConfig() method. However the item does not know all that stuff. That is why it has to ask it's CMapDraw object to prepare the QSettings object and to call loadConfig(); @param item the item to call it's loadConfig() method */ void loadConfigForDemItem(CDemItem * item); qreal getElevationAt(const QPointF& pos); void getElevationAt(const QPolygonF& pos, QPolygonF& ele); void getElevationAt(SGisLine& line); void setProjection(const QString& proj); static void setupDemPath(); static void saveDemPath(QSettings &cfg); static void loadDemPath(QSettings &cfg); static const QStringList& getSupportedFormats() { return supportedFormats; } protected: void drawt(buffer_t& currentBuffer); private: /** @brief Search in paths found in mapPaths for files with supported extensions and add them to mapList. */ void buildMapList(); /** @brief Save list of active maps to configuration file */ void saveActiveMapsList(QStringList &keys, QSettings &cfg); void saveActiveMapsList(QStringList &keys); /** @brief Restore list of active maps from configuration file */ void restoreActiveMapsList(const QStringList &keys); void restoreActiveMapsList(const QStringList& keys, QSettings &cfg); CDemList * demList; /// the group label used in QSettings QString cfgGroup; static QStringList demPaths; static QList dems; /// a list of supported map formats static QStringList supportedFormats; }; #endif //CDEMDRAW_H qmapshack-1.5.1/src/icons/waypoints/CityMedium.svg000644 001750 000144 00000004646 12570062516 023226 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/DiamondBlue.svg000644 001750 000144 00000004067 12527654570 023346 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/32x32/Default.png000644 001750 000144 00000003603 12556202320 023270 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs XtEXtSoftwarewww.inkscape.org<IDATXWLs$" H^MͶ%D>7@#LCC \fPRAT 3UJ<^3N-2; ."z韞\z]g4'zCQ%wViY4> N̙~^y9 /AQQ"QM߯ La;v[(,@I[ r.5A$c+UQQqb<tLmpp*-if*FZ(]DLݲūeei`Y@Tɶ%GF}g5S~yhLGlsUU\M|w߂  $ꊊ9wׯGp"@Y@wATBvpee&&, KwW %?BDTDEm%W:Og4Cx6ޘ;"v;M<3M`0ScQiN2&pWW0nߎ"HUy׻a˜m%8DGB; 8q55>}:7~~0qVШS/ߵ >k_ ˱YB60 %vqJ- Xm[>|DD -ƽ$I4鄭4 5Te H ڣ1| _)y5 r%ayP',^Vh] s8yS(MVMS.'˗GHO>ܹWS.*/ղNTh?v;5%%nֿp%Ɲ \O(ZZBX8 h@d`Yf V]o3֭(>h lKF")ºu #C0l% #:DNSY[x<8woދGWDLqoxqHtDj`rB{0:;d3OLoJʊWUj1M'Z1lOSݧA#h_~q=yj1:j5$}}QҘzm%?/ss~PK 镑iMQ~~Z)|񠊌ſ`t:B'&,|YV 5ȀN\''|m[ryh(G"'!hk0SL f퍦 MtwE JTe4u"J9ҍw7o".q@$1O@hPӋ%OBom t=q\@b ད8{+WF)ܹ5ókw1DL/Ji;Fkk=y7u$\F@T-sRYP,KQd4~눒|?=\Gpt0SD)9l@c$ "LJ(ٿ6{N-K9Uyv;IENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/CityCapitol.png000644 001750 000144 00000001134 12570062516 024134 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX햱jAK"P"Z}Q./6-y@ѠT yptSSTxTpylw66x`Y6dǜz]xxKjkt\^YEblC } % j1zV% S@ާ8 1@1SIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/FlagRed.png000644 001750 000144 00000001264 12556202321 023212 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs mtEXtSoftwarewww.inkscape.org<1IDATX͋Qf̋I(/iQb$+% bceo )ɒ4v 5%(`y.3yif5#'Pp$|%(z#Pm|n|AZ^!3Y@ՠ5L)uM%-(Cc]]hʩFG5 zK%b MXi2+"^:E:&n<&cDTj>`'lO+=9[ y\e#c:F8ϱxC"&2 еLʅ nX|ઈgKi:S)"F+"-q":[ @Ji QZf r=4Xě ЏY@\ 5 `+6O *׃Mx e 75lߗvIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/CityMedium.png000644 001750 000144 00000000650 12570062516 023763 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<%IDATX1j1EBN8\ɥw1lmIbRd!!Xb ;Jß*׬NfDĈQ"bWZIxQT$X9G=!䀹cfpmӜoѪK596S~>#\򖫛1bMƾ"r=I+ @USax$h}ջRSSgiæAe Q~Dsp@Y 7 |-5Qf`/MVBIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/FlagBlue.png000644 001750 000144 00000001273 12556202321 023367 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs mtEXtSoftwarewww.inkscape.org<8IDATXϋa4F$+% bceo )ɒDQ6ԔDj~MX\J.` <@]Ycq8!|F /?)`oez}Px{ SJžʂo+a wpdCmո!, u7WPm*NAXU*.GxYoq bs%>p/BZ]9-aQruT,kaDgؐ߷AI~-aYµݬcb2>5[t6.b۸bδRJJbXV RZ4o_.yhnj0[[!^llVV|fo8lΞrIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/DiamondGreen.png000644 001750 000144 00000000664 12556202320 024244 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ƶtEXtSoftwarewww.inkscape.org<1IDATX׿m@å `.h2@sފ l-Ma?wqI'Q{~ t& ϘYq@|Jj]I^@Pw]J7J@~Ahn58`\̬30L"0OD I@T܉ )x,@,D=>0΀qE;/q8@[(o#7*܅9 > I-ƸrnX'^Kj UqxdE{l `qL|" > HF'nDD|x0`?6hԶ> <~IIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/FlagGreen.png000644 001750 000144 00000001356 12556202321 023542 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs mtEXtSoftwarewww.inkscape.org<kIDATXϋu&]VQ.\آ6-h(h 27PDq e)u>3wdz?99s<̔0cԿ7+qu5`pU ^RƗq, DrDa 3ь+ьw0YƗW* |(S,@wVʻk -[8%F3ױY:_TEwx) '(̲ژ}aJl]*4/֫btьq,&7P޴ju3K 5*p;Ƅohy!;iegl*T48+}u Pua~?GqoS˿3i%H>t-Gm'RhIK8gķy4o?Zni%1k~?P3:'=aZ<t-6W8GAv+2SDyy*1q{91Lxn8$2aa{0^#8n ߇ gf>,sZn:IENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/PinGreen.png000644 001750 000144 00000001246 12556202322 023416 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  d_tEXtSoftwarewww.inkscape.org<#IDATX;hTQCVJJR,$Y0(6>pB; + FR@bH0ndQ{Ny,]H+lFGK܋RJV7@*3^DZ(ۆr:#H9s~Ag~p%p}(śVlRRcLEsZ,l+Φ%XV7&i8,ЮA ĉ`ú9hS§:[ NƐ+$<]ykanwQx[5:xk&\7*O;X=?m3ڍi7K[1xՃ8y[DL8*p {j7% #qlh^ު F)ubyq#[ddWJ#8،ʧm+~V3@+*`AXZoMf@S@]_-@w{#"lEhӷbSgQ,Ž|F\n$&#\WNvX}C~Aljp!Z IENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/BoxRed.png000644 001750 000144 00000000663 12556202317 023100 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ItEXtSoftwarewww.inkscape.org<0IDATX׽.DA\BB hDlxz:[*u : B[GaݻfOr2$LCDJtf@ԙ4FJSpms󂫂c"};~c%&;2(U '@n7IR!h/` ](ZA{5 p`5 vL?5ST`b/5 xG WFVa+kqM786a<(xUFƁ7&<&b5Op*8LO8_7 ܕkT(IENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/DiamondBlue.png000644 001750 000144 00000000511 12556202320 024062 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ƶtEXtSoftwarewww.inkscape.org<IDATX͗ 0F?a cd P}=ԧ*?6_xOrb@HB"R>OPYa@U՚Z%&l0Ip1|\>&pI8ï%Ac@%Pp nS)OqfHRdCiX~-Kfs˩aT*ҎIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/DiamondRed.png000644 001750 000144 00000000507 12556202321 023713 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ƶtEXtSoftwarewww.inkscape.org<IDATX͗ 0@ްcun12NŧilH\yOUe&Dx᮪B:@ԩZEז 3*Hg%\3QDW"#?X' ~$Am[vq\~ S<Q8E3JюS $)FQW"t,?XI,]~IXSik=9IENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/Residence.png000644 001750 000144 00000003460 12556202323 023611 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXWmlS~}} Kh>#CZf#i4Ė*R ZuP"jR)vZDb v?a)VW IҨ"(x ځė~㑎t}{ϽeDlXEmNɍH<y^EI 0@t$Zs"<%%9[Y,&x=$ch_$\!/4 boV eag^TqC"YIe[ M s榹t~l6YEz")J:ˮcbQOTzԋtn||V##3QY~X[7޼7߼xPk`06ۧ`PNߕ,q 'v"G**ऎ+5L(%z kɇI&&T]C8Aǎ}B<; q;UWD { %h3-k0m>=\rPAln'{,vN %k$#?b: K }fY} S]]/MO]Q<5R]]oj{$2-,ϊ 8Od9zSo(- ^~"rr|OŅ 79(pr#&&sp4B@۸R18xg?)Uݻ7^yT3x;૯쭷.xrcYY)tlyNNʐ-[ryN*SOm@(BU\$+.^jOy=a5fVA:XX|N--WrZ-,W  /$- +NE-5(ͿL|QԮj@wIo*//X ?`y:7lGs3|,z-C[KL<ܦ xd,򊢞cssUVV7,G8sع8+}}㋊S$-"ݠ X,E8wnoF2!I jS?GԠ<=ĉ )q^^>>_"j K?M>L8σ9 Z3S;jpf}zx$@cAg;0olMs^Cy/$pd8Q9+ 0Qx9i|e94W&h0|M˓̿NX+n9d V{Y"ml'iev7,kggqOY^5FELܦ9;04V,UlfUh~u{%Ir=4Eb-H!||2~JHkЁ,)Mu]KtJGnԝ`XzbU)IE|PgјҼݘVn}Cqs3'&܏Ka;zIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/BoxBlue.png000644 001750 000144 00000000666 12556202316 023257 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ItEXtSoftwarewww.inkscape.org<3IDATX׻JCAFoNT|+@^h(>ة.n{da #".U-(S3)LI, ojh``@xt"K ?Oq&@"=2]gSxd>g4GLuf1_P/ec"U 7p3 F(χ(wpԮF0#lnie[dz3Y>D,a" Qܔ;:oKTY AEbdTIENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/BoxGreen.png000644 001750 000144 00000000655 12556202316 023426 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ItEXtSoftwarewww.inkscape.org<*IDATXױJ@ڊ(n%,(. *,+TEq8+jѦ&ץ8 i%"hB!DmDSc'Pαn[Pi{ܥK`s t'C$1[XL?_ Ȃ1Y¡3 6:VОWWpAGԵ$Pu'&mDح)plD9g0.3}EU'ñh/o(c 'ןp 'o>`l@j8b&4m|NEW/\@IENDB`qmapshack-1.5.1/src/icons/waypoints/32x32/CityLarge.png000644 001750 000144 00000000766 12570062516 023605 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<sIDATX핱jP "udCzOL .J]2')F=M)'ݐ_%"\n@)*ܳ4R<v]jixNyQ.Aj fErxDi5􁒵1C<".zw[+g-%z~!@GkN~ Z3^|! image/svg+xml qmapshack-1.5.1/src/icons/waypoints/FlagBlue.svg000644 001750 000144 00000005017 12527654570 022640 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/DiamondGreen.svg000644 001750 000144 00000004065 12527654570 023515 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/CityLarge.svg000644 001750 000144 00000004643 12570062516 023035 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/CitySmall.svg000644 001750 000144 00000004640 12570062516 023050 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/FlagGreen.svg000644 001750 000144 00000005015 12527654570 023007 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/Residence.svg000644 001750 000144 00000011642 12527654570 023061 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/Waypoint.svg000644 001750 000144 00000006600 12527654570 022770 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/PinRed.svg000644 001750 000144 00000005713 12527654570 022343 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/Default.svg000644 001750 000144 00000006101 12527654570 022536 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/BoxRed.svg000644 001750 000144 00000005202 12527654570 022336 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/DiamondRed.svg000644 001750 000144 00000004065 12527654570 023167 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/makeicons000755 001750 000144 00000000166 12527654570 022335 0ustar00oeichlerusers000000 000000 #!/bin/bash for i in *.svg; do inkscape -D -w 32 -h 32 $i --export-png=32x32/`echo $i | sed -e 's/svg$/png/'`; done qmapshack-1.5.1/src/icons/waypoints/PinBlue.svg000644 001750 000144 00000005735 12527654570 022524 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/BoxBlue.svg000644 001750 000144 00000005204 12527654570 022515 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/CityCapitol.svg000644 001750 000144 00000004630 12570062516 023372 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/PinGreen.svg000644 001750 000144 00000005736 12527654570 022676 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/waypoints/FlagRed.svg000644 001750 000144 00000005012 12527654570 022456 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Close.svg000644 001750 000144 00000005441 12527654570 020170 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ToTop.svg000644 001750 000144 00000006427 12527654570 020175 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SaveAllGIS.svg000644 001750 000144 00000010201 12527654570 021003 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/VrtBuilder.svg000644 001750 000144 00000130536 12527654570 021211 0ustar00oeichlerusers000000 000000 image/svg+xml VRT qmapshack-1.5.1/src/icons/3DFix.svg000644 001750 000144 00000005300 12527654570 020032 0ustar00oeichlerusers000000 000000 image/svg+xml 3D qmapshack-1.5.1/src/icons/MimeDemVRT.svg000644 001750 000144 00000006774 12527654570 021046 0ustar00oeichlerusers000000 000000 image/svg+xml VRT qmapshack-1.5.1/src/icons/TextLeft.svg000644 001750 000144 00000006540 12527654570 020663 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/NoGo.svg000644 001750 000144 00000005227 12527654570 017767 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Scale.svg000644 001750 000144 00000007472 12527654570 020160 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Down.svg000644 001750 000144 00000005640 12527654570 020033 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Off.svg000644 001750 000144 00000005770 12527654570 017642 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ToolTip.svg000644 001750 000144 00000011155 12527654570 020514 0ustar00oeichlerusers000000 000000 image/svg+xml Tip qmapshack-1.5.1/src/icons/LoadGIS.svg000644 001750 000144 00000007154 12527654570 020350 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActSwim.svg000644 001750 000144 00000007334 12616741641 020470 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/AddMapWorkspace.svg000644 001750 000144 00000011402 12527654570 022122 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/RouteSetup.svg000644 001750 000144 00000010355 12542033107 021222 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MimeMAP.svg000644 001750 000144 00000121175 12527654570 020353 0ustar00oeichlerusers000000 000000 image/svg+xml MAP qmapshack-1.5.1/src/icons/AddImage.svg000644 001750 000144 00000006264 12527654570 020562 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Pattern.svg000644 001750 000144 00000025203 12527654570 020536 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ProfileToWindow.svg000644 001750 000144 00000007750 12527654570 022223 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/GpxProject.svg000644 001750 000144 00000010211 12527654570 021177 0ustar00oeichlerusers000000 000000 image/svg+xml GPX qmapshack-1.5.1/src/icons/POIText.svg000644 001750 000144 00000010155 12527654570 020415 0ustar00oeichlerusers000000 000000 image/svg+xml T qmapshack-1.5.1/src/icons/ActAero.svg000644 001750 000144 00000014145 12616741641 020435 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/O.svg000644 001750 000144 00000004216 12570062516 017307 0ustar00oeichlerusers000000 000000 image/svg+xml O qmapshack-1.5.1/src/icons/ShowNone.svg000644 001750 000144 00000010152 12616741641 020651 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Link.svg000644 001750 000144 00000010761 12527654570 020021 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CutHistory.svg000644 001750 000144 00000015067 12527654570 021245 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Print.svg000644 001750 000144 00000006742 12527654570 020224 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/PathOrange.svg000644 001750 000144 00000004727 12527654570 021161 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SizeArrow.svg000644 001750 000144 00000005515 12527654570 021052 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcDepth.svg000644 001750 000144 00000010361 12623413746 020732 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CloneMapWorkspace.svg000644 001750 000144 00000014646 12527654570 022507 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/PathGreen.svg000644 001750 000144 00000004725 12527654570 021004 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/32x32/Track.png000644 001750 000144 00000001500 12574344226 020721 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATXOMQ?S4̈7+XXL? 3J#Qd3e#lD1E6TDYcR&e=L1-6|=Ъmޯqb͘Nzn{`/i5Рn Ti4\Eilō[o{׊_.m93ޕ1VG Ov X@-<|OT Ie!(McL)8""c=ӛc|LwdfHn&13cڌ1Dilbg]W pjU QJ;nIc'W=l_mIdRf;T5-,奓|wۛȀ _ n(}c:k421'yy L4 (4Ι^`3 `Q7T`XJk@Sfg-&/?c*!nQZ3I Njmo110y9Z*=\8z;oVz3Z3EJD#c,IIENDB`qmapshack-1.5.1/src/icons/32x32/POIText.png000644 001750 000144 00000001322 12574344214 021150 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<OIDATXOeϙ0ɄYRbq1) "m%H7B A&"Pred}hQ-"\ٟ 8ż{?x=s{yIUy0ʢ$G AU 5îdh@N2`dZp1;xuZ؏ep$̴p=Q0,@} o9\MrxbIf[;<;NIM/SKMnhmgINL#ڎacicH"ohce$"B֘xP8Hk  %02@y[:`kA{͚]GSTխkup~@j¦}> ,XUO `P lWz9O.~ómS~zH5+Ѐ<jg6y7cg@8}/x7.6߷ܧ~Z_QdG |?6u$y/f1=ZwqalWUu{6{XK9u`-,`n{!mC$xIoHzIQi8qE ma{xee ˙=z`hʔdmVD?%.j,kٸgϩ[T$o RR%I~;w]UUWHz6^7#CHhdرdemGrx챏XǏ_&~4g6V0Bn H֞^gbcW)i/¸qD7Ž l50,*<!;{'ՍQY%%5!6l(Z(*~['OcZ'Hg dFXk?>w7{!$)9bM.!,W"gǻ&Puƒ真c:O(V5CEE{Z-aŊѲF̣Gkq֐+jMM$&Cr zq44DU//sq()X"s}Fޞ;g~ 4D#nb*& T rm@ p Gx:t+:yU&)55 IAI=]TfklkW5CK ScΜWvv1M6o~a}Q姬 ;04Z䐐ƍ!^C|]@m- Z{zH6_[!.. l% 4X}`ʕ%Q>cw?uc|u3ftfd:[N{ݺo%;{'CAA9~9 ^hllk;vû?">{M/SYy͛+X0ZyE%ӷbÆ ǀhpbkk᳜7p1lm,{>.k }|޽FKRnwQ7c)ӧ Bװp$$t̋)Sz(⺵E3T A3Sc$I%kĈ2{ $קqKK@H& S~8e*mcLiOEkyTyIVvoiEEVN]SSSA#S3R!iBc*{t&Ҭ5-c pjll ? wFIb' "ǽIEI )|hnp.R[IENDB`qmapshack-1.5.1/src/icons/32x32/CSrcDepth.png000644 001750 000144 00000001337 12623413746 021503 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<\IDATXۋMQ9g`$ab$Ń<()P k\5rIb "E.ŒI ggOLfj؍i^oG*Јrv;2۰eqIt7GtRwIjLEO ?1jqτCTaSW<ř$48!8p+ޚ#Ļ[;p0=ӻ ?y}cqZH"[2чͥ@Xc6T&eD7l^NB+I46v|x՘bUXIEoF]3z(*@>һV2߇@h/E14=BX$.:sԕ_: \E'0!}\t37dB2 ׿?Ǘ'"IENDB`qmapshack-1.5.1/src/icons/32x32/Redo.png000644 001750 000144 00000001644 12574344215 020555 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<!IDATX]Tusfjj;Eab@)E7]$T]Ah^F%ED 躽lV;;{bFvvFÜs~9LPĬ7h fV/Nɘ=P@Rۊ[_ѓOk fqDc4+)fa ZJ눸DĢXG#苈#f(O1IADlpnÈ ʙ\,X{;BN:)@Dx]m},G+3ǖQ=I"b)6V/ncu[8-#dY,JSG\=!@D,ՋX_Vs(d!p"9^@K.瑾7djawW Ze'ڂXNKz>ՙy+koŒ~[#ݙ:ADNN-c,zvXe e<9쨰F1qkT0_}Tbbb/Y1{ۚޡڔRmQ@#؛k22B8c8Y8uuD9g`g2R/;j,ve mړ|Zg3N-5-6Ѫ=OnhR;;u̿hrWEwWԎ`EvSYunCiy7X85(?X&2hJ6/) ͗;\P.ko:M@ΥtwYyZsa.Ft̐IENDB`qmapshack-1.5.1/src/icons/32x32/ProfileToWindow.png000644 001750 000144 00000001772 12574344214 022760 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs P[tEXtSoftwarewww.inkscape.org<wIDATX]LW6Jf4 8DJ ,Ƥ]p3CݨS1,W%}1ko0 Nt-4l]bIGmKhl;ys~{Ϋǖ-IRGsw Ge5+ YRal|eh FPj;.O144Mip8VnرI@"9ncNYYA|z }E$E{ehݛoJrAA|pee@ԩb~K{+_DQb~A^:.'r/xwVW%PQb6NQPUe၆lctRGKqxtvze9 S|n?&>_9yG**:xJpn&'lɓyNObqsKnjF^֭*Nϕ+婩.BV200_"7 Z@4*c4ط{lwpO XXa0tQ[w .voD1hKhsL022!sN03*=`py8ehOudL&;O/-L[Lghir hGGgwcw;Çinv]]>,oͩIբ˗28dzz3g,KKO)\ Ǐg"{|`LrٵkF^ 1ik )JŋlL~"4Oϳ R,IENDB`qmapshack-1.5.1/src/icons/32x32/Cancel.png000644 001750 000144 00000001562 12574344172 021052 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATXOP?-2HULhĐ0"2:/"Nn:B*89޵>oz]RJ.`O&IwM\ o*yJ`O+.$G=/aFBǦDjD -}tCPw|;fpB8c>|wOZp܏B^}>+OAKX:B8 ~$A^B&Y'ۓz|MBtxmu^:ٷlZ2>p9p.Ë2lRJp& 762ب [P) HJP(#3(9|5I^'K,ByJ!ު C D_bIFD%P /ÚI_5nv΃h{$sH 7p ; 0 D AAt zI30IBhYBHvKz(xKC$AB0")4x9=9gsÂ_{Izܵ\Sd98'7Pf$- v8dq_aAC$;gi\n"uw7c7!;݃%X_*/@YfLk7Jbsw`z'u_lczq Cҡ$.~AC [kgvj, D;eZ.w&E/s`y#W-߽x \VW =nΣSB.8SxP[%@緋\[еړt fS:;!*R* v4("*J4 P[)B hW̠#̦IENDB`qmapshack-1.5.1/src/icons/32x32/ToolTip.png000644 001750 000144 00000002066 12574344225 021256 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXݗHe_ӛPIlSgBox>s<'Rµ/dgK X'JJ8$" T5`i퀈׭ޕ+웫&3󩮫YV_v_YYnU2DȼxwGF# =?PVYI0z:::UUVMIy0ZooK.kjb`ώa$haYYh~^PrZ<uJN"jЊ߷.UK= ZKϞ=@GEE^/qz ho@~Qmy`g{z߳'NlS,-((NDVꈓ)p xM'jk'9@P {]- jTnjzT{^Y@5@<^q0|+~w $:x g76F{[Ė>猈n;;]fdsD?̞`c1E\l' n/{ffug?p$热σ/Ⓝ߃Ȗ.7~X`:c0TgY$Z:x1^P.5`!e]$1A͓YP<#1\UiKwbPzNDIU,63jvww >-h Ne(vJg 0 T܂켌؋cV z\z]`IDLĻ{L'T`2yFK@S%XG0s1, Zsa5fqCSyFȇ s}A\2}J 'tb]:!b4W18&Dkh`-"#MMo<,MпҶul2b4rWT6rp- ZV&ba*0)a)84"^Ncy}loj2zlR>ĆoxokuF &=K;]'+P~ 45[q]E /n6ZĬlNO_ 3ցmn`x']rRCm4l.ǝ;&qUnv4`EMqY ɾJ] ^J$WbKDtGEdLV+QO0gwRK7n2|0GqK۹9įG޴e2b6_<+ H-U[pVe;9^ze>J:<),xDKkj_n+z/r7e^JO z5hLDmW2RbmpO`LAlU\REf:_bO1W.lo᦬lWwX/U@ ru.7 H߷=m] |j\_m/+G}u7TI *`MJ_AOLR퉼!)\U_Q"ۃ}o@}j0(WP6ꥨ4)E{a~6H/aU؏Е*,O9={"`z=%o#ƔIENDB`qmapshack-1.5.1/src/icons/32x32/GridWizzard.png000644 001750 000144 00000002750 12574344202 022117 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  'tEXtSoftwarewww.inkscape.org<eIDATXŗ{PUoe5dEE(jf o3 Fid& $̻dLJc̔ "4 o("r7s{}9}?ED?ie^`( D%C+"EDZ""0_)0?YD<źi,+VLHٳ|$.h!kc1z/`RYi ŗ۶d|vUzg3pf+/7ymby˧9{РO8RZ=V+8v@(I>Mhs]{Ȳ*W@ FSz)*&r,Zz//;w:(dLMuQ" dH\1֮];Zsg"$>X5< VwVY/4v`رssorV<M V:u~]Xj-ހJfi@TGZ2EIh`0.՟6^XxCoB`.GL7&40eˈAAVx 'i73c^DW@us]páZ /\& 5ۣ}lbnY(Ͳ4'7H{VUDvb, Nr̓ӊ(IENDB`qmapshack-1.5.1/src/icons/32x32/Opacity.png000644 001750 000144 00000001413 12574344211 021262 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org<IDATXŗMkQ;s3Iɗmc,E(`t!Dnĕ" A .\֏BtQHŏ"G6$i:E-4q&r>s9s E8)  .H`0X5@`@ԃΠ‹;&ag|9߻PHJh9*wz%C1CpAK3 $*Or"+df3dTrqPؾjb&p).nrɩb}@@KGpM.b\F@ Z&3ѦDNQKWTDf[@9Im@qD P/-T{5VNK2ІE`Suo 訄` zRA\ >ʷYmSr] Rk'@)NVQWKإ2V]4jm:F%@l.Bj4pS3?2[(^"ſ$ԇg$IENDB`qmapshack-1.5.1/src/icons/32x32/DatabaseConvert.png000644 001750 000144 00000003022 12574344174 022725 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs_<tEXtSoftwarewww.inkscape.org<IDATXŗ_l;wv8qց*m#Bk,Q)}hHU$x4} MlJ(j՚KCP% %u 1IEDQE٬of;빧l+ـ#˝ssa;A */.Љ@Qj9.RYr:7@p E:6 P/]ڭ@x kq~Eg(.#x1 ?r3PEu9Ձ}*uKаEx.dA1~(J(_ qZ|>6<0ňRj. } 0Ni>g#=9@n[~xIvI짟gx3ܘƤ%-Pj3m t̢agme\]s"Fe Pê1.^"C#Yf#;ė!,SA*VYg ׁnyqIJ\"nܹT ߌ-UUg,5m-~h43~qEO;+7-~ƴ<3xr9KƱTjz;۶C T\pMn=(9s pޒT*$3ʓO%uö8xg7\e6Q+)l֗JqDfcQLNLMzxn|>-[ih4|YG"bÛJ8zD9BG>6ogVT|dh4ͩSYH<\2a:;}YLL{drY[;:S2DDsm>\(}Ȳ9-jƽi2ͳxa1x$[D,p,sܮSWD^&+r?T>Qcbˆ8{f/%z#1H>Yp97uWۢ.,kߙlVbGxs9d%|o 1TXQs <X1sg_t:P.n}.<.(?U`~RIENDB`qmapshack-1.5.1/src/icons/32x32/CSrcHR.png000644 001750 000144 00000002456 12621572226 020750 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ѪtEXtSoftwarewww.inkscape.org<IDATXk̛F0#dieV'c4A8tQADw:stfϨ~(nN}(0)4cSFEM*˙y>=}~k}y2$\0 ` `9_K8\q8 siD;^>qɖ̀K(&b1iAO<< h,`|#^r-g c'MMT(NhMJfl`y`v3\C >ZgQ|c'wa"|IX'RowSqQ\oiš kJ5(p.F=_v _9Nk6HN NjGizcu$ejQϿΤ"fb!۞#sbX, [3XSO8a2 p koɄʱ5uN 3c8K3^9(48I=6SpL\7_+1kc$҇ Mέ .c:+'9_oW UהޝRW}:=[df³ds(HH$?yrw =~"AUZ\a#4L{*$w}N UC 0rf~&`'E+-)315fmѷNzUm;~ Bс.7o:=?8Cv9qVSxn/RdՉѾS}`T߅c!N8[|9C ^AjT|3_f=aI-׋//=xIRg-gU2 -QN[ A@Ʉ&iZϨAI۫<{ WWwtՅO/:71?,sr.t!aXl8%R[Pl`sgp:_؅s/tMFLŶp^v/z"RidHwc!aXӱS<`&b8Gm `t4m4F|GPzXIENDB`qmapshack-1.5.1/src/icons/32x32/Grid.png000644 001750 000144 00000000767 12574344201 020551 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  'tEXtSoftwarewww.inkscape.org<tIDATX1KA@B?Z,,lJgoea!.`"i-V,,UE Ƭ^r ͼs;{'ZD*GրIF\h"$bv"NE\/yֵ<8% 1Y340tkB Ǿ[ xﱡ@x#Xk r¸nj=x ah@b` XĔE\U~XWn8Z@a&a x2@ϟW\jj.|$ 7L23\+Eモ[n6б'I>%0/^Vo@MՁ# ĝC B`{; w]jo&O(OCIENDB`qmapshack-1.5.1/src/icons/32x32/SelectRange.png000644 001750 000144 00000001477 12574344221 022061 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsbb8ztEXtSoftwarewww.inkscape.org<IDATXKTQoFcJ4~hRT PUh!Am*H146ժEEK) iQ(D2-3^;%M p}{s9sMU0"3̬,gּaAP ѵ0cwƒ씙Z5`xmf삙m_QIO1`/p2gdx)oE-oJA8 57@SVg\ xpJD5]f`O1p&$͚E RfV@IK}I`IKiI vM9@]`F(u.,kb0 "OZg>13'jqmWk<IENDB`qmapshack-1.5.1/src/icons/32x32/CSrcSolid.png000644 001750 000144 00000000324 12621572226 021501 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<QIDATXc``` jO aY I 73RL0Q:`uF0Q:`8IENDB`qmapshack-1.5.1/src/icons/32x32/Time.png000644 001750 000144 00000002415 12574344225 020560 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX_\uonklnHj(Fƪ@kbSL0LLPԨQ0&&6 hRPDP$ZR-9>QufvD2[k5{fJAzBr3Ur{*]{ax˶)Tb}=h_C$?tQk}C)9l?ʺ ?w+՟z+^( "*xdN[,qkK^{FV6"/ b A~M+(Lej|2yd~ylFgkJirM>DaW8?KW7)ޗ7oUI၈ݒrjmQUJSJ׌`|'yD^N`:Ѿ}` h vWg[5Wzx n'Lpnҝg5TqX9~U${>0(M1-0+Zb+@'tn u0=@6S&m\XWȫyX xEaI7-jkHk輍(˛FW:]${x9"$>I3_u-+Z֯7 LIENDB`qmapshack-1.5.1/src/icons/32x32/Error.png000644 001750 000144 00000002152 12574344200 020742 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N Nw#tEXtSoftwarewww.inkscape.org<IDATX]hU3mM]TlD_j*T|Hn_X_kHQPP$P7+|C7f>duvf6zs={f9$ &b@Cƀ 3g׍a\Vx,Fpe`?T2B/',9 V'&UC|/g |x >}ƳV5U$'X<o9B3<$Z!v^JIÕZO Y!>}SY[? dˑD iלFOCޑ@>ab6cH!5""-KH{01UdԖ#NjFESfW?ʆ͉a]m4O]^N'dyׄ7;e_c0IwG` "yu^n ?z3Ys8Y0>twp%,|QӀV2%7X'G:)Ȭw,L66_ cNPl}<Qnx YS0:B60 ֯J#0B 9CEWs{U{=u3Pb p{}7?K1 6eJ [, \)  J 0GE%VV F&GiDu,uNe]ܚgskY|vJm{Q(qےSQ-?", |}Q,}_7j9ܑm63V a]NޜP8 |hpvԕ. $7+ͷ)?d'$IENDB`qmapshack-1.5.1/src/icons/32x32/Right.png000644 001750 000144 00000001223 12574344216 020733 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATXŗkQfDnuQ  R {U  *B|] E$"DBڔdr?͹f$щ iz2ܾ A_ҷVz:O'_FG{ @c39Îw$l5_qo1*Ą^3@оa R0%jJzI}x5ef$kW <|} FzNB)bXq3v6A[fNx [=70= ,d G%-w`Hg!?,i,%V `b nI{+lKʎWgyUAy;,t8^E>P8SoIvp#r"o~“fVH%k3jswZl/ 03 tw<F$5 e9'{s@l ‘dHIENDB`qmapshack-1.5.1/src/icons/32x32/SaveAllGIS.png000644 001750 000144 00000001411 12574344217 021550 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXk\U24!#1V۠څBl.ѢRJ77C+Mm6; %,%FMŐZd.GƙL\rs=ob(#5U'G18XS扉9##5Y`އ>DS[ȵ^mllSS 7vخPhirR5i'NpxŗoST+olPUy}8oppB.R).Ym,NGdכNkS*q|6 `U~'yı  nG1*KZDvaCVI/oV`Ȟu)_}xO3_V RD*6*C]WD0JKY|8xr5sE0+i\xDk'R9\@OOlyYbV_%a~>v;us>&IENDB`qmapshack-1.5.1/src/icons/32x32/Info.png000644 001750 000144 00000001636 12574344202 020554 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATX͗=hSQDi%␡(EAVJX()n-ZࠝJv(b(-ҦCMԶ4/MLއ@9ܛwBJ 1ד ~`H8Hk ĭZ[F{CʫIWB׀fr@BG@c.$#8oD{WafFݕ`h|9ʝ;c >rZŦRZ<\gU Ph]PxbGVUCS>wUXA7 WqפiELFC VGaj KbɏRz pem+U_0yccfN_J/睤hW۫gJHS<ݑ,?~bqqi3 Xū;7) '#=P%5_\lrW7i؄>Hwh$d4' \IdqปOr%O,%Eg4"1P[R__`D`8 7H[p)ͅx";vlsR|Ũưo7:l;nˑXkjDD"=KM~{!^&EZs9|),-0JMl188a U456bZZ8B6qOTa9``8mGVj55،.sH#\%\]1INξlhvu137;pu5v'9>Q1fkуѹk *5;9?G=qm]|ͨQ~%3pTUv `+(Uq Kp<~6 <*xIn.[]$@؄^T P=TLbdJam۩8\,K8 2#7!I[q }26 pz&[~p)tО8܆z8\go{sSIENDB`qmapshack-1.5.1/src/icons/32x32/EditText.png000644 001750 000144 00000002055 12574344177 021422 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXŗalSU:J` M6EɢLLAb2> 1 B&+$$bN q qecU 92}}ϑ9׵:'7y{~ܓsS"T:`k6Hw.?c0PKN{8PWW7+YP$syaa{НZz` n|9UT`%|>wbw&v9ZTZdZB쩴(@)} (F.о8=>M 7AKg^kn'5aM4Ѵ^o##G,Gƹ1q'|8s%:Ý?au#?r| 6H:n΍;?b^ %``` z&"^ ʧyK3܏O iC5Z9@17u||?[ #;uCyI9mo_&93C451a;--|Mz'2.Dnwƣsu}Z~̿rY6d n]Li$>G0^Dl?}WD !ܽ)Ԍ"0.ܗl@TBz')YQR&)"9/>pSw*ر2rPY肽oVf+ EDlؤFD2ZUk,`rrR B*--ezzdҼmh Sj~+v87rrrRbx<ޔ1kJzIIENDB`qmapshack-1.5.1/src/icons/32x32/LoadGIS.png000644 001750 000144 00000001726 12574344203 021104 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<SIDATX[UϚ8c91JdES4Q/C CE/_*F A! #rԌAᜃc3F2S[qL?h.cliPޖ&ÖXA'El"O״i:KEt\m" "ދ-jF'hVl0(x)4o^o7joq(^uul@uM~1KGg9R'-RWiV;E9]yfi|ɠ4"4[Zdm'L`aGzC)hF-{:hB-v9, .,Ȱjmgч-VSWl]k.f%WKAK5 GZ'dEM4dtV#eVgOpyAAqMvc-#e˥QSx3sc5bƣh4o3GǢeg`N޺gjs0/Ϫedž&r8Kŋ(jM^|TXcV^_gesA+XK#nfhMuOny^4Wu {fVffۊ!t:u47?6@@vdVja5i<"m:g]B8{BCKTo'1WBgӰ .aJF˻طx'z&Sظp PE-.KU`}]Is_8hV>Grem?cwf<҆_u19Co+bM7-Fe38S?otlFpf5}tbfv܁F\.YA8`Jn*sNPz,Ƹ 1]\=W{!&vҽnk5<_4  EmJsC@{h$ꁓ"1n !AE~et,6L眚;&:p6<˞u!{OS\!'x)T816<_NzdmG6ϴk~cBGgv>ɵY4Uo0+͌P۟c}4 4 9ġ3Ƹ|L`ҎISE+Mu%wnASToF*ͅd5 0Y G)4:\ƑI$&4$Y\xȶbɛ%:FBy3pZѻ?Ή)(igIdL|iiϤU!*]enR PQR©—210wnvFz1g !3IܠT,_{,i(kOl Iu!!4T8Q ވ Mj%uOsMp eEn܎CIMwD G^<ËEi8ZC<`>u@'rz[hcZ1 yqgNv3bN.)/\b4J@KhM)I=® c/.qwA:>]y=XVJKɞij=Xّ ,@3GdNd$hCڒ"R<}#DmR]md2ɖ =cE|T=c\AދIsIJI=lSb/~8-ih2 fd;P8aҁ4i%0IO9OdNg?IENDB`qmapshack-1.5.1/src/icons/32x32/Link.png000644 001750 000144 00000001670 12574344203 020555 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<5IDATXKLA҈B H`b M Wxx0jӋ&`Bb0zLN#'1&hF^bSx>mg)fvgv]!D%^N F)B Z!( Iٵ/o݈Ӏ+_O7;OXIKWRޝ6F x p:5F8F$b*`MJH<R#X,Ncc%؈p VSƱQ?O<.K@Zagq8}}(*xJxJszu~Xp q@:lĸOph+]rf @l 8b-t}7NRSS PuhR]}@\;5EHu)U W0:z MJ.WtUXW )aK &Y'*𺺝!c J;%fxmm9cc:On&{1[㝶 45=~ @YS_v=_u󎮮 s1|55a{poϟeDB o"PWUg||w pL'p:So5M 3; [5ej~Œ-Rޙ.ѥ(ϙ *}g|KM SM(؎D9Vv`2QJHN)26p%:O"\S%@J+a6 =J+|V\YJm l mޯYbX(UEn-ǘEܪR{?8FբW1IENDB`qmapshack-1.5.1/src/icons/32x32/Database.png000644 001750 000144 00000002375 12574344175 021377 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsOtEXtSoftwarewww.inkscape.org<zIDATX]hSgē%ikh40ڱB`o6^ 7]z30(cc2/&8Ա+webus#zTkj~'5'.ēDcx8y߇?O^IA%HQHAl,P4OX?`륊ztg HkE!gHu `6c3nہnh4(FCn,٬ Xaz:tw7j&O ѻR B/AXsd2-jӧŋ~}?i9ʵ Hq8nG꾺֭?pj:_Fm^<Ȍ0>>Co5Ej YP T6G"I&&Iuy/psG,/25fj*\UZZغo$VSVŢAwwp?!b0H(rh4`6+۱ZM=OȲZMXl2UAֽ7D6`ش$LIe#jWB}^" p$i.]  U]6Ymv!lk@OΜxNahȇϷ\hT[禄f3stv17zI^Ch;fbb*6wf.j5Yp8q8_&*]Jڠ//ab*]]㪩\>$Y|+^/8u]/6#,(rN xzuX,U3fyoaJs78 a~wZZJڻ~Ν͓L.%~tU5GM } S*-KV};pfkL*ȑz@x-?_ r,IENDB`qmapshack-1.5.1/src/icons/32x32/Close.png000644 001750 000144 00000001527 12574344173 020734 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATX͗?LQ?.Dc DL'NPpSIIJYi7݀tplt:75%1"9^worC~}Ͻ{wRJt$az=0 5) 18Y2-Z8Zr1#CpN8#`x,e6Wam|lth~y`ܕr 30HIYNm#i07MA0e.eR(Lc"|vT:i *yJY\ւ,׵)fT nbCH)g2V{ضK4O[N6uu) ,j| (sLf|>.?</L܀=Ij .vwfö]սAgb@sи-`%p?B^Xy U "J5oXz`pE @"y @YDZIaBj^}k 7mZ*^bˀ*%6o6=C~zuWsI"p\8*@T.8>p:NT'f7[:M& փʄ~${CD*SllpeMR.Do3ALۡ0Jc/<scݼ @A,Ձ)T7A$(3B#1fw89_0,%j ZIENDB`qmapshack-1.5.1/src/icons/32x32/CSrcATemp.png000644 001750 000144 00000001361 12623413746 021442 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<nIDATXMQ߽QWR>iE6cAD"KI3'i(),lD,|,I&@#!1w}3l{s1 ǎd>\wײ]h"hUп yExXI@4Eɳyby Lh%K~ '0/rB6VJѣ%{2w݄MTiHp UWSlCe6,#|F@e"A#ݡ`ΪQ8P̹:@,Ժ' sqմyNe-_aL1]@6"0#RPj@!\$Wa96 bp\YB:%L$LLpЍzGEoQ9X -L==Q1:G#IL ^%4U>nbooy%dЉ5Uv\@| }.hah' Պx"Ք꬘n穀)8=3G`*. 7вWFKf=?k! @I8EԆ2Z$ xެ\RSlCRdIENDB`qmapshack-1.5.1/src/icons/32x32/Lock.png000644 001750 000144 00000001644 12574344204 020552 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  ~tEXtSoftwarewww.inkscape.org<!IDATXOhW?I&7 R dmDm [ =H EoГϽv!( R`# F,4UC# !qw;'0nfͩy}}ޛ7of(>}J|iJP!"[/RjؗAÑةPoEզ(A]8}׈,\x0R{Nv3?jQ[5!S(b-!4_ݻ.p==؏Yfv' ]GlM{YpNG9h*64;Ǟ0V]g.@o(~%"*)@kȼ) p`OįOCo@ y_Nlb~~gOW3PT _? ʯZj@oJN]YD=(P:/uVAM^ gDdn-8ipj. "O2t7&?[ǻZ)i::&XZ:ejF7I$H.J]"#* ,fD]:Lr 1iVD"t`{}1`pp|>O6oybB=ܗܾhs h]$L&dNCҜ:5eYX8(Uxg9z=22L[_[-ۻ ,5@icp߆И)FGǸz[J c:(NWA{nl;7q@|u^xaIENDB`qmapshack-1.5.1/src/icons/32x32/PathBlue.png000644 001750 000144 00000001030 12574344212 021352 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX헻JA Zie} o|7DYX%jBݘds?:&6g813;02H HHmG=jQUdc է7[E)Jy`-dtM_;Yuf64_,60a[3섥 ڤow(XDֺk.rһŏa&oul GUm F F cPQ^@d/DAلVGDP944z,P*F  _|?fw ;lvV˞6}i@"^W_>TI;SmajK*$9R]NNKZF'"IENDB`qmapshack-1.5.1/src/icons/32x32/ReloadImage.png000644 001750 000144 00000002274 12574344215 022035 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs+ptEXtSoftwarewww.inkscape.org<9IDATXi߹w`նNb:Z)Fmb1FKcƸ|1Rm,4j4`(VE1L(̜~x]iInrys 7sGX=s6jY񺘕1I8V039^+p/f(xئ)Q0.:xw8#|aj~ rLŹd?:߰3?Y9#_>ux oJu!kPFHK> l,}`VO)ܐiKmlĎ ȭV*|MJ *{i7OQ/ޭQbDNO3m j[L:Q҃f61\/b&^M17Zt&KxG)H5q/G-]kqI}P̞8Ӫofиd Μ1zgMSׯ{ lG&fZL϶շYF67aDW TZZAh>OKvj'0ύμc a% [)=᱀~2}kvfʜ}guc!093Fp;SOm򲪲# bɵfaذ˔$[PYC0 -gXt%k#wJ*]8 61 o&tP*ks'Ϳpb0E'"lUuzj#~pP8Kr=bPyw"*_jZu 3#zD1=0 Gr l9qƑ!8:r(s g?99tIENDB`qmapshack-1.5.1/src/icons/32x32/Reverse.png000644 001750 000144 00000001225 12574344216 021273 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  *tEXtSoftwarewww.inkscape.org<IDATXNQiZLIL?WQ"[^61M|1NVhh@0 )ڈbri'M^O2Iޞs i7Hzɍa%Z_$xKvJejpoADƻɵT( "6+o( Gan/tBXj s dX@zRg;CڛmF$LF3]9k-"s"E1AOCg0'f*"Or;D T}?3a82Ě 4(&BxJ3W[ PWf  @Q-? $ Z)T&@$`-`|ԨkD)z`UT3 yGUT;N/; Rv$~3nJUhP5 3wX{D6="o14%ݏɿ{-2娈O 0_/V7x">)a@Q ?GzREdAJ!3jփ LXl\z㦷 vo.I0/ :@f`IH4i\κRqeihw]U ONSO[Avj`$Ȧ`+bT?mL`1f6ԼOx+pP] n Hw U0-Fj 'w"2&<)-fGu eE]Yd$ʑ#PE="V9P<UP1HO0w!?"{j{Io%&nID,˽/;۫d:x\)фFjE{TJxĎ,(p^`V54F2Ipt_Ӌx#Kny_-Uݟ y6DJ`V)vpi2'c 9 `@v`4V(45L2%P\pjY fOrhWT\褓ZjC5ԄlS^7 0- I [Nq q‡ZUOCEgV- cϩj0 0fs6/*PTkU5Wa6]BtTX4<.u:IENDB`qmapshack-1.5.1/src/icons/32x32/EditDetails.png000644 001750 000144 00000003207 12574344177 022063 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXlU? P`\Qa+vmnF".քfE3dWِ8.iul\]Z ^[K {dOMN=yC',+ 0_r4\.'( 8-EC0o`nsxp8 i%3ŲesXj!wݵ%Kf"Fwnhޙx37/0$hm`|< w3|{Y ؗE*ծ3** ʕUתcE1%iFtR6RV6A.NJO{rlIm;vj_NccII!͍ڦ3̨9w:Nw.ibj(+ݾo_wf•kkʕ ((1aMOzH,bn(h7вn]mIqIM_Q0#0 &PZ=VJ>.iȠηpG|NJJn`zL>cCk@0co0$>zt̟I[IOw$,42r9s~iFFGz9?A|RjSpOcNg!`g>IF`1ZZNЅoeg5SXo;7CIf`\Jp58Ӝ۵װhD{,x۱ k@6 ׳0syv{rۭv[pH,b%K8f-oQ xزI RSש=D?ldۗ%[C;wڵ<77Ivv*`@` 탘fo}ZD%`[(-7)d?=" lZ`4H!ڊ*4\ \+L& +=ܔ.Qg& p|Ի$?? <{>pW:NFFϩU:/)jasa34~&b-JcΜ"#_JMw8LD$hm=/غuAc i2 207%$),L",l">#+k*:ݬ$$~oT@4sR(bzqZa6|.q^sХH?nn:24A~ UZ >pT< L>@@)<R4puh.}l¸at$h4rNgSDΞmQ Nh~I|H|}=q8nwf-,^ 11~ڰ~^lP?. 9F=?,}E`!+8OQ\jMT/mD"k[JjiS_soV47/՞=OlĹW@`YpˈA1df%0 }Y 's+wh* 77RRb ,Yrn|`9 ^h\$ UW2?zzmh1{ZEtt/.ӎNܿwt8 @wV$/@rt~`v$''JYcRp:]=8ݬ}u8MrT %̮Y%C XDp-Qɮ]fɺuRP)55ߓ Y'OiCi>g@X^ 2u< ի CͲem]lBߔ&:מw,C)&ocF!`z{ˁgJKϠjdIޒHpna#稫H(<Q$Ee 4Vs{f]K|cmB5Gƚ〿 ]V.2a,R~."_Zy@;c"n*""aN^Ƥۍฝ7 v[_oLj\cT(戤v[ fk/ZCɢk"c%F{k"fS7 1eaKXT?gW|"o DQEL)&C,P?1S{X؄KV>9z۱5xd~aÑzD]R|xAf~N`]P0D<rTG!`` j] ndnU}LsgE޺w9G +IW8~z ٌR(i;'Jve+vpKVXo"roTA~Zjg/- `Ч\DxɁc: > &z󀒟rmuBBKlTU 8b< @X ٲHYn=47/e80j !:Qne١e!F9򲢢:hh878jہ Ʃ.- 8Y@2tR)mg0 .U;܋ dSK-Ź D|9 1{Q5Ðy j\ |IfÞ=Ⱦ}u1 ¶R禌~(y\iծt~E=},!yP0[1;X @`%"}@d.w7\(XO`Aꖿ+({ 0lօphNA*S9*uB}CbB2_J;[Mle]iw=KoWU{|?|0k!0n"9T}I F<R!%PWe &UiD^lAy$r1DyxJWb/>17v,mrHR6 mK@AWBz ;w9 'Tcs BHB5CJ1G+Ɩn^8N{(kFgA:%n7V`J9$ːm 50#@ O)YDJX Q\Jv8KBˆl8m,j<_0:1PlPu:C 7>gm%\B2,bT?G:ת_჏T{ \SpêN^k xύwDLkQ" uA3Bb>)=h ۣ_ )HguNF.)Ẽβ`~?MyKwz0 2Y]Q2@gCiI7s]@SV ' iU]LKĴ(99@D6,햟&:N:8p*y?IENDB`qmapshack-1.5.1/src/icons/32x32/ShowAll.png000644 001750 000144 00000002516 12616741641 021235 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXmlg96z%-F4AӆIL2TDDp,J%n,fAR- FWI1èj9}9ye9w=r]Ze^v$0tc6{FaY6}vk@=p8c,Ò8`.rENʭ[m377nӧ2fL?? (5ƴp޷m۹~ ՜:uxF2xpOzN.\ͩSWil 0bD3IͳTVSsEii_ |k-:+ m=Muu״tiY'i/TYyQJ,xm W*s UXXΧB#-kY#s/P8l4$*ܲrr~Q盢ZWĉQ]ج |1L5I=m>{=̙{nEt Ȯ6sf,+Ba[}NRV>Gs$IdŋG]g۶/iɒÒtH1ޫ{ܸ"qZZ:o=q<..-z˲:;9ĉ8 r9ٱ#y 4=n 7D[N bbY$&;w?;$]MM`#5 1gޤC04;1_Mܹ&~6%=n Q $gn^<۶MGJe˖() peF33q]Ӧҥ;Krrb1޼{%=}ʨٲl͚7!;{::"_ݛ7(TTT%I?IzJz-G-j ՗TRr\+VTByǏ_RJFSqq$HJ&IЭ׻^[V( V-\bb>dzFvK.A trϞ?\"iРZR 7 :rį9s)>~)S;,H'8b9ֽO7o>޴69/zp]H؊!ɓPP0I1G#Ր$ڶVU|TW_:91f(/&JJJ@1ƎxPx{-Y:0< ;w 1R'di~Y?Z\4& <IENDB`qmapshack-1.5.1/src/icons/32x32/TextCenter.png000644 001750 000144 00000000563 12574344223 021747 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXc`ae 0 +^F&f7ZVA:v W`vl[  :Ž ,$oW@- $ !\ Tu'VqP6tcP6tp&؂` dpdMß_?ȶ&|s36 `d+M^[Ni`-h90ZP L`-F\͓ 7Nl0 {)E=IENDB`qmapshack-1.5.1/src/icons/32x32/AddImage.png000644 001750 000144 00000002141 12574344167 021316 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs+ptEXtSoftwarewww.inkscape.org<IDATXŗ]le76JKLw+Pb?h%?1DePbAD#+F/$xa(Dk"Zڅ/vvf%{9}c~[3Q ~Ev(V16, N`e^ZH'Y'8d8)58< ^'Y%ay(L ԰0q>܀trJe"&`4\@qZN:yZZ/% y~n}j /x CHhacRbhWM1IMAȃ/D)=J2=SE5Q.17ФBƹq|Ŋs]\vB)HC/4-7Kd =pHd܋v?JVz)=Dd_%Lm]jc oAY?WYCRI|JP y4xTacjW McܣW.Kukx`7S <]nH>q0ۻR,@-K>Oˀͼ^6y{9j]x"eGq3Q q혘nW} $3(B @\U"FcwviepyeFfP/"#}\V bBh5$`B!1a3GEf´npo_hꗅ8|fS؜ik'D8C T_=ڊ"ӤZ~Q>iۜ܆{f0K+3 #\OZŞ;rO`g(Eu@ N3 f8'Yt]v|HTL_D%Y Wtcqog.AwGbv> 1| &IENDB`qmapshack-1.5.1/src/icons/32x32/CSrcSpeed.png000644 001750 000144 00000002276 12621572226 021477 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs >6tEXtSoftwarewww.inkscape.org<;IDATX[]c>3-m) jxpIHh#B%Ut2`wP:5:HSQ'>I%hE[ӢըE8'x;}G%Qn<"kA:_:?8AIT9_t bEnҭhⷂ6ЍqoQ}GzC q2>\N܇P!YOݻkIڞè2Aomd90Ј LԼ_HpgܘEC{ t/E,ŕ8{=[#h|(MտP*Q%튷ZG,`wp Ffxa8[1]va}3@.ZFNZzO~vZߢoKi)|3}unCҁE,;$RFWMlkJK Z#8t6&N O3NŘGhS0; cjK07l{IG t64ZU_mT'َQGw 082 qaZMҎ 4쨧1$ǃ%benXWn_qxp#f'q>`_-uIqI]h^O^`Si=L@8 7HvJ9v1/^o |7磇S>,إN$_/(tz Rlݺ#Gj8xq=%>]+3?~gD$aH0|1 ]o"(Meuk9-jo"{͚iD45E}p144Mw?ʕNҥWә;}/'z QK_ϓ' \:ȣG3q}0>q;7qhL |hU7\+9&&26@EE~4V\F* X\Bԫ.s8:9q1?ّR5uW;2%Ν{]_Sv@a*,eV._vqkqk~ehyeaUU-(,gҸxo8O`"BaɴIVqIENDB`qmapshack-1.5.1/src/icons/32x32/ActNone.png000644 001750 000144 00000001127 12616741641 021210 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX?HWQ1 E$%Ɗ@jW-5ImҒQ5AAADJXy~JyOKΟ/{ߍNhkl!1~'^1yE6aD}a0=qceJ-AD}w nG c:>Pi$2k{ ߾c*W66v \ dDžᎈ-|e -*Pj 2g9/n9Q`aٚŊ0d"Xaj"o'x1. QQp7p06cp s\TAVx /ftiAӪ[2i; z3]RBZRz=BnVê*Q tm< @&Vл9**|S.X?\} hwbnIENDB`qmapshack-1.5.1/src/icons/32x32/Down.png000644 001750 000144 00000001217 12574344176 020575 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs )AtEXtSoftwarewww.inkscape.org< IDATX?kTAM0Zh\&(*jeRiQB P K VVE@;1 "!wUnvFf_f930#"k#C4ײ95o׸vQ-q 3KKs,~Ծt!eVT hZ=[hҾ:x-}ڪt6Zʵل!-R j׫y|Za p)dpM[ꑈx򇧔RtV/?T`y C 4lY``5^Mm/۱tlS1 uM0IENDB`qmapshack-1.5.1/src/icons/32x32/PointHide.png000644 001750 000144 00000001455 12574344213 021545 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  >tEXtSoftwarewww.inkscape.org<IDATXOHTQg7:XBe #"wJ%AnF-'Ѣ( ưaLW"*jfŐBPRI:j)ތ$\~|{wOTk[g1?Ȋz`"G@0 Au6WU+*^aoښպnDJݔB]Lvt..j"x40ًTkiismT26EVO/K)n-|K.I$2M;mcXEEfZ6^n|)Djz{nk6K 2mϿ.C]~W}/?]_rii)^\OS222rln>>@M$X OM.zx8UՋ`}itsKuؕdyH/`Xak f]de\j o 'o˜6յ 'cUDBa z "ʒ j춡7Сb6݀drtt@0nCgNI@izmDg` 4UiIENDB`qmapshack-1.5.1/src/icons/32x32/MoveArrow.png000644 001750 000144 00000001346 12574344207 021605 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsCCxZtEXtSoftwarewww.inkscape.org<cIDATXŗ=oPKڀ"Ub(P{+P$6$fD " .MKh$EE: ׮}ı(9|y#V"BSJ)XӗR4^ *Z_nz ^5^>ڗ"3l%^LxQjNV.T7 w@“g`/;e;=ȵs߾Y#Dpܵ^Uwp; @Ӄvrmo\k嶎aw'9I؋@XjY.3D̤RD=ެt!` s<}YP8R ~i8m+.84Ri6+8m= DDR- <.V "?n{ᶠI9! Ӆp7+. ']|"@lgO˄򵔩GsB6:FYj$혁dXa6ޑ$AqDmZd/\IENDB`qmapshack-1.5.1/src/icons/32x32/2DFix.png000644 001750 000144 00000001313 12574344166 020576 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs Q Q@2tEXtSoftwarewww.inkscape.org<HIDATX헽ka?\$զZk1Hb \ࢸ P n '/8(:8TRhizO]e}侟rI"GSP~|qUO;k.jtC h{ yǀa>h50(@%1Ч(9Ga`ƾd?0S2 O0Ka%P3y mwɷ· l0 SY uY>|]xbk P\IV 9.C;rbL w;xUMeV.P o{t_sh^U2v<(%׭X83v 3i~ڠ9']8(>d`($nʰSjAx zω#}+Kmlڐ,ϏQn&}ښO qג{J]ֶJK7mfR8iBS!W=j(+&D]Py+IENDB`qmapshack-1.5.1/src/icons/32x32/RouteSetup.png000644 001750 000144 00000001512 12574344216 021776 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs)ntEXtSoftwarewww.inkscape.org<IDATXk\UϙLBZ!kqeq!J}QTPQpE3SuMK R!IlWEmcR ۚyb:L6?ss{ys_|?tYGJq7TQ)x_!Æ2QE=` Ab8gJb]/tPÃ90`{"?ȾF&C&reHdN;&v|0{|m)e؇Wӫaڄ] - ~<[W>:NL8ӭ)7 [_m]cp"B\hOTR}Uf!zGf]WZ3ݝ X(۾De;XST 'a'9^˪vd2­: K*wچ)݋D\,KHEX߃JT`maY{W|Z~qGNU UP_FZjx]¥ KeqXγ slnsf`qpT.=OY.hgF*w#/LX,x%p4R]o X*yۊNrٕa)׻OqU \mzp HRIENDB`qmapshack-1.5.1/src/icons/32x32/DeleteOne.png000644 001750 000144 00000001357 12574344176 021537 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs tEXtSoftwarewww.inkscape.org<lIDATXOOpw @7H;͋Go&<Ň`C ,b1FasOn|=t9:YoCWiM݀GX,?::6x>FWxLRH$΁PaG33354ED$Ɉa% :O]sssN,T=1 4;;kYu-ɤ966rݧ]zrdd^2thrrqJm VQx<wn@e:D`<DT olPM*%| `Mhb:\,ۅː 3v :"}8XW(6/:Ȇdݬ.|A6NU5[' оH{,B` ՅΑ.wnFCÄ *`E']`'iPVG=\jK}X= |/@D/ODNAǷL@ȞA-d-/c.Wph"y7:XP(ԡkV.;( l hCs J| `IENDB`qmapshack-1.5.1/src/icons/32x32/QmsProject.png000644 001750 000144 00000002501 12574344215 021744 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXl^UӾe[jY#QD"_-!3dQ#&!&Fq^7 b#qƐF12AX4 1s"\P`l elk8n&|sϽ>?<^|p pqfjn<"t2gU=|vPXQ5^6sυ@Vb/U&Kl:]al诊hlŧ*}ysx߫ʩrcD>ܚY+0k;rJ/f nDG*3JJrRaTKO;i p/>BqZ?ٶ>h( $ ;di 6,SZޕYgs++os"@IENDB`qmapshack-1.5.1/src/icons/32x32/DBProject.png000644 001750 000144 00000002775 12574344175 021513 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs_<tEXtSoftwarewww.inkscape.org<zIDATXklTEgv}բUQGMP / &_UJ`Īc[@"((` &5J-.b]n-:~wR-?y3gfEU0 |uÂ:7xYYq;U5<oݫZQ 6Gۭ_,)ܯ{|իc>/[R#m`mOy"rx(58ZL" āOCJJۮPDa` V*xPUI -X o ,"Y*8ZjjLR @4 K)ȤIE{ÑL["kqE<ZJ$9HKK'r9qx<P]]LHamm|. έfc8 }ڠ]iij 5t`F pd"`_M@~&A;jkYd x<1@4`87=v+@Nc{Tv Ψ*74`͚D Nԩ>,s()fnMG "Y `ց.ND*FrRYYL~-9N(1@ /7O #dUMI 73uj&1qb>&io>@kYCШ'3V`EåЮRǣL~N~G6m'B|.fͺcǞ!/ϝ0E5t{J>-e#r~̜(O._P(};w.Uʹg%"JD|L CFik,(),̎wwuuEPP%К"i:?vXL8ɳ%5(+˥4\**0ud]Bg@aߟߟʹiצUR096Vݭ|{Gx 瓶 $ W{'x%Vm0YFDl-bcfp# ,fj@ī} 7.Ti-O"pmgǰ_q s%?ְ."3A5~LS|L߶?ÅIJ >a( ?؂z:x*ۖ:^ժ&)AǿoWl;/5qέD$Ʀ^j7ץփh^|"To)y62} `_]!Vgx^5άDT6pCrWc5.WM2UGCQw# > ˖, uiIENDB`qmapshack-1.5.1/src/icons/32x32/ActCable.png000644 001750 000144 00000001224 12616741641 021315 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ǠtEXtSoftwarewww.inkscape.org<IDATXAhP_-ڪ&+cNNb&2xkEP/LbpBO9Y3PQEv0\yHRgki!$%K^"VDdnj0̫Ӱ>: XADD'bxK@~ 6$^}ұ?J~ ߀G>7'R@IN @M>0Au"s;׹ \TKQ@|׀SŔW\Pjg xArA_9 %䟫uV@վ rߪ{l4/" ,+$lpUFgynM R 0³ 1kܧTZ4`hPgzP,8 8[3w34X3 lU}+Z\a"}فXOnXY05u'o}߁* |!H£`gUඪts/XwZsIENDB`qmapshack-1.5.1/src/icons/32x32/MimeJNX.png000644 001750 000144 00000003365 12574344205 021134 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ::tEXtSoftwarewww.inkscape.org<rIDATXŗkLg0܆΀,jEAژ~Xƴ&llmjSK4v]7mjYV.cI P;m۳Yy?<9sOr/I.B*FTb# @N kFmiTP,`dc}}'&6+E z]fFBcb|f0o#zfjp8pXܺp7RǴUVby{mmn30Lhptk48V6a7[OFmhН;8vګt)waMvI&c|};6s?%&Uv6;F2JJM>>}|]r2NBvh&%΀_x8+lIaVR^NKy9۶aXV]$bNdbdd(,d! d|hr^8& ^2,Z yyn:kk"%?hF#+23nJtpB΢[zεJիd{&U?,*UT0A,bbx@ֿ& ,~);&&w/;FcaX"BYaGD\zߏF 0OF#2Ud$j5:;&%̯~/)ϭ@_cGg1i}n.Z#g .+C׳nj6(11vmL*ێm_LfL&[mPRO묣Zr!)q7Džű7k/2AT2QɎ&;6bD୴T2]?Z't) |5mk!tuoG |mh11g}W˪Uh]4+KɊY]A!o@׶.Z#9p2ii\Gwq`h۠1qFe8ns/jQ%WqJjKPU\L.5I'N6<}c >n[Q̶Pv k F/ZUH^!S$PleTu'ÎE;$rsk# 1Lp1tR#9pD2}X['jBX+K93?/! lNLp?[oε=:) zumTwT3Fx 1e44JtS7S2䂜)#d5쉳Ǵ`J 3 cK>$.,n f`H H$e1PUh$F$ƪ;i;턪C)($5*s'ei_K.޹H]go{aHGJ4 IENDB`qmapshack-1.5.1/src/icons/32x32/A.png000644 001750 000144 00000001247 12574344172 020045 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<$IDATXkQ?mE<[AŃ BT+T/ ^VhŢڣPWM@ 4;҆z|}}3oU #w"^pBPI%Ho_ۈr51tT$PBiՙo>; ;[Ogڽ3%FDf/u%` &܀>Ug6~C$߷(' `}7.06/7]/Z"swV ո3FE3 5i胪5?hQ,xj|qyւ65SWx-`8ɫHrB55 ~AvѨX:fVgpN:tu f 3KM3=4Spf}JlGmf4iƜa45)?Vk5%Q@y6sD@%ւk|Yל|vџG$铄>00PqSʜJ/1)D#<x0(8R L*@IV 7mekU|5k%Y"dōE`L4,F **Voy߂>"{.h!;CuէW,o.S_C!c*ss ~H4B|}m\FH i$ @߿\SIENDB`qmapshack-1.5.1/src/icons/32x32/Print.png000644 001750 000144 00000001613 12574344214 020753 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYspp}LtEXtSoftwarewww.inkscape.org<IDATXKhQs/ʹ MUИX"BĥtU\إJi2|-|n&KA% "> X%fhӚv&$U7sbfvL &HQDjzZlc>=dSD=ٹ@*5xM P^'b;l<B<`SO?0 RDp@U(TJp3c\׍Dj!OQ[9fo_}پaKU_PW>"-H@#xᬭj:t7)p)O~,CXo}ɣwѩonl%!Gv7%atdO| Fa;ۖLL}$MM̤ 1~l;Vg75蕿Nulٌӎ屫a>5l1 p힘6zw3U4!فd\LbmN\97Et&&2CcaJ&̝Yp;n}˛q_{ 7mmgف6ZS~VJe7`Sູr+# \3ʲ7V7~kujM4-TVUW[Va zе&!x;>jg?'JŠq~c;b~gN]קO("TWqs^y6t{aظd)'<y)lJߍbԒ|]ٞl\2-Ͼ0_)i=)pR`u4W+vp>UJ-N@uwuM͔Ć:Šxo`\(N$QE`gZ9E/ӓ MWq 2! yŀ&P:b:P;_WJ- p=x.++]K *8㇫i`X <_ bRj;*@_6f*J0K Ȥ3OWUwh͚5oذyxnA<^?qxd,zj{U^W'&kO]^]WWp`'j(_|/e_ƍ{fD9s9"3n$n( [F$ p}k3p{7H aLs}x:zC)U#"GkFFȮo莓8mi_#=;vEq[:W4|R2eHRMԃx-=2C5c /G]==çưt7.&<3Xg8n1kf2郎[2Κ));<qDLīXp&Ņh§XNF+1;}Goˈ83 7OAWUzrgә+8buxD1oqFǍhdXϪZ|43Dj܄WVfwc־Jt+n̍5\?V 5gMߣ5=#6M7=;tc:}$èz˱2/=ORKFJC/kVZ cՔNRZ;~3NO@{ZS-Uq-S㑈33wUR^[~ވr~'&)~*e~Ǫ>9uDXo =8FߞuDzeB ]՛ۉ^#+ Gp[=k$P8E]^Ro!򈘆ќl Ꭱj-37]p[VIENDB`qmapshack-1.5.1/src/icons/32x32/AddProject.png000644 001750 000144 00000001434 12574344167 021706 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXKhUω F8%Q,PBu#v!҅b.C+\ub(>Q &Ijq1$yt'.9ýZYĦٴsdliF+VMKqwKӷ9t; g̜_-@uCS6G;2:.JV5pU9t7_$GfV1&c| O"Lb%jsG BV=b6 w\,4$b/{zw>V Ox2wM~g|w-")~Y:=~̩Ǻ/֟G,-t .]%EtJ7:ogrUoLLap8t\ɋ_ |'Z&7A6BVԪ}l5R&woD_>,8{k1P,YoևWϷYcx Ooq^)IENDB`qmapshack-1.5.1/src/icons/32x32/FolderDEM.png000644 001750 000144 00000001475 12574344200 021421 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs =I8tEXtSoftwarewww.inkscape.org<IDATXKLQҢ/ꊕ 7u Qu{5`+511JBBGAEy!ҀJ  }љq1@[TFÿ{νs9JNjS$@&sFMqMO@VV-)õ S  G+r_Gy.t NC0⌌uXT+H1EҪp?zy=sό۴Pͯp#|Bt}v1bukv@n3G`;AT2J(A(z‰.& f¼6w\40B5W`)? xK8زּ%CQ NȭiGϞm'-3GupAlAw+,mǚ! k01P`fe88\*%~(7iuZZv-WDK?J9`4sPX.KCF@]'ɟVjWo=)JpP-P^ ]e$7nl޴! "ܪYOd{[yEuC/FڝPV:Wg\pH =Է*`|v缑Qm#o1(Tvo[JNbѤRANzH[5n5+ I̚];r=_ޝ+X(oIENDB`qmapshack-1.5.1/src/icons/32x32/DeleteMultiple.png000644 001750 000144 00000001602 12574344175 022601 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs tEXtSoftwarewww.inkscape.org<IDATXKVή *A‹|vhW&7%<@^=.K#Mʨ(̎kbn)氟}=gQ/|8^~mۋ@ݱCпh4m"Wl^O mRDB4;;+۶BeI8jhhHse F"}lԴ===U[[2<̌FFFޮlkkАٙ. `߶m : QjiiQ$QGGŔJLLLN1+`6W'OZ=gzz.Wytuu\p뺌=/{ 880nXÀ@0deNo^Y 5,cِ-dw q2aޅ2`n {$9\O`̕A:x#}Q^݇7%8VQjNMH> i7u=\&QXmwP`!00 _L>Y#rG/%b)XJ9`iRźƁ hk?YI+igc(IENDB`qmapshack-1.5.1/src/icons/32x32/Image.png000644 001750 000144 00000001750 12574344202 020700 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs+ptEXtSoftwarewww.inkscape.org<eIDATXŗ[he Ԥ ؛Hvc5l ͋)KXzA$MX$,->Y!AXԇ.)[6mvagL漝33]},}x'~*Y5!n'cͷlH2G$^a`!P5^d gȳHnE\80z9E`X7B╵-G1%6Ak;FOi e'>"strv?lzO&Ʃ91[$S|*M|'8|cӡlkmDm c/K2VyZG0(p+P@]ٕoyԳ.mh<Lu`(Or e\.̭>9S[L|5u`ۦP CcPuh}Tz%yOU _ @`MKI C7B;GoL= 6K#_Y%XI҃Hƈ\y7)pQk;^0wzK+ԯ$uS8 |6¹f(.~Yp5-U_{ l^ g#h t.:>X # ;_>bLڽ즲R( ^X7<^-fJ-83Myc9o+^Q^Z%; \H<)yZ΁&wkq|V ͼw]}A+ *wKhjv}[V7]0OG`9~D& =6KUIENDB`qmapshack-1.5.1/src/icons/32x32/Start.png000644 001750 000144 00000001532 12574344222 020753 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATXŗKhSAilSkZ\NTYAD|ZQD t#p!ZR| |эM5&qqS1ܒfq3?3wΙA0vHN7Hq/‚ޔTVKaT/#nW$!NJYUP~]H'i`5XV *yyOT&~M}f@xx,i ,qh 澰:gt˯-4O/UKRCti3 `M%6Ke-G^ê2Ekhk*\w28&h)jma 6 pIR=#2fBjGx\=%P8|!؜?5tñH- OI79.5EfttQhb@8 f}-.tC: ̾ gBT {~CN ̞JfvY&1~ȬJ%Ko65_4Uo/{?ogc6k3-fz܊@bfgoDQbw|w0 {7ًyl z?A@s"W͒UPnz!(-Sq5-(EC._8 fIENDB`qmapshack-1.5.1/src/icons/32x32/Area.png000644 001750 000144 00000002224 12574344171 020530 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ,tEXtSoftwarewww.inkscape.org<IDATX{c׷ï9%*40CsXcL j3$?9,6J,cr,EE-r̟F}}Fso}s?uS)JҀ ݱ }Jr(b"~ 0i8g1h,bPTbVvR1(%Ax3k4ԼKEQ\5a'&}6`:bcޡѢ ¨<p[pOtNfW|Q; sqx kpkP:gaRݞi/ hS`9VTZm-pk9* o-'d9)xci^s)wot>Cݜ_W%eC݃MCq6?`wU4gb7y؃a/e ϋ88#`8<`ccZ Q'3,`Cg센x@Hu`9uDeB@1?0t-DO%P" :o^Pg=Iȫh>1ZWeu'Ut [tH=!6XGՔYP39%L֮͑yG SJX|RIYz;'`< :81pr̎صy7U>Hd7NNN R9)`c5~ Z\89&?E;N:ί#lFj &(;t"^]Gnx*T]ҹV[? 2`q&Wlֺ|IZ{7+`=fFސ[ahzE7H5րwfn+LRžԲ*{:x }mE`R%` [zK+,㥜=2p2AM1 5[I_Vw]]/}{dΎɑUjsگf#.Ētc\)B%;Yj=ڡ_kkY!j/A#wIENDB`qmapshack-1.5.1/src/icons/32x32/SaveView.png000644 001750 000144 00000002675 12574344220 021416 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<:IDATXݖklTU{)0}ZlTZ!%5H@D ?$FHB&6!A(?I)U-(%Z0V }0vvf?JksDDWsOr9k{^l`0)ޒ`N_A,Vut,:8x^o;^o;mm$&"551̚SXlq uX=/|鉐`2qb O:nw2@/>_ uu ,Z䡴>("ixjc? ,%&nCUN=RUy=r8 ,-XP3g.I+ 'wIQNv+oKj_c9[r=>8'I_Jʰ%vǎc2 K'҉$xTVvZ?jWWwQyy0,m|DѨ%6\hI'yҽ` #֭Xa[TRRh4#T\$}}sPKg.|W`mx*aYGmׅQU ,kCBfƌݤ&Q[[Lz0a'󉉣6s3wSOXrcBK@7}^;r|r2"|~GBNN w9^@~2>$ VDCݎiiI@նm_橧 ~\9sdaK>jA~~~N0p67w9V8@jjK#eJJf)*o$''1bh0Mի5.p8ʊx{8<>_(0u2 K۶}e[`,(l?"x>uu\#iaITaF`kk04o[KWWv?gm֮\55^E"MfQiE:/)]6V<7k vjϞm Zhk jӦr:kܸ$FRPXd}~~!:6VV#*0L<;u@s;vsw47waPP0;I#3䓥x<)q^g07l4G2Go2ՆtJ&MJK{ٳ74yyE] "Hw0NNuX]y\/ WnKKpp,WY@iҦ@p5Ba9u gU(9O ΞxƖ (ѿ6 2׈F[ݒ@Gwiy<s3x89IENDB`qmapshack-1.5.1/src/icons/32x32/Path.png000644 001750 000144 00000001030 12527654570 020553 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX헻JA Zie} o|7DYX%jBݘds?:&6g813;02H HHmG=jQUdc է7[E)Jy`-dtM_;Yuf64_,60a[3섥 ڤow(XDֺk.rһŏa&oul GUm F F cPQ^@d/DAلVGDP944z,P*F  _|?fw ;lvV˞6}i@"^W_>TI;SmajK*$9R]NNKZF'"IENDB`qmapshack-1.5.1/src/icons/32x32/Off.png000644 001750 000144 00000002061 12574344211 020364 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs tEXtSoftwarewww.inkscape.org<IDATX՗Mh]L&STZ,hDԍ .ԕWVQ7Պv_rJAM** ט&5II&w`y9}3w2!,ahKi[ZXCS8Lm(^Y4p5TE>'u~ 8Wz>m۔;gg ;С+i|=Jѣ*pSӼ:M <`+tR7kx, }9ӧmsg`l.iַoeX_(x;w0c1<4۷KnvK ~eL:E~rRҖكa{ۍi0?9IWs}=y38`%rLݻI غUiyb/_J]7xTW W AݻEdQY4y^s~g O& `*k&@i+V,@MR*@͢y6㪖H[ ekE;&񣤥?(E*E'7oCc` Ƙp͛JR{sc^1?kO闸~]ʑ[Y਩_QN&2@e>H zs3 ll\y[w*z gY*XV!,KYϟb֊doR+hT R.]mg32ϟc}زW @u[ڵje1*=~3a 0 ޿,&B!z{mm dٳ銽3cGQ(nF2Hyι]c$='l_D"|jA (##R)h3->3_4bNIENDB`qmapshack-1.5.1/src/icons/32x32/CSrcWTemp.png000644 001750 000144 00000002047 12623413746 021472 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<IDATXkUU9&2G)>(! 00ʂ2 r`faBT`Ȍ8==HThZӇ{9s3ùg~}#X9|Nط:<~$^@DŽ550<Ỿ9330:Vd܊˘CE}UUltXc #NSgټzt-r2 bsN}N~=^  !]GQR ԅ/˯~f2FҹJ$36E؇؋gBI){NSmc==KjkCf^F$z>,FWDvdMs'@@%&ӹ s0|!ͧ#H| ić(7|ͧXũM$hֽ?M16dț KIU~v3`ouG ҺQW#==ū1F\n/ ڟ-Mټ?.xcLNFLgؒ>S$m+{iCbLZ>)F+ y-*t`(-=XGSck-é$L` $[ 'рR7R9uar *D#3|--??^a:wY%:2xQ[z~ Dh8e}Sy,HU.΁0Č_ǺD$36>x /t IdbDE8OKs%bcOprEv#Sظ53ȶm)lBBϧb6(&8|ǻ b2$I0-sEO8sfm#UUdgQ]]}BMM+wPZ9 mc.ti栳srwhyt2xstt \ eeꘜonqc"/ju{ =ŪU"F ɓ"簠KQY{ٷo=A^57_C(2bbԄCNNKذa9 /$""=QzzƼ ,z1LeW_bPq೴Dk>%deűoy|SlP(-]ZϮZh6%QQ!""g+ J(6EB\6^=zy&?a9HNnΝ)ɉ'!!\ ,L(:ioIjj$))&_F9NC_NJJ$3367SdEE+Xr ~hiitI9fs@TT aa*d\>AYY>#ݿBX.fLxx|=_۝4df613c{<6ʹiי I+Z|8}C׫{}@ &FZOq*2:Jveu(6AA~=J{BR>Wn32g9PRLII26&'-DF.y@jk~YQ/ $I͛?H%z{K3##SW_ݖzz$Qt,-I6(g.]!-|rIK;ƞ=zXyyͤUq[|N||=7 K?X']JJ9_|qLKLL<Ϋٳ7}{b0b ]MRRWhhJ7HL w6 O1^g" vRWAg0v,;ve\ԩ Zm]]w;w(.Nf͚Orr8_}z6[k?ZOWWEg[پO"FMMx\JFLMu-!AMMyy钸v8qwɉȑlڔ );;}:;=k a]qIENDB`qmapshack-1.5.1/src/icons/32x32/ActAero.png000644 001750 000144 00000002153 12616741641 021177 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs : :dJtEXtSoftwarewww.inkscape.org<IDATXHu_%Fq˵ c3XyBLد dZ& ՘5:YTA#ak8O!Fc2ׄs}ǻ3ѫ7|}GT1CZ`5D_TQF bG|r7ho;M |zx2#"m%0iXN@AզV 8_:8ig`C1v>):q%m  WnupOld%ρٞ CT$ s*EZ$6`,.qWka;Ma8O sֹOE$ 'ShBt|ٶF)0\.XAہ(3T-*M[SjqEOwQmmtA]kk  Wm_g`;F=Ȱ#ҙ?_o`(xs@=\3_baǁ.<-Һ)h1Zlͱ&O]9% #"evX~ xCZ b)գ>󤈙0pn 6y"IUc_Q,Lrh Zʬ햞/?KU-2h鉍)ʗOLuD@.߿-{ |c7Fg@wEk *Hd;P݃c TN`(77S 6#]S'LL]Qѽ!3Wz=l޼fPiȩ_C(`˖5}}?TW^+--IWNyS"[=UwjU~ ԀT=:1 XsxxE՘Ily@ h7NhE@jUO9[޷a~UC^KNQY22'a\)j vs IENDB`qmapshack-1.5.1/src/icons/32x32/SetEle.png000644 001750 000144 00000001173 12574344221 021037 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXKTQ-ʈ@h  [LR*6-k6V )$w-\i!AHQK *ܶ)BJ(q1<͛} {sq{^4Y 8-Cm HHk⳨@*P_1@@E^/%~&g!G٫4Yg1%9|H0{*c/?48&Y v#[/$7&ϙߓ%9]_\ڮh˜vST[-y9 p\@:sg w9'r "@>P2pC̞UuoΫ| '0 tB;:8D "sJ1jK:QRqGr>W'_$ ,"\~\."cm+1OF"l ȷ$пM5xh&")}JMߓaUɓp*"Őw"(4• !+g.H %<G!\@Z1x@5;S 1mVթd iMN*lIENDB`qmapshack-1.5.1/src/icons/32x32/ToBottom.png000644 001750 000144 00000001224 12574344225 021426 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs l(tEXtSoftwarewww.inkscape.org<IDATXϋMq;4 YDJ*߱,fVRXPlI_0LR&R,$K;Qb1s>c~{E6o~yDZ m![aOUy#ŲbOaiDUYʩX@(7hZ-(zpLJ)}'RFk(ѡtLݖw9MܾS}RJIY"] \/3p""~d)^M:;//Ռ{”J w)d2Q?=g6&4 b?21u#x!|o8p٦~Ktv@gNI /Lcg.VْK,cce@D ԯPX]9nY5O(==v*Ģgj[S5=cL1K6{Nttp6R=,hHi Ǒѝnj bZT!v>V@kV'گQ^&HL@ IENDB`qmapshack-1.5.1/src/icons/32x32/NightDay.png000644 001750 000144 00000001671 12574344210 021366 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<6IDATXMeߙ;s玌3B, 4$!arF!ZfTMQPm*h QI`_ /p 2h$LΝ4w=>{6)o^rƜM$2P bkS# !'\gH7T[%1wp`r Y6Of҈x1"NJ@f² ["IhP v<:zi.Eވ\a;vWDD=t,cΆJ[d渁>b]ǒMô( UC=S]ӱSe/dт隸 (a=V1r[gr4'x笛[veHmd– a%~^wptYDtc6]S(tSND}?U*?c"[ADyn̉Y}y fdkfTJEzl̾nnDrgqjgDQooNfM@Sy֠Y|bS*A⢌3\-9\8nFĖ*W6d Ա8-]񿷝nfIENDB`qmapshack-1.5.1/src/icons/32x32/SizeArrow.png000644 001750 000144 00000001227 12574344222 021604 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs / /9 :tEXtSoftwarewww.inkscape.org<IDATXŖ?SA/λ| gXX8!֔~f@g(N$Ԏ *3!R]dw#y~'1qGJAw 2oY8@܁vz {_xN)ufsF7Rh> aoZp0w p6xl 7jkz T뽁D_GY}xÅ$LKs™I,[?tZՍE_"{4o +Pm E'INkiM2mZ/p@Qwїȋ`2FD~aIXaHXOŶCN>$r,T (v:>sFXX&Hvma2e &u`G9iIENDB`qmapshack-1.5.1/src/icons/32x32/GridSetup.png000644 001750 000144 00000003673 12574344201 021571 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  'tEXtSoftwarewww.inkscape.org<8IDATX͕lǿgw9;KX@E4R G5MTBK5!UPi*hЦ E% *Z>ωϿ{G5@R}W:|sJ)Jcҷmy%bɄt>KH%ĽMHgB>)&O.& Zܢt:fX+P$tKl.0g+Sz( ? RqVe0^E ~.Ap$2p^cA,6o!>j3&bl۰as$'8!I>zܶtUuROLFEVUUqjEQ HD0Pr"k޷4 $Ixx.[yo߾A썍NQ( =||a+rXzaL&/&ԑB2n޽{l׮qRǻ4 |@le~S n8t|Mrnpr<} uze , P;XtXl|Ҏ_߯IQ3ψEP`KINiI E\uUunx ݌2> ʲCRiIOCgkv&SJ BdLؚ5S_\q\(BuIMh0yӦMfpႚ|r?ijj 577\/Iҕ@ |(8}tLUH*zR_@"nyҥٳ (Pp8v\ңqsکZQ:ϊ?i^5d2Q>aVL {=VO8QU -V8~'T_ލtp8q6U+B8Q?زeKHER:Q~r޽0 \`c7o\K)Řju֭[VDjΜ9kQJiˁ@H$Ҫr@tAիbiMd2֭͛[G4Mj3QEQJ5gaDu]{IGꎞ5\.^[[{-4sWBAQ(\y@6uXbwsslPYYLNNP(k@K+tMA|V|XXP QJ(Kׯ_dY$ !>uYe7n, p'6ς=œa oȲfY<MΔ #3Q.KSajM@Uo|>/۝5RdQQ*;wL&s߄2s>aYDTSáQIjY:f|R-DQ|ʆ tInK 3L&(U1 bE\ij:Pm R\8gcccZ.?9@)Qܹ3<:( ]'bkkk\.CuX,L&twwf|ŧz3K, @iGy,]|GӼgY :u*iڛ.}d뺾_ٳla }j~F)(>L+**)|?t BW j= K+2&(0LncI~XNzIENDB`qmapshack-1.5.1/src/icons/32x32/CSrcCAD.png000644 001750 000144 00000001531 12623413746 021022 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<IDATXKlUE*|E% (jbtah%Qݹ@7&…h 6.tah0! +wYH1TqjE3.ibo魉 [yÊVWh[܅u*&Q*Ⱦ')S9qs|."a/&;'3m#CO35Tɮ] ėJ.Y[=x &4Nm tQ»/ٴfza?:T# @=U\ 쌝pQo-x2p* Mxi w7Z?r[K@mg5hE٦ S2lZ{{ɆauBpfz.U VQJ~gl%u,ݜ%tu(a xs ҕ5 HF^$%5N嫳ae/Y|Ԡ8)ٿ7$@9% B#T/%4G܉fVh"y^YeIENDB`qmapshack-1.5.1/src/icons/32x32/TextBold.png000644 001750 000144 00000001027 12574344223 021403 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX?kQ<4MLh(VFEARڤm+;Ke `! h!bll"HD좑 -tU+iUy4XQ't?/aXPQ|()"_N 3}w*Ia$rs?ۂAfn)4)(y_Bv-wטq2c̫'@>{V[e6xNj-gA&UXkv0hr=\ %N'o/~p;_Թn[)SefU]^kΊ:4EOQ#3UՈ# ]Iy$|Ӹ*"QOPC{bc h Gu_E;%WЄWe$zR'EVV#ȴ ha`B?փSv h5dj5_[tۃ5Src-y}X2&t1=)AJZkPIENDB`qmapshack-1.5.1/src/icons/32x32/Device.png000644 001750 000144 00000001134 12574344176 021063 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs44X7tEXtSoftwarewww.inkscape.org<IDATX헿O@NbdD!BQT 1de@bD,L, 0TH 00uF/ba'C|hN{u''J)$FA_Էb:Yc@l`Giꏷl;CR@DoKFCTq ee3$`nM q0 V )@ Z8Dn{]pw糽}fN&'߿>(ML @raZ-Q.[3)P(-bg`y } 17wO¦gc+mR:|?@D3g_'1K?qv06geeKNOYZI=yf};52{Z@Z/=]dW{|\ckKp\owQ0ghMѼzn?IQGpIENDB`qmapshack-1.5.1/src/icons/32x32/PathOrange.png000644 001750 000144 00000001032 12574344212 021700 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX=/QswviPREh4Z" :* RG)T:[ H֮;Ga\]1{w or93ys$GTf4սpNg65@TeeFnbEX~fLf `HX@fV֙)&"6Mstt̚.-OͥuԚ?hI*C iJX!A"(`OHxfzo?}s%4/j'@#O45uV| Q?ȬWAaa-MS O-AA$%3yr,/ˈ@.3;.qǎs;Wɤ'..hkShiv EvDOO>""QD%5EHf 9sFntKIuYg XdԨ予Aۯc"RkWhEGV<-wvS6l(,ߑ/HCnO\ `cʦj`ސMd۶Zojj'ҥ't7H|`qL+vSpoΝ""I[))9 DY٫LzAxvNuΏ0u>۝짲ř\ 4 Gd7 @#p ؀V5sCii88+0PϟNeΜ|?zPQQx<u*5o1fUڜDbb!l^U{1u\8XTYfӜ8QƍEFK\h`ɒdL&=" U2D[ӈ6lHedT7:GSt] }NK{r_08vdU|WmDEc̙31c{=*Wsqh^MV=$-Sm~Býi^kؾ}z@hPB0p" UWH!0s/^l=r ee7nz-[ F⎱coP٘D?c hC-YP ˺z?$`C'wyfBq@ic`dJ|Rc5.p0pNf:_>k>< 3`O*#-`gDJ9[[Ϩ^7BOq#g0h^ovD+f(B0pRE&r' "76Ƚ;rn0@9լ U9i[Î| ,|LF@1hnlm8Hpc#m n7-/Nr_:i[ NHx$O* i[@ti%R;S2Wvklv'E)<ADk;8/o=uhRObfTIyg' zfeC6Һ4:o`r&IENDB`qmapshack-1.5.1/src/icons/32x32/Save.png000644 001750 000144 00000000554 12574344220 020555 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXcd`h0i -g````AwbeǏ?g(,܇< **4qG1< F0Q:``DoHI0psIJ_3<{E :FW@k0Q \\, 22\T۟ o$ &Sfb5lڵ O|yy 1[OKS< F0 :k1ju;;3,E/RJS$IENDB`qmapshack-1.5.1/src/icons/32x32/MimeWMTS.png000644 001750 000144 00000003376 12574344207 021273 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ::tEXtSoftwarewww.inkscape.org<{IDATXŗil\Wo7o̱\]n:~=`&Z`:F,ziNreѶ|ܛvxZFAb~RVq39eAb8B) .`0c0%ɬ,MF'Oi64=.`'Yr,3/YcT0N/{8q0c;&XW.p(;Á[~ şD:߻:޲lt/`Yq]XYV#Jkxu"]>~ٴ% fPz0C(bJрnġ$}gSsr7sqmRC|ʒr<|m%3Rb&IENDB`qmapshack-1.5.1/src/icons/32x32/Add.png000644 001750 000144 00000000552 12574344170 020351 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs Zm+tEXtSoftwarewww.inkscape.org<IDATX1N@EGkd"I"E.wI T\&E 7'‘&Ez{,_23<4TA~38/̬&LృS?@3\7#%!!] Iv>w!tpi_f ؽRp˃_}B B@#-g ___]&8/\ <%3z|%*3;x3!{~ >]cdIENDB`qmapshack-1.5.1/src/icons/32x32/PointMove.png000644 001750 000144 00000001710 12574344213 021574 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<EIDATX]U߼ź)i&JR$^VhA RiTva!xc`]MT^l aP%EFQKマ.spfLWS*<"#ΫB "Z>o"Rmt\\6<N.3<|1vC|KE|]u5{QKRx܀O\t_+aLtQ@DFč("VD*kwjVxw0{)LJ3q]a͙Dm˱^6`f)K`}Y\cO?fe43*CG9ɛTwt ҳ6bâޱRnUeG}xW57F8>"ь3s}E#Ş nv8(-3gwp_oEli'ZEDЃ¶ gKNc0sW:l6 zѨ,ax Tkm8"sVf暈e؎=#HUpMvs!;bC{^hzZ80B+t^p?"Vf^ymeT "b:'+ xRf^_8z43ϝS.t}{R9V|ZK<f\,Q#a?5b;Dm4O. řynɤL%ɽF~ whDGj'i3_KS),̡g /Ep'; IENDB`qmapshack-1.5.1/src/icons/32x32/V.png000644 001750 000144 00000001250 12574344230 020057 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<%IDATX헽kTQ]B ؈b# " ~bt3,B#)R  XFD҈EFow69վ9gf޹Ik @ݢ)lM[:qiKK׾ ͲāCWiǏ@C\U'_q)ywmgmǀnNYVgT+L<)0 (2'D+4Ddjܚ1'~XgCiZ&wCmS1|ri)|M6Фbm"\V9#-{m aisv ?@#6*+;*tFX:xrvkՈi#̡eoGa7@ ؆GдAhnSY"zF#zŵxh-On% 'h؝sT noN)OgzBJ~`3Y帋Nkz )e<]"zQrLl XRR?78fsaĊWZsx9HM8Їg:3:JdDU H<|6ϠIEl7K>۫]Lq:WɵfzG5\AmDZx_Pzu+.ϳ&N`:f+;hZI8bfswMl6S`lؒzf'twxx/`.,!ή|&b#(g0m6Zy<}FӂIĶ|!\n @h 4P~2}>T<jpR V̧Tko6#Бo B/}2Z,IENDB`qmapshack-1.5.1/src/icons/32x32/DelImage.png000644 001750 000144 00000002065 12574344176 021337 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs+ptEXtSoftwarewww.inkscape.org<IDATXŗ[legf6h Q6"n6- F&i Fc`1 QD'I|h^$C q@oF*{|23Sh}|gV3?r[kv~PNI5(4s]]Xo"$NJzP1iY3e1ǀGk,+Q>#-5U` }~(i+a EdPWwU0,7w%'e5̍Vx ,~ ]~sKAI2, D~ | /Z<(g)n84IJ0h1R%3|N3_߄QBG#8-I-YU/xs~+#!rZ hB.Q z'Pت'xʵz(hOxx9,Ь>1s)&>|+bo `lj?1Α<}zn:Hv"EjE0V6P/:|{d32`& o0@kHUA 00EƁ \:s3E-Y-V}7hR 10v8/AnUp Gm>@ 0vsh R =-_d!"-^5F Hj|0=<1S ni/u09l, S S"v`΁8?2k-n@dwf.sۅJneag/gP٣?ؾJy$۶"҈v_`4#hh%OCϗ|=X\IENDB`qmapshack-1.5.1/src/icons/32x32/NoGo.png000644 001750 000144 00000002571 12574344210 020521 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs p'tEXtSoftwarewww.inkscape.org<IDATXMPSW )A$p[*F[kK;]tөmvQVDtQJ.e ]SV((8Dq,{(?9$!@kp0N_+,ֆ9Pw7p]ibY㭙`1K)HtT}Ӭ['&ag7=Ux1;Z-@40sE )+EFSy=B9U$6s8"g !RzGQ'4ȏSY6zw=WW&G'&n=CB uGUe|SOib#s>H'Ƌpx>a 1^P9郆'p7aִ򅅺\ X6?N~Fu%No|PY,e|t1Yfq5BCkLDK岑!>EHHY\D0>~a,N?1y^ɷ33W5By U3I!Z67PMc9)` CPB 0[̏q8%{To-8Ha5/9D:h3(sմق[6p9/~/ǀFMQU$զ ]3mXYN VBb\.REVX60c)cyӀZnPIb,IWFlQkRQѐ ]ۇP_afGp NLB8ε.ApX(hTG}H3:ͻMO) 7/#nhٙVh8 #Mn!/ͣX;e:"q7*T ōS*~ށ|v_%mf*Bx 22U>-MDdRE9@y9L>w`@VsBS>rL$ 빣PٚtJ,˿o]xJiaFFFnpTEH]  ZBl*BEK*IO)<`xsʕ2ȳD+5O/}73gΔcر rߟR8 WrsF@ưlYp庄FD%f6s###hjj|OqR'1J`$5m;:Zo%IOaӦ2*3xc|M-r8pal6h8@4~ C{(^^ӣy{;gɒ%B?.>唬[7;txj4uxjj0eJ Cg]z#,C47 j$&#kחJP?> cx34Sc'77 PCRRE EFc(:TܹI~~2d=J~6 L;@fĈy}0Sx>6mr#HM9P'Êdf%P^Ď5@{rmy@ 66p-errƳfM˖MC| b߾\SSp 9$$Dwo.*B.+7 &7nELLHO2rX ^^ C΄řyOCf`HCbH.Gi%Z^om֭v]].>}õkHI-Q_߆Gzd#ř ,gnoiow_2shnjzu@^p}{뵔ΡGRT0 ,X0Y`HJ2ߌ>Ta BW[IENDB`qmapshack-1.5.1/src/icons/32x32/MimeTMS.png000644 001750 000144 00000002511 12574344206 021131 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ::tEXtSoftwarewww.inkscape.org<IDATXŗ[HTksqM " DЅ 0AP*5 *E%̂JRymgv;gf!׷k}{,m20熇VS]x޼? vg[L^/$'ぢ"ƌ  /,=]TՐA{;5 ӧCI E]S$={DEVxtyS@n7bz]$RU%%""5.JH5k3&LJ]9900YQ8}Sbj}L |J(/5pI}mL h*x~*X/7Pn2 0S=˗!6|0olWhmAO__eԸ\KT' ATkJ-˖Ͽ&'fF,IHPrrl@kAml}slHJhºuJ;=4? KK@CCpmm:5~v:U:7nhv9'RX({H'ODEÜ(xڦ"""}}*I Fc`N֖yJKhy<0s&SE:@gАvͬY&Z[~_p{{u. YY0c>_a =[ @Sߡ̙DE7 !A_kK}r2LC'=^) 5,\hZZVf^@-[`TT#onW,hpdgî]yP'kk͑SM'B >s"putG235o 5JJB{>zlZ,#:@Bm6E{c;08nҌ). ڽ^@o/\Ç&S 9p֭W6`dX9- 7ٳpl`ccaNR5 (+V4`+,ԯU4 ܀HMڵ: tu;u~QYsH1v{Jz[1?_V~Z>~jkwIENDB`qmapshack-1.5.1/src/icons/32x32/QMapShack.png000644 001750 000144 00000003016 12574344214 021466 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ::tEXtSoftwarewww.inkscape.org<IDATXŗkLSg-\Z@:E4LA6p F'㌘`%odehH0j&o\iO# ps\yWPC V  ?&&5:q/ߢZٖ~TyySYقzUf8ׯ<ލNKVVZ3[t8<׷! LW͛/u$ɏ(J71wo8p;hi;~h::L?0#0EYt^o), %%穬ϢESnÇv^.= paܸ(lɢ %J%/߰d) -X((8Ͷ MiF|lv7d^NFF ,\ (J TT4SQ̢ESp:= صCx +J%ahx{{GvVL#=}%%jj0eJ Cg]z#,C47 j$&#kחJP?> cx34Sc'77 PCRRE EFc(:TܹI~~2d=J~6 L;@fĈy}0Sx>6mr#HM9P'Êdf%P^Ď5@{rmy@ 66p-errƳfM˖MC| b߾\SSp 9$$Dwo.*B.+7 &7nELLHO2rX ^^ C΄řyOCf`HCbH.Gi%Z^om֭v]].>}õkHI-Q_߆Gzd#ř ,gnoiow_2shnjzu@^p}{뵔ΡGRT0 ,X0Y`HJ2ߌ>Ta BW[IENDB`qmapshack-1.5.1/src/icons/32x32/TrkCut.png000644 001750 000144 00000002743 12574344226 021103 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ] ] tEXtSoftwarewww.inkscape.org<`IDATX}lW}P^R(&1g%d[6f0]8KɒM0 0 V(/8^ s-+}-{B;Is=}s2j95H &.-۰Yc>BHQ$3;11 7g]aBc_R*ӽ7vKϱgNgJ3H~t<C#=N/x]y9ȐyХHX<0Pp_6I5;*'%QҌނ=XuII5|'*"Yo#1#˓UHy˘[gӴ^b}R/Wz(8:Nf! Y؄vL@#zC#ލIoKFyO,&X-όk& ܙ}n3o#G-~q9Y (yxg6^qL!|JBA vweza'C B(<1OP9;#*B f0?X/* g*>u4-j!tWޮk\16]cĹt~*Wf)5b8tpv3܄u?LU12k) *ģʍa< .\*o;60^Jv})}h7!L2'05 ;n=l5^1{^eT 1F!o%Ldg( 7;BfV.%rwr{g L=:G_s"32 {@*SywcGΟϋ/~SO-PZ:nH3!LxJqb̺^}po/8qd+o>5$%Ϫ*N 7"b_ Ѳ3?}%Bi 8d~N $O x9QL)ZOұ3ѱ%xYrHGknee[z={ DcmڈƫImzxӜ>=N[[N@ͣI9TXt(h*ȧֺ9o}+ 5,^+9w'm]!1Ɔk%?\C)X{% < 8';'e7[X ; shMMS񫷩c*odNzYM&<2K:}SqPHs wut,RA,bb)==m;7M1 xyKu JMeɒ;TW}uZ[ک^ $$&ehAv7*O_ U2"Ms#ktIENDB`qmapshack-1.5.1/src/icons/32x32/SaveGIS.png000644 001750 000144 00000001411 12574344220 021111 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXk\U24!#1V۠څBl.ѢRJ77C+Mm6; %,%FMŐZd.GƙL\rs=ob(#5U'G18XS扉9##5Y`އ>DS[ȵ^mllSS 7vخPhirR5i'NpxŗoST+olPUy}8oppB.R).Y&%NGdכNkS*q|6 `U~'yı  nG1*KZDvaCVI/oV`Ȟu)_}xO3_V RD*6*C]WD0JKY|8xr5sE0+i\xDk'R9\@OOlyYbV_%a~>v;u33IENDB`qmapshack-1.5.1/src/icons/32x32/TrkProfile.png000644 001750 000144 00000001752 12574344227 021750 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<gIDATX_H[gF$?n^G +jBE+tN8ۺRz]fvb'KKm4j̻δ1&&zě=;8hDD8BD hDDVWWz{{l6%`2lD"U7%''4M9٩:@{{;6G.b066:D$unөq[[MMM{TTTק:@QQFPy*++ 011:h,P^^NOOJ~?bz}jjh4yyybSlnn2>>NVVsss444PUUE45  "I[!""bZ3Y2F(%%n||ODDEQYZZEQborķȥK?J0.*))F#@fq|xwjjv>Q__^qs;7ۮ{iDG$"4775|kI^XАzjm}ă9>l)K'erWO@8>Ϟ]7oœ>}WG,Ϙ 'Mƍ_x|`/?b'kŭ[&09~sK/x%Vp뵸\W4 P]/^(/OU :u`?gcz i9@(Ǐ`v57o>E(>ߛPMp:tdfFmlIENDB`qmapshack-1.5.1/src/icons/32x32/Pattern.png000644 001750 000144 00000002722 12574344213 021275 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs B犱tEXtSoftwarewww.inkscape.org<OIDATX}uuRQRaZ413֬XOZ%m9çik)<Ѩ9'a3,#@X&P"BxPϹu[Ν{?|?A9R?'4*/8{Gq.'*veхؕYM ~gp^{lY=8/ c;6߽r^!:rU ؆ex Spifu8u>N#Z7+>.ē89r y8=G'jMĬ04w$Y͎hKRq1,Fw& 7bt V1ñ0󑕘K#Q^݌YĻj-)؏D,\I(b,]8ًOQ3Ж,&[^C.""6"lάҿW7xQڐ3;auX_cob5[).ވB:Vi].SjH{KS #>>`T1z:##qvҽ OXYMFėj_^aEp2[R7sC)~. :Idz5v𞒉-Ybx'_^^{ɳ0YgDž|L֕3U(EȬ~~ 2qNܒ%= #Z'bn(0v;K e|^}[zDl?Xԃ'g=XYx?QZњyYablDkzȯ[WdV 4=LдALlʬnu//'Y1{Gִ?1uY Dn.&*̞GMXYѼ]DMw2YL!斻Ff5|a &΋hMͼF^6#?j6ˬ"ZK&*[ ğ2[J&RЦjD4r d"3ʱ!ֈbu֦99Ԓk9%o2A0гX{#Z]- 3}b &6#?j_C{>F}yMc Y&ԺIENDB`qmapshack-1.5.1/src/icons/32x32/TimeZoneSetup.png000644 001750 000144 00000004321 12574344225 022433 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<NIDATXl_}mrBkW)3@U"nF,& -e3bt8禌4aVZ ^?-}'&y>yޟ)%!B|>ϟlllwA)r/$ 5k|r=1VMRRJb1b-B؀V99RD§ K)G+4@$Ԥ8/5yX-B Ԩ-6C~t'!$I)gSi!rA0Vu`.}7&@v8pB@s | Y'!(1ATR-@J4CFX Weߖ9J)s{N/$<*a%]UUe:}t* ŋr)eZ os)8D…Ѩbb" ]*OR~2 }nfʕ b o"($ V6HV{exxGe˖ Rx3gtX{{{ mB,̧OJBQZL=@KsBK <."TZؼqƧ=L<9m ;7zu&~qg#PNYu<z45?P]7I噧`՟LMKK$QFx:3&:IRD@DCEL(zuH-q(mY_ȱSla̹;+2"DLo'ѧHR$[f׮/W;^Xٳ}J_[#N} Hrr"cx6o3 P(c7^dgq:}"qqq̘ \;]2>n=Wyݎ |~废Ǖl^EM8BB7mH0?pbqXaRr.`pȈ}OCC?n}@&~~z=Qz ˰.dI/$\v}|oT xKA?q`aydl66mʧ<ADL]2G&@.a2c0X/lM h7쩏8T :_w&ۃZșՋhdwrh4d|5+ӹ".J@֓:c-I#ڏSZb)Sx3Q&8FuJEEJJhtFs7ۭobixM ?XZ]h )))4֝#}߰9yifZ}Yc曣MVfK42w]AAXXT#*M &5}Ig>յ;VīKQkM-/.2FZZOXDL$Y}gt+O֧O! (945V21,ZyKx|6;-/Ѽ3DǏLo\K[nMU5df#!AF`6 &()id2@L0 bÆG`6${N* bܹoCϱc?eUU3{z|1ljdIIVf[:7y$ Zeܱ="ͶSn]gbz*/]Ī[8|jΝn+ fbYz.gS# .ff ,ɴ1'V2? z귉MM]>[ۘ3fle0Y=xdZoN1q8o$jxWq 3fl-eOW!=((El|`mmnd'`x>}tGGc?N?DS"FtLaԏ|t>^ [8w΃V4puI $K xa/żˆ#ݶsl1,8mZ SS9jGjd_‘qsߔ)շ &a(*ʅOfA$Qxsx<Hӿ $g!=݂Qa2:鑝xnQ&Z|ѹ?22eѢOzիҶn=/;#M"YŹ d\>8mg'*oM=.ٻ%"v„&L3_~5 !dACTI_UCr0%@8u 70'Dq';)h)nwbZ|˖d@UUu{{ɶ6%1ba4ge uStPHOZ@AEd)_PSnQVv#h톛6K &-`g-8ӷm('uǓ@4/?SdOt-G$aԂ_w7~#'{&*1/IENDB`qmapshack-1.5.1/src/icons/32x32/GpxProject.png000644 001750 000144 00000002365 12574344201 021745 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  O(tEXtSoftwarewww.inkscape.org<rIDATXm߬7fnL-I&*MET#*+kE|׵VTQCAEM/[FD׍5!z?{Z̜sf9s PmH?l?GϝJMx*9xm`oݿ!n%f}[OXVto⠶ eی&oėħ}ܩ?c6>^ܘG}VK"M@eH]5ux"kE`&Y]kϿLX+=T(oScUt06aujZmq51w S$j;=rjӭ~=V]_nW!9ψOy9*7O&sͲ7Z[H|0^vEAbDu 򻐰XgMɑQ\aSu'5 `}DI)/CKwqn^8%t7+RJJ̎jzU/cYe|r.(I"'ܒ>5QG)Ća\:iR۴/?U9Ҽ~}`jEN G6#) ]ҟՓ}/58;_hpD\܌߀I1 -) ߋ@8A+Rk8F(s{.fl97~w"ꋺxz?|.C>"(ИEĩƓ"0S.DǶvJ% a)#BBr.ftX{g]֕@D(Ὅ8Ȝ#]r;wMuQܑ43\IENDB`qmapshack-1.5.1/src/icons/32x32/TextUnderlined.png000644 001750 000144 00000000753 12574344224 022622 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<hIDATX헿K1?]š['Wqtu\\q ? 8" " .b(rP=\\rp߈RfJ 6M[U7.*" 鞫S ԴΑ۳r|ҏ* XzzH;`z~/ 0ПE#lm!Mř5tYkěp69/ X97@ͫ,$ģ~-,{$10C}q $@J=4T ɜ7 ]kցS38h5ϊy<}Y΋ V<1d?a ?EMIENDB`qmapshack-1.5.1/src/icons/32x32/Left.png000644 001750 000144 00000001235 12574344203 020547 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATXŗKkQN&1Qڠpモ((PT|஛( +\v%½)B]‚K$49.&L޹9pw;{EUe!u>(Dd+Ep}]"܇+8$ "¾"=p$gH@06F795C8}|X 2LdDd~& "#ػvƜP(f"Biʩ$ P9CXތ2޵_aCl^prX8#{: %0^WkL˾BX/ВI@þ@$Q'-Z Y¡]p0JlPm8mPU"rffa&ؾ(ԁM^ $*"rl\9 tT cp'RkO/ I,$j7GSSi mYUUE{{hRJL  299WJZ3Lv+@qq1a2+xx>WN-c x^^o^,3 :i;- cynkkcpppχ+e=X[ "J1iEɘ46cdd$ƎnwDH r}es\%.՘JdFDr*!UUn?-Л/<(_6׹Jz&544[[__{$ }`^@k:Y:V ջ+++%6͔5s0{rn(JR#hULmmm|uux8}3_ י1gFI3FV\q:;=_\\DPֶ0j qVe>ـӤ B>w)QA+D:y;nTX8_i/~x(pW/P/q? "sJbIIENDB`qmapshack-1.5.1/src/icons/32x32/WptProj.png000644 001750 000144 00000001465 12574344231 021270 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXOeϹzkh )AdEjHڴ*WFM( $X)XBgO^{ν㝙Kၳx^s~iWaF[)Yy?z?cB ml1's*keٞW j&-ƛ2zuqs.p~zX@D1(^_u踇6\R8T͊ceň0I*IJtшDP:Fbkcwݏp13fMYyEkQû؎шؙ#R)S$yg:]=Y BeY:Im qg<7_7EDyګ]X.H1 IENDB`qmapshack-1.5.1/src/icons/32x32/PrintSave.png000644 001750 000144 00000001453 12616741641 021576 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs , ,h\tEXtSoftwarewww.inkscape.org<IDATX]HSaٗgk_1ϦY mZC@F]dD y#EQҺnl  !o\E䲥cYM<3by<ϏƠ\40|UL:BW33/nT(* @R-b |) ?G}}.tt,89ާLu(A`4jJjR0baa .HpBfYggPBtwv6zׅ%D^txTNo4FFf7"w?]|Y~VWw Ue`,VƻZual,n-4Y%@lxͯb>x޾IgR׉Ҍ&lD/]5;YsOޠ)O_=-8{k鷷>o8sNF%hU'8]W tcq׌%Фϐ @+#P*s:F"Hl`9oWڈ^aGlEїc;wz.Wshns ˨٪,rJc' K`lh  nk&t* 5Ƃgsݮ=d5NtgNJ^B"UM8q$k.@%ENgIENDB`qmapshack-1.5.1/src/icons/32x32/ActFoot.png000644 001750 000144 00000001202 12616741641 021212 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsbb8ztEXtSoftwarewww.inkscape.org<IDATXŗA?/[jA]q?m?JM BBƈ5w)b-?+! ,Iܙ\^}yvflDUY(LlnAu LɌ(F_]y,3!95(*0˜_)=O-@N6_g[wP,9e d>sW!0u{ ]WEZ'0fUk } cF=s'#@jD}!B>GHZ)Wӆa}kMi \0̜_y"lD$DZetd pNٟ*c$ pոHѳHjTO+G Ķ -'PmAS0jp((+FێˌdG_Bqph&d:| p(ak\?bΤ~V G1dv/ܘŕ j<ďz5~#(HsUY }4,9ss󼢪/ykanN]$S:Ȯ[VJ`(TZZD@dkˏ5ˁcǥ&[ Nأ 9@aXuu;v|ر)-mێe^'0Gz $,^<`={yb Ç=ٞ CavTִwZaaެٳo\vƌrjjnZ+N @Z^ 4FٔH~#JӼ'))Kaa~gx#3mNVbܐ3_ڧLqG*]{94+1Ž3=ƿ&\"+ k|,>:ߨ:T} yEb*X zx·{}gQ:ݞֱn0TRu,R J`jɫ#; Tu">!=ٞ@D$PijNts>[0KUEv S}@1hLmŪN2'1ߛliA(MLz*F@@$6̲n-#"*HmA` H`r.RjDA@#0خl>P4:Ǧ:d~@s&<|< ܜb:@fI`cn5U!%V|`Hlz(NzD7bJ)~*h,8Ќά,6ZѬF>Vsz 3$f; @-q? 4JTu86ѽpMč>z.,TA*׌}A1[0ZoAI@F ~mZI Kck@9 djdss>-_[4baѿ5 QIENDB`qmapshack-1.5.1/src/icons/32x32/SetupCoordFormat.png000644 001750 000144 00000002124 12616741641 023117 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX_WE?g]W ҬͲ0E˵Œ"D*I-J*z(Q,[q\-|0T®#6|OVi쮔Tkaθ~{[sΜ=W`cP|>OTg$"[Y@۔/SVU#H =nݞl%},GN[QnG}0 8u0r"~:I{vU j|W;r|m ?mYk^#l\`;R 9q_w^"o$WjgV8lDC~pv^*< ⌽Xmt:5f[2x8)f@ζ" V:`2;`wgQFU7MMVfZtZUl t#qaETOnN /KWHRr#`U] IH`ͪ/uZU8YǀXpI(D=[E(ǢJs"~#3؍XC4oJ})Mr< \Δ ׫u]s"~?%Gڜ;=nx9@o^Uwa3ϙ"ǩV1c7}4)8nϫfll@+$"pXa,J1UL^C [t"415{MO1=A\H\7nH{ @:mz_裋ܾ{"qN>M*u?k #6E~~lv4H!`ƍb(/w~~D"$yg$ExۆxU/.ÛG$b<55a$p<z5x< I@)0> cVɲl& pQ4Bl;7@!i255EJ KaB.,v5ܼyx<=2χiXE(b۶mD"}0 $)(4_ic&!ذa===9;^~=gΜa||H$mxΏ?^¶mHgHǏ_ gSβԔ IO/ۇ@|?͌.G&K zqb9Hё}B dl~Fp)` HHV:āhoe˖ (--%0I~JJJXnP//70% (<Ou]_Aw(}Ǿ}N# ziP}n`VikӸr" Dq͓l\Վ7ߴ~`14:4Mq[%;ڲ1٤7oۆ m?fS:SOqhweo$ܸܹ3N_8׈ڀ4ʜI ۷kvZ ڊǓfP3&'807;`K0eٲ^~O>PSSC4T*iFhj:( ~YIJ,\.cƶGHIL8qub̶%~M2i=дs0E˗!(\nt_4 ʼnp8δ+a%S ~7uu .jܤRiR4ܻw)%>HYaG&ɖ-PP$py sd>NC  A !EEN'tsBB ]חa圓~xx_`-)"&R##)w4y//qiÜwuayIENDB`qmapshack-1.5.1/src/icons/32x32/MimeMAP.png000644 001750 000144 00000003465 12574344205 021113 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ::tEXtSoftwarewww.inkscape.org<IDATXŗkPv]_; W (Ib21!3MJi3Ƥm3iq3N'hSi6J 4U\ ^@ev~}/{>>?~z(טRDͳ tE frŵJۼ*oDf'<9Dspi=FLV$/I> 8N.졥 E)&h@S/ `>w//cfFh8Jo&Z1 q.Eh@g`ʈ@%>ׄ[Ei~:xn!]e,kWڇȯ᭹-T/&<)5.IGCIm!Qi4x6!m}2mGy;ETAKZKnyz?u w@9fp.r&uI7&+__܃&z/qe!FLv@gt[;GH\ Kg`An_k~*yсqw*/BpnfP X8I;Ϊ, WIGݜ$4!p][N-KGgKQ*@U-M~a!QatX,~9o޲?P3{+e{BTjNs͎Z@.຺}$ݢ  htjR&$cwVl^6j~\Ѕk `ʌp'si9]htj޴(K$u]"֙9+>~z S PTS0><}m1 ,VtZK:DcHeZ0,ף_zj x[w|Q+WPq޼PX{x/WH| ?=JۑNu MT0b)J:ܞLlg;ӣ֪8x s/eq%Li9sKr7eg&g1txR8fh*^ 6Ao_Ow~tmvbf[uz>fx“C(zaY%lxBt:ίʳ$W@) âKPkU7FW0B^EOt2wnNIvI!|KAXWU8׃#EvaʌJٮ"r˳ /̗P'gޘFpl*o\x7^yA\N}I>Đy#UCJ\Wcpo(IENDB`qmapshack-1.5.1/src/icons/32x32/PathGreen.png000644 001750 000144 00000001040 12574344212 021524 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATX헽JAw 6VOalll!vX  ; +;-T3. E-ⲱp`agcfvaDU餙 ĜBgʚ l /b rYP VR&wD^<df3 +^y]m>: ɯX [&P+& O@ n.!k.<~Wvvfu95eߖ 9,@T@y ZF@Qv-@9?<aDUPvEZDy8N?':Ѐۭ]fb=Ot9M45 1RQ>N "o0CI[_NEe>.>IENDB`qmapshack-1.5.1/src/icons/32x32/CutHistory.png000644 001750 000144 00000001756 12574344174 022011 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<kIDATX_L[etT8M[ չ50 el!f1N.$$D:s u[ #1rɒ:Թnˈ:((+`ERJwyr<_y="و4-.*=#&\i]zuiQh5Xq; Mex<_Ñ3b1z~gdd)/o- $fzz\8۷t5R/@1>ttiZ) ;mm \S:dԩVDdEDdA3}>'6N D k&gDyGP`.?4 e#G6E?wKJbeeV&&^3X.[ rs䡩!e8sW-ר| ;ߢ? 0BOP"ĻOoA^^٪׮!px:4xC0v? Y@Pպhfx.np3p%rdpxƪKRߊH(d{9y ͂_r: LےYଈZ~Ey8PѓH&(|;,G* )g?6}`xnTjhR_p=ڹsܜժe@i,~5MK@+)٦6n7 pݬ5ĄG?3g:RJܵ}-cy@:;:9ϟp8c=pte~ 7_  %_q;wk M̺O޿lIQ|!7IENDB`qmapshack-1.5.1/src/icons/32x32/Up.png000644 001750 000144 00000001205 12574344230 020236 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs )AtEXtSoftwarewww.inkscape.org<IDATXϋMa+g$BɆ"e,;# Y,cfZFY!R0!0eph3w~y:ZQDtQL#?3Z )l"UR ZjU%*1S͋[8ɊdW0s0y9c J\U \>mXFuM".b-D:JeשޝO ;K,̢cz5G=ykFU` ,qa8}6[(*n1\Dof~ ":>gKa5r{f)ݡeV1=ǵq`+q栣5LI{/4}uXP֩5<4X&?Fj8WGpJf7RD,)H/3dfKhm6>ļ/c vRz(Cgd;x+IENDB`qmapshack-1.5.1/src/icons/32x32/SetupMapWorkspace.png000644 001750 000144 00000003744 12574344222 023302 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<aIDATX}PTw?UK0&X׈hAMژLF:㨥t4&و8~$d2kc&Q֢!EE&E(*._Zvd:g=ss}""P7n@ ?0G} 50 䩶;zI^| _gKR[>C 0}J{m5]˗x<~z z}6 bxwx ³k+8vAOllbcܾ줥Mee.(/Oc$sUDaHQuu;vMW/`zR^ ˖}!l𑔗7|""/"U6TZtdIw SV(բʦM'DDHT0~.;{MF(.lUɻKeeSиK2lG6Ypx~=l DDN.dfnc®.܍S9sNzn)S dڴ""ݚ l*(J&/x<-ZwQ A"f""r􍢑#?fР^`¶!2r ^ouV´=^F79rd6))QU^;(N`9;p@[M`^M_h6PT4V͌E60 1 ܗW@p񻨯?vvcZj!//OU[[ܹl6\PlȨ(C'ܹK/Ňv3d;J KUPKǏW q:T:N9x曗1C() Rl> VנѨHK{GNɱcWZЧ̚5BII fEQQT@Qj5ɓ'>$$Pݧi|KGrcƗdg#hyy`< #pR!##sF\\ -@VѸ vtxOELBA$ƐTT\lG1LٳB}}=sZGt9zo[*KAYYIYK\Wi5Tl߾7nЯ_?8Ut| lҞ>|ǹWl^S'NKIznf.LP"# ٻw/iiiܾ}C޽JKK;w2R3ܑ'SSS &I#"]v|`ױ WW6noih %./}?[;ň|֭sjMsәޫl\\FQm۶noH"l7'Nco?CUU3uum46"|C'pi1cFsС.2?o6# $1'!!aaaihh]ljj4hАSUUUl6hѢ#G\>@]"^|XRSSdee5Yֳ@Ųp愄EM>(6))i߂ srr'NjV\YV[[;Ix<2EMM1cKZq׮]󵷷f͚V5n?p8*Æ 7o޼rVAA$%%mtcccQ K XnX~SKuu1_IENDB`qmapshack-1.5.1/src/icons/32x32/Scale.png000644 001750 000144 00000000772 12574344220 020710 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  'tEXtSoftwarewww.inkscape.org<wIDATX헿KBQ?e ShM-PFSKs-A&8Ր4X}է1;sr5&#btz}"J6t#L@=?% -MԤi[HgY-|-/GZ6ju(2A6кyY ꯔ'6hQjELbT*+>X.PXmƵf|vXc!  I0՞|v˲,gmU*]Gz>@"2:=cCm`P=h"] _g{_p/|IENDB`qmapshack-1.5.1/src/icons/32x32/Progress.png000644 001750 000144 00000000747 12622430447 021470 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<dIDATX͗;/DAw/"K#DTPQH$t:NT9K$thQ$"`Ӭd]g_9d'3̌UJX`p*Q$7zFs)$ ?cP:/7Bx.; p ,ځS`]#䎁Nv .m_M @ Hu,`ER+3O@OZ8A$%+`M#E`TYmj[|ҝց+R;S-|ҝT#D@΀w'6 t0g ($a`E 7ށ<`m>}`nIENDB`qmapshack-1.5.1/src/icons/32x32/SaveGISAs.png000644 001750 000144 00000002500 12574344217 021403 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsi%tEXtSoftwarewww.inkscape.org<IDATXLUe?= )hݸeNSԙKck-uZkN?ͦҵUjXҘEWHi{=O *^@|w{9{D(`K_w?CC5@r;[ Z+ 솝Q+@y='jO`Lp s28wOZn8!t~.`70hݠIy*`O;@eeM%VbfĄz pa3N"& 99hVRoMjIM 2CƗ3G$_7С͞Ʉ CغgLj< | Dt&e[ڬǒ%X~Njjp(7fy-Sg=p.\ZTU\`kcJr箷mێ N(wbZ`֚&r0믓 `ovV~~JqJemZvz>>PBbAByN%*MGZ<܎mR䕲BI$ Aw`ϯ(n9Ck{CІ(/ WU|hAy<] }?:5l\os$IQ3#D75nLM[9`r$^Gg}p.||ϤQ$)D;ݟD٧.ch/Jo`6BA73}2`(~SDM}rhEWm;TL~`a?Ƅ3Nwt'$ Cp \͂}R/<#OkIaV7$[00]|S{+\,8GΑ_=\w/Yߣ-k2m)|2%9 ^G2coO>1eu&xzv0+؝iӘxo{Z0銿8;a|}AIl΅'tH{QR B (ԚG;(Jҵ"6c EFV~34pTQBŊ̧e62>\ظc/;bA$S6Nc׮&xkrKQ4)Z !P^MH &UJvEb +98@FVKG]޺^Ԇ|lao]/+3?3뷡N@rⲝ߃fs]Ef9"2 HHJA]535(U ֦z^ 퍬^lkkV]^H,܆% It|)6Wk{}ͤXɏ 'sbn'INa,*ꉸaʎcfZZ2soᰙHH|APݵKg#nq/Fֳx\?ǧBIENDB`qmapshack-1.5.1/src/icons/32x32/WptProx.png000644 001750 000144 00000001625 12574344231 021304 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXk\UISDMLP6JmtBMqᢋЂBA,YpN .SJ".jPTdbm̈́@iǛL{};""TsL~O"+ٍ`)7<=~3sx;3{]H/$y>"TJɣFp6C;%o{EZV 炻zp?y f04n"J$fHWJGo~6;.ߍRz1ԇ$q2vHZ3\^/^#NIV0[&T}7Skr~h?c ˉ@$sv{ܳM >& nVܭmmWDLO}kᚈ[4S[11O1@<Ρ"|=$hYZXv8ƽQX5ɩ[#+Zz8W% :aٕєڨ3HF=c.U~3F3j=/ЅN<ʖxc|7Q4 %`vQxXmD)h;#׃d^(l,xIM) bdYDҮPFPPv:c)eWKL"ˠ"*z(DeY،8z|1j|{^ נ96~V5-a&5Y0etp"}xQZ06`C0oWg[#`b"zT7 jY@ (]3$b*`Uυ|LkI|R '`9b&= +:z5p>uFo]@ջyvA`3 =J)껃#6z/Zo5U>} .AG$P+cAJiLDbR*5Fāz5PGPZ,eQACpN*DRщceeu/*{'{@_⢚+3vS.(CJ4<+с[rD=Lυ)gFAd=g?cr_SJ<}"C`IG$>s$L% &Y.ֽu&pZ)1>37vlw-QVTU )ߏ9^zJ#`CM瑲ޡ@pn_;mEܻ怀Mw#*mJPRZR|nP`lK-x(]YުW@C(VK:  éw8P!y7֨؃q]] l̈́- fpM8|"bx'R$ud)w!I5f$^zo%)؀3*-kîG9[#%c?#f=p!BrC8o=[Ñp/`TrfMN~Gޛ1%^؉+VkyoI[kȯF(ijF.@sRnAlkI(e-`V5H |oR6܉3:/2n.6mu_IkrŽԷ?,p_z0@)u;u 5gO[(t(+,Gx  e;N}^$grZ$azA)w@ow ,׮Au^.W$? 1Gt&X4ŖNCvͱְoX+'+IENDB`qmapshack-1.5.1/src/icons/32x32/LoadView.png000644 001750 000144 00000002750 12574344204 021373 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<eIDATXmlٶ[ʖR$*!F*F 1~ !!GDH; #&VZE"TQBhB-lcvӳ;y2IuvPV֋2 SVQ{{Μs^ҧ}q*m2 Ss漡ۯh;?Nq|K3g.0UV_6I׍ȐƍSw޹]==V6lڢke!z{--ZC`sIeW,@5^ẋipl-[V-0UYyLJ%}uZANGKL:~i^%%oMOz?*pM~zz"!#//3J"XB@ Nq각Kٽ1q|nӁ%Aa$ny@D_@#a#+/8jn]H[yZI"jg]=(\02 }$b{ @VggȽrrrTU=La˖~∣AvunHrYs SNL٬\~e@åUPGs)0uٲxqrmz twu} Lm I_J24"鱢wԚ55 c~ӦM詧V]]!pXfHQi~ٶZ$!XP:mګڱgǀz{-mܠ5qK䓓T')wgp;3{2o޵^=+n.[`,p8uͼBA ؾAn1e9+䲜c!z~۶ƬYO#_t25@qqeer[.$ځxֶ?T~t*=2'$}xB4Y"77t;g?gV"h.) Xk`?BhT΂`la*1[Gh9Ik\M'N@8L?RjWGjmma{j8xRރcF BKlx $N:cnƐWk_jȜ04- [X_x4d j[IENDB`qmapshack-1.5.1/src/icons/32x32/Cut.png000644 001750 000144 00000002331 12574344174 020415 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ] ] tEXtSoftwarewww.inkscape.org<VIDATX[LEg ,[\iԶiHS*oZ5&FMQlCT|0m15C ȝؕ[,Z)e/3s9̙AU?>H{l-%8 6d cVU#@NSxʀH1ضR}]1V(~ȠOUV igPrVY*L̊@;yumf.u;TDC̊@+GB,GfA(Ή 0ss.zUʃJ'  4a- Ea퀈B_$ GΓPsa' e`K#E/:m]UE$v@b!.Tu\3(MZ0E)h_bg g"VGڵN<Ў|Die>$([D8  7S [P娨h>f{An W tcmpcE(1u[  mLX @)0 F!7B~O3sx<񓴵eŸ-;tπn^UU[GYoV~À\B"`C $ nU.0s@^B @Fo-g 4'"٪:(oz||\;"9S`MLW}9Z'ے}[׃ݾ 2.诸b=--qf (v+/$%%g &` d|;XDԩ3ήڝ{ܘ7l+PS+47fjjn搜g&k5A hC[w0%H:d} ՀL:y YIENDB`qmapshack-1.5.1/src/icons/32x32/MouseWheel.png000644 001750 000144 00000002313 12574344207 021734 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs $ $I #2tEXtSoftwarewww.inkscape.org<HIDATX}hu?w."1J (SҤEJH&)*Ұ@K00b#bt3{^$TNskowIx I.hP)@PL\ɬC!iŐR(~R%Iӈ7A1ȼo\? ԰/R![^7/HzlLH-FB+.K@zI 5쁲sK=zǼ̬zi;t]cޑ466"٠0iG~pksr~+煬ɓL|,2wrP lXg%~:k3-yQN(ToDl%nм9fyMHLA $E|#sj97:x 1߁tJ+.^Ғ!0O8뀿 [P!82[g;%`0=BH13a!8nMǃstWd`Q~[I Ʈ @ih %Wz6v,Z6Dzh揌1A-P3Pjڍ w'u@QH 3X toT9, N0F%U)`e}'..6Wa!=`n]#EFrB~Iɮa%4p+IENDB`qmapshack-1.5.1/src/icons/32x32/Map.png000644 001750 000144 00000002221 12574344204 020367 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ::tEXtSoftwarewww.inkscape.org<IDATXK_s<7E$eFID jDHiT:\#ZHmX4*  4^ԈMFJUk:{;.j8{|wn諸#+G`'tʺbDF(Wc $9DTf) MƇŞCJ Bd}fbq>~̶O).2;klo"0rKNltc)m31K17"&$ʑ0P,^E T@^{{^yXy`yy#֒o]?k5E[9]d\Ǟ9?>!,&W`a6 ` )|\ %K3)RꙠAvE`)X^ j!4Rp+8<] Qм1V _Gnq-4n|/p}h˝q7vY˲{8թ`L,9N₈Ꝋfdqul`n`&=>m{'\l5ywlϊXNc<5Pz9e74}>? pXxScm]SPF@̾#b)_InpUa+?re9+Fi R:HKvrc.=o7VWSDH)nDĿ\c= ;b%߂Y,jSˋWNM(6ůXjΥ9kΝD7疫9RX[s)=| Jj 4ňl9Yp.WXiϵ6+GUIENDB`qmapshack-1.5.1/src/icons/32x32/Undo.png000644 001750 000144 00000001651 12574344227 020572 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<&IDATX_UUϺ{G'dШ$ @ $"kQ/=VPK=>CD$BԃBPQBD"ԇ9޻zwrit~9?k:{Wd~Qf"<"b$"\x>FX#ƽ]e {KDkD݈/"4"Թv qG*3wd}DDM>ci0O圢Df!z Ou*Sf/aK|LoN3 r>[Z'~Qzc:c01I{#ON C`Զ_8. VREljwJOxEcO)D%8=Xmꯌ4"a`5YAA^ќv>3?W.DA*ml-jo0!*ZeEUEnb}wf{dR x; V}2<K ;hE%a-vIk(3G{鄶ٞl#PaLn{l<3 PbuŦZ2lqc=t'g浦vf}-Id_IAyh: (n6H˦3킩=mCQvRZzjgRYQй],A/"sf\0Қ;Ypo`O1IENDB`qmapshack-1.5.1/src/icons/32x32/Font.png000644 001750 000144 00000001156 12574344200 020562 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  'tEXtSoftwarewww.inkscape.org<IDATX1kQ3Fc!),"4]B,[%U* NH#6BXJ@(beXȺbvp|lM` s7{73od:nW*Ԛ= <̭{tcEx~yps;ۭ~ 8c{`*3$ۛty텒5fX+x<,[vWU{%ob s_j( JjOwG'ݴ>//Ln'1p@d b2M=ح{iA6*Mgdڂd* -`7\+x s-܄^6FQ*RH:"i8$I:,r#W ` 0U ŷ; 6%U=JLke8;3߉'0 ?֡ 5X(t C%IENDB`qmapshack-1.5.1/src/icons/32x32/Move.png000644 001750 000144 00000001545 12574344210 020565 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ? ?pSxtEXtSoftwarewww.inkscape.org<IDATX]HSagn4͌Ij!"t%V^u%B7EuAEB7^HAA%YajiN_ө.8pWB$2zW W8v\*:Ilbl@EܯY8=><`30'X) P(g|Ӎ \<ĨGV4'fwl;Xb|Jϕ)໻[mm)it;Sc ET uSq9 ,jM0E_0S(:aO@.<Ӷ!ŧ`Q&@'+s}oixZBO>޸$( .]KC@+`w( IQQ%ʫW!Fjϓ86A` ;$a2Շ:sMyKZ6lz֪x ]>C,++ϯ.ZXs9Yo7FRv@R 3ZkAvk 2n>19!"脃H*kUơsԬo`s %zw4H 1k99y9 } LeoVoĒQzG gvz|QJsK:>yz`[fn\fzrEFn 2K q0>ڋ) Tss?Y<䙛c߇sOt=I3Jx"?Lc=;M}^bŘTBP3~\7ZE"z.l=,)ȧg1} ~+>H!ޘEMSܽ)T |wgZRo"x=N'iU>N 5 ͯ7I,V nT}G>BxP Ax{q9e_A @9k6=r"@ʂR}bI(nWs!isvVo%#7gy(8nl'cJRP- Ky :Z~_~kn&65?>Z̼{)nD>gbDBR6Y;I0<@OAojsdVljYLpc[O(Go0gI\ZOzJ 5O%F >õ.F/DLO :VltMHYGcJRV5#@u.RK9N:yz*}Ȍ|^Ͼ%a-߂F֖c(1A`K3w_0zfUv>_0abRI\!> Ayv9DɉA)_zcﯭL`dr%FskD d b㞍.hYBV~?12hޒ,@0mJ]Dژ$8Dk(,kDFK߆4wa4,%\x!*~/' k,iŒ"sZ1=Ҳ8}i lЍ1 [g̒MyRVJ3g`(IHAjL) /eǞsU4rO"`-zY c,$E}Un@\)Un_RϏ`HH׶v:\)e$Y Eڅ3oߘ ̾;"&t8֜@J<4?*+Fpf.'-! 0?c>ȷjvG02T6WftT`B RYgTNE|b׆n*0쥈]ϗQ=lIcEIENDB`qmapshack-1.5.1/src/icons/32x32/CSrcSlope.png000644 001750 000144 00000001572 12621572226 021517 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<IDATX]U3-A Dm/Q)%yE&Xk-YΙj]B )؋ +/H|IaAb/zeyxm`]gnwusyִR_K'd.=4/=P= 0j!adj/D1!:CM),R'!&P" K::&KO8=E¬Fs~B! ᪽ e-,£d h6#F̓(ad(HL¯ǰFu%d'i*.PO3k 7w WjԸ+ Nja_Ĝ0w3Tp$Fr [.b^2f }M6Oߞt0MU7 IENDB`qmapshack-1.5.1/src/icons/32x32/Zoom.png000644 001750 000144 00000002202 12574344231 020575 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXO\U7sgHmi@ZĐ16EBiB , + l ]AԈF ˳ؘ> v<^gKNr{ϽLNJ2mT{{x?y !#C*8Gz0Ҥy[2s"b9X 8˻(ɮq/2=3[sDJX?IO%^B¦kin]=@(4.S<3̂4rj{G)ߓ^Ń'5Ic?iM 3ٙq@ Smrkr[o@a5q#iPvYHfMl<"ovXI23Ѣ*;88 ^*xG Q?r/KX7P}93wvfN6mōTOƣ,ebt/ΒB-&ƘډzPq&~KWpr|otgQPÅk^W-S=BLVLqk&-3 ǰ6$|T'pnĂd1!f}f@x*V8Vɾ^r)\t"Ė|o f~sɯ2*ScWf~)mU|v @Q{A-W^VޑG=ƈuCɈ0ot6TD|y#"Rf8Ɯ84iϏ-*&f k2u@|L$&61I+3ߢ~[f20WIENDB`qmapshack-1.5.1/src/icons/32x32/NoFix.png000644 001750 000144 00000001225 12574344210 020675 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs Q Q@2tEXtSoftwarewww.inkscape.org<IDATX=K#QgF`$مҠ DڤN,l,!`e@bD Fb! f I⚏B6XxGl1o7psΌ_G7a<Pd|F<1ێ5Amdv1r-vPyUSZYYXJ8Lͳ]) F IENDB`qmapshack-1.5.1/src/icons/32x32/UnLock.png000644 001750 000144 00000001773 12574344227 021065 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<xIDATXOlTU{oSL] Ĥ0 %Բ ShL7ĄcBb]@B D?Q S:Cx:owy{F)s|MOz|D2YEKiEm-/Y,mW18iD_2J֌DQ^ʘyS ! =0@EE Y/oRNE<6T*9 _-v R؏ЎeRJX:Bƴ>3$*^Q) f<x-Ba63n!e&> !kw]a,&l U[3R_#BW d=ބř(*}ȆRT{GBI&u&⅙JnQχU*! {ckk3'.0W(ɖ *ց(9 hcܞ7*֙;yjy:Bi5]I'Qͧi7|iPVӞaO*}2l,<Lwʊ;=E**nc8Ta̞2%ki٧w踤e_#0.8r7b[΢EmJ5ӺNbSP6kͱc'=ziXʕ岑/~&=eVAŁ>|XV<ʒdBWW(qeYaqc|>/Egq@۷}޶m ڐλ=j!ܜ}8>cӦMr'I8{ȜIrʞ=_$YV[9k֑ }6另]z9n"YIENDB`qmapshack-1.5.1/src/icons/32x32/AddArea.png000644 001750 000144 00000002551 12574344167 021151 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ,tEXtSoftwarewww.inkscape.org<IDATXU 4Anꍲ*0j֜e*m6'.̖V ]JcdDx5 4sx s.|?_qvs^ssn'R2܂Ӱ؎E S@Jja>0qa$ `@ޓHϓ?%>%bciYh>feс{S? 6K3烾S= vn#9X<lTv+srWLpG5ȓ^e+[}ǔ3X{|+UIwogR ?J[HO{j-*ԴRZ'6Y^kq8/r[3;VazS N`w[S*׃N_s+^̨`˂+`W [XY1+إ?mXUL2'\T*ej`fRpm$2DT9:;b 6=x!& Pm:&?w[_~L- /[[Ǒ^"|U)xg y\#Rz3XJ V.PQnfL=n\-)>}"FqW߉Xq0ift&ㆱZÛ"?X,`#%U7qSIC;Kzj}OjG==sw]k{}iTxxsљE) Jg+y+E/eޮ]Vuoߞ)^C??bM{:9Vlqo)`'",Y:YQ|0a{JF\xx@Cu{ ҬLlm),p}Xv;2p0<^! πb U\^Q?B{!^P)݂^\,bnEinܧ4ocmRV%)-HeXm D6%ءk_ѳО'?PpH[EN|Ax=<nִdFl*ս?aדmUu0~>'G:ΗZQ^[W_c16ciu0=Xy$7"DA(obPRZEANx&ɜMMC0 1;Ulyfߙ/3{1/?QPIIENDB`qmapshack-1.5.1/src/icons/32x32/Bubble.png000644 001750 000144 00000001271 12574344172 021055 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<6IDATX͗@ĤB@DA&o =xzi=KPAP(ZRD"f֕ln>0~B :Gɲ9]0씪d2?EBs1[1"hX-5Vb0vY xC&8t:t:m)y)DFUUsK =...w=aVCղ hƌ1C!XjFb3 ;hJx ^FO7E(EDoDGUUi~jZa25N@dY᜿4g$I`0jZdRv/A5[t~]$bAG+\I!vo\.Ǎ_t隦z??;bieR{Ϻfxl?⃮S^v{5;Yo6NVw,N*qñt:'g "r@ @Dp:pkM'4(fj5hÃDIENDB`qmapshack-1.5.1/src/icons/32x32/AddTrk.png000644 001750 000144 00000002005 12574344170 021025 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  #utEXtSoftwarewww.inkscape.org<IDATXOlEv%$%e5\HCB)   MXCћb%1hBĢ5 FM 5?(XZ;[f}v;gyLsZ M=1$Mm!-Awg@^ D%x?L_u."='ވ(g9`FmL&cdmi'. fQ x/6+YApυeAvHƽ/=,s[޵ RY֬Nɵ۽ 6"x`úv׬N [ li5`<p F)^Y_TTr7<_6M Ym+Cnmh:0{F!bC2)b{/Nۘ;+`x!w0'範G61* VHpH#q?8dU?X`wӀf1Eq2^m#DW`  CH m=`^ m\J̶v/e0?ƀcF\rbք4 l5`*}B;`U:3@R F!`c },/7' uzt("nϼgYr{g*n8pH5?2ls))NUֹY`lSW0kpoNa֌4?%4pRv ]p?M@lKG@ p eJ0^#Na0Y IENDB`qmapshack-1.5.1/src/icons/32x32/Copy.png000644 001750 000144 00000000777 12574344173 020607 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ? ?pSxtEXtSoftwarewww.inkscape.org<|IDATX׿Ka S0@\D# ʱIZ݄)\ S^A uvR6%wRy{pBK;@4a<pA-؈ùdZl uZ-PxyY}ת 0a0xZS7cm&KZI~Q8;3A8888 L>2: 7E;;d3CILM3 z[3n7(+0]\AЊ C%QB9_( ݹpnqd@`qM}ئ[{ tJ QB:6_vL+nIENDB`qmapshack-1.5.1/src/icons/32x32/TextLeft.png000644 001750 000144 00000000516 12574344224 021420 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATXc`ae 0 +^F&f7ZVA:v W`vl[  :Ž ,$oW@- $ !\ Tu'VqP6tcP6tp&؂$ hX0H(J3+jGh90Fˁ40Zr`4 XtEXtSoftwarewww.inkscape.org<IDATX헿KA?s(* Z] k,&eP0KA˄4 Frq;dY.Y#VRK eT9!}l "}I;$șW ]9u(~ @k x{ #U32 |5NBi9 П*=Fy-(P8HƼ>iexJvcO)Igu uKK03dm$# f>-9lfm:vky ,q]PؔJXq image/svg+xml qmapshack-1.5.1/src/icons/2DFix.svg000644 001750 000144 00000005325 12527654570 020040 0ustar00oeichlerusers000000 000000 image/svg+xml 2D qmapshack-1.5.1/src/icons/UnitSetup.svg000644 001750 000144 00000016067 12527654570 021071 0ustar00oeichlerusers000000 000000 image/svg+xml m ft qmapshack-1.5.1/src/icons/Progress.svg000644 001750 000144 00000004330 12622430447 020712 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/PointShow.svg000644 001750 000144 00000014303 12527654570 021052 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/WptMove.svg000644 001750 000144 00000013316 12527654570 020524 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TimeZoneSetup.svg000644 001750 000144 00000022734 12527654570 021702 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/DelImage.svg000644 001750 000144 00000010464 12527654570 020573 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcSpeed.svg000644 001750 000144 00000006162 12621572226 020727 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TextItalic.svg000644 001750 000144 00000004765 12527654570 021205 0ustar00oeichlerusers000000 000000 image/svg+xml I qmapshack-1.5.1/src/icons/Area.svg000644 001750 000144 00002054664 12527654570 020010 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/GridSetup.svg000644 001750 000144 00000016156 12527654570 021036 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SetupCoordFormat.svg000644 001750 000144 00000004615 12616741641 022360 0ustar00oeichlerusers000000 000000 image/svg+xml N48..E012 qmapshack-1.5.1/src/icons/RteInstr.svg000644 001750 000144 00000007355 12570062516 020672 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/DeleteOne.svg000644 001750 000144 00000005043 12527654570 020765 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/DatabaseSetup.svg000644 001750 000144 00000024075 12527654570 021654 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MimeTMS.svg000644 001750 000144 00000126057 12527654570 020405 0ustar00oeichlerusers000000 000000 image/svg+xml TMS qmapshack-1.5.1/src/icons/PointMove.svg000644 001750 000144 00000012564 12527654570 021047 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcElevation.svg000644 001750 000144 00000004625 12621572226 021617 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/WptProx.svg000644 001750 000144 00000011671 12527654570 020550 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/EditText.svg000644 001750 000144 00000013752 12527654570 020661 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Apply.svg000644 001750 000144 00000013003 12527654570 020201 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcATemp.svg000644 001750 000144 00000010564 12623413746 020701 0ustar00oeichlerusers000000 000000 image/svg+xml °F °C qmapshack-1.5.1/src/icons/GridWizzard.svg000644 001750 000144 00000017100 12527654570 021356 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/QmsProject.svg000644 001750 000144 00000010173 12527654570 021210 0ustar00oeichlerusers000000 000000 image/svg+xml QMS qmapshack-1.5.1/src/icons/SetupMapWorkspace.svg000644 001750 000144 00000016211 12527654570 022535 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/PrintSave.svg000644 001750 000144 00000013223 12616741641 021026 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/PointHide.svg000644 001750 000144 00000016155 12527654570 021012 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Start.svg000644 001750 000144 00000006713 12527654570 020223 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Map.svg000644 001750 000144 00000124312 12527654570 017637 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Undo.svg000644 001750 000144 00000004216 12527654570 020027 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Left.svg000644 001750 000144 00000005656 12527654570 020025 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Add.svg000644 001750 000144 00000004252 12527654570 017612 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SearchGoogle.svg000644 001750 000144 00000006346 12527654570 021472 0ustar00oeichlerusers000000 000000 image/svg+xml G qmapshack-1.5.1/src/icons/SelectColor.svg000644 001750 000144 00000015354 12527654570 021345 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SelectRange.svg000644 001750 000144 00000015134 12527654570 021317 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Lock.svg000644 001750 000144 00000006630 12527654570 020014 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/EditDetails.svg000644 001750 000144 00000012736 12527654570 021323 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/A.svg000644 001750 000144 00000004215 12570062516 017270 0ustar00oeichlerusers000000 000000 image/svg+xml A qmapshack-1.5.1/src/icons/Device.svg000644 001750 000144 00000013636 12527654570 020327 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcSolid.svg000644 001750 000144 00000004570 12621572226 020742 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Database.svg000644 001750 000144 00000016212 12527654570 020625 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/LoadView.svg000644 001750 000144 00000010715 12527654570 020635 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Activity.svg000644 001750 000144 00000022506 12616741641 020713 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Cut.svg000644 001750 000144 00000012500 12527654570 017650 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/PathBlue.svg000644 001750 000144 00000004731 12527654570 020630 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Up.svg000644 001750 000144 00000005642 12527654570 017512 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SaveView.svg000644 001750 000144 00000012121 12527654570 020645 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActFoot.svg000644 001750 000144 00000007334 12616741641 020460 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SaveGISAs.svg000644 001750 000144 00000015257 12527654570 020656 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcUnknown.svg000644 001750 000144 00000005230 12621572226 021321 0ustar00oeichlerusers000000 000000 image/svg+xml ? qmapshack-1.5.1/src/icons/Redo.svg000644 001750 000144 00000004216 12527654570 020013 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActCar.svg000644 001750 000144 00000010437 12616741641 020254 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Time.svg000644 001750 000144 00000014045 12527654570 020021 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Move.svg000644 001750 000144 00000007062 12527654570 020032 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/AddWpt.svg000644 001750 000144 00000006215 12544272413 020275 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActCycle.svg000644 001750 000144 00000012323 12616741641 020602 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TrkProfile.svg000644 001750 000144 00000006634 12527654570 021211 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcHR.svg000644 001750 000144 00000006074 12621572226 020202 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Empty.svg000644 001750 000144 00000004465 12527654570 020226 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActNone.svg000644 001750 000144 00000004767 12616741641 020457 0ustar00oeichlerusers000000 000000 image/svg+xml ? qmapshack-1.5.1/src/icons/FolderDEM.svg000644 001750 000144 00000004770 12527654570 020670 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TextRight.svg000644 001750 000144 00000006541 12527654570 021047 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/DatabaseConvert.svg000644 001750 000144 00000032205 12527654570 022166 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/DBProject.svg000644 001750 000144 00000017744 12527654570 020750 0ustar00oeichlerusers000000 000000 image/svg+xml DB qmapshack-1.5.1/src/icons/CSrcCAD.svg000644 001750 000144 00000011747 12623413746 020266 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/DeleteMultiple.svg000644 001750 000144 00000006651 12527654570 022045 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Bubble.svg000644 001750 000144 00000006163 12527654570 020320 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/AddArea.svg000644 001750 000144 00002054733 12544272437 020413 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/LineMove.svg000644 001750 000144 00000013462 12527654570 020643 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Save.svg000644 001750 000144 00000007160 12527654570 020021 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MimeVRT.svg000644 001750 000144 00000121226 12527654570 020406 0ustar00oeichlerusers000000 000000 image/svg+xml VRT qmapshack-1.5.1/src/icons/V.svg000644 001750 000144 00000004216 12570062516 017316 0ustar00oeichlerusers000000 000000 image/svg+xml V qmapshack-1.5.1/src/icons/ActShip.svg000644 001750 000144 00000006252 12616741641 020452 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/CSrcWTemp.svg000644 001750 000144 00000011443 12623413746 020724 0ustar00oeichlerusers000000 000000 image/svg+xml °F °C qmapshack-1.5.1/src/icons/AddTrk.svg000644 001750 000144 00000017716 12544272461 020276 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/AddRte.svg000644 001750 000144 00000010231 12544272370 020250 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TextBold.svg000644 001750 000144 00000004324 12527654570 020647 0ustar00oeichlerusers000000 000000 image/svg+xml B qmapshack-1.5.1/src/icons/Image.svg000644 001750 000144 00000007222 12527654570 020144 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MimeRMAP.svg000644 001750 000144 00000121174 12527654570 020474 0ustar00oeichlerusers000000 000000 image/svg+xml RMAP qmapshack-1.5.1/src/icons/CSrcSlope.svg000644 001750 000144 00000006060 12621572226 020746 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/NightDay.svg000644 001750 000144 00000014634 12527654570 020636 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/NoFix.svg000644 001750 000144 00000005355 12527654570 020152 0ustar00oeichlerusers000000 000000 image/svg+xml X qmapshack-1.5.1/src/icons/FolderMap.svg000644 001750 000144 00000124172 12527654570 020777 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ToBottom.svg000644 001750 000144 00000006412 12527654570 020671 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ReloadImage.svg000644 001750 000144 00000012147 12527654570 021275 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TrkCut.svg000644 001750 000144 00000012147 12527654570 020340 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Cancel.svg000644 001750 000144 00000004374 12527654570 020314 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SetEle.svg000644 001750 000144 00000013034 12527654570 020301 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Tainted.svg000644 001750 000144 00000020332 12527654570 020507 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Paste.svg000644 001750 000144 00000005451 12527654570 020200 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/WptProj.svg000644 001750 000144 00000015172 12527654570 020532 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActCable.svg000644 001750 000144 00000006121 12616741641 020550 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ActBike.svg000644 001750 000144 00000010464 12616741641 020421 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MouseWheel.svg000644 001750 000144 00000011737 12527654570 021205 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/FromMap.svg000644 001750 000144 00000134115 12527654570 020465 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/SaveGIS.svg000644 001750 000144 00000010210 12527654570 020352 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TextJustified.svg000644 001750 000144 00000006545 12527654570 021724 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/ShowAll.svg000644 001750 000144 00000007412 12616741641 020467 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Route.svg000644 001750 000144 00000006024 12531374550 020207 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Opacity.svg000644 001750 000144 00000006471 12527654570 020537 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Help.svg000644 001750 000144 00000010177 12527654570 020015 0ustar00oeichlerusers000000 000000 image/svg+xml ? qmapshack-1.5.1/src/icons/16x16/ActNone.png000644 001750 000144 00000000542 12616741641 021214 0ustar00oeichlerusers000000 000000 PNG  IHDRasBIT|d pHYsmhtEXtSoftwarewww.inkscape.org<IDAT8-KC\ď` `aѿ`qqY:̂AUaE8Az\/ '28(Rc(ױy$V ptyUXńq|l>g؈(k'| ,`gfh4vH%1GA[&W3.&8{t0Lu*Q5_lh!.Ŀ]>@+DIENDB`qmapshack-1.5.1/src/icons/16x16/ActSwim.png000644 001750 000144 00000000732 12616741641 021235 0ustar00oeichlerusers000000 000000 PNG  IHDRasBIT|d pHYs'tEXtSoftwarewww.inkscape.org<WIDAT8ӱKa߹WiCFp [-nm[K4N$$!B@kOI 9od~P닍^ #v o2"[.xwvg3KDrD9mO묯ɷ:p; v"":8_81ENq*N">02)B8$GI&q\,68iH;Gb\kX|}(}\2tm41b3/B*kI;NTu^^31-DØVf92(p]R7;oA2]mIENDB`qmapshack-1.5.1/src/icons/16x16/ActCable.png000644 001750 000144 00000000670 12616741641 021325 0ustar00oeichlerusers000000 000000 PNG  IHDRasBIT|d pHYsnrtEXtSoftwarewww.inkscape.org<5IDAT8ŒKBq?% APKFM-C-nÖ?CKsCkNmQCiP]9{ν_ĤaVr@H^)KG> e<6lz9P+p V%%aVY*J, &I˔vD 󚾜De5<Q̻)VA ;A5KjfX fI ~Lpzj IENDB`qmapshack-1.5.1/src/icons/16x16/ActShip.png000644 001750 000144 00000000710 12616741641 021215 0ustar00oeichlerusers000000 000000 PNG  IHDRasBIT|d pHYsnnУhtEXtSoftwarewww.inkscape.org<EIDAT8?OTQgEj JAhc) 4$VBPbAak$$tT6&F  څwl^tg7y?hjDt , >37Vs+X=̐;%t< b&cx5ѽ`9_3F's_#ՀK ݒaXfu=)CW=¢U1Cxx՚|m+h]c ˗3.W2ܙ(<5p+8?U 9w#j؏6FlOq0 'en/j3cˇ+.IENDB`qmapshack-1.5.1/src/icons/16x16/ActFoot.png000644 001750 000144 00000000602 12616741641 021221 0ustar00oeichlerusers000000 000000 PNG  IHDRasBIT|d pHYsItEXtSoftwarewww.inkscape.org<IDAT8ӽJCAoB* mll@ʇQBPSH`}T`X brv9sFfO52q+8kԈwTnN`=<Oتb 55l**8 ">.G>ND~ yˁHk'a;hVDt l 2w/x%v}nʛ@/sEV  Rj޽xmP1/ouMvFK;IENDB`qmapshack-1.5.1/src/icons/16x16/Activity.png000644 001750 000144 00000001216 12616741641 021460 0ustar00oeichlerusers000000 000000 PNG  IHDRasBIT|d pHYstEXtSoftwarewww.inkscape.org< IDAT8?hU{{ƠiEDj@ `[- ID(_~ik!Q *CA`BN7Mm&y Ig8>\(%ܦv*!ރчXO-NEVfj4Nc}m Qa}>hfq#<@~Y? 3Vq{mDmfqÜET#."q>8dzzUa>vkL;Fme#`6O=[McEXl/ktbNgIENDB`qmapshack-1.5.1/src/icons/48x48/Track.png000644 001750 000144 00000002366 12574344261 020751 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<sIDAThՙ]Egw]w5uM˵ P0c(ƭ6["̺."b‹@Y["E[%DJ] bŲZ*zO4;;+ ;|DUi&Uk`{8Q֛jkL Tz lx7)0ڔ4IEDN^x\f{p .U L`+P:,\+So,-$>s`_)UU)DXgaG8," 9F`w_z3$ h۩t6wjD m2+yex$6͓ ޼pկ"N<%l `am܏/-{rې3~+-ZU߲7L[;uR x"'bN#Y~  4|f8yT';4zJorӾx=߀) F8L MRpzL|;p<tx~p+ k$TK&U=Ts)ϒbrDUDm" R'6DX!V˩:0{!+ϩD<]G\*gq.F|xYEDy\:#&[B`ؤ~vuV>WjH /$෗" |GO.'"WyD!x_Fv Gɗ0iD̛jVEs`c ^׿G 0%MfAwW@=)țw0 GIKq5Q'-V d˧ XhioJE*i pa_f̝|eLI*v2.;Vఁb{\g0V|>u/u`W[>kHS'W#9pŖ_O>/X1V0,fm^_n)iD.wVћudKk*ҲGJd 0\CzP lWv5,T-IENDB`qmapshack-1.5.1/src/icons/48x48/POIText.png000644 001750 000144 00000002113 12574344252 021167 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh՚ߋUU?tFkbd6EFHjCCXKc B)HDah4AT EaLa=x}g/l޽Y{{efʸm h$mtLҁ̒5 `Y{7܅k&4~ 0#`&҅^ޑa5#L  w#@Kv$MOzoCKJK5^@~9^F˜ׁzݬRa`KN<Vǝ4h$m{o k:h>t{۵!ҽ,xX/_HZQP)d3?E$V.തmoE@$*4qA4pVXS@}2NI*%HD`%!BmNbm< &ր$=l޶S xVҺY@x[[iI UzЈ$7sM8h7݁{rSDc 2fZUM Lu/W,ilmeZM@}hS1 .E`wxssܜu-D,KLUIf6F e'?Wo{I+,pw36x7}ƲuHiHSv!I_xz=>H"f^~2[!@pj=ĒV:sv1enE3x 0U^h;̹:}# ЩoG~3|&?ܺ2'.{.V~+]2"T3ny߹hOѾrp+|׀;+](Yq7KȺ#g`tk=PE + H obb?|݅ɓ:5##1 |O.eh0߀*Զ|ZYȆ {[]ў l6UܺU~`0}Q ,6`]Ӈ- %`áB/?•+6ӛ >'Cvc@,;:u.r%k5a4e0 8d;wF$HsA>Uzzw^}SM~tn>jC+>>i%}%I>`zm^$ۥe$??6ttrKnIKkk޼ʕGe?;$ݺ%%%y5#T#+HSJ6%騜H?XWgה)rGEAO1'D3gP\ G_]">,=8Q%K5n\'I'%^'f''{|۷5~H2-`lFβr,cI=zjjl7Im/]&MZTUUY=)7O^sr>}{[$|$'\II ^,m<]wc%ɣzRR|.]IrHߴak~2x]y={V4+)zE<ߩӇ5M qn}ÇuaD&L`֬kv*3^z <|Fyӝ;5={+q+qc~c2MC}%YKU;|Lt^ScGs}i!M/+zIo G z/{ylgt龖,Ux4͜CtCR7f_V?X3u ,,$%%.;Gzf5'Osurr.PPPX|q2F 5 "m(O%cb1S^YADD{""ӶfsׯXn_"ax=x@yu8p"%\/ȑLԩ1 ΃:`aThb#0׍XjjI87RPPAqL&%b#$$t֞^BFllƷsglr ÐY@-5J9H5m0qf|kFB{AN[E? O 0C~VUj B4 IENDB`qmapshack-1.5.1/src/icons/48x48/CSrcDepth.png000644 001750 000144 00000002114 12623413746 021513 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs a aJ%tEXtSoftwarewww.inkscape.org<IDATh]TeߙԈ@ "4* 1L(X2#2?ήI%Uh!eb+ѢYk]ie̶gFg3#a9CuT6iVob|pҊI+Pi=95P=\l\l4jD[ OUҪ15I2X9Jv٦|{h fӤM8k ,o UPcdw1DAxmsygk̖G܈^HhyCxXĤ bs0HlJC[$"pe=Js𖒵U:0RԷ"ڀKE{ž4XN/ƛ;q_=-ebA6`*抑Gab:yY]F%◚+Ug5g 'x4 EuPN%ydH#Hq}ƈ(eDc(%sōGˎDW)وw%qZGc񗽁q2P# npK*B^dR"O=P#wt}f Otq4#W8'$3>ht QG9K6wZIENDB`qmapshack-1.5.1/src/icons/48x48/Redo.png000644 001750 000144 00000002500 12574344253 020565 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh[]7x Px`iV ^o$ZԇE|)/>}Q4VZԶĨEZ51N$k039̙sa>gZ{;2S7gXo$O<j؍x2"N*Q 1 wEIJuipvCຈ:"Zҭ)qnЗwDeSLdlvY8~"'wڂVǜ2Nxvn̞ό,"oqi%Ec rfMkqejF?~&X %džmO@D˯{)?vl< ܈ei+ 3ExDz܂upg(5;Z]83,>_^Ƃx_7{{Wo]l߰ <.3_c"3_TN!F|N.̕2GazVkS8k\q#:r{M DāST}M _`H<|#OB0<wU[䱙& V'v;qASg>.<2smF#rv٠)3ܚ P5"XZY/5W㌱? 8^& *swxıQI fsk3٘Jw} D\*Z|Zas{&70EyoǎR2O| *O7U;>Q}Cx 3_GVЛ_( *ӣ3FEc`wVި'~?SwWk܎{24XLCOaaf#G`I)C~?V~g{2>p a"OjĿ`e{Sے4[Hk#0W ^>:Vdf>32Ue7HI%7kWevgśW8n8_Z22 +Ňm$ k!njdh lkFUJ"qM(Kh dpĴ+C Mу]ՙY#FXM!btSHiMfga`Wḽr}J0R Ч% eZIENDB`qmapshack-1.5.1/src/icons/48x48/ProfileToWindow.png000644 001750 000144 00000003161 12574344252 022772 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs DHtEXtSoftwarewww.inkscape.org<IDAThmLWba ͜cFe,$JH&1%-M:lj#(ZbЯјxjys=jBN^N>WDE}mhnnh%=!<=eS# h=>qn3|%ر;vp5DJ׼*++()`f̘HRRILMb˖DGl$'p-i R@[e˴8PRV.j4?ʇ۷uG|{:VOjAV xǥWhժb0c &y{@BBTz ̙+VRS<[tsjjd DŽB.f/ [DD|),']9裟EpE|=-Fܹ{>!ƶm`2SUQ Α(*&1jTee6M 0e0#K)+>8  qɩD7[ e`JKU9x1U#鉊zaxJeקkj!dW(|a'ǸKpf\#o4TN*t%p[mzkye'QQQ} ܀ƂOdž 8&~W"8٧>}@nnΥwG9̀`fbufèTGx}F$JU@KkU$%ҥso@yAV&Ng'nS>%S̘KCCX tVP^n`„=z aƌ\l6ySKnL̝{[xmdjgɒB\yHVVYn _=p6b1W˿'ZZ,T&\.^geϿN^ދFtf\Yܫݵ+6n<\ʺ5BF1fF#72nXPóg6gWB%LwAcc+E~ڵ'߽ؾ"%%w&LO/V.-hEٱ2uǻRtYdFcKHqS ju)>&&܎, RuՑNNx^4 %@ZxcD=u_^HEǟj -7q +Z-w@?lg,FIENDB`qmapshack-1.5.1/src/icons/48x48/Cancel.png000644 001750 000144 00000001773 12574344235 021074 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<xIDATh͚Kr@L- Xr$ `(6BU#HJqbIb$c4z*/dg)ro</`s`^",ǐ0r%~si6~;*hpnYNDd׵ާDddxzk}@~fR?|eD@=|VV]$+{J+Ƚ7k%,R0=6SJ=|]?x;/􃯔T%Qֵ%!||EQ?cй[iG/f<܆guo湡 R 0*/V8|W:UEDF8M Qg"@N𐻌gG(1 JUC,xx$"u%&|V% gP zaQlm>DexVlmഩfb|BuT)ƴ|ՐUnJ1J"ԏČAӎtOUO`IENDB`qmapshack-1.5.1/src/icons/48x48/LineMove.png000644 001750 000144 00000003370 12574344243 021417 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<uIDATh{^E[`{K!K# ((`DFA H  Z Zڨ[nw>qΖwϾ^Pd̜y{gf"5RJܿSpI-}v.oH)c>Gj}ԄD!4q71>"Joɽ11R'P"FY{jV3iUīU!EU_ i*!h)E5n-&8` 7P/QAWeIpdKZB] g /Q & #~P|}X@´`YWs g\l:@ttob[׏a&C@6H8PB|zu04|37Q!^&ߌkqb\ؕR3kt*m.ԫI^)8+E8bK>G%".Hi^QݗZsD1m҇VV^Z3{\qׁ &,8!ֈxxPmēyv|]#n')""*Ϙ-U v};/J~lFzq7M濿YuN#/b{;loE\I|>:/_ϚF٣;n\ v+V=IKp8ucMf +&c"ʜ" L2Eiv-t힩k=9ISHJnWcwpl͸("' cʪO[x8$šE-JfB+U˜^JH˽=vQxܼl "jd!("ޯOouu#M=+/N,TpO RU܈z܋&ݒ&xKf=ę_ò5 .ÙYlX%"/O)84Lt>0Yyӹoߝit޵JOWHB7U?̮okce2< ꢁ~:r1b+:*4WB.! s >5ҶV~(=y{H{ ICr?z!W yl%}_60i쾉ӹi\M YJql_ne_i=kW4 _L1J& lY8+7Px)?U i |.}(ooKPZ0xΖYG:>Ka "X, Nwd[JN?4nl :_ gh`8jX4FǙG"@^Ub`C kG`eA|/^ơu |xe`lQ50eyvwJ+qJi=+l:,"6`@>g4O߉1"S՚6cNTyNX{lu;p:4KOIENDB`qmapshack-1.5.1/src/icons/48x48/ToolTip.png000644 001750 000144 00000003063 12574344261 021272 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThilTUgfZtAhv IEl KJҐB HHl4FDT% .TUbX &4"@"eRJ㇙ig^g:Ԅ=={]U Sw n1ɲ*# "Çn4:Zmay1<)"߫@g(p7q"<0dX/\jeiOcYw"ffg-۲V9:`,٫~IU{wb#dF 1Q,$* Xr8UOLG}J $DSAUTKH`ȑC4;<ܹ8y&1Xt43fzJxvvJ EHZ9R]Ms(,Zp% xg &E>XeUT \#EL=8>ffS"`q,T_wu~\+068})#:jTC{L&9 t>Ur2Bu%`!0LQ!7ŋ9P^NzA,`{j*WQX0tH^ ԶqhR=*VnqL:qM&fݸAܣ ph@Dn 7s,UVUO8Æh t!`XFa\߆}]M9m+9 Xh-y냤$6UV zWO @N ӭoU B:֝7B^U-ɟdpxj?ёE{յaH"Ԯ%n ^+|`0 >K7!Ȼ~U9ս[ gKU ગn"/Tʼn2|VJBb Lp@"+IENDB`qmapshack-1.5.1/src/icons/48x48/AreaMove.png000644 001750 000144 00000005376 12574344234 021410 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsG2QtEXtSoftwarewww.inkscape.org< {IDATh{UuLO ã!h@^BW,*myTQbeSA  Aw# `Bq8wn~bu֚g}s>{}nV5b'u0Ȅ g ']'w^ f{{i+- ̟K2T|Dl&::ʰߡScyoLpY`Kjp`RpSОW怆k:u_d#iy. Fg=Cۂ}H+ `RyOC5YavxZG9󂧳lWjM{^:ULxn&9+(xΧy X|B`쭼M;aV냅[-7#v?G[tuU ~<ԓeAvVF:TM6wMh_1+9}řFs9U(<!W!crL0 { ԛM >ό.[@"pG8E}c kLƛQ|Р-܉}?? "%Qv vHVU3{9XDToqr~I9ans[/sϰNJhoi{}@V47`f ur[$gkp/`v&ds6'X[ lŘ]3w0t +X)](pNmu/h1QZ6WmUnfS 3D lַOJ$wQLm} ͳWe_XpEqg9➕FDwk'i ;[XjZ" ~ J|6ޛӕ(bSšrߥ)//i$ў"E\ CF:/)(XbozG\CK{` WcTO cvcj?!tinj4A`NXjBtY8z<̫xDvw/NQI[3D,zXî;n8˗7&:CtT,YoX|e?byQ{6Rl9w`*"[f)^҂"^9ǜ tNmg횄"R‰`֏&1(yt<="nPz=G1_"%m@Q՟_xLNQy_|۵iQn.wQN>]뤾w/cjl|-?7Zo.l.N>'nNggGa_);)Iqur3u׉AO\7L RkZ5V)k >ޛcOfڟ fL8`UA-LpЯd31:Rf` R5Ry<703t]R:щ?j*P j{UsP[l'"uZڛsU~ioJRHi$T܋r/DQq%r[O զ[ze3fpo:}]e¯ALsGiNemz-xRH'C"Lϯ*zqzx= 5x_m|N7Vȍ ~A17#ӕ`,({g'=&һ>GA | ۑ_7 tg 5 S7+З yI7 3ln6GOOxΑQthr5J3+J¹_'J*B4"|^jR% ssexbޭ[^6HU96 l$_IENDB`qmapshack-1.5.1/src/icons/48x48/GridWizzard.png000644 001750 000144 00000004557 12574344242 022150 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsߔ%tEXtSoftwarewww.inkscape.org<IDAThՙTe? ?tR$ԖNij5=VӵH=ZYi"Ojm Q"!g2+E 00?㨀`hs>QD_24ځBw@*p*ۿ"rPJ.ԫh֞ \DDd0x؁9'"RRR"kDD>0v ڧYw%ݶn**nCWlRSwhm&E{}1@%0Jm7<@ǰac#= >p&`'0fj֏Ȩᮻn@Q=HSS&?ÁCg\JKKY.,f\+"N4k@nw~r۬Y ^xoĬ&:z0Fefql=bpaXq8Z@(k{zWtɻ "IvOESO=99vI2cG= c>j;7ϲe}wqzYDR[Rb`GGD~H@D^M(`͛#; gμQdΜ9CqQ@V+2|x@thD ̛CB9R__OHPe(h|ym;ѣC%@(l|]G@(,'ÃVr⢩X|2#' իٸ@~"ٵ=φ=+W>ĊgBVV=FbLl6KF=@gںԁ@zz `ppt8xQ^^k$#QՒo@N _@JxO&8N,+0j$ ;vh0b(͋=UNo~.ÓTq:[C /Ԗ=>P׿2ũz*jm..[qq[O}i6m:@b:};Yp/L9FYJ>j qߦE]_v2 1~'b A<*+k@QބéRKĠN[QQUa]]sC~t=:r(Ss ـh`TIS+"p8u@lU"^:n\8Mvv:S62Ȍ-CZkAP/"UQQ_0 Se {2?rÇdΜPAvdҤ""bX%zkjZ$B}Bip@9s qFD&\hG8&N!2r DADF@H޹/s N  lm'9s ^~ uϚxudg L>Uo9SE$t?.Bc’K7ޅQmU < dd8)nY?N?Qkr[KUs9QxNҥ@+:Ma Sm?fk??"v@&g4?D ۬<EQOp%}o`kJK{>[hYYzpC6S:2x-NhhLM]@Hrjv{3|sF1ʔµ:bG6L˩-t:]铟֖6ӊVu}(~q˂ C| ~lyu-7gFc9+,,|6XkE=2ݾreݨ?=e6HkŠ5xQZ:R;w\~{1zhs(Y<ƽס"pmET$m{EY>!4v306-rrʘ uF&GduN NԯYFk.'>kug<`jqPZ9 31ު'u;νx56]6fLwP VBn"2S:DzAԅ)%"MhY`&IENDB`qmapshack-1.5.1/src/icons/48x48/Opacity.png000644 001750 000144 00000002220 12574344250 021300 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs G G.!tEXtSoftwarewww.inkscape.org< IDAThkU?o~nvF b-xVQoē^'/݋"X$49)1m)mK $ib6?f~WV;M =T'M1- `|v@G d+٫uMO91[}⩔aDLnaNi-8VЅt0l$"`W$]!<cƴ qcxosrho ޲16zG@<5j{k6Fk)bq"'[$4uvPTICp&( D8Q9l#C :Dn1tWsO[ uOlS+[5BGA[}o79c$q:lo<ҹK/' !B`k™Aj<@lPe8q2 [** _0tfn!`[ uo]/RDL.2B/ND 4[IA@kg D&&^.9%B@EIGT XP  CP=I Ԏlm IA@&%a \7J:f27J:B0u'$) kx8j4!H|SXL&}R-^IENDB`qmapshack-1.5.1/src/icons/48x48/DatabaseConvert.png000644 001750 000144 00000005071 12574344236 022750 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs J J'?jtEXtSoftwarewww.inkscape.org< IDAThՙ}pT޻Ɇ&B "6@v$u(cglʼn@+3:Up)ScGlPSAJ6 !ٻ{?6,d!:gfgw}<={ j]XR0GC\lUKNdg8G€P`Af+D`0;xX,OJO܉Pj*(jP` b]gsp p(N?B-^WwV=Mqk׀Pk_(~)eKL+/x?OKG< 0›1tX\+ |BY.|X 58tXI\o*fЇTyNcb" wĿP,MT<,C(FmUhRJ\~Le@1,Ƙ\xquaPxh8FG& UnQJ9V.6,j <(D^ yF~@0߱ 0R*Zj \ l C) 01b0'*woWǭ2FA!hW4VۀL+J6KsP/(/0wU!"V?_%l Nk'v iGq1_"\1vfI2Dd7I,-6z 1 'gyJGRp =G-G%HC) /BDR "iP (U1pq8@)<py/@$׈<9`D0RC- /Gb <ۜW{@,DUU($;D|+xAC-T ޠ t8:G۲l~v[-i-f<_UoYz"c޼BRS]8ZDөt;Y/~;o咙(-ݲdo||˒%K[[ﰸ-˖@m 225+Y((H4-B!O?=]h6~b}["/gQ\өX@ ֭Ghj&9 W^Sٽݻx*B˥S\kPP@20LNvʎ?o`F>(iLssP3eeT La)io2pt\.S'"1LKkk/.+,K$`kS_y8:yyiL--9s ZS&M&;;렺[M!++3szS/)ɥ$7-q#=…7ƔwoDXPW׎iZbNsLutzUUxظ 55'3ZK#77TB47 xiZBm%L]ԩw^^>plVˉ ,Yr+l|5IaVo'iu&`5%)ee%(UUutu ]Wdf0kVwYOf͚B6Nuɓ]$';z=det]Xyv:;44tIgg^c&it. C#??+%IKsQTIQQe?%;:l7~i޼7vwj o8~ ޱcLK~~Z#b֠3W_%`ah,^L<FF򠉬ÇFwtꑉ$5B35٣=;9 벛zW@(9!ՋTeDiN²7.9YsӦe1mZ@g!ICV:q4p{`cD}u!;Wؕګrٸ\zԄTVem4PqP!-dI琓5ڶ={y#lph Vmfڶr8t{ƌkdQiZE `i+ 5Dܲ |c@_9S5*X\<ޡi Ӵ-,4-9~UZZuXx)2+Umس6]ƈH%";F^Ȇl6]W7۶"A{PTBQ IENDB`qmapshack-1.5.1/src/icons/48x48/ToTop.png000644 001750 000144 00000001606 12574344261 020746 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs_tEXtSoftwarewww.inkscape.org<IDAThMUuϣS3.,j@o QVEPc()7-&!jD){c(+"("($E9=.:3}=w< ;s.lAǖJbܠܜx,B/dlA[\ _\4q[͉_Jv$ۧ䕈x׳wbͼ3f> o<^9wUorF[%>T:46dLU Df1.Vo$(&ۆaI$`Mgp-Q E}}"b/r#6( P/k8@S2/қX)/-^sc{M<u[14WsD/j9>Ҙ_acf֕n Dx~e񖲁;Lo;jd "c7FBQU-8WqRC=@D,sx\MpPj[pTY~33mu卫UZvĠն7~lx fy˂{p"mǧe6e悲|e2LbչPچ3g DĤUc+e9ۊerØhhO!S6e.:m}W# ?uk,݁uJqSgاlb}11 k0Ο&DZߕ1-HfQ= $SXAUp @ @ @ @ @ݜFW/'*EkD1zaf/R^iNVqh|IENDB`qmapshack-1.5.1/src/icons/48x48/CSrcHR.png000644 001750 000144 00000004172 12621572226 020763 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs00M.tEXtSoftwarewww.inkscape.org<IDATh{UU\^hYSjf:@QPGIJFIðSuyؔFf#Ӝd]ѱAHIQ {wu̙wo~{=a/p ;xN ?_Ť/^]/l2pݏQ]"$ݻ|g^~PÁp4~ۍOoPOv+1𧘬r`hڟO%kWv=N#<3[x K[؇l:d 5`9FF F##iD&4=ҍ+ ȗHg0* ݈\#fW~#9Ԙ\[0dLa쳴m|x00"si--O0v N(Ƚ2ti}5R0^gLĆJyi_ "÷ CBxqaMie[hgC C\WY& J=473 ςHC%N`E9ط8inB8 OD~{pE%h :#xIqRDQ1DAD2uGxk"'T<6Fz-|6X>_4A}}t੨|h":=0A*L ?",SQ{F:Gdsmf:uW!GTbvy|TM@vsygMn抍uB2$_:yK#PZGobv)vw7v{Mm 5GW06]3*c񥢲Q#O2ޥ(+t.yNri섉&N V#a +VT` ~dKpwd< w Dϊi 9CyiVҎ ?:H1./h_y#>82gB8x'Y –NITyU!s_u(3ىƨŹJ1o ˷ҌYR.r11:.ކshRIHK#0'J#vV|̎7qvacb!pr 0*ujbQ:Hz1d7FɅmVL2R$XsRvj^-*y1G~6t"*0uZ-:M =p`U3M.iig-:{#J ,Oq.`X u˓'=OF:{Ir:ϖ'cK8-/t5Qқ\V Ȼs0;k]YEt1}Ý`z;̊M ^Wcҹ +oo%,oC( 񷈼P#]Z*"l/a!07z0NMpOҎ-g!FUm,D `T57St |Zsɏ IqwRu!6t 4T Ц9rvmπ!`1nX)PN{2B jaWg383@SG(_ѡ살xSC@.-S Kh+m-Q!˷KhA̅ǁ'&~Y^le x 2p|2kc<9{$eAlD8FO= xIҩ}'FjtQPqH*JN d wz@}<qy) z!жX  ;ׁp:jqAà.d*zVvTaajPT d5:qӜ mjJ)"I:Td~tfPɷw!PU1O+nԌ&`XYѰch5q(C)RGJ@+451WAHD@lI6DcD )*E V(EF Aimooצpɽs9sιhw}1bř9f۶mo+3?lz(3*8l+f͚iժUޞ[ne˖^iSߺ`-Z}ܸq7)--[?#[wh[zo?fu "Ǝ;7"9s)SZn݇k׮ݶtmۼy$7 "`&a1- krOfn 2!(m4FlFQ]LLunq3NDĴJ gb2580Oxo_jx̧;yheAN>~MD8<ʊ|WoQ9e)2@WcI^~\v1<|gj݄l6 ʩ;+^2 "Qͩ8jI< 6|*m8*_'aXrntFܾ܌9&3W46 'mvOhav0Xmf8% Ѷ$3})bT>V*:qr q\ +ibaсk&H`ްJWx(Y㕹]>$19&ا>Yf>xᗴ26\Vڰ:WVy /W܃jILHFU 5~ɘ_)f rFܧvx~i"3-@Eorlxu`(ȁm{|:V77)7_E'-e^r(Aµ;QGGh؎W4S@G^ku4i͡Dž2N>y@pHycՌ df^dMs4Khg&8 @MF>lʢwSu[]SeXȷU5`]F:5Ex91@2x*S\ŘYn{d?zQ_/gj׌' <' ACp_P|jd{d!@ Q9RÕ 8bV#W+pvߨ ۡkTxv?)笾BKY{bߨh牯#ŌP %PNe8PGY/T%}=:3n$8Zdl}ГVPJKVO_^f:L.2Yb0IiڸJDIԾƱ@Σ"RaP Hqy6(1Eaxn;"R&Lwx9ژ&|Hgl126f>vTqi(uxU܈k*Yvs3(m #.L׵J֛G'nNB?Xɗ.ьE .2p  [me!$/yP ÈL"2ʥ !.[#5me< z$oTq""b[مk8E"+ˣd?D= ;+WYȏd}f$rg]9V \NUAU'z 6Z600 btق5FLk Ţh$ġMWaƁ=;0{uarru q(-uR"Fd7"e 8Ң!A1> 05oSB]6l6v3U T~ W']%J4LZ*7W}idL wEFQ h3)_#h N/X)@ 8sl̢avg"F/uxqS."fLO]7 Hӆ|L7ˁ,s/ォ4m80sWiw~.5qTD 8Ѹ ]FJ8Ap{:ClAͳ%%&['@@͒ d K 6N%dBϤXJSO =2Yv \Eq?#tAԞG 4\+wA/!"Kc%$m`|haʛOljf-q]Jƈji[؁~eV;6F\v"I/7|sߏ{}s̴TQ ,zeY4rx="[0"⮅U.y9шX (hW8uT buR!܆-푈x(Fx5"竮<]~>ʚV5)DN5eδ&#bE'mR#vFD\KьaXs[`VtDr//vªrqF3s\c8&_M3l5;3csAq8"n-M4ffnPv7"UsP'؄׮'0c>p툸lk ">hZ4.eqhOnžhf^8͙y]' 2s>odW38ϟ5SLH>5B-kqab㜟-?)Hf&8k <3W|}!Ӳι~$STo}o;:Qo339Sj҅hos*k'|58#3W]1O8;f7Þ)SЭKEi[2s[ P`g q`$fn\< %{ig{~IENDB`qmapshack-1.5.1/src/icons/48x48/SaveAllGIS.png000644 001750 000144 00000001735 12574344254 021600 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 4qI0tEXtSoftwarewww.inkscape.org<ZIDAThKkQm.z*…ZPFkDQ7.NgSp%]uх((.JmEm+RvL$M̤Lμ}3g*G {)XXwsg]]9L/y M!@M!@MH(FFL<V:BBzI/P|9 wwGzc:9 ?#g 7 $3IUU9JFwZPtd rأ~[&*X @Z<F*`fS Β 3| |HjT͛Xĺm|u1Cx|T53X%3&ō T_ 3^ĝ;MYx/mU36W U R ɲp\};S?ӪF?NX׀N7 \>9 p j4D=y;Ӟɬ0=X $.Ͱ^h6Kl$L*6& Q5 X+C@35!Uss"Vvչ8Jbɭ/sZPU\XKoZ2-Ow[8jz(mk 1^3SUa.BX@]1Ec@LT$p2ix D8SCf+cn b5`Z*:ЛrUEZ]W9# p@k9'gl_23#NU3(8|2P M9jm([OcUIENDB`qmapshack-1.5.1/src/icons/48x48/Info.png000644 001750 000144 00000002572 12574344243 020577 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<IDATh͚_LSw?P@DFtKƟl۲h-H|"e]\&s6@iC4fDLK 81 lR`l`AZ-=ܶS4={r~9+a}=1a{ö: 7;`> 4X~ RCB\,3@+E$XKR,iⓩB 6\;nj0!=LQtZ[Rj^ct-&SfNj&&N1?ӧg?ofz;@ B|vZ~" AvL-_mm&F{{@U99&zc2UH3yyʽws-jqLs$IldF+W(*С״drJp xDgO)s +]B8^Y+s@yUc(/R]SGNQݹ&h%To7'Te26¿h瓢V_^QR-> 3L!0;a# Jh*-76v5͊V op XRTf%^oP &C&L[Rjb1! Jt.,FcA错̥iY<yVV$~M6-}f` +dnm)Of=RG т& <@6Z 6wCLkQ9 v%"(.\ǁJess~3إ8.(;;q:?rkܸdzY49BVk'իPPو {)ΚdnWïSUCUwo GW=;184؀)=kjg浵ˏ)EB:-݃&>^o[t[[R] ZIԊ/jѤpJjAchĭ[~}DU3gPl@CL8ŵk 㝝p/h8"YE 'CRPy;=k;{Wvfi|/sΜs~ݗq!a܁kq)Z5!҃1&)zS%Y%(a ,/NQ"M6fOI#dWލXчh/xvWE H D=5xI{,ξG(8Wx%Ž㵵yR-óhn{o h^% 87|=> )` , ]$M֞j<\>yJ(>܋"hٻ=ٟi1 $ң(遼d`>[x{ _NFn¡" h hNમ,ʺo` u}(i|P2 `~g]?U*6K4~Жgsp1e1K<=+9[3THy!3W|S[>T:p"l*YvT?D60:73 w @4 &[&2c# .g-<>66Hd՜UL4O1oVt9@ 0Dw kݼy> .TqKȒ%w&0F~5<&\:mNpK.{*qNwn !8p_@""Q 6;6h,͘wFĨh *X}ՇQ0(x@  ]@勸".{ԅ:~t؀8e1I1Iu7~a(>rJ`h?OoP+LJF a>6|5{!ߡ/(=9@!RE!Íչ5zʋUhi11`2yjA׀ruF".l6瀗n~%i[F".@=qD P451kP3:c=:;-pAv~u̙&Ca2|Wrذ9#2I]]×c1yx=ضvAqv̠>0 (,$qJR \:v)dЎ dΠ䝒E\nٸrcF*n'***lX,ӧO98 HMM t$fF>Ks$Ub + t_L >ǽ:sd+IENDB`qmapshack-1.5.1/src/icons/48x48/LoadGIS.png000644 001750 000144 00000002744 12574344244 021130 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 4qI0tEXtSoftwarewww.inkscape.org<aIDAThmUߙ}E)}UJ, MBT1ԗ t@Q16ĆMb AmXmKZMgfw;[gwgss9;J!"~KA,<*Zp>nr<ߐd3SԅFay? IEߊbea}DD`xňiH Ny1RzKIO€p碊GwUiIFx4 1ł8"#xԖ `{ϓ- =&pt*URX0, Jƀ/)M](Mƈ ΓnƁ{).5$ܬN{>iWt(IِV#*(IҊR'p aGB72bXÒRkqon=ݝ %]l[hELF$jLmӣج;mXO+A8 Qk}y[wz)uZ@S1v p6L" 8iEי`*R8%>Wvb+E`Қ&տ6JP\hgxX{}!}(E t;6W"XX`Jغȁ&"v |232C4.'&kcR+~'DTWa=>LkY3ͽ c-xZDT 9ܨs4/W%vx,E`{#$>tj≶;i>h"ڻ?&/?ib+/K1ԙ_[8]@w.ԮM29'eG2Z@8D xÙٚWR#;2}ޞ _̞7Օ /!<]?=#lŚ̼X̌>2o[ϰ73oNxaF(֗U"`F)17;Z F8ymJfPuPN=.ssrJ_&ͅ^Hx'Uw D|{>ZE,Rr[A}nfKܜL@Dp?1sܤ'77S! qnU 彊vh911#E:uj?Li?:s*gq_@m|V9m4z&9K qb'~ lY'+Mlfq}^CS_̍aL^@6hk6HIENDB`qmapshack-1.5.1/src/icons/48x48/Apply.png000644 001750 000144 00000006246 12574344234 020773 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsBcOtEXtSoftwarewww.inkscape.org< #IDAThyU_H Y $, (#*2Z:J̔::35:RADTYFQEg"}= al, yޯ󽼛tխstӧO ii#䑙OdBkxj-$'Q!xSy|e@TD ɧ]TqAnG깜VDLXn;4|i2ܧE.i}8ƛyGbd/\d$o8>3" y>"[`v֘٣ dfMAhFAÄfVv5k(y3pUg@Qw;zmꥴ^O/Xi,h رfMoWiL)3/{ D*3p&ƽp'vvغ˪1;٥N&,KviNLj3[RoEFđ?C"P MvzgaQfL;3ڌqLCk~)KU-%5w~Mf.9T?fnp:z9W.vD, ^M+!(SD|2"="ΎJZӜ@DRf|<"N׆8e^z xcZ \Gzە>"˴>Ȼ[~8qgJ2= ?_oh]O=:j{ȉ}'V?6-L6aAb[bq\fn@Pņ?9VD2GwQB=.a\>+.3se80m3ېMk1r|l1}aø.>p>x$kއXAR]@G\*羙clWmmpȀUQeoanU޹3<LBj7.lCf>K ={_ˑLf ms׌v#o?IҴ7 Q5Y{2s Pf.cT4CVO=C97%]f:܎<ЦzzxBɣZ51̒m@lc?tyq6G`-^Y R\^Kg6%T+Yy تf'2z,"9u |)tELODu*a! {^x]4ƪ{3~>vq-歹ìX;">G)>R:'378h3'mox*m-EmAx+:833/_!"Fnz{;pvI܌cL`\oRbJ4}_7P7CƬᛔ Ze{5m̘6D;;g`VFU۔2pv#x*"ٺϷ> P6\+fnrO5N4FxgRv.ZTs>']Jck>&aQ=A}0jՙ;(lڋzz/;wqdZ&\ڿ&>[LaQ^7¾ 1r +"ǵj0$K/̬:Lvfވ'"n9\ ᫙~qqfʝȓFh-*>oVGfntgnoS|~LAAf.kcu}=_ |hw@\Jb-unP "C,)UYxk'D'&ן̼ݍi`~ExY>ѭz-?WbGLy[)?^ 4gSD/&ޢrǚ9ج3~JR?@w.ooQG|o7#J޲z 3Wx?J놈8eg (iUqpR=F.2eDD+*igɵAi;+pQmZ;[q7w4ZO]fGeJWnM߮ [otw9ܚ,]#gloVArV㜚[ 6,6gUZ͟7O!z*[!_ ?Cwf~z.k/*wPf-K!2֗6u2oKwYg棙I_?%sO'&g!5=_8lIm~ a[$/9d|N +es/tg ľJ="bO|Vk2slg HkEIENDB`qmapshack-1.5.1/src/icons/48x48/Link.png000644 001750 000144 00000002105 12574344244 020572 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThՙOhU?%H`E]T/ԋR,MA)cƃ)x)d @5$)A*g)ݠC2?/,y;ޛQUl#R/_ OÎ4U֍ hq="4we˾v_t=;b]I|Dwg iDς.$y&UF֤Z;/Rpsj4m|"%mvF`WYXYQ\w `}|'*y>-`#u G~JϿ4D\`gURjQue#ƈH @5 `)7+)|z̺79y}|C,IYLLf8HFG C c ϛ~fgȑZiu+̥Ko@P/IT 9_*Y@;*R( nf֭  :Ɓ]*ڵllQn\DO&j1ov]eb Ǘ9}j)۽`V˗/sݛGN tUy:; >H qԋWD6|[%"z ox\YM (7[lmm'`z Hs;|?KBr$uAՀIF"qp=!9MR#!9 |vHٟ5pf~+㲒lԭ>~*wJ%dnDyy&rXѨ%D"1nw/33q:`p]qQB8Cg[u5tuf )});⫯F|3Yн,>g9bɻavDZc=c&::j8sGxy`p=)G"qc#Ѩd%T%U9, i9wH?R !\Gytm{,-r(޻UU@KsUiLS]D>_Ӌ%04(W'B8*&i 10E$1``0h0j*D(HCBi++lc]SfBիNrkv֗Y =HOvJP\RKts$c8v'.et>v_⦳B8N>:PUUʛoIkܾfii/ ǎV$:]l4Lw5vUJJ yŮ]/ «px:]^'Ex늜#'FVu]ǵk>j B`$W|ŋ]|7ڶ;ҀUpD=f W^iܹ㌏/1>;!SS+B9-g&PQQZfj-?MUUl$)Z[J?z(^^xJ =Ż_KJ 9p$ l/:l,\\ 3F^Mgg-fil4SR"5'lxEFFdɛ&jCڬfk(_B[b˘/XŢ?#ˈ0#NR2~])e-;d* WY^: ^{o`(JYY].V7pV<  ҟxQNnʏmL<0@CdFr$u 5vbb\9E <<22! ]c >:IENDB`qmapshack-1.5.1/src/icons/48x48/Close.png000644 001750 000144 00000002321 12574344235 020742 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<NIDATh՚kGTWŇA!` CÖ79$詇ZJ+zp:`[Cl0եun@B!ć~TDvG1i#}~~wfyg|gq'`3)'ľvTB|f!a]'-ȷH++Q.` ץ55 D3 w:0fΨ}塞=1 D}Xލ[򋽰B߳;|nSxGυ5 4 ĵ N@ɶF /LXb^ʯG 86p*e2]=y)e[5`},ULβ٠&ɚ7eYE:0gRR~>KciK\򂇀-&{GDc(hw4v))p %=JKJ)f=`A='y%}ó/bjګ'Nv߂m96Z$֕IENDB`qmapshack-1.5.1/src/icons/48x48/CSrcATemp.png000644 001750 000144 00000002070 12623413746 021456 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs a aJ%tEXtSoftwarewww.inkscape.org<IDATh[TUr4dfxJ%*2(|ɢ,(B*O. !Cڅ 0+5z( 28=3/ fk=i\ FdC2E5Et +K8-ص s*?؈Yc aNRhn6,,I/Mx4Kͮ18IR }L=DI!u+I.l͘RL3ޅ/]`ћܝD۝xp&nS:92bQ_,>E~RL9Y* /l|1MZ)jvTҘKh⪷D@=~K7b5aB ,z-(-R+r!zE~2Yɪia*s n?Sձbmkǁ* hZ-R i?{9/ԇ1uyc.!U$p ҮΫ0M@4z5YNT'XB2- ~1Q ,. N񟟁U""@ Lw-jO/d^~q3ߢi ZprX <`ozaFwwПqOccTx<>IR466a~.]Q:;wyzGg.ƝyuJ`ҥ jd8@-,!N/N1ukۻyÇۻkrO)8ͯJfz{zzزe Lc9YJE"qFGo|r&'~욺 gJ+yhm}h4J4e֭x]*@*ަ}kܢU:ӓL&{sLǘ!#˽blֆ1C88I`8$X{Q8&pxp(xݒunsXqAz2#m-c\hE ;-.e*bd ~<(lhX:2`V۲ [}νG.tʋ s2Wӂ2 * PyU?WP^S6ɐԓ%4Ay6FI{{P*I^+!i`=yj@a@H.a=i`0#Cz2SO݅O'ךJqߢײy M(ʆ?5KtF#=#r#D#=Tacј|%nu)eA;/6xfτOB.hA.\5x<܇:1o!nm BIՃ_b D\Ջ <1;%jץ :%Ym/Opm*NqXVOICڸXnڠ]tzJ?JKxz1,+&;ރcTQ|n&lGXN(2ǂ["t} ]: 5ZlW51FG74 vt+zN1*`!& ݳ!A-4dXY\ro!=kxq=~!+Y{fh 1뛉}݄~Nv7W!҉;KW-LBwsB7/λD~Z}XĴsqmLSmlvctڀO)lV(LOZs[\HleɂUu*m^YM^JQf.4ʣ$>;)S<oI#uLN.tĀՋ;+ݬ\E4b&+^m'oߵLy_1>mꡇ g+k.F-1&?ǹ.whJ.nL;ooݥK 1*ƨ7ƆC x.ßjI5XLh\]^*X(=|?^K)7C觿qbT_k1* FaЌM!H_|u Vb$|XN }hW!T*X/q'cn͏UʘR1R<M&tƘm;!887T͎!S1foiw?-'a!|&rSfJ`L b"[xܘMK= !Sdo ̅E0T%*e'HFC*:FrYBh; FqD !7K)!+0)O>1poő*B)CE+-x̀ !7RpXD(B]V-O\fJ#T͡LMHK({a1ۋ c8E\H\(Vlmuf%gp747L7S!Fh2B.e:Z*?5xG^|`H9[tb^ڮIENDB`qmapshack-1.5.1/src/icons/48x48/Reverse.png000644 001750 000144 00000002100 12574344253 021303 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs4tEXtSoftwarewww.inkscape.org<IDATh՚oW3'T( Hx( Z*@]P.dE ;VE*R]B@AUPTC%Тx9]q؞#c'Y33>:9jPUNsڎ퐎!|E$k3DP%/ xWUڈNAȉAQS(^<cDdhQS`oYkdGal$xոi:90No "ep߫ 2?3N#Df1QrbW1v+.30Aƥ5b^0ODj5rOypfu:ڢ]D-dU>NE ГF^+"?8Ng֠pTu ,\ZNg֔2?rOAB K^0CEd;7a80v$RBDdx_k@jKn-d | k UgB/c-`5멡b >|7{iWըqx~8Q 1)n]MeU;18`=5XXv+pju҂FU_A,5GTO,WW-" b/P){)&kSKu'^1.]&һ[U8;jU G ,+k prpV$YV >w oXKYlrIENDB`qmapshack-1.5.1/src/icons/48x48/GisProject.png000644 001750 000144 00000003624 12527654570 021761 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs J JBtEXtSoftwarewww.inkscape.org<IDATh{VUz5 Xڨi31fR̰1sFSpPR&L)4qBJB"Ospyϻ w^k=^ϳ"x@jW#Sx*t:Зp(qg҂GZ960p6"-擸!y)zTj<&b_ )=R:v[ f EÐm!, _a ՗2^..MVʰy [I?*3#" 밁ȎG[b>ѐ􌅣8_tm{[E{{{+Cb\4ɛŰtG\v)ZB7Β4l.A^]q1խ/P6xT@<7?7ݜK > ǔ8#B7S29h,RNFDD͜ԩ 4P^ѸIs98^;wJbE^5 7G&9?C~BEĤe \#H̿!ձ&벁F'RYvU`?YY CWFX@gD x:[d0|yc\q8@1^U܁:H)3J键Iq1z1070/ZFdV}eYWńXٸBh2:*Nϕrc_{ŃPTq/kNi8Ume}bǞ:8"葒h>88 )NgevE1UWetW/YI&:R( *ۡ2YlJŚ<߽V}?Ѷl+Z$[nژR(TO[MO-V~Ĭ&y'l|)ϫG$8S315 t ]R\S)WxmOI6+Ke't.bn"-x3rJiGom[cK*;ǰG"40moS|yq%#M!D4.߼FVT~by/oܶfƋ8iá 孹f 7dO| yY?48Z@;K,Ѭ٭n]RF x42Si|b 4뭷n-wD+)ءC<)dS"y~70|c1ԼUkJ}!n= ݛ֯Y]̀ǭwzTmt OdlEUuE}:RAmfQbudǫ1-"69ت6jþ%y\Ϊv:2.`jbM[݋(Ζw#+j l(YP|cr,Q/clOi?/uv geSK/IENDB`qmapshack-1.5.1/src/icons/48x48/EditDetails.png000644 001750 000144 00000005005 12574344240 022066 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org< IDAThPT?!#(45Tbm31453ɐRm&ubYcĂMP㏁A4",(.r[VE ә;s=={SDgQہ'-9nq=#-ejWz3+7Xu@" /]jz**.R^^OKK>>18~##] D^QrrSTT<=ݘ0NHNcܸ( gׁ5H_l6 ]u0Nau !!F<1-46Zhlbsj8**̙*@> cwDgȑs(2FDx ]7CMMVa2Ub2k@ hYz3~_UtN6n1j?0wFCp*fMr^**.:7Q/N"S*2xfͷb˭6df#V;1*۷P1әzY+"r^A~ UBT@!Q*@~~6ҥ}l%w[mE"E 248 ŤP>;iiS_ߢ~C{ch LLWÆxEZׄOЀJZyQVED |CTU5h>9s}w`4\`„;onmf(^-xs0i$B}BV_@ ܹcx8m;ѧ׫/\_,;cv(7*4E=Wwॗ"=SINUo~>}YYE,p-tVp`V,:x`НܸqFVˢE[;DUfݣNjj4&S ;'8$̼k&0"R[]mLtl1-.*y2m:IHȔ_-EEׅ #A>G@vI3:=LDkl"YO>)[ Fߋ4 +VD" kJD:ߧ͗_c&,k]<}Cgiy8Z2Dj"¼)h!=d77573svjdʔl>F ;^Yـt*q.ZS6\?ZxddiAǴiLND? 1J7CfLJ6 48g9,؏vu?Z|lIAg_6|9?:U6ts͖na6k>-HQɫx1ъ p`00%'G8}e#CzƫN s(X[g(`19u*g?c |Z=)bs.9멬l@W9V ..hu u hk0 Hٙ3'iOD"|+" 2di)"DD"DdwAg`E%BKEDi[+">]vV`2hζw ISkSxdv7Ӧ܂jv1nq\i۽Fxioa'-$"UDo[F @9B|xRF#S_'/ʏ/Kо\W5p Aj}?Nr3oV^#Q`guXఇcomcGx*dM}ϋx&"ʍ\IENDB`qmapshack-1.5.1/src/icons/48x48/FromMap.png000644 001750 000144 00000005730 12574344242 021243 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< UIDATh՚yTTGի8!Q5JԸ q(*'F4 l[A , :Q4qD- jDAB7PnhSzuޭwo0' hkX$`NT %@2UAw=U=` 0.C1ڭ ;91`c +a#p ɣmFДS&: lNw ۵+O? !=|xEyXlݻbiNIGS7EF 駛3ؿ,۷VB5*\hfB=< |7ȑxU76NW>ͻusoޭ{ @jj*2f%}NfRY'40zQ(^xy'dJ pY6MҕQBIHƢE$'y!,Cyrr2YbbRiҤĐ^((Pׅ[B0z5]*Ehx H6ת҂]{:4n ӧؽ;Swp`ˎ=V ztؑ$™7|+{=ZCW9q±'0o^:]QzI~~>.."(ʞm)a"н ?g-_}uQvm , Nb6BZkU\vf%ĂZ7@ز%\ ͚5#->yyPV>ǎԩ|+iBE~ zJLZLus ) ER*@0d2eK~~㈋s2WAX=Ξʸq-=}nnE2OYo®Bl)k< zZ4тg]+-@ 36,!Cq֭}x 1t/\00EJ$$֯yۭ>-999̟DXXFSHl5֭ dѸBCoN~ xnHiHf6iR5k| BB!"" ;z||a9s]R +/siƀ5 >(/\T=VdgC^^!5jd9 c;|Cِ[[uR~xasqq*99 ޫdع&?PޝGԫW55ǥ40=X))go8tB 8q(117PU1:rBh,cƷ&٤i>T Uy X$ wMB E\yժUK̼Ftt}ĮLxLdzO;3%'}@W-V.HiM e$n()JNܸYl הIX`']hmau j#Bٮ~b"*$smɔ{xw s2k ~[W8 l̖Ҥ;rqךrvL}) 7QH~4fP 'E g2yKi6-"zIixxda r(*x@k{ݏ|bz+ҥgJǼW΂V@ma^P&)CI`Z l=QQw&)"5ٟ_.%8uK,VKU‚')WKGY\٪1?/YS|''(TQaV=n=3IUh7>5K'xqyƪ$@qL" _L^(n4> .&AI8hS> a)ޝ8_ss͗"#,.bi$;4:2PmTxZ( rp!`n{a}f̝ZŶltKзY6r<1C~m𨀌<ˣou3;*`y4Pg q_,MeT,@ʩ( GVV;v\u}߅A||{FM.[]U4w^3gtmb16ePO:uJuB,!z' v gF#QQD8>'+ ʾVIU*` ,8@A #IENDB`qmapshack-1.5.1/src/icons/48x48/TextRight.png000644 001750 000144 00000000625 12574344260 021622 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh1J@q"E*-4)4 6  VK{:h%NdAL3_="/XSd~y2/\.n]+ A|:5k3X;}_߱}`:K.zTivlPuZlqX xˀׁ׀ZB&n"DnKs`>, . ,Pu~1q}T!GxY \yTUc/"no`!f~TsT rnXLWu4F4"@ZLQu^rBlS(6i^:-" ^,Zǀ;U@W~ ջH~$a]΋"`$_UH#x[ci`Fu#,:۪U Xd\ qpM.r=[CHMmiʁaS!3$}1:e<[-`+m(1|Ц Ȑlbmmw X7U@gTI6ýw:ψ/=Wexn.uue GJJXV^ &7C7i]}X`"LnCa޽,Tճ۹?)*Ugk# X+TMK|f^tA?Ō//?{Baeн5OWL u^=kI7ߤM WկH\[>o# ^1~^RR9<@߾۶@=U=nɡpkF&9-'r𤷽zu0cϲK&۶yV QH1Z<@NȕWҪ];ƅلJ#DaEmzp]X)a8$1@O ,bUg[Y!o*qiq⮗r>?qiѣPYɾp-&=]~hAf]ZZʁ Sո\Waڭg &$j6mOt  Wu!"1{6=GL>})&TT=0g `hu.fΡ!;0@D ֋7XxK jU=I/fӀ`"`яаoDĽzu(Puւ93s!DMn{T_Tuc./Pf ͂Q /;0&L9i|.OZ~Z#@'nxfwECt\lZ+T ]`egt||04Udq- T8v17W46 ).Mp qv~'0 L<4&Km՝P9 q;x kUyNUl|}e\b8^ic%@Ugvs2r:FͿ'0;"nĬu2\wvL8a:՘ LRv{^{}|Ɂ X;uSDkn Pu>nń1W?A"veƺu^ aR .L)7A鎬^{yS†xVjNsYb`nL~-/1>y~Ǝ]™3!/RXvw1-; %Y01{8JͰ 3֬M~ٸi8GX`kS}wm19sOs6 $^U5V,v@^ޅ Й .h֭YTVVR {=+N6f疀DL87/cށt ӽsC0i!V`s YJػ%m`50ۿÜu+}iFа%qaiL<4ٍI`qs^h6Fs*`P'=r%݈ α,>kIENDB`qmapshack-1.5.1/src/icons/48x48/Tainted.png000644 001750 000144 00000003550 12574344257 021276 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThřU?g]7]K h4ZH1KMbŚ@bTԴJV+yb@L,QkQE C7bER v6â 1uo~3sf{̜9gFTc `TO;<~ˀ.bdžF'&nT5c1!<6,Hd[=xLj{`( U%bmQ,j%WIRV!E^Xƈ%b'/'.!8g"mD2wFWCwЖ}N^חj 󀣞0x V 8R͠UByj`mɪilY!b4CܲKpm}vwJuTP?d&0ArSb}8N#^%b4{u]@:8۠-1+pIjhP5UݲܒD"M7jRm)8"9% -`x r T[c Н)3fV鱗U,A KA pxxWOȆKUze^89Ky` ν>FU@/?dгo%? [@>\  }اj!=4pYQ`)%ϨM,r 7t08τ@ LP576XXj~W9QBj C v'QNq-< z"D?s`{yT9q;$)лI$|T @ tpZ% m`y(.#@U+䶃4~ l e@9XS Iy9@=mT5@#t^ԣBuA[g 8UxL2sc@kqa u UkU[i8x}u:\úi13-ĵI}E;yN?b b}a^QvA ILX`:r<_iG"mT[ދ8 (yJޠQb(ՖMPܕb1 \"66l+>TH}}5#Dl=^jT:Rf!P~\J 﫚>} ׽1{CqKu+@G޳OfRUufzSU6<~E{˅/U- Y'Y9.$*Tv xNo,ntp0_}'pΈE"bv3eO ˫T+g_ۃ3M8cTxz>T=WX|ŽfO#w:?G\bEq'.< i VFWPo5%=~#W@Qȉy7VR$_֨G~S"PxuIENDB`qmapshack-1.5.1/src/icons/48x48/ShowAll.png000644 001750 000144 00000004132 12616741641 021247 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThkPUr^F`%QH1EQLC$UqIt)(h+)fTF*"#&ȣSJ4 }{;@Pf8Yg^01a cz\EV8 ~8ĴnEJ;TDqEYY7on =O<1;鸹,i PJ=V" &ԓ]I^^h3}%4T3خ{Dľ=nUO<ww'F=f6nj١lSJu<9jrNwMy;ý&$d:MeðL& QPp'oiͻ;bR~#"把W 6RO]]V*ԋE$QD#%n""wјls>{_$3XQeܿ e۶sqV"#o3arybJjE7.7&s초$2jypgʕ=_ë3]"Ugʕ[Vv^ziMWV6v헔6pSdgfxb- lƽ!rsXx&ڟbcsaG⫳YE 89 Tn|efFSv3QQI:tu<ПѨ'+k%(,#r`k@Jj%v* 2rYY+1 %=υ| Nkػw911+|)wEd ++vc2Y4<@GSڭH]{8:jˎ&\PN8u*p9:`}Od`0ؑR3yg5hjꤰZpu5b `2Yd'I}zz999)wBX Qv֬MPVGD&,WWl_Z[o0cR)J䄄DEyVƎ57Ξl0_siaٲuc0ؑggZ][lnZF;j&i!<1YoRL)eb\\ ׏[D !! ZF'bm_q8,]M~/5z6aal 995k DWDxy}lW\;5UVU$6**%xFƿe@((}*QM&s^Vmo={J@D+A!Q^~?RfٺP\]hjfD/ED+dڴm6J%JTήy7|%&m;645uԉHxۛ:ݶl)d"677#$2r&Swav.]…rs(+\<۷/?#RJB؛7RS/o_)?N;ao{hi颦2/>MB|-z 5wJ#Z xb:\kVQ]ݬُѨ'4ԃh_}rB@Z_ZׄѶZ*+mbLfptg'<=]B@#[ Rj0DT`1@ 1mj]@pn;Z<ۀ.-m1a cFpRIENDB`qmapshack-1.5.1/src/icons/48x48/TextCenter.png000644 001750 000144 00000000627 12574344257 021775 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThڱJPU:d]] 2|_7 8:u tnm]RzÅ8?Ȓ/p }$@kwSs|

~6+aU~R~]s~Dx9{ pu=$0q;dlR(4w@u]lSԴuq+ 6Bm.w 6BiFĦ]mڅNĦ](4Bhش MP߉0%0!cP'IENDB`qmapshack-1.5.1/src/icons/48x48/AddImage.png000644 001750 000144 00000003014 12574344232 021325 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs emtEXtSoftwarewww.inkscape.org<IDATh}hUu?Ϲ׭͹|ٜ|)$MʰL*4$ wWpY0" )|)^KKtlsν60v>D@wz4&QAlQ ^诐@$|gᰌ1`\Nc3j^3&"wX]ڕM'1hf#0DwdͲx)z[o%" *x5N$ #sV,o9;~ e.YF'$w!ˁ"WVByNUd`%˓):+'d<{b Lhas|?1gL,q=Z$jӧ&Fbrq:Gkw-7K=fئ+U_mU}gc^r{[U tӄ̀qsAC^ݽzÞoM0(W:yBx A<Ղ@;f' Q>lwm T:EAXPF XT@$0 *bh kz\ ,ɦ tG,`l`t+H" 5ciȉ;E @"m]tYQz.eR1W[/w>V` ytq&j!`G.6 q[}@'`A?y@]"!Q?58a1p.f 1wh#HvIENDB`qmapshack-1.5.1/src/icons/48x48/CSrcSpeed.png000644 001750 000144 00000003737 12621572226 021520 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs;r)tEXtSoftwarewww.inkscape.org<\IDATh{UrD4/e82b8ը64Ckb.{vY "Q̱LdU[I8c9 7\v؍{^Vf|fvxy9yLھAZ%lM$[O<+kYapne\ @؋Տ%FS{`nZ f>W"|{0NF [c^&\_e;nגh;p{1M\+GJ /9Wvr|R<18`GEOx+!)Ci^QJ@XO-WfINzyXpI A>[9ؙOڱG_{}54cNcð sqf\bq=hɕYYXz+_ɴ}Uut-K ؛w.gf)&+m$\C8C|}qgD.WX|t#=ƇqL:P-4kɮ ֎71OL Kr2a)2<]~Tf8ůbR/?8^sK-W>sk5b9|i1xgd TjK#/ žԝ@X/X֜]lwZl.j[fT *5Wds_&<\U9E+q%XpG%k'?_ᖬ*2az~ bxwR!n{=1Zx0Vyf  y 벖?΅wb+qBcYv>0sҗcAN.أ{pq蹤aIENDB`qmapshack-1.5.1/src/icons/48x48/Help.png000644 001750 000144 00000003352 12574344243 020571 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYstEXtSoftwarewww.inkscape.org<gIDATh՚mlSUYX&t8A̢hDё%Q>Ec/&?h~ 1bM ! a$"&/nYant]mI99sL!Rp>_@C+^ƶ (jAs%*P@;b R"@ ;0x<*YL$|v#>v!tup}3X', @s LJˎ!p7>V̀N-C ~)) XCP ݐ% ;`H)T@a.sϢERT4BCCp۴?dCZ$^Jcq_:<1%Kٸ1/MNKѣihirvss2hΝkUub+HA$'NeOttN1hs;%P u͇I}СŤg,EqZTc/ kH\NrHY5gZI:k(*XB6+* Vex<ϦW_4-x .Cuu]v;}ǥK}X*~v^s7x%/+}˛awjjd4tJ'be}Ϳ?89u=*9.|uQmcgУT_voe3!nܰ`t-{ѩp-kg]k ΅27&*J<݂M3z|5fRZP%Z .IЂR{qSYA7(.3x줇Z@<H)BvBKdǮ]/MHD"1>;eL]-(s QZe2>|5kγۺoּcHL>j`lWsr\V$߸2 Eq3LTq]^˕Z]Y&B8.+c @uHAuGIhӰr)aQ MEfZ[{GЎbX,7Uq-[ܼ>ݛ.4ZOVjz RQАA[[ƫ61㑅8ӷ7:h:YGNBLW ~8]_H"?{oz7ۉQdIENDB`qmapshack-1.5.1/src/icons/48x48/ActNone.png000644 001750 000144 00000001455 12616741641 021232 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh=hSQ/ JA+ AR*(n nE\T5) Eš:)8tR)ҡyroB;|?/|&vF&# mtq:3Pȁ9HQ]t5> v4&yEYq7֙2l`#U8bxM8w`풷7Cf/p7gh¬x5<\W4AIih)xI$T  }۬\L5a Dq#>4i-LsR-d9`IZ3GLOSge=*\sH)| TꭥFwG#z+r𹛖[&x8W!s>ߖ0#k 0+CepµcC\~ J'qIDYi4^ƁThnV@Y%_EwĶÒw5)筨Btm!VL(Cc+mj{+Y(ze9d@8;q/_vGt,8~}kבj8@bH5EfV[2 )is6~IENDB`qmapshack-1.5.1/src/icons/48x48/Down.png000644 001750 000144 00000001652 12574344240 020606 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsYY2tEXtSoftwarewww.inkscape.org<'IDAThO\E_fBXcT"!*h. j?HK'4*A QQ` "!%FSzgή޼C5]]7կedCOp9Ba8Wu,mf]=}VC]sXg8=yCFo#mc$m16FH@ h#mc$mEZܠZ]f|Gĭ'縪^ZX+:O{ \]eJum;5 x6L4@I-ǞnD<KۺFo̗:ֳ waf{~R(?pof~vQy="{R,۱P]/wd-DWS.fpnEwx  =_-ȳ,h0^8k+zrXf>,A"F e4"gؕ۠gdqMئaewܞr*-SYr렃e[33gn43O+bVdnpF_*8 #īX%/B~̧k+"zdI)坱({YeIIke+n? (vd7CVDąX6+ߓ_t]a*3 %O4a2ٓ<\{/b?V7zܯLd"{5ᜈkϯ>V"V#9/(JӨH#IENDB`qmapshack-1.5.1/src/icons/48x48/PointHide.png000644 001750 000144 00000002227 12574344251 021563 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs;tEXtSoftwarewww.inkscape.org<IDAThYMlE1%:HN E(J@.6qr@B6F=DpP7B!G įh@!DvkBjYY{SɟogvfĽ;ԋF)h PADA ; : }&u~}'9Y^b`fO|>f`RG6Y?6H (C>fy?ﴬ;\yUwFn&10%vdB={6oAwiW+Zmk+aZby׮]HXڽl[Zza0{B͛! 9%ZO/E"rW~S p1]:,11Py&?҅VcjhYV=Y`={g`ȫxC2<hGF+Ŏg]ֺH` LԧO24a?Cxܹ/|`̿p>g$?dNѧ`SL,%XJ$ } 8`,AM_Ӵo6g.RGͫQ!īnm|5rÕLj1A߬mmvڈ,xQLL۷bw+5(FDt6w:D4 -Ub_/i92wbֿT>w;9iڅz+YZu,g̼fY1lGFF N 4u4u ؉JLP,k" hs~S-˚PWٱJ*2]X]NMR)m 1sQW9k^ aF@~p)?866fIA ѣ/xM0sO^UyUEBxeJmeyU%EoK*mHeVל2p,¡Sʶ(5 p5k3A]i4JdRs!D8_oi? K1G `l]sVAX0V4H"@o!CdoJdoJS3~?J< AUT} _Bh<Ɓ\]=!?Ø0ze^]hIH߆MHWpZ1FGf!, `A8OWݮRF[ ('cghՋ]ݿZ$/@^xM^$ 0jsL?i0==3B :(|t{SI zO8bN^bِ鐖@ Z'CVj;$[6hIߙ$ah]F 7= LSn!qzXjVC3Q7od%2Etoas,}<3b/ȧb4x哠yG6йڿ^QۭwBؑᏐTTQij!ԟQoc+x<3Iz0MF G#Mw70v |qS=xT_ K (>ngbpj¸m֩gE 7`qYgBRbk A JOHn\= wٖ^lP {Ėɇbb@kQ}3y7o**'?18%x{.7P,7LLRcRKU@2k*\#b'>*%~S27%~S27@JmpkxY`}7*IENDB`qmapshack-1.5.1/src/icons/48x48/RouteSetup.png000644 001750 000144 00000002355 12574344254 022024 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<jIDAThՙ]hUoMcLӒ&%^ĂHM*hS/ J&ET/EPB45B/Q6@`XğZ6n68Ivfvg;{~sΜ9`(VQH(0Ю\ — Z$Z>VPdx .w UTp/7= )zp0mf?LOY< ҫ7\ ^. `ha'1zG^Hubim?:R~.zUN[I J -f>C \Q|xxR @I{H|+=jYpՃezh*S ]yv6z!P7<^ `0-UJ;wXJŁP[J`LK[߲A""3^L](-܉giˏy04 rz1@_=bFh[;IoeQ<-~0R7]+ :u9]QA8 i`[#DS6[?ԛܷOoʽ[ @M :) tOBAhDYҭ mq&ƑҔ[H)[dB4 󹰵4;BsakPr `YWKKdU<4,Dg a)'BH$#5zP6U@ᒪj(qGe`U䴳$I = E ~(D٣C9S`S XVӧ@r3B>Yjs/ $f{>t!@&݋P9gl! _명β_hfHQN$kYr/NIENDB`qmapshack-1.5.1/src/icons/48x48/DeleteOne.png000644 001750 000144 00000002155 12574344237 021550 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<IDATh՚OOGcTT$$(Q!'n=szWz(~(84H)$@ I^=l^>^fFϞwD뢔zXf6EړEDcccĄk-kTJ|RHPc|jƘqweeE,-- | c6YXXժtyƘ3`6 |itWWWSdzzڵnD [kdqqQz6bQ}" ڝqb ~nߔk:WDDޛbvv;mӻj5O&ZW`[pks8o 2w`.w]yء7%ft3PH2 sŀ\.@9E!#yi'(uUjEztcM <s9{ޮ$(y BwҔ}7EkQ7@C<`ev'ܖO!<)"L9̡5O[riE ޕ!LmzrE.o!(/9AԨH" t1̎ԣF({9(CR5%]}"έDťL1 n{ok  %oշ(b^|ɽ "ƒOg-7w ? a` =쟰ap>IJ9ikx e /},M/ju)IENDB`qmapshack-1.5.1/src/icons/48x48/QmsProject.png000644 001750 000144 00000004074 12574344252 021772 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 4qI0tEXtSoftwarewww.inkscape.org<IDAThՙ{?gv.c[6җU>j,hH"e? 5-R,GmiS*bRYpey̷;3fgvTνsfν{p_p8lnȴO@A 1-vぴ˴nAĽt02r | m(L3KF3֪5 l5#$_ `f /;<…Z̀QL,'l}/hѐMOCc>kT^ TaL!ͫg{nCAy^ @>t^1 yce9'0*<x١]@\ܙ4Fvc߰ǬRS_%1ct55Vd#Gv6? ]g=ވu6P@ ^*\ x58/@vWWuvV*6=I`ba=jsmDTG˜f~t0 }tJ:WW8qy9S;+IpfEf}i\/VJ7Cx=Ge@=I Ɗ0ifWHOшcopǯ ժI` k=ہ8QZ=(AuRugRiGɝQx;B6zi2A90N :$y\Tj<$qIXifO%Vǃt,* ܑcЦN=$afgw7<~ < icI%f`8U,wJ.r d N>!^.iJ si @׆WpeR:3[:f 07,p AV` \N5[{mq0.zr)n8Ta|7O-QJoH3 fQh(`$Iz'w_& w$f3aijhc`"؅OHE1}C҃⒗Im#p.5I$Ƃ.] |˒s.'@x\rTn.]Ӹp|lRK+C%eef %fA?V=#Og  {WCjT~BV垸e2:_Q͂/Ac :5$%`R`v{/R%Or!Ԅ M'o!g|0]J,sd32s̖f3{̞1YfV꒤>#v wZ`|.g3k&HZifSpgItr /u BJr9R GZ1I7"o͂,`2!w3c6Hל૸+˸?qt ,FKD47(lu OWGɒOl~ tHdf%S.q 85äeј9*1xIoJJpʆjRId΂:89\]I\HDWp!U|~T2p>:\&hGz8L 3ȭ"t3߸3AzfpPԢ!u`w63E 柂;ZLahL[e4΄!dn*C; uU% ^q0&y5l撉F\q<l[Z'V$@ w Mz| d?7Wl)ի3Cf-` %B\;!}Vc>R7_RqۺzŽSCs8ohovr|t-R:a$i CHc$)0?pmզJ` @VHKUس[.PIENDB`qmapshack-1.5.1/src/icons/48x48/DBProject.png000644 001750 000144 00000004305 12574344237 021517 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs J J'?jtEXtSoftwarewww.inkscape.org<BIDAThՙlS?6vqB 86!C`]UuYY+UJ-i:@l]nզUZmm 0YQhJQ! i _Ď}dzIl7W{sb'OT5("?gTA R Eq:؀c~s` *5j|j3c?"lPNPEZRA_̐D dcc-PS}{$=ݡ\Df T9\^?h*a)-i ~Ą HԝE8(H)LAR3@[lb< Q7+_*9X;qƔKp>"V(zk2+&ɼ1TڄB̵$wz`_ؚ98>dDqpfV;$'ZVCy4vd\ o: '(%|["+;hƩůq4yn(VOtL@f}4UQ\?%lStE[]S^t*.h ;akMU()1s E/f]YO6Uw1!mf.Xql MaFQQgb蕸=47燉l{S^fħnbb782~Y>ȣ 8Lr?Q;k53|G F#@Y_U%cχJ |S2WMf9ё#?(=*yisv !}mv"S尓@q [x$FW%3ИZ x|{o %T>Fƻ?r"p̕ߑ3, rw܇pOft%y=V v+X8^cԷWBARs]^ʬi^w",,K.Va._Y9q~=n*u2xNT{@Yb&{yQ@XГ@݊=T 49bM3;FD KES1*2\*d1&l /\l ]X x`{_J͂|p*RW7tjHx ܵu:*InZp?-p:*TK}Bbi)\:Dm O16v`msՊj:;L<@OO#] 46* Cs)87mpIR65e^S65e^s8 lll[$I8-߉7% ɤļVV;oK^,,3<sjŁ =>y2S};ŗ.!HXW̓yqR 7NބM@  FCU~>vΨ q֍$<(/ cϞ$~=QQF^ڲ2s7W !!ȽHFCǤI̺t8'A"Νq};52ZⓒB-(+ $]-\ڲmc#&}[ϭj;r$G}LH@RŒ,v܆ h5ed]jXJ aa ԔcR`bcMwe ^nshjhjZOkokqf*l݊O^8xx3X??]љ3 :ԖkդY))9yb 7.~~t5 J~hsyx6He2zF/qI::H_$0!{{UTrF!)"0!]TTqDjg7֖)S 8{{Vѣ\߿P'FSAө+/e;2dz;sJⓒxmmx^~=?¬,j.F֛VIn. g(9>s"%D$&ܵKhAQ.]HذVմ4J\s|<]6գ<~P>\ظ^gthӌWwٳO@ГhY+wr2ÆqL::2 [Ǹ4|z6hQSR<~̣_~AѴi|ʻw=x1&wc^n}:e^_kW-++Gg"j|}jc,@Qv/>&Ll?Nk{77ʮ^eئMb1ח{_pQm@_Fd i\ݷ˩f 2pƺ:\Z2A*+"AA+t2fv #mhH$x_qDPScDѠO;/wdUI}G]omB ޵+nf)T`NùwuU A pPQﶙz1J&7SpT=u;.x!^ @== ;'__a5S~g#=*ң5jijls|ل񋍥\ش J YM-0ax\YiT=8{ww@6eWCDDS_A}j4sq#4W[s_FnldUN.]ʶFe6l`[L bb8:k(o巎^z:bb{Ϟu/u>luy#Lac&3gZZqͦ  :M86>=&N$t893蓦xS˖m$^ݹ\NU~>'-ȌWU!f_X3#֥ ^9Ý''|5??+[}fD,Z*\9s vncƊ~m!?v"L.4qVNyi<rwX$`r'ut%,ZZB&j)x'-p?.gBuޝ>s[7t(w¿}}yTP@}U;\nxZ H$surj*ٛ7S[V`rԂDHYc$\72ޞ:dU/q;O,/`a."1gXmz"Amjl(iէ?ߗ*-htφF`ϣ AIENDB`qmapshack-1.5.1/src/icons/48x48/MimeRMAP.png000644 001750 000144 00000006041 12574344245 021250 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< IDAThiPTW -Ȏ@ #n DT\PR-D$ĨqZ*H*hrMPQ3&DS&DE\"6,M|@&U{{{F‡k C4Y[;nƯwNg3wPr\sNag8'o~vVfu'ؖ?x0YEM gґE9qjZY*}K:thuZd'QL]kY6\;;Pd;KG/.5]ێT45b-γ땔K7 8bBc[#^^ u*[e2TUnN_)P)-߳.bS}2=u:_$2 , ZVh5, XXU,9dWd#ȫʣEG9Zt8L(ҠH'pJ;%{cK!HsYYԌvѷLT7Us./mrkprFx貉EZ6>B㕊+H%RG{b1N9 3 ^Ό!3 eŮ黈G&C@:ο[3N9ъݮh]北iE]NbF"_G ^/A "}"Qs#LRRpitKm$HjBa0'ZPeQ^3nhsB[G(?S/k5С㍿lz+_0J1 N&D+SO;$I$MHP^M%s gm>(L< unGn>?rܤ&܊EEpAO$f=_\KBz6:mrk222h Twg+g`.5'&0'5 `ifIBx'o#"#C^U-,r*sprӉ2m4Y#\#?{?)Rz4(ƾYIkG+S+̭ d]mIYy8-cTؗA7٨7"7iI-HjU"F0{Qa.gُ#g?}ƒQKD>=w ޙ{Xr4S)D'za)I!&MF̭8s֓:'ъѢ>>T6V׼Hn.5Sxoմcc~y [z.o}NFOQF1j>τjIrUUZuS5v5V@ivz\,_\||1 J93fff!L䍓3vm^:W}|[i&)4 %!'cq$5o=v}}x6G.c/=1vd 3 z)y)߱c+:&jO-He㤍!WkW~z'ڞݦ"7pI:HH]Og8*Wɱ:o݂0_W7UC͵y~33vfaB`A8@Oa4.^V-VqŤfB*453T(kk).ƍ :->>ڷuȯ =ZUyWam!UUerxjU4?h5, Zfd3J] ONtHMu密v`+)=JgIENDB`qmapshack-1.5.1/src/icons/48x48/A.png000644 001750 000144 00000001655 12574344234 020065 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<*IDAThKTaǦ MDD AQ>("""Lk٢euivm!Hڴ(jWXŠL+gyvwyy9=wFT5UF@I+H.(?S ^75Pv{w*iD #Q5I;ȷ/WȜwD?gH؀HXhH֤Ө@OUH4"a-0 ,U jHZzI$5` `MI6̱[H$#"/#TX|#A xr~;xT\ R4mˀH`sqY$GK  sƙXDBT81{Lƙ6*J[E@T51 U ?g#s$"ٯ, GDQ4EV`S}faQ-HkEQ[*8cum6 6 ZjPvyS=EP==-580G>eNpX4|ISg {Y8p}w<6ƅ0-$r&GeiȊPUܡ<*0uL@:ĄٺrøE6ȬUOcWG568%!ȵ7ouj(k_jCDI+ʠ5eWGF^1Vp pZ@7H"%2Ci\;^zyL$<d̯ y*WUF@p0IENDB`qmapshack-1.5.1/src/icons/48x48/3DFix.png000644 001750 000144 00000002477 12574344232 020623 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYszzzStEXtSoftwarewww.inkscape.org<IDAThkLSgQlacRD(A tF:/x A3cԨ%.S(x tX*hGgҵE\Jzir<&>?}Ԙ|1)w@ D +턏X-& ĚxXyHP%Qi>T/408 (_DNZdm2 Vg+Zۮ#lʂJ,ià ##͘fFPc %B3`KcujL&J+-OKFٔr5rC8Ae+Ru-m¢EAFk#]{4:'3$6[K?i4.̒3Khho?-:MK2GgbdAO=avHv]E}̯22z + %&|>*[+Mϭ= h <>>Vw_ jb?"]7`^hwi9cc}P22w%TyaCjg-h=M{pERtr`䅜Zz dm2:N4qq6_i"1;Ϙ/ҳ ٲW@ҒNzRyO>|þ:D{oKm6|& KF[(PAQ($`ȪUSo<4[^nj#3hsElvttydAtvr|F?eYβ7JbǼ}]ЪɕEPT-GRNDt&W*z1hHˡP(XoշwΓ vZ\.Xoh~iՉ$%$15u*Y,ZϜ s$^K/XK6@YU^ظ"^Ш4LJĊ+9vdޭgB PSVCMYMP T^ddpcHM]zr *W$ Q'(/7}~Oc EHקUkW.g{&[G xv[a!޾`q[0;ܷߧo/p$/_OwO:j,0|"]c$ ĚxXk^%&,|R(9ЉIENDB`qmapshack-1.5.1/src/icons/48x48/Print.png000644 001750 000144 00000002212 12574344252 020767 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs N tEXtSoftwarewww.inkscape.org<IDATh]h[eONbRiZZbS[C  CYN1܍0^W?V9m2ڢl&Ifڦ9'9@s ;lm&w<?* WqUd|LM R-34t.hQ=~Np46<+ )?Yozl=9*c@L^7g\88>(Xg%q ۋw[[MMܸuX'^~}Keɾ%.@txq9'IENDB`qmapshack-1.5.1/src/icons/48x48/SearchGoogle.png000644 001750 000144 00000006057 12574344255 022253 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org< IDATh}tUՕ^# )` TUt:BZjkYÀk"WuXteZpQe23uYt*KD)h@DPB@$}wqoBI|quV^g>SۀkA$S=vHw" j7A>W'y`:P As!ǁ  3qZ܁ :GU?~jv6`uʁ6ê>ݍ=d)p50{$p8 qY@ˀ6 ,Wem 7d&P/3;뽔G-U'8pɳSQ7u>'uZfQ; ~Qr[DU@QM;1Ed\* PtS@Rq܉޿#ZIũ.69$M3iIR3[9@hǺLTtb܎_a;v#"QF&#& 9SUOJ89u0 N~SD66Q;0d ±p`A9u갌E?@:Y,F厺eL梜""W4xCl940E9 M^Ғ לl^pEٝ"28;͸KF x 9LucX0& 7 h ~/5w*z :cw'ē_g*ZQVpuGG jĹ&}, Z T5T6X[YjcY#TmXLtq-)+qr,7jmSm<֨UJU6\UgG7lǶV?<>ƖRnj>z=DM~exvUߋI$DY{mW[`urqszlEK/ 364|>^p:k,v(_U2cTeY}gvbis?H㖙;ddոMcsc|]%nc4j"1ʓgyf2U5s pC5UtO>["kI@>V= ~x9 zWsC!^c_C;_?+"O_&|vi`2 ZiGmく*"NeEٛ3P ȁW-P3'@,/R# L©Dg"pA[+JϕTY[D͙:P+P7χT!nE7̸oOD;-UsSeԅO-C^(NioQ&ǺP|J,)P< dH4G`k?@qrGY \Ku+4|'ɆxCkI=RAg+gx 5otx ~bQw]XBn{ \Mׯ_dQQѱӧ 0jŊݽ{|8ptv |x{k<, _ ZA+dh"[KP[GXUE$I۶m˽҆Mf0_;}m{= =] H"̎gv}O|JU}J; 7!yGiZ ػHDTUt ਓ1@UC~i_/cF ;2سmeY"BgVԎ~rЛ<$G% d 2֬@~_7mV ^zŋs+bo%~¤pוwucuǨƀiߙXƎFO KIsޚ0dB0_ɓ' ȯАz`Ʉ%Z]a9=¾P9 ~&"+Z&{w&80oӼc\TuF[[ Z9M 8d!od݁u'R><_tqotA}2͗vqs)|rqz%\㇁)JѮ!j9k3,'>1wGᭊxS_{rID |{<ث&ē@IGZ{U ] ?n_spn}q׋󻞗'ZC20sL\;'vE~XT7V=n̷bv|[ۅم.B`dW6yn&[q$Yc7l>Qq~#'Cv0sscao l(òQisPkAIENDB`qmapshack-1.5.1/src/icons/48x48/WptMove.png000644 001750 000144 00000003001 12574344263 021273 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<~IDAThՙ{lTEg>xbHR$FWS.K-J$|AY5X BaYkX6^Bsn\o6#cohjqf"n{"Z }50=sA[X;BsЯW'9`w. ܖl+c%Q܂\Qh >1\|e`T |x' 4|p~+Edwgw\Ip7hdf`7}J dF5AڝH&KpKkudF`<0w2@@ϺZ|H"8ϵ3IW<$5GFzMIRbH@k|mE.Tp<$B@C%) \IENDB`qmapshack-1.5.1/src/icons/48x48/AddProject.png000644 001750 000144 00000001775 12574344233 021726 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 4qI0tEXtSoftwarewww.inkscape.org<zIDAThKhI#M|(|Y. zY/1*`YA<(xAPɊ+EE">vרD%y$g=]_T}-J"5`^鉉J@/L)_MQJFe5p(Ț)Țo '2T'lhcW&lf+{VCjFo-䘋ofݷ3?l kF ac1@GBϛt~8?)>w4Eߑnc cK'u()Hd/@&IsodS؈XAG;" X~ T7fn@VF'`)bø`J lc?πǃCUsX NYa@3pxj>F"v40һ U5ɏ Jn@.NV|GS) <*">VӀKU=4²MP5W%e@ pk5"UwB'Ъ6p8j*1Ji@Į /vC|N$̏k6bf`b낶hP[^tynڠj\Y1'bkpU2`3 |@ njBAxDۀk&浺Ͼp>ݪ9EDl܊4^_(zp_q!3qEV#/x+ہ;nT͛BĆ1@_VUоWX|j@É* %9\"bgʱXj_߷#4;Y䀃oVIJ(`Sp8T*m>T ZtIENDB`qmapshack-1.5.1/src/icons/48x48/FolderDEM.png000644 001750 000144 00000002157 12574344241 021442 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs44\VtEXtSoftwarewww.inkscape.org<IDAThk[U?Ձ:RW E& 9etn#*mfummb[lv}nIMIĶ7륐~9wOn* m--eArw?k6UVnVn얥}ՈrQ+>g等c |醎kX\cr* /Ý,hie]T*&̍e:M ) `~%xYcsF$%S`1὞,sw}LJ_2V9g'aYcۙ,$Ksx.iO7R<q~E[€p ?D=:9V+XLOzCd.d}np@|܏AS~Mh 0醩iepD<> u@F[w 4C_ߋJ0?-eB_sWXI#e*@N5=ýeCK56#5Н& ~!f%7L[oqH)5~e8Q,nv ExSy AW(|# ndFd^{p"*ވkQ_t CKKtB)jB:w2BK7Bv643BBYk-f yv>Lr+pxiCwuV8F8aT")}J'U$z#2@092pHfiW;׋t`Ep.\W]l`H7қn(_q$Rr4Ԩq?4oժdK%Q ZYzHX1hO90 rXIhpr;ܛR-JvmnVnm{KC\AIENDB`qmapshack-1.5.1/src/icons/48x48/DeleteMultiple.png000644 001750 000144 00000002540 12574344237 022620 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<IDATh՚Ohg}-T-P<RУ"੧RD7IxMK&@ n6jhkIul&f?Clޝ x6ЕL&57J:u[nvṮ; 1s'jhqg@,hppP.R±cǴG x/g۷oΞ=r:;;uIqÇkzzy577e>>|#w}:ͩ tYk#I]]]%?Zk/cV?+HUW.xΝ*nkkS:$iiiI:qۧ&ڲep]*#G(_bxxXDBtZJ&m6uttS׮]SPxẇ_ 7x^$>}ZN:7nuۻw8/Q 8~ $sNvލ12$yCSSSk$=x ^I`|L&\!9t$▴1<. 3.ĭhj&c@p'^M.ߍ#1؛0 쟒[KnƪiCdWCaIx|s`i ; H cAҢ8WBrA;r>= .lj"jU (y] Kw܌IטV0a N>paVָ- Kx\L5 H; IIw׊XgwXmEKC9Ml3p? O(**l \Ǐa%\+IO V{SRVTc^TmW"ʩE/`+y;32›Nu++ ۏO,vOK2N]^E}a(>lT_>6 P5arhÿ)(z'h-`~#5 U5MPXeu'##0P7RՌ"m_c\&bgXxѻe7vUG8w6CX5JXL]ȅ[g"Vp]R{T3qfCès59ݶz/97dn%Q%2ݷX#bM A CdpA6q 22 *nJ 9E u (4͌}`+{V.(C0f,` A4d S-mi^D f4=.dr~*WϦ䗐OUs96{DZ {=Q8rp~74L6yTgQ (|.տyf1/O^4$duIENDB`qmapshack-1.5.1/src/icons/48x48/Start.png000644 001750 000144 00000002360 12574344256 021000 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<mIDATh͚[Uu-%L,/"jyJ#H{(bIASh:!Xd&idy0Q6sfQ'93s}lS)H%㬬0/JYy @E/3@W4 JU,[  S0`8mVϥ@wIRLU20 s eۀG^|! U60ˠ&I]f F IɬlCCRҰ\3k@5uhT.Y5dS5d2HΨ)m/J^+u/$in&=Q |k`ޢJ^ ."1FI![Tg@I%-D) 4~7u' jWAjpTJ.ESHNL7ڈ+[H^i@ca@2vR>/b0IO%=!0;)`@ˀe9fϕ i$&,8`z]IKnvAz5I._ RZY-,/0hǒ= {)O)e/ F-d#x2()o 1x. {< a]뺠%wKAs)ޑ>Kÿg alt'ȑ;h q>掭cx/Zl{b'O%w_Q&p0-D mہ^sJ/dw%iLv™:H :,+ݠ׮o|l]8?*#'fk 4$Zˬt8OeNs٢85SHi`s 'b{~9 ||fgX/~ b{,Lf[B_.tdρdɜ8?:H:I_< iB5P1HZQn7[h/ ͅ0TG1BYdI6b`ɉWf0T [jPt]o{_U@7vpbтa~SJeIENDB`qmapshack-1.5.1/src/icons/48x48/Area.png000644 001750 000144 00000003545 12574344234 020555 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsG2QtEXtSoftwarewww.inkscape.org<IDATh}VU BB6RR~ !*M (d8 f&VZ9YHMf(PND9HVd%diFXPFhp{{>0L笽{=YV 8]Ec/hZ1\X'pĸ[mAEqZ ?wE>.Lcڌ_RPEb 'b^}nμ74݆8#};2M vV|j8AKIلiLELl#1؎us[ 4xއk,o۰(f{= ֗b6/|Ff't "o8uY8?m-:7_ωގ[0h01Y؅A~tb&R>$8)3I[jpbń͏XQ20$f X_b 0ԌSc[,(p>J 3.cx $FZV8uzq1?=KW n$!6 a =GNg8}G}*ɂmh<+32TT \Ye_ƬRA[Y@% bT~ek3R[L]%w$g o8͒Hlih npwd(ܘDY-3ya/ݙ{y/*pf4/9N]䏵 !zO_*!]1v[r n$0)ROq7q"m1fkf4E*IIY@Rp[iL/z!$1Zj0br h&.Ԍ(Qu[~/4[cRrD10cn-ݍjx 75Vc~=2 3d ]ˁcRBY%:Y-tv 1+5aRB*;*ׯT(0KZ^֛r'c3qq$SezZ^V;`gj.$HIvl¶N1=`-/st$^vǎ[qDf6=`^9ޟz'4^v9qJ͘ill80$"%]1]gf:W3--xk;?l^RkX 6'3Py }zf"Uplǂ nr,/hc/Ԍ3é3R|XZ.X2澉b\v~z!ΓzSes߄ ګkkp-n*})kɽr(ޢ 3p@,׌+% 'wSgphfMaAb;pfT=+vYʎ;*̭ @/c>BYe_?.%U$7*aG-47ߐl3a pIvJnΐ>=sg)t|T:;<1U"3ZӭVJxjz ]/aZƩnK*.,NX()~;E:gy\)t$Нa̙・=wf2ÇնliACs._{͗0 x@Am͞˗Wf_ y+RY@z'PXX8y{ZtF jkzkŠF x  jz>pVclLzd]&ӛŋY6s 9~fw}7 ӵk0ϊa0̀ĥ2{$0Bk?~#8JMg?`X,Wܹ>l䏟(w]ZZYYXr* |nضm>/ҽ*̙aΪaSr CZ/2cj 91q:HO^U@`ʩyUm65鈎BcooMVV47Xg]q={.+N*5unMLrr"̟rcb&Ntnf?)99feE訡@BIIFQ0Kc ',̓sf@+(*2nW911S~qfcP '뛚tv7/;v$DDL//G,-=̟?ܿFn-1Dȑ^nXYvwdg?jSrr:$uDDd Z@7f[0NϩS7ȸƉsB "&2rRld.n|XV@yA8}bH  Fd/ZFNؕ:w;>iu/b8FqP̘1Դ==EiHAIENDB`qmapshack-1.5.1/src/icons/48x48/Path.png000644 001750 000144 00000001364 12527654570 020603 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<qIDAThjQ3tʝ.|B|+A"Dхt!Hd,҄&Ð:Io.@I8s3DTqL sހMiEʂ X2;Z שw ujZ>p(xݒunsXqAz2#m-c\hE ;-.e*bd ~<(lhX:2`V۲ [}νG.tʋ s2Wӂ2 * PyU?WP^S6ɐԓ%4Ay6FI{{P*I^+!i`=yj@a@H.a=i`0#Cz2SO݅O'ךJqߢײy M(ʆ?5KtF#=#r#D#=T6g=w}3;s{Ai⯀?@ 0de6Ƃ-؞>2/_biOb@Ŝ"r6Aieax/"ZkTiEI|\_X=Տ Ku5ؼ3S<0 ЧKI ֺ:ւIHPK /vZ .O1b>t\]{عs~nH3@,?z|SIIj`ܷOD>XR] v׮jj۰A]Xt7;hzhCѣ@_{:!4']Ve4"UžFko?>Ng[VL|<1܌g~b#GSYI|I&bʈ_`ሾ=SY 'N$#&9r=ʘyW~өSv6't٩D6%U$vMN |mmk%67Wb ЧWϟ bhL;Ds&[ij z(ѦJl"JN_GG# n`@XFL IM=H("V+1)?2j },"O 0i4uP fG,`pTr3>)Ywp6(ҥ>~ť&KLfg+tMꂷM1ސee}_%6[UES552"U'{}VM<}}\V'1^Ymmi70{0_gbm U7(Eh/ISTΜ!?d].٩ T\IS[C[q$lqe@nY{ܙ0Cٵ~Scx9;'&t:؜NϞ#]14IIwt7%@0q"Ol'Wg,u h׬QE8`K޾vjűcsu+4w7#E yp80]_į65Ƽ\\K~}cЅ5Y:dIld{VVFvˊ*\{ Wv=D#[&4_ɕX&y*%F(L h1# 0z;F(M9GtqaIENDB`qmapshack-1.5.1/src/icons/48x48/MimeIMG.png000644 001750 000144 00000005410 12574344245 021124 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< IDAThyPG?3000 0xQ%z5*GnJMV"&e'oR~>ry/5 IVV"aa Μ?co/&"—ロNLŋΝ&ॗqiف/ fCM}).%.n۶0lX45Ⱥucز,EEU+t] E7߼Lf0S(uEaa%&]վx{;Y5JvMޮ*tխ;vZM  j[MϜ 'EnbTOW[5B.EFޤ !CHNF,1jT @^Hd+WutWE,!@EE{\0ah9~ .N+gRP0ysKNIJe#k+l00+SvL,ɹ^o0lz~i޶4{ݺ|*+ [r`ܸh4Z~Fc:Uիw ulO6Lj|җ`Ҟ3f.ͱf0s فrRSQU&o5LQ(de4T">PJY4#Goe#ùsմꬶ1cF55V#G3n<=&++]&sH痣vO̯]*dgb׮_9s9Ø>]͎E@ϓd}jU2z{/rB5}HJkQ><OO)wu0k1kVO',L!|xrsC$j щb%KOƌէ,dKg$}(I{^vTU5dR޽݄oVLjiiqf=:nڵ:8o-^^NOH?"#0ΠZKCcuu;wpss嗏0wn *k 9Sa3ffʔTU5Z(R.^ڵcHc r|y"[ I^o.{y0qv]{Rڇ~<",O#bcE{-(2t:=m=4ݻϣT0d[??$5" 8jHMÞ=dl" AJi.k^E70sf$uutO'O`Mܺﯱ}{cȑA,^cJ1aB.fa G\19S~f?M}Ǝ폳WXXCMM3*.MNdwNF1{>҅E0rp;~zMƚ eхټ9 =HOW"SW[E@YdWІ\ < {o$~˗kHHbѢ8AVN= \O.c k+xW*]HLCz޽{>)/טμrN* 7&gGGd32"r~m&NU"':mmV⑽N77uk`6ۢ\KTT4%e$xKx$z*{{|ץ'>^e&{eJKF9"σwٶE9xuK t_ꫫ_oybͽi"{|P?>0^KT*GB ڇ#̍iRnn 5uP(l@|_k;7Ƣ\חbcvb͚(e+OLsSԩe{À# ZL{#s!SqrTjOCL&*JIff46/J:t iipuu$'u>RLl+VNי+r)(Rh$ YͩD^^9:<=7>VQ_ߊB!c o>z~ ;q^ .ܡ.R҅C-1uETB P>8PPPƍ#6xTDX+4xhIENDB`qmapshack-1.5.1/src/icons/48x48/ActAero.png000644 001750 000144 00000003313 12616741641 021214 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<HIDAThՙklTE*0V!F$ Q֊5>>5 ݽ]1>!(`@Zjn{0mբ~9g̙{EU?## `@^A@z#) Xׂ< zHhӅK$F]U  D 4 i.N`0R)[<!5T d+d,P}xcN@lnr-Zz`woNi,j`"=C̻sCĚS ;a4-Ÿ惛Iі#AXwtg X/H̼g"Ȯ"V! a ZUɃQ V@?"s;NzO9;@d5W:Nx::ȋRdx|EM]Vn̩_C 3蒀>B"Qg9yt]O~3m 8>2x_.(d(hkmsp%`)&agK$4݅h%j7\ 7; {@[@FNT= 4[@6Bzi :(Ɣ dh5H:p--f<% 6AêK8feD>P~ X-R6=3P-u^7Er3i.ᰫ֋ܥ@π ^% LkL@n6ƮkMn3V5.0\t-6GT4z%IZyzXagt#"T%΢εP~cY`XmJ~MApHk8uFjTuAy%)b/ug=?Ð?pqG[R9+JTJѤdJhtSRItbLB g%uNrRAAnfAAnf67#b; V ]OYE VdgBpssk.lcc3$'%'v|vb֬ẆI6hf_=2<}=va]ͷ}wDy$q?"%xH5|_'55u޽s@Lj<_H[Od4'oGmzϖP( "'wۀA2%\``.!;UtSuuqv\``2M,T={Ĉl /j}eq2 e dg{ʋh>LvUo_O!4Hffª9W!//ӯfGOBhJ#&}[a|Vy`?On<ڵhKmmpNhS1>>_C2x(:>>sFdeԇ@ijݪy~1FRu+)08S0-Wrpˍ.O,|(nF4!F!c󇒎ݦD_<Ѝ釐70ḩS/bҽN;7RogD}=fte쾔"/qo_Y p_IENDB`qmapshack-1.5.1/src/icons/48x48/SetEle.png000644 001750 000144 00000001607 12574344256 021067 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh_hQ? Y &\PB.v1s0+b䊈](D1B )w\(7\FnkXKLIu{p^'3= ysռa ϸM7>֙_3Rb=ؙGa6Cq y38]m#h&› $oeZ@ v2ԙ$} ,d&@S`h? V Tc EpaJu:LrFil[?٭H`\êݎm?A <c7EmEOo)'4\Fq'ͥ-C$5 *b(>9I;Erx1VnB<㳜T`^MuE/l1'ǴUOAVob콨f!F ϵ1dI||$7* pmaz#<;H]Tf- |>qy\5cMWyRJTeco !̥<{ ^qXc5!# ay>S:"Ɓk'!b]Tn&fB$$|2n˿11 yWX٠ew ! c4XCC" p4sݿa1\"r)[fŶax IM+YKo\sÔ.OkB¹gsBR}>|H:I14kJvd0B%nd̐}wjhy˧,V#B )H?{_0:5qjK(oOAO$HfNK_ ;PCs85<wrb6~<1DO'V'@!4&C9l>Oo=;cϽʹ_B$V;Bm`EkJ?y= Qv.Hu#.lfo ! B(Lӕ\[u/QwT@rH!uX)#z$0_oS< wCiz6FPˑw=6ԵpF+_cqC7+b# 9'laKu~LʩmCt%#o _£iACoPZc]p\Z={ON .J_K N1Gs)1pHB%=W$"R}g e͎M18QI77ӵM=IPv8YwdoѸsZP`tI \\y~t4MoHA˶]=%K9%I ~0Pųt3t cӹoqYʦԿ6DBe1Ʒk3J4RM~L0pL G%ˬ} +U*1p?(,?+Ɖi4 /NT!CE.1IENDB`qmapshack-1.5.1/src/icons/48x48/ToBottom.png000644 001750 000144 00000001644 12574344261 021452 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs_tEXtSoftwarewww.inkscape.org<!IDAThK\EɌa'5  AFQ񁈨a0$B7 q%&p!J*>At%Jԅ݈ N4㢺۞tiCMݪ:wL܄_}*sUqNw10ShŀQ>6ኊCVcvGU5NdoCw)ah` A340h4Cfh` шnEˠڈEp(j1OmԿ^+ݰq438F^Xۦ6to1.Rnm]>Y+"e axvfhc}%fqնr_GlWKWʕP5 ^410X!p1Ŵ֚xX-DBicCϙY>̙̐33gOdgo]vds&nq p'VR uE!pfO'|"b/QH SaI# qy[qf);O,XQPi:gNؕgB)\;uXPl#ʬ/,tHx*l(fH3DL7=\_acAtp}ƁIfʧF ")j| ?B_x?*ɟĎi|?3wKonHx/..?ԗg3Agbnb -6.xcj ][*ILRM@\5T)T@!Og`Izy%Hn8K#qV&`)+Pוi '/96}.6KtP.U&jbsD,8"ⱈ|ghl@o..7_omܩfnCӍmb;pkߊ;,Iv;xpc ~#gq}okHGDl1Im=@X=">ރ\Wѱre҉<~ql7=O%"b)էlTεmVOSgbI%5yI#~Z8Lޛ~2s4\ʍn\kZ8RmcM"?2KB.@w?g㙹'΅e*_֬ OB 7|%ƕ a GӼEc\F+Ʃ'ÙL=ܲ5=V5ov7jԑ.6_Sq^>J0{0a< Z ͅxi>?Kw ƘB|  T"IENDB`qmapshack-1.5.1/src/icons/48x48/SizeArrow.png000644 001750 000144 00000001447 12574344256 021635 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsFFztEXtSoftwarewww.inkscape.org<IDAThŚKnAN%l+bFX)", \ Y!HYt+SՔ=553]Ujg뜣0Eצ9` }+5?} +l |\=#h̀ X|xP X|xP!/V>3\eXk{& _B-DrcG h=`܁Ƙ;rWpz8 W(H`'J@1{bGu' H@(BHDFsG`%!1݆Walk} dH" a  #rz0! C־9۾AvU:B/vOl?$RUB;q.~ @ ı!txe0?su%)G\m%-w~.:s6}cp=_Z?kqOC{mI6y=^|ah}Ӷ! X@kf #thftlz &ZA B"^RjCDu!FiRb(=TJY P*FTă"@j%R 4qʫa锵>tC~V?<; -$]|e?"zIENDB`qmapshack-1.5.1/src/icons/48x48/GridSetup.png000644 001750 000144 00000006367 12574344242 021617 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsߔ%tEXtSoftwarewww.inkscape.org< tIDAThY{lTWz8s<<㱍=6Ya-vfm(Bش&*叆8Ҷ"R$"&m(+EJ$q̣Y ؀}@ i;<{"| [oZ3` 2&-~@6LίF*;z?^ 4bL왼0Qu)'p`^flLe>02q ~ޱ771qMc3\$KVm~?]q](yjhvO6,?Nsls- o"*obYD_'կz;`Ȳ]/aOg0Νr\+쯭^ti % ."ϟ!feec L&,YCkѩ˲ ۶( d9(###>۶a (x<ʲ,eMG~KD7ec\卖eLD)ݫh^tˢ"̙3!I JW^-F())A"@ssmOCYkǿ>9cl=1]W 1;rV^^9G$A$2% B!Fi> /7­( l] Kq/|EQ˗/zꩌ*Y222^+**x":::F1&bgr|>8q")": r#@Dظ^Z+Z`vrA4a|>̎jM&i4z!YYcaR n9rڷo_4ͿM(0V!Fr)NWnMÖs99٢$K,X!n f !L$.`˲fgg##rr,bW`tMn]r1OM(R'Q.Ӽ A `tmv|tJ')`_ؘ/&D6& %&J.;x>w\gc|woOFc~K7zUU`դb޲,Xz{chm vPBQ.Ǐ{ʲ_Mk <9/<3//o>k׮ᮮUޙ`eY^as8~l2Xv9L_iڶy)'O>]Ka Praƍ.ι 8FFF(;2lxxfB0.\섦i@̔U0 jժ:;;-8K83g.:'OĹs.\yڶ2":ihhobUU7 =4m G}$Iڐmɲ TTTx{zzgϞ7JJJԊ 8t4 @SSp"' D;sQ_~uׯ_w_=c^Ap18O'|W]]-~s}1#Τ7sNz<x<7i)+@ `~ 1"zmӦM8رcC/^ND rrrwEc-oTn˲mvO X<@(6-?kkk}`6Au,hhh,k=qo̐:V{VkrJdY~) _b'Ceec8~GO~LXx}ݯUWW>,c;cTBK&PU$iضW\\1X,֭#ӧO'WMM H$wބiOx;F$ιm۶# =SޅZZZ677gHD1ckڵD8b_ m… EEEB(B__"5` bid<7i}׻}zض 0¦iF(0 lݺպ|o0 e躾F[o 1Q¾G 0d &BwNEDH={rJ p4MDt… $I?BQ_SPPEݯ(/n9RLVW_+Oxvqk/9@-yz o|n'e?\kdb~002,Z4ӧh/`i#3.|@),,D,Cff&$Iի}L _8sqYDs[_&SpO:&qQ,GFGGps~hɒ% AXux^/avu;vm_Z@D)<@.m*a.FLӤHcT<|xq\ͽm<0vDٲe;3"DL.>8555pDʻ񪪪2кI8`>|f5u@ZPPZ[<5nw2 Ҳ cLey9seYzwe,tݻ`F$G5M3,+++=UUU˗u4&7%`u@Xݳ WJ=++kXQ)}s>s3tnжVXKSFJToΪ*I*W"눻Erv03,̔Cu'9{4waHNǒV:]?:*v^'/) Ĝk{'m? q#y,>[hJ}m/<Ry +C˄UQKּ:z=م}SЍdω Uc$+ 9<\luxӤ?9W}һJ%=,r 82J& )M%o7Z$?C˛A'7:?cTJN9x&0a ڗ1^cu0N,Opa ϧ$w L|6q~K8أ6!F?E\Kb @\l ";0U%| j [nfcZ¿Kd䝙du?gG 'm\Fa<0<[hDeO%.IENDB`qmapshack-1.5.1/src/icons/48x48/TextBold.png000644 001750 000144 00000001346 12574344257 021434 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<cIDATh홽kASHE$6vBlAA X(i,hQm)DH~a#Bb&3f]f8޻wvd7\tTN"B8;_sx8 ,cPۀ{%%\H)UI=)S(qŢ l x7ëXCK9Wq[}kw5p?tv8c)ep$JzlC8.ЕqE l ρס: HڗG\ 祤<@)"k!p:V!Ъet |۵Fa=p^ %5#׀#s Y-*g{ĨQ> PC*~sĬ^H@L0 WdX=!`X.f(N<~%-):hF~ORU }zj{6"˸b~]5MBꌺq2:F'@ lZsIENDB`qmapshack-1.5.1/src/icons/48x48/Check.png000644 001750 000144 00000002560 12574344235 020717 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<IDATh[\uϚBЖۃ"Ep3>@hIy`EnO[-* ` -x h xiIh6pf̙a=痜kg}Dfm,mgOb}}kPG4kX-#!?gq{#ygf,%m!UMb[N8A]|D~!3ژ3cS("N3g9^ui&mb/%ԉ8uؑ\^TV_c%Sn8ګfǑ|.YܦG)^ܟ^'cE{f  ?q%<U 3wbSp63"*{%U_pց{yu%"Ʊ⫕5؞Ocp2/y/x낓? 1Ծ)"~9 ךVuli{[v[+q ,d냥p!PQeд܅GanJ%?KP0'[<,(&pWd0e/Tϊq Op WV-3&{Pu>,5 O,4x9DZƩ-3A@3/U,W0xDq7Y-3}Pɿ+çA,`kD,8_W1= ƴu|\8RrO<.m,$'8P j{Sua&"ornqlYdsqkGcy!3¿^Ndq 6oNۣ],StEZؘz=wS{Nljс[UO@zNDf'MPmTVo[> M{-/fo|^W@ V ڤ+ ¾cꪾJpǃ:MK;T膦ܪ轊37C<3ГN&p; ~tۤ7/Da2oHuDMڍȢ H89Y-nr!^ˇ6ڣF M Vƭ٫WIENDB`qmapshack-1.5.1/src/icons/48x48/Device.png000644 001750 000144 00000001471 12574344240 021075 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs N Nw#tEXtSoftwarewww.inkscape.org<IDATh횿Ka^jhjRkL(ц ⠻AGHпY ":C;ME2IBbUHCjX.igL~|| AXqou 6OAE==rkZ۟6LͿ+suWMLNv 4Mc}=鵡xAz{]F8Rb br9)hoo2a{̦0ŋm[jl[jlB~t:[ŰWffchM2aT; O\+L->EO cE@Bݕ'noMB!3fo"p E2j`+##^NNJlnf)UkBʳ3<`oX\PW֬BJ%$I*p}4?DuyLHj$<hO4@U5$I 4S;Hd(tu9?'@`u#7Wb_j%bguPU£}NEI TDrfB]\L>_FEDQ$/?XjI]62UX^>f|FU&~njřIENDB`qmapshack-1.5.1/src/icons/48x48/PathOrange.png000644 001750 000144 00000001335 12574344251 021727 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<ZIDATh=QswYWeUJPZo(X;a ~ DB, .($qY:9gy397DT˸/suc^7~wS9rztаʤ +MX)"-Zi흸 S.s[.b{,"ܫ,qLQ;rGrhs&kzIr.5ȆEKb}- u!xT35*ew72.-2vP@kN+!g/BœA"uS79O:'@4{.FhLl[?2" +r8zV d#l`y[ȌgA"CF_Ѽ< RBЈMt,0@2Y4sKc +,9G+YX?D$k0̔k:0 DJ²69&uN FǴ7rPMu"& ,=F "S6T1aᆣ{uCl;opJq_EaՠfƼ:ʽfIENDB`qmapshack-1.5.1/src/icons/48x48/AddMapWorkspace.png000644 001750 000144 00000004560 12574344233 022707 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh}TU?aeAU b=)ѣ{ҥ#ͷ=kMw3wq ;a Ả3 30s!?Jts<=>e$!d44 Ӹ+Nv L6qKk@R">XZ%  h)K?r'V)@:007QVVMii5ee5Vc5a4zb4>cbL V;Orl| `{IM 55`$+u@HO+Ni 6m:|s{J"4@`VŎ҈ |}df>qTR@b6_`:sз*m 4egjE ZyY A<.oʆ$ƍr /)Г3 VMmfk^UcKJqdd$3rd$Y8 O3+ٴСz>Zq8 aѢimP{X`a@yY> bIrEFA/C{wK0-{ `p@~?e̞%VbB^?##mo@0/Fr Mێ% ";{+ ~[VzAxdf|ԈnByDqV;W6RI|DBB|kpoM7 xNSݶm2aa| f}NsێT*Iu铞<^UrڋhYVj~6xO9q |˗kٺu2ZnOlysӓ?_j'%/cƒXh@.4@j0qb{O_-{DnJ쎆N`{3I zP^ArV6խv?wu#>ojr=2{N>0dHoIIw(s_K)zѨ6ډCp9姯VɚGov{1o.Μ $AjjS> 0N-,}io|: eN(F85,}5Oѥi`es+w"+k?:5FE3rd$& 5RIj6I<3Z.klϑFo%eGZkޔuQUY]aA:PIENDB`qmapshack-1.5.1/src/icons/48x48/O.png000644 001750 000144 00000002102 12574344250 020065 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThOUe\-EBl&h(\CcBjYE#cב!M -@ZD- 4&(qi[SO{Iw{a`y>=~{$f3:f@ 0ӘM١ w eRԩb2z@&Ey` 8Kaee g7%K.Ly`]h 6;ߓ(dPE0q+Љρ'Pw{0˖_w^ =dWP? ݦ9/kW\FP`X| `xfcAgᯠTk;`rX$s0:aQ_ oy\wPOR#$ևG^jOCzQ7q7[siZhN`U I[ϧySl):^Z"\^ǵLoO&Ϛ^Z"#t )uZB 15%/@CSƝ yҺx7.q Uh ZPɥ`,ŎUhD?fy~wGomx4Jp> /}^  y7}f8Q/yuw?.?+zI54ǀ QIBC{pL'?I!Φw^n8xS06jhgn^0^w,{4KgRuGSi~OaҶٞn~oBnmPv,: ||*ku@ͩ6Hhɗ;5T,|Ct 8'0K8 <"mCvOhg ,́RpQFLқbrQ{zB&;K+@#:P i{do>[#NLآ7b箴U:&fdK. E/Zs5ai;z-6m=/IENDB`qmapshack-1.5.1/src/icons/48x48/Save.png000644 001750 000144 00000000646 12574344255 020605 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<#IDATh; @'"(> T@RyA \A!6662ZY5 30 *;@eQc6m"d؁_ބPyãG~XalXalXG}`EH=al Гɤ)i_uWod 0B!Ingȏyzfs 8n/sH l]>  ?B, `C^@jjZ,KMU&sm.LU ?B,{,$wIENDB`qmapshack-1.5.1/src/icons/48x48/MimeWMTS.png000644 001750 000144 00000005706 12574344246 021313 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< CIDAThyl\͛}<gIFBL @® *b4bIY - H6iVM@XB4doc;uc1ɌcP",w{995͂"I;ݱ+@IgOXa|F_\={.i%p55E23Z wTuJVyR! BιJGz+⢶i:]?y=YAMX4waʯZG%O[DTWJgrAX2QrTg$\/(ED4ŃE=u<Sog{+" sL6T.d3k{#l)qVG,lȓVNf)߯D 2xyh.dwğZ|+ED`_Rޮh,+#ѝ9ΐ?4c51-s*aDvE}h3n`` q-q-)g<[R\( xdS#x.8*r*a\ M~";>T< 0" E396]kf%yߞam@K!+$~7wv֜%Td>gOIlRLKx_:lB O-#n vk*筊FδzrN4f'Kl^ c[bk5MuWѢ9ԛ%BdW.-qΝ 1M兡vʛd qKa-KрzM:V pr\ - ]Yj+b>9J5E8#=txHr (2&ͱaf܌'˕XEk59Xbb'w(] ^MdKwEKx$#ƤzAjG3I2,=ׇx)p8C'2DDSr~:G%}rF(L{S3f xx$֕4cW454@4pQ-8vE-6lr7Tsbĝ:qT0l`eMlT4^jgod6g[wc>:J,nlyxw<[ӥ'є!iD7i= "pO>VιmMHlaUAU땣8tz~еZ Oae:qpD7M4 |③9H X=`r?kekxpEޘcWxqm9lX;*FNmrPksl^x8tz~ ocq08DTbx$#1ʓ14$mHfYrS*o-A!O16V yl9xL?Ww~ymMܕSHFtLـtFx}ڔ\WMxhN΂$=+5v,Kn}!WfedOrD23<.gFƟY[2/Kn'K%vTp"#iiS7@g``81$RL[ܤNec{؇L|]qFPxt HLͱâ& =JVTx%SjC`rTH0n41ŧ)1\:#&;z+.ќ2j4sc}V8k+q> PTS1 "M5V~mxr #=)GQo'p7AD;EED4,z)16Ѵ2 Xhq@sJh"9¶ 2Q1~FKl-6$AT2Ӗ̨sM&Y6(NkdSU%% #L(SwbǚBVeȂ1bVFX?e2|b*(XAk K{rґaG2RO~cKZ-n-T;$s\n~%ʷ;>D/}YXPjR@M 2Cwu';{V~U߁PjR@M I5)ѿUfxaCe6/3ؚ@PIENDB`qmapshack-1.5.1/src/icons/48x48/PointMove.png000644 001750 000144 00000002756 12574344251 021627 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<kIDAThՙ[lUZH&h5DPh.h 1ƐxIpKOoc(R0pDȥ߇=%ә9mdf̘$4dY`a173 lwS0[:L'#p?P t|Fv`P[Af 9Ud"l l ('DҩP:[|r4(N !3 iN>l=^7l3BFi@a}w0@A~Bf6 ؉+ւ(J46D#9Ð%]j TP*l9T0G}|y`I][U?cgdϼ2E`fT?Q>OHjA@R-%@!p vHGZАZ&i[I^HKÓ KRP2 ^t,h|0w(\uQntrmbe0G`;b i(t2:}r7 ܿ)c-P4ǕRlJJ2fc$gӀո?ůKrOM@s_jC]yڴxBjLB4I]]DTMab+![KrxNsE^3۾q;44 Uj :\6ʽ>+I% Z)Զ@v 1=ɍQ5TN>OYE \ ImǑ1)6h*6}f6oi3X#i*>زcBXhl \W*jq!Y^] GRP+rT̂z_K[ըR; H38R쌙 6py:t9ff咎BfW #z!sy0 [_Gr*z]. z3Uٽ *JZߊ #QEJ@0Z?GcA+gb`QH/ SB2Aq9 EU9/ Sh A!\T=1U\9R>sjDZ5f,@]favaёbK9=]TI.!c'@ >5Θw6wZe'a=&~D[٭HOb8ƌ4cAD fD֤zc5o)%3l+RJZzqw{}΋=;爪b&z4)Soj SJrb'd2.ʦXR[>W5 Fi J Ƙc$$TK8 =Ơ%qU~CĞ_$nSC,,zEWDf˨ދ {}qo@s".?DUAd5) aYCj E evSY8 =":p j X/`DI(@i v234"tZaD<{H{ : JoOمLd<܉bJN>$O6l+Mpb2~U %Cmq/{:".cyxhL:M~OiDʀ[x#PYװY|\h"e-{!HAjTDf|wa2w p$ SպR' <0|neYہgT(u*Q 5!]z:t٨Z g>xU 0 ,.x64nR44,+q;'Qz _x0/#T)kPu!e3VރXG`D<)LS~&4N]~\>?aeIENDB`qmapshack-1.5.1/src/icons/48x48/Paste.png000644 001750 000144 00000001040 12574344250 020743 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh=KA߹"9l6UJ~RYl,b)b#/l$Zڄ Ę%k=IvÆ!0O;;3{)0x:%ԕvA} pB)بkmP.L}:岵ZD `Z^Σ!JAytwX`*KFXL#dub_y]7`M={b.1{3ǧgx7KGb61w  Ə@>rdA䚶cm2ʧFnDFnDFnDFnZ.7G Z͚c]KӸCd̗c/`p;_(z*aN=WIENDB`qmapshack-1.5.1/src/icons/48x48/DelImage.png000644 001750 000144 00000002642 12574344240 021346 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs emtEXtSoftwarewww.inkscape.org<IDATh]le3]Z"Pi ! @ `jD#*F"a;QBB"D.^h""%e JH9^nwڝݝҐ&Mg=9+дxmM:YHҘvȥFaK5H"u$d2qf!tRVfW,1hm`5` 6ufRifAl-ɻU""e z/ADn~pAU ( EeK1etӛ`,yZ{hK\ 7Yϣ 32F($M\Y\hg y :^>CXo+}2d =3mCg?E~#t3*PGUOp^ h Eia`73qQڙ,A2[ ?8[WĬXuS(\똫T9.MC4:1wE"S +qV5Mqg? xI4f ⲗ*'p6Bg!>`Еy1'jt'>@ԯpoh i?5r^E]0 2A7?O XhT@4cmOơ?.jgSIENDB`qmapshack-1.5.1/src/icons/48x48/NoGo.png000644 001750 000144 00000004267 12574344247 020555 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsqqF tEXtSoftwarewww.inkscape.org<4IDAThŚ{lTsᄑc;62`(44 x EiIh^wRVBZUDGQDVR# 1~{ N|klo4;|̙ٙ﬐R_kfوRIFQsr,[UdsQ߁j Rw% 7 X""qxnFq^mG|G8iA#jɔ( ^&l#v6]`+庶 73hԖs־쓟88X~ %kk_g%eBQ}ׂPF-}g5䰆C0ưVxj)J|;ch-ƇtOv0ϗNRʤ ޜndnT@H7%1n9W|xʬC{0o(C*")bm4CfbmHS5 [ӭD>:Tnq"InF3:VHny2֔}a(XсyO=~Bxو <^Pm<%hAcD#rmvOM9 $z(gY[RV5t{}V?$b95N ާ`Mcwl/i՘B@A7i+fAÝſG)Q%(Հud$^IPC{O֌IOS%bKR+){l.q^8{42.GrK`#4uA/H=U7d޵ԈVj%,a -&f .J8pXʪPcNzI:IENDB`qmapshack-1.5.1/src/icons/48x48/UnitSetup.png000644 001750 000144 00000010177 12574344262 021645 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThZyTwڭƳ$ <㕪u}W0<`-22Jij V1[c}f9xyo fr_P Vt.c5Mk)3R,]N=?b<.̵s|w̙S|/ݻjj2/B}yiRFRrk`'Zq5hG* e̚5$ U2ujr+W^6o))֩xCII!b_w>G*0 _3y<y,|ѧOR6xpw&{%I3 n^>p`̙j1:;5#qqa ð4|@)B"uf(g1A >yWC.]-j|1: ]*0K!ʙ;O0N~1- _; Vvswln 6DhM6P@ ?_:i~-fg>i)(DEh6 k? <ܮ*<n~55.1.ܷ@]AUQ+u]o7+i0w/;2Ry,_F0ϭ#qqq˦L|ƆQ̜|Q dY>z2{C@EO5?&;[~D@b [ l6۸UU߿"|" eѣG$I'MrI&ILBx3d̜)vq"jx6s3S3>5&&&,--M$I˵}rSIm5gtt(]ԘvMQ(ʛ$=طo_TTT0mP*EujG*PzA-%K^EFvEH6!8t*++qYTVVjcǎUv;^}U"DͶ0LA*4MKcjx%P?neBVa;II7<#/OݻVR(^{0(}DT-C ۩fgož=eHHǑ#aѢE0H9s1110M۷o׊{A}Che ~;ӴptD2thOTVV03W9P |AK`ϝs/u@Əj<|Jpz |}8L$" qG "**\.qf%X 1se! КԩSWPYЩϿ3gn5jk̘8l|^|q ^/-[1 c/ ҥFDbMMMM-`zTw:wjqذavl\aܸ4ԕ㏧;9svHV{w'~6] lel6L8~? 2Dl64dɒ^Dx[PYGdAHv\())q$$$`޽p:HKB[˔.n<=n=[ u\\\nAAAjyy vI,HLLDbbSgcƍn":%@FlD^VSRR($$0 _~%,Brrvz|>bΜ9RSSʒ#$%%RRR0ȲZVTT;w6444((4m63J(' ~psHMs޺ukrhh7;;; ::pq7(2dܦ/GqĽ'O"..vvya .dD9vo G0?{z>`44ա.,?ெa0Z0 jl&FE5m&EQGC O?Ѱqm}PQk͚5ٳ[#~,Q~ݻwOM3 ewyᧄ̴BvYuop:8f:ur-هiӒI)=:OL,$ݻJ('p?絮8̪G޲׃曕 : NIIL>==4X,]rs!3l:JKKGn{dY6%IvE@|t9kt} u!\AiǏWgϞ$otg۷o$jk+ 222믿^, zţA,+F]8vۤ΀%%%x<;=@aV5?t|s= DS`ܹ6Q=zXuuuqO>%{$IznP c1vX9((Ν-X`|@(//?nݺɪ²,,YħiMnrU'f2$m)--%Q7|{YLLDY$իWPXXX+ޯ_?0`@fJDeY'OxNUU HEQݻK EQm'mQQQī"@DCdY^u]Hֶ3f?PQDֱi 99 )rSօe׮][or*n$ILb)rbȑtM-&_aai$o߾ڵyȑ̼*>|>0.D0$pڵn4M[mYrx4-Gŗkjj :3'>q[g_S"s=5_e]QQfr,kii{@OYÈH`_}sMsf7::u 8}w=k׮et⯤cy(nE#S?@?UUEqߪN+'IENDB`qmapshack-1.5.1/src/icons/48x48/FolderMap.png000644 001750 000144 00000004720 12574344241 021550 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< MIDATh{Te?af.#2B hZ'K<.VvYrVvN],oQy5l$rB.]LsX_}~>;#W V6fsЋiHK8|FVVH{ HIŮ]5̚5|Wz ٱcw6sWSBV$ >$G,###Kֱ[aS[u:#qq!ԴdgdgEc8_$R/jjZCb_ع |6,X^-˖%8 v_{{mޫZ(+m[S;%!{|_AdeE# ddDPȬbb`gش-;wH@.v/ۨh^$9YIirRRT<>߹ۨ.q"#yXn6nCwu2fOJJ#\ KLfŦכ(,/{"643"ʊFBe cb0(+hN|J1x}DD;T)6t:# Toӧ;1ᩧ~ǃa6ƍܶ$%E|ٌJ-\yh2'5ճ4{ǎJ<}ǿ XJ…Sh|em?̹sW A*u~\[99 dygYY8ذa =t;~~R*+ʊy^xaFm!*M ..ĭ_;>>b6lHs8 WkFG(+H~4:;8u0?^}. N!8؇),L`ٳg[l^y%fZ#`8LfiԴT)*`VLdժv,] 4pN֭YLfo'==IN %8؇+W[<¼ lWX?**,C,"\uH$~})EE`޼}lvܡL&\^}5⇘>=¦ۋ>|}mGJ"1qb]5p~O?b0p K>W:܍IJ `8{Ka\edzçZGG?cx&L[|hh`yH ˹x͛IH~*+5rgO_jd2' wi6e`@τ %V3W`IvyG?,9Y鴞H$kW^Ga0ӹ1D,}NObbO[:&J")DG9 rɓLnAv6 ζͅ}6 2]L&3+V$=̙E2 ;ij7Ú8?9s"Y.mJhQ,u3>|fOǫvv >Y`2~~Rmvz qRSUlؐfd_ddD]8CD?׮S6ؽstt\Ν <0ݿ›"K&7Wp3X@PkrÉtFݍ.^|;W!ufDw6ur[cD# 5F|;=NU7>9RsW[ A@,wKJMkx,`0hM8̦MǨ.Y`f3;v64-Zzz+44t tFNhv/&f,/4+G.{?.zioW‘#?-^99SQ*44teKwtכH 3'ҭCR==/:\RHII['7ĸd֮s5*aS,'uo5  UHo֬92\ nzG夓ଳck`ˍ1Ƙɓ1II3z1{leSYiftt/̝LHXxim՜WgOML-@ȾmOg f̀)Stӝw-)I'Գyl~?L zQDzB74@]47PSf93a<Hɓ  #g˖й ~I\kZ:EEz^p,mW@cͅ瞓'$Űoh}CT5vlLꥮN 6NΌPXnon/ +VA [RNòd(.~.Soț]|1|va4/?Ø1jr#Y99#jOc]>HҢԝ~h. .0\_~W̜ ))2sE3O׹޸Qs6MF$'eeq)@bd: L¦M׉:=oGFN>Y_Y)W^ݸ@`{ᇵ[곬3DߜMM Q-̙0d`fzoB˓e~+Ü81:={pgtil%Ut6͘?x×-K.t))2%ر?eHLT8@Gaؾ=nHv_UKn-mo%:k@ƹXvs1 OtÇ}? [`Lcݘ&Mr{޺UE֓OxkY*+W"XDe̢"c^i*ǂ+ 8i# >lLzR}_a86?p̙#c{ %7mm*ct=/-=s #F{Rݤ2xYl--ƏW'yt`mSIkek.p(,TiY2hXJ;M`^ZM|4 =7hyGVYefٹu JJ'gfk Ɓ}**Dب_%KbWxj_ jO=T1~E+<>QDHIENDB`qmapshack-1.5.1/src/icons/48x48/QMapShack.png000644 001750 000144 00000004720 12574344252 021511 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< MIDATh{Te?af.#2B hZ'K<.VvYrVvN],oQy5l$rB.]LsX_}~>;#W V6fsЋiHK8|FVVH{ HIŮ]5̚5|Wz ٱcw6sWSBV$ >$G,###Kֱ[aS[u:#qq!ԴdgdgEc8_$R/jjZCb_ع |6,X^-˖%8 v_{{mޫZ(+m[S;%!{|_AdeE# ddDPȬbb`gش-;wH@.v/ۨh^$9YIirRRT<>߹ۨ.q"#yXn6nCwu2fOJJ#\ KLfŦכ(,/{"643"ʊFBe cb0(+hN|J1x}DD;T)6t:# Toӧ;1ᩧ~ǃa6ƍܶ$%E|ٌJ-\yh2'5ճ4{ǎJ<}ǿ XJ…Sh|em?̹sW A*u~\[99 dygYY8ذa =t;~~R*+ʊy^xaFm!*M ..ĭ_;>>b6lHs8 WkFG(+H~4:;8u0?^}. N!8؇),L`ٳg[l^y%fZ#`8LfiԴT)*`VLdժv,] 4pN֭YLfo'==IN %8؇+W[<¼ lWX?**,C,"\uH$~})EE`޼}lvܡL&\^}5⇘>=¦ۋ>|}mGJ"1qb]5p~O?b0p K>W:܍IJ `8{Ka\edzçZGG?cx&L[|hh`yH ˹x͛IH~*+5rgO_jd2' wi6e`@τ %V3W`IvyG?,9Y鴞H$kW^Ga0ӹ1D,}NObbO[:&J")DG9 rɓLnAv6 ζͅ}6 2]L&3+V$=̙E2 ;ij7Ú8?9s"Y.mJhQ,u3>|fOǫvv >Y`2~~Rmvz qRSUlؐfd_ddD]8CD?׮S6ؽstt\Ν <0ݿ›"K&7Wp3X@PkrÉtFݍ.^|;W!ufDw6ur[cD# 5F|;=NU7>9RsW[ A@,wKJMkx,`0hM8̦MǨ.Y`f3;v64-Zzz+44t tFNhv/&f,/4+G.{?.zioW‘#?-^99SQ*44teKwtכH 3'ҭCR==/:\RHII['7ĸd֮s5*aS,'uo5  UHo֬92\R U5U,%J躅\lh_԰W\X#M)>XpBÓRdӛYU-Ld$AXHKrnAkF #A9dBUwL/}쌱wI=H~80\9C6Tp#W~H9ZW࣭c;!?ZqolU%BH̹T_1_Qp(ψY/0ojB;Y% B[';cq'pB_N8M\ Ǝ_Noܶs[<"cEw<Jfqzl}K&^ k7qHy߰Es&;kZ Eo1(ŕ.O7KHdyBh|i?=f!Z '&h Ux@%kboIK#B8C!?Q2{YO`jx%,PɌoWf!l7pyI˕C{SH4A fa3, [@ R!vkݸ'5k3LUOd\W}I^k"ny^1UBBݟ6MdR~ib>l|eULC5X<`GLt3occ,8t_q>D*\Ds͜R7j(Q5//%Bh!sW2!f5DwX!3gmp>3|^t P}Se9eyvFύ!Bz |_!O{Qhy 5$'Wh/yVZ Ivuq(N|`930 zycݝh~QBb+' \d{]o`e 4tr' ͞=?p$r+]Dos$% `#~_tK? .539[ iŕ9d$_A}Q㒹>x7?tw c>p/eqSrڇ(l1B淇<و 4?=NŹ()I%B||1Y#zkǺ+$GI `zy1Q=c( "׵ z\w|Kë́CLLT3wR q#ʪUc:}qˑX4׶U,KSi ˖]紶RdW 7g1柚ԥ&<f{81H-.wL1x&'_ >OlDC;1 f?;J~LdIENDB`qmapshack-1.5.1/src/icons/48x48/SaveGIS.png000644 001750 000144 00000001735 12574344255 021150 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 4qI0tEXtSoftwarewww.inkscape.org<ZIDAThKkQm.z*…ZPFkDQ7.NgSp%]uх((.JmEm+RvL$M̤Lμ}3g*G {)XXwsg]]9L/y M!@M!@MH(FFL<V:BBzI/P|9 wwGzc:9 ?#g 7 $3IUU9JFwZPtd rأ~[&*X @Z<F*`fS Β 3| |HjT͛Xĺm|u1Cx|T53X%3&ō T_ 3^ĝ;MYx/mU36W U R ɲp\};S?ӪF?NX׀N7 \>9 p j4D=y;Ӟɬ0=X $.Ͱ^h6Kl$L*6& Q5 X+C@35!Uss"Vvչ8Jbɭ/sZPU\XKoZ2-Ow[8jz(mk 1^3SUa.BX@]1Ec@LT$p2ix D8SCf+cn b5`Z*:ЛrUEZ]W9# p@k9'gl_23#NU3(8|2P M9jm([OcUIENDB`qmapshack-1.5.1/src/icons/48x48/TrkProfile.png000644 001750 000144 00000002517 12574344262 021765 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThMHipC5KPx"!aYЃ<*{kӓ"baFQhVִRqd xuiɘL .``!CE[@\.466bDQ;UUUP*^1Q mmmA"@ )))*٘@\\FGG_'###tqp`nn'cKx"&*BBO\ȨR k娎@bb"sbU V8L&CYYG`qqQH4@?ob%JQ} @s@tFF$ shj@P(LN<VXGJJOnfA (PŜibG[[␞vp crrLhhh!9<5[NVV@8455TT*Q|<^&qss3233Rɖ5xdB bB%2'o\ngp/o!dllǓi2>>ND"OZ=Bqb1m!:PTTF B4]^oVګrrbH}zvCP(zFdgg!wb+`ۚlD 111df"_>*>|n,-p/"[ D>Płtwsjk2 7-p罄ii#ֿ^Gqs"j9JNW;UBKK;0p=,`6D~g./ƍq:pSJYC|iHX hi x`\t&t&dsVV>ntmև%$`~}L οb?\*nona*>}rr33hod Oc>))B83m D3&6/|5L^yss=Bk~(IENDB`qmapshack-1.5.1/src/icons/48x48/Pattern.png000644 001750 000144 00000004324 12574344251 021315 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs225_tEXtSoftwarewww.inkscape.org<QIDATh{Eߜݶ\DZE$ʥE($LJ1@XoT F%r XJE(n z3{* E=INyٙyy/|Gh ozPoL58yCkt؀,D/i-Yj{Ox 17_U:̘Otz^L$T? N躼A3lQ6`_1bJˉ1 WM,`c~M5ˆ;.%i&"v1G 8jlq/.`".H>bjDu"NtlҐP=n%ѹ}Ĺg={WxsUL\%T{EN7*\QMnrhumk蝗R}dM<nr9csm5i:v /c)Vdl*X$i9$ if*@ϿS3؂3#ꋨL1q̢6Rj #j՛eatފ[Cor9psN= 4i:%%4H |'i"^^dsVo~lL]t6kH蛭6F,YEb%ٜ[d*=Ȧ"rzd@63ɣ8o) )&?/XEJn^b-žxz;<YEr߶K5rW2GT$~1x(r:x~ fy^?ΞO'WbJ|hub'Y6܄- = Ԉ01z9}IY>"~y|FOh5GSvGG?VbK}.G"θ|OcYq2ҋ ||z[ʕyTM:x!IY6iq@̲q1r#6oV""Ӄ"[e9dRY,Dr[唻>ֆ|s{.!D!e9%OV|P~|wljB9|B>q,/Ȳ98OlllɐCX762غA`]ak䮲Cf/"ۢ4tkBZ$E?/Wik ͧ}r)":AL"a##mS(g@"ie)jk˰|]igDo1ȅlD%J%R},r^^UɕA\hO:>kAJ'FD FJq]# !ۢ꣠\}=:/fg9M)gܯu)?jЃ=rN$Fw]s䗗o;՚w!qBSvHtqR}{i9&ߨb" wۀ}8}ľ[K46$7Nbz仒xP,܀)wbۯ9&7&A5m]ZA}$@loQ&!MRƗX1xcUcvzRZI)j]qZ# ]*߹OqRp9Ů'~B\Y &n?&߱b+bݣE$l vhHha(.0{5bǾ&|aI܄)sHݱ شQ!_2] ?tO/L7g:.!9$,t|atYJ6.ESGs4 0N?ru4$t5iB^čMsߝlM ALoatcn=p~{ >׆ WZiǸ_zSs[IENDB`qmapshack-1.5.1/src/icons/48x48/TimeZoneSetup.png000644 001750 000144 00000007407 12574344260 022460 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThypUu?KD,@(K0!#dæ[v46e3=UJhW[ 6 -R QA.(a ,$/=}/ =-w;FT8RSSzjJ<x^QQF[[[H^~wH P nZiPxWՖA *Vۀ \pۺOBǴ+nV@ ܝfq`It~We"8a$Pj/ V_K"| 5>YOQ':Ќ}ұw%XY%@[ΝZ *l^ e t{Dl؏})Wէ.ygYP_!ĶL`0;Sm?Іm:6xޝ;ؤk "E@ &Z+r˝`)DGI C)"uh7&^' rݕ &Y~`0hydV~HNFZ "jV3 dz!/$38?v ]FC?z.~IJp:ÇG\:8|*"\۷/po?1j`2;uHBkk;`5j NA @$TCCCX|>^Ngvǻ F`G> ]X&8oe;wqFkȐ!o;6 `LPՌ5˲1MLD\>>d X+"u14'@x\sMg׊HV`i,vϞ=72G\rssӉTDz 4tĉчzx+9&`Q">8sJKK[92ȑ̩ȑ#>|>"559s0bĈ_)ST'N: *Hv`n cAS)XǏgk4ihh 77K֞;i τ yRVVhhte:{"N^l{9>4^k5jk֬i(** jk$!x{xWطo_fH"$vvSqED<"r髭孷ުmhhx̙P(D~~p )j+AhlmmmmmKvsO!!`+WltrfO4iy2̙ݧO/0BU@iKk"R9.))I5ޣGܹs;Tsh41cxƌéSa:vٳΦW^:kjjll2;ހ}."Ȱ@ _~?-//Oӱ,E5677R؂ʹs纜Ng;-=mذaNߟT,"ի#_}Վh4eYq I 쬴vj]TZD䗝A$vL8t.'a,˪ r}[, ׿ Ssb1vem߾eܸq&;;cfYRQyx_,Hlѿn\ X63ih % p8=z`ǎlt8B?Ζ-[kjjeYCnO.`0ck(T3yjU=صl쫐c/Dd%[U`0x< =Ϻ@ lǸqp:X)//o8w4Ux<1o޼tә^]]w~/>;J;b*W!#"].ӦMs&X`08p#H @A#Fx<~JU7u`v3gΐE<tF b[8W6|~dffr(**j7nm`!f͚JϞo^~s@ܹs^/oii~eTUD>?#  L<DDᣏvW )))**BD8u7nر㴵 4etSLq9RSSc$뵕v!5MX,֚U lچ]Q^`u'}$KѣIA8v(mf#G x: ޼ys@qqqf0|2nb/G1''`0CDF]66U} `_ۥPb9|% CҐ3229\փUQ՝ ---eYSE$?|2ydhkkGxSRR%˹.fHR¥^KOƀhjj߲jժH$0׀&=}ܸq3[^^tf͚ѻwo6oٺuk]w2dk>m0ʈV0 6Ǚw>UQ0\tM7=~MOO޽{p8ڵܙ;֯_X\\Z\\ Böm4BBtDgO4)% zNk{ӳ8ui 8vq0f3zhߗ_~$]PwB!ʚh4Z8y$˗/?UQQQV^^lY0K,9rkHi; RWWXD{t viii|]__O>},k(@[[gpxhuuSpx_j[8~4X,Vl,˴,ʦP( vڦDey/W`0:EUOQСt i+G_wj_LIENDB`qmapshack-1.5.1/src/icons/48x48/ActCar.png000644 001750 000144 00000002126 12616741641 021034 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATh]hUouS+6 FԋZt&z!ThPC.LSѫF iJ,x!BMI˦BThP1D ?ǀuF Dbv$3k<0Μ3s̜eEUYX~( &7AYB^"6`߿PUBbZ Z sǁ5(Bb|H2yIZZj[td8,TUΕj SHtwƍ5"ʕy[NTOOIޭrM,4EwwcFVHl^*GkQ' Q7ѱ͛D^TΎw[{"B.Ec>@*fhn645y paXĹX @ĉAz6Q!}/h`pFPi[@)p* Mo~eIENDB`qmapshack-1.5.1/src/icons/48x48/VrtBuilder.png000644 001750 000144 00000010106 12574344263 021760 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org<IDATh՚yT?~ d $8!aV7ԫ9$fi7]eí-loͲ5)@p"@ p87~b-~ϳ~K<Ncb44 bk}uAEe-ƫYQa:~Ru PP⋢>x"mgD4dQ l Qq5W[KX{ɄuDpsĜFRSKEӦRs feM)AUe{`:_T>5vZwd aEN%y(~_O ` .!1S_o{<A/ @-III¿UMcB|)- NgJO?&11W_], 4Æez}CV,׍oeԨQ4j1|yݯ3u\ G<ѓ,GOU7 7A]D[gwl" a,)=Hmk1N  λrY<<۝M7}F#2999r`hN+ eE2?{ s_M-&^Xbw&*:a bwJa7~A4;N@ ͼy XnMKnz qq9r[Wȸ]a ~PtW;\H@D9GONc[G>{2£o`4_Y hZodf $kp(cfKFU=6nj&ylRDS2R5RZdMmW Ad fTʼnh01H+@ 81@ W7P!bGKu >ML] 7@HR(*B <\:ĤII:1C@PQ.8 2@pMdIS@U ?ejzVEYq4Ú4d!22uRQ ۾;e&pyml^o.Ww Msbc +IHJЈD =BSeCbB˰PŲhj7 {xw|U?aM^ \"pnJ\80W%\<Uma} >GQ[uC%Ox^xu(ʕoS[[˘1ۧmr>'w# Ew#dg2lذ2 Uqr:R3| ?K8ykg﷯F`pZO v̶n݊> !{5ha阎yًyϷ1bXVVU=4]:ϊwoc;v4/yYn"K1h=euN\~by|̦EEqa\Bg3e;x~4Wp6~~+^lpSidC\@=>>~E`Jw+WƦ/\Ѹ P8}Kp ]~5Ib,$а⮙HHpr哌 dyWq<0X7Sa$nfĸAS:]jlܹHO,fe Mע{#+a!%%ɓ'_©Sǰ6:t(>>]sDJ}{`獀v=c *{틏_0wܽIr_bڴI'L@@5a|9g1\|B:|YWIB`Mt&X,{-դ)L0vhh} :[LPfKcE***pذr&::? >j^㣲|DĤp槝}cZ"ۥѕ%!| 5|)?oa2 8S?^%{Nl UWЫ% 5c:[-DSe~ȻڻkpB"7xJb˺'T2bܿMyeTJT_nb3pp{s}{Ë5=Y@!; &,>-Wa& !6a{[ 0>.՞n1{?h!%}j>U9rp_h_:t;  WcM<`oǧp@&_ :~oDݍ.G@4yu.NwYYL?_@`Sp4C\` &NI71z2 3_g.Qqu?x\qVϸ+J Y?htp%Zp=ZWT;qieu/qh[Ljv_[yYq#2 ֚`0yc^xܵF[N-.Op߇;?ӎkyw߅%": dt^_QVnIENDB`qmapshack-1.5.1/src/icons/48x48/ActShip.png000644 001750 000144 00000002165 12616741641 021235 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs J J'?jtEXtSoftwarewww.inkscape.org<IDATh՚k\U?g:1i_Ak@mĴ r_QEwN)eڤbm!E#+p#hQhҢXd.ޛq2fB {=gλ̛;23V2rM }Վm sS0=+'j۽i!%[N`0 ~+s p | &WYƄC!93v`013Rq`|pB&p yPJ8s<]ʁw0raq`Hː U=<\ hفRrus'; 4̂*J. <\H['Fم._?Ԉ)\pQ5 tQ6NF[U.'R5Ƚh9lw ҖJښ _LG1j\qq(] n;)|MCi6Rii눞q`.$q$ L2`fZhBSR=of0PG0+sl.-mH[Wv?Md\PT7ژ#raTm]$@:J]J[W,vawv(m]؅ii`0uE3PIS 0̹1ad]5K;6g6@/=WyO@Oanwp=@O.MC]f@!ftXE ?s_mH=ilΉ f;fJ1_u!p,mw[ 7{Ytw+*q _sIENDB`qmapshack-1.5.1/src/icons/48x48/CloneMapWorkspace.png000644 001750 000144 00000004731 12574344235 023261 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsggtEXtSoftwarewww.inkscape.org< VIDAThY}TU *RFJYYYZMOĦږ1txlpRCRSQ-Dc`Z {f>{wF p7 XLOP Bև/BNwB7AGH!!jōw'B||AM6?E~7fǎ(+K~ 6vF  --f ʕ/Ov*4 HJ$/+IQ\YUlj2P)n=SSQ632沐LYsPCj9aMN <5KZ#΍OPD@)Sgs$7[Zq^ P3gZY]}FMVnwq:ŲK$YF2h( r\3 bE6!U_fsa`KcniKR :ƍ˦BƜ InRA@GAH}+ʤJǏ7o@2DhKJꐑcIeCLL,`֬q>*+[0mZ.Fe122h!oR-)Âq^ "f|0ii30v3s)S'd#)if`(,,,< F?~T7 eٳWcLs>(J=HO?itFѨƄ ot--+P\|-",,p4+!`߶ ;o1}7)SaٲGj4ş)Ʉ@\h!qdhĉ(/oNtT$& O H~mKJ~&gN~?>or]h0ȗԞ'ի26vvٲ4}oR:_j4Y\|+.+jrvuYHYXxk$O2 & E\w 3;%#,+D$' _SHcpp:%jP|5}f:.XuujjZk֔ynޫF E,K.kSZZѣ3蘞~$I*Jo,z;wUu9dQ\?|f7_KL,%Yə$ܷ?K:ffJo>tvZ.|߸EdqB;߾Lq: ~/XwEiE$%Mzj^ ()ѫ pKHI%Kfsm$ Ss(}כqNX]60PQSskp46tĉə͛O`@{,'ڵu1k CM彆KeU~#{zq|dh8g@w#-tJΏ;@ùkð?78Rm82:P|A6`l;:juo/Onp?I 6r WS Ck3,-G L]@u󟜔ka'$#fr~'LV_K?_ȾG~)C;Y=P`fovr8@yJɰ'9)ٝ"oIYvpI^ Z0fBNWx4 FAn5Mn '9x6^9 %E?dJE nv'}6wS3Su/їG#D= Jr_6p͇@@Vf7 nz?*%@Zswiܺ@`G(#W?+g vbxϨp5)A{كcI h-^KO23퓝mip?]ٖ>{?qD28LfKiƳρ1.Wf/馜 A-eIO80/;YWIZ| x_%Ru?C$K~D~:aBK9=pp@g3t8= j $ic l M0C.$X qį$T'9(O~Ni/pfJ!~5D)'+)Mp>`0>“nsi .[Mݩ?f.G VA'vp-//cPV '7jih_ zYΦvZ^(kz+Ofq p~.''d RtJd;Z* w ˘=t풢Sƞ%o77VRkox-g0K͓}Nd7+ 5J2蔢bVqO4%Jv?0]X pմWы2ECaY -`<O]w6ȕ[FQ3|,4 0sFvGk{ ? ''X&UX|) %rО݉GuR!`/~ x8g%W(7)t, tx@rGMwsN(?\#EG|bpW3~I(;s+o3 kݹ:P+ ls V/A*IENDB`qmapshack-1.5.1/src/icons/48x48/TextUnderlined.png000644 001750 000144 00000001165 12574344260 022636 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh혿J\AGO ! v"V"b1|@|A+A S+++؈ɪEcXqe\9w~WTVV[bRC7 0TT_iU ƁOMuC@B[eR+VH Z 2@jeԊ~n}L ,"1ᅺ:FlZL_5<C8,#OmJD/SrJeD$W[`NT5xۀ:k5\8^5c  }ځ ג0}`48=*@1Pp k@zz)`Wbs~u S` Ap[3D]|E ~,ocUqNc%"4&4n1?8N-`[UϢ!@D>nl1S3!Գ~|uG$-IENDB`qmapshack-1.5.1/src/icons/48x48/Left.png000644 001750 000144 00000001720 12574344243 020570 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs=tEXtSoftwarewww.inkscape.org<MIDATh͙OUewd9O԰$ $ &-Q'[DAmjSBhS+W3D55JhSCP DA˅t{ίwwg}xy^y~'B;VlAH^?CDh.t7N Ji5>5+M5 iy Z"W4[`WlBa.Q" HFR6A#1J$ Ip ~!}$@s0 z^~BaRpu隀} 0\+io8JӨOw7^3\6I!,QxbR)|ZHZ]8V2Kj@RzZV6RE< ÓE Hzx Ɓ1JBކ{TJ ip+qr$;走x -gid׭]z"@g7ne>ۿB8P>߀ +qa} *aU0|P֣lWP.Lj+Kim&}fK4͒ۿANppǫv V`KD)8؞_7 "fLno6Ç;`,-]/w䰋zͶ{:Zw?M~Ya&ziTd>p ;IZ ) j0F$mF#&Yxn$XW 0۩*sr;}LrzE5S9L}IXTE+M|FٶX%\1cTyIENDB`qmapshack-1.5.1/src/icons/48x48/RteInstr.png000644 001750 000144 00000002756 12574344254 021464 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs$$[tEXtSoftwarewww.inkscape.org<kIDAThlSU?+cL )F!duĂ !PB42P $aB4!#1$$A@'qs:6Fhtl׷u}Osorӛ9d^=ݹB! @~233ǔYx^.\`5@DƔ(HdYfbեY >}kX),,L?ЉǾ@詴ц BEǥѲ2, Ν˱cǀi0 ۛfɑ7OH:b]h70vEMMM5/Y3L ~'nk@@߉;c* qa1w!L,Z+VXĉx^4# ]XʒH$b:ܹV `6lϏ-g* #é!Ljld֭)W=˖-[dX/tg94[RGr%U lڴÇux:R5JṴ 䅖Rٳ uذaD`=Ph?^(ǯ+A4m~jii(~+((PgppkR[[ 8^R0Pw2yz20LC`/"%fօ Z*J,YډP^\"ޛIHF̒FGii)gΜ <)rȞS/VxvӲؗTdHssl#EEEzNn> Mb a+m@񯳐Rj&0'''y p׃|.ŏ 4/4xTbuJ`ѐ?gO$7@w&p\.~?n;J퍰L-؜X45dc&ĀRPSZ'~LrF&9x y06^ ym>~`u[2m4n7o$KU>!RdV}?"Ǔ)h,Z 'rJSc\e.SV҇yRV c`0sQFjn7~3CwNsaD(9Pgt-ԀȠ tihסzb#a!J $8q-"h0E)x ͧhoً3|Fv ""{v 8lQN[ u[ADW-(o꿝D./#2|vFYk~O*&-DdU`1ZBy!r1knwsIENDB`qmapshack-1.5.1/src/icons/48x48/WptProj.png000644 001750 000144 00000002327 12574344263 021311 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<TIDATh[uY\.dEh] 2#0P=DTD[KPVE bEQCYjRU3; .w9LdCm6`#jh5cZ1""EO]1}ZG"qp@7q񁈳U w\E,qAZF`Kq.JZJ DD_Drt10gL,{WV] AN?YL)[7.vn!~ٔ\`}na P|ߊxQo =dgc{& Fzu\"6v~[{`2m(#p8 0d(k@zV|U jETDD2Sle.t>1"(Wv";I`&a{D<20(WV*EW*u5+-Mmէ1㫐hg8gfQV\bdg՞ZCs+2y+Bb+3n̟F)UmLnQE\fUx֞\\s싟?vwWe|~4 Kb+8ꀩm{O>YQ#-'2ik3/Q6FUۨ pϽu\*a[r|fq#UфvcgDlt\>mͰ';6|2cxnzfPU, H#f: `|p?0dz%Qb@#ݯ, Ø>x\6\@LZ6_ѣ/^{ԭR760PJeK%922Ӯm<5W,"&j0!g;tYk yZMBW1qsvHӷK-rS/^i-I,/[\n}/j@P#3+^]e#b+灺:ٹ5xw3jx֨)!!߱ZBJ"QRbJN^CxNUIENDB`qmapshack-1.5.1/src/icons/48x48/ActFoot.png000644 001750 000144 00000001347 12616741641 021242 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<dIDATh՚1oA1 EBЁB:(B6gz)AJeQ@QhP@tD.ޞ;{gnw3ʢ,3:m'pz@۠Z|w~bDkfgU}ʧ'4U<:ۚp _vnkC,VorVNJfÒa"[/ ~ۚ45fc3 ult-Vzl<^حHv;U5D>/3gE_,dG /aeU?JA0 5P|jX(HD9SM8.=h H4F _$wBMB гm$u_K?gzD5K g8â1WaOI]  b @${*qc`˪ ?("qvxB WNߨSo: ԩ OeHq!q4y/csQBԷF >DX!nZWܛyo\}#PmDP# :w' @XJ,=F/YIENDB`qmapshack-1.5.1/src/icons/48x48/Empty.png000644 001750 000144 00000001535 12574344241 020776 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsCCtEXtSoftwarewww.inkscape.org<IDAThRPcx^9>+.>0 &rpĢ2Pdy %i Lnfa;f\nʽpSBgjM>zB 2{plqUc HB :܏~B^7iy> b' OV'.|Ghq{00(Ozl{b1{.44%V5ۀ :@J#uP5j |, l^%177 d/YY ܸC~􁆶-Jf|-O@Eu`2p7!vnRլ mՙ_d tN+3Q5UmYeZ]{Iľ j8c&R65??= K'"bK2VƒRaJG]M{ +!\~䗾t5HhT}쟩Lc>VzJRtv6HpDu"v4yh5 BTMP'%]C"F,UQR lAtt *q%⋠wzSEqa &ֲ#|3@d@TU |a;_v:V™?qŏACsp5P l|+WDJq*U{J\>KS nt 9?(}/jqrgk|ý"#n0H8}|L4v)ق+O鍙b= 0P({34NVișQUsv\56\X!p _eas4@D ؏;2M ΍փTE6mTQ :XuZěPR'Uժg`3m#` pPؠ0r&9J8 U>2`onH9%Rv>6 8]i>,ʯ!2w3O!3Ue)e_v^6!8I&j1МNfBa86ןLQ5N*br`ҵOu T1:ť".ui'OQ}˸Pq缍s=gCC08u^{?g?׹ilW}l1DXj~%" WA@ٝkpy^E{@Us$ Tܠl ѦNY ܒ^Hݦ:r b]u$+DOr!*8q:`>@?@wBDY:CK@~!j֫6!@ dL>? @ߔlSr?:"v8*bQFLwz{SnˆkE%ԏKISf ; bw mf!N`p Bg5jVexoK:@m/%̉FOꔦv<}P[ "& anT F&p)08UOYfP<|pȌSJGdӍWya;.b'؉Ɂh8W,P55^W ՕK gSEE 4f|#S>>np^,UŽr& |ӝp3OIh&@լdsIN)bWCH{:5;)퉜2X&08=NyjM p9TdfL ?縻T]U\=/b3RkǓGE:,IMV,VrT'0?/^įjq[zB 4 6666YjgU[;q ˁUg9 uhϠhјmg`8y$ XLQ5apaEd|2C@=!bcw=x(P;D|V҅|j`! fA5𬈕P'X }׉Riܡ 29Ѵ1@q[AY_ܱ#êf QG"U3@N^Q[ATo~,L.2U%R@mj(9t𐎪T:\>Jzeu8"cLwLDI3@.;.SV: >q}0|8ζN4 ˝ PPٳ UmJ&৯Rەfxy-Əz\F-:pOY[!藞\8~8p_6!cqE_{g&K Gj6'ٖ;~p_sn U5gDٸ!QyUR>r ~'W~pW5I&׉ՠyGՌ +1f`?N /UQYm l^#7?L,nc`0P5;Dj` 7lލ^ Xs[`֤|*ۍ&4_-El^F77aYP v:KS\k IENDB`qmapshack-1.5.1/src/icons/48x48/DatabaseSetup.png000644 001750 000144 00000006424 12574344237 022434 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYstEXtSoftwarewww.inkscape.org< IDATh{lTם?1/ǞC6)4C4 UM7%*BEQJEPMnj!.D6jYEMM {wWaX;8x׮?nmṌ'֯(*Eg'?iNozK}_LbCsa. ˲9}/~|Ԕ.\`GN;*"#,OUU*?EҬ4d``[rsn0::V۶ra eMEze<Gxd$I dhhM3>݌rio¡C9x'Ob Y͛ZC_r[>s;KNTVٶm5۶f``Y&'\w(.cɒ ֖re ;"xFQ\3,juX4ð(* :>֕X,댎)((c`:\"&4;w"ImꫯrRǙH@.sa:uFLLwwwruI|rL0R E]aѣ\t} k@-ش $ `(Refͻg\ HAww7gϞ\z<JKcx~/~s $dccA&&$b1Yh5D ?ga2lIA+TUw*@V+Jfbؽ~Ǐ+\@QSw~w&{@ EQdд>ꦭa::reL/}d nބ{ov:irݎ+g2 Iooh4F$:#Xu-"=}(m#"$!"KW\Ѐӊ- Khhh0 ,I(XrxgYlh>^_K\^E׬LD";gy 2^T{EE6mJC2227%%L EyBUՕ8 Ox̙Y|5.^H{{;ØI,#"Ke LL8-LOOp~  jUU8 L1^3,\Ɔ Ks1o '44MdY& 8K_ivJ. I:ͼ+>kl2ggCo q 4vs|By<|  IR #3qqvϣW  ۂ##گ矷vˁʔN\sFVQiFM;!-_:t: ڈ"xIIXE4z0`1V.Z v*zn 444044ey irUA`)iO00iJ?-@*Goado?%kP6s `hL}۶Y]n]͋/>LU?'/YSRL:ۍ$I,[ϣzד53>K6 E 3$455%<"Ouu1ؖe144Avڕ!QA$Kj*A.^죻 MSi:~CscwΓi4IEQg)ɀ M̛&QTMMii'nwf gGhm#Mm?qC8{+' Ka ^PEػy,˦+8D1n' kB,,P\<ə3&SWW<@aS$' t lNXLfPojjU]]9}/ݢ-Lk-neH2pI ;($i>P%K<`1:G>;7t '}EQ4nh2!L,=#K_E~RXX%# K%uUiqXv Y^6}}}444$& <ݻ rʲ()'fL E91`%Nzys1 pBAĉ455+8 (s 5kd9vXd?6e7sID~iaïpϝ;!yI(++Ce钇[2lzH*-ӽ'3~w b?pxo )˲Lmm-7o>:0 4߇ @OK=!nPjmIOoA_ꐯOYY!>{x<M \Ƕm$)x+σϝC,mͶޱCPU5<(H+!m8PXNO7nӄcǠ *+7nĔ.^V%XHUUQQ"mٶ Xz_V45Sq&]`|S/Xq.Z=o(3EQؠ7~w9Cq ϡǹ|*2@yVGq 9'|\IחJIJAoVyV{r9g>@&u 3|=ުc9|f~^h:^hf<;'IJt<"J/\E 44 VL6 8uU470Le>JY8TG[L`پ,[IPcv5/@ת+DC>_7 G7F(l(+{ЖܠNz0n-ksMD'OaKsCEhm2y_΂/PVƓo{G;ּG/IG9(A&;9']rdr d|..zi%Lx&+P}#eL56s.A z!+;ZuD<9hDQd}pG3Ǖ Xd )aLZFzB4JF-E@45Srþ.JB4(+f+qB,vJ[2>}Z]U=;k(?ҖKyeD #fR$'#6IWq(7ߟ|#:ByQ:^$yj?joSVݑţv=DŽG42ߝ֩ Gr) ](K`{0_*/&.J#{ z?C2p81b r|ҠhߍKG#D8ճ'hEyp/TJ=̾ &{:jH>aS8`4k!D WnϲI_<)m)Gӕp9@6ܬDOn#ǭk?֦Vjoq"PSYGem8܅Ji6)>G#+3 FxTd3[VJ_ԙɸv fdEY5kjnZSyH.=V\J\=,%xҼ~${8D4q[izmw[[qkGdrxRK @8K[08< L+; 8 VJ3[Fע֓z:}C3 t7isT9ߡmd5jMA5YhjXX.Ȕ0a4_Ux-foP|F@9~@%1NuBcDXCE&w ˷LQ?2&K:1)m 5ޮ'$!1 tGP[;93g^&bߍ#O$ BlO_Ƞ_Iq$NT9Aq!Jʸt~G'g/қSbCߪғWTV4M+XC7I2%N9@}R-(džۓ?E[rODlߡ]:+%hKnr!.- .ɝ؝/f7Fj,fުbb6qR;Ba@".->":[[d$Z ]fs0#wDouA&0qe*± oĽ3Rr=WDCu#J6~tYr@җ6)UF]c;O2#j?]E|r`,XBdbGFIջ(“0zp3[8{8`E BC~_5*?+ (~cxw*C++ tvkIENDB`qmapshack-1.5.1/src/icons/48x48/PathGreen.png000644 001750 000144 00000001361 12574344250 021552 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<nIDATh=Qs3peJ+ m,z F2XYY`g⺈DBbELvf91dra9{.QUl-`Z އ5)-¸~Ilep7`yOԁsY P:s&gP ,MX$p=;I!k8kUQqn)eam& hTڎ"^EUnΔu!; HgMOKZ?Ȭ7 1olUT۴S |9Uqw D ;3Q$ |CDJ]Fe€ 3>4`O|0!ycC|ώ A7lA(60$ n瑧aй,v߉ .gE/0`ku/hW WGON/0=kw؀=X%<؅ЂPp9||BhrGz@DcAwÁ x.b6i+5X0D\}UvɯlvbÁll6cX4iKe4` x, 6laC[CcfƎB~._ܹETUY}E1R6,#Gt @Q5am56Vix@jrsrZΏjZMƌqSZP>P^3g\0W-:)S8o8…ǩTĦMo8FBhB?H,D!%Bʁ&F ]d ~!RAxʶT(b)m%s dvs̟o`ɒ/& }`f6O7(1q>MBCRJ: C|N${fٲl͛0x0mOAzm׏&rr(/a enG{[*tǙ3'slm@[Ba[zYXȑ#tEX,_Ii__)Ka۶(RUriX^44$!QSQ{x7u u- a9n EqSPGoQ_?PB(ptJٿ Q}!7jj&SXx8oɞcà1 ba2pH ?)Tyl-7pOv LL{P LTA\Oh 4!;P zp4$ e$ c ZHJyER;, p:V%DdH9:)o>"(p1 f$xW6` 8v\AV"2zon@xx%MJy_JH)OKYCK 7>!wn}{.fIN>$`(!znu">jfڵ>}7nfѢS<|~45 澐 򪤤'2 / ;I}pqg` ͓eVLM͐XǍ#<0J)+K0Jh~+in6"{fLA@*/h9 B ̸Aev ).@|!"A#p[NJ/],uȤ'0#,/W]]-N^}*{WqrBmEIENDB`qmapshack-1.5.1/src/icons/48x48/Up.png000644 001750 000144 00000001623 12574344262 020265 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsYY2tEXtSoftwarewww.inkscape.org<IDAThOh\U1SQĴTJUP!*J(EHX "*(. WDqWJjڂ+$D17I$} o1gινDf"64 (3ØC.]ݐ6@X,¯NLaEk_}Wb|=ݍW5oGDv%r'A,e#J>̜{~ D-0q /q |2@D۸aÎ5N+7k73YC"⠲L۰#]VO]x<3IG]q)@\10hoRT@DlS:s./|*pflך573\[h HWU{JW7"b_f<=]q.x03^SZ"9[fگL*}1M<̷V@DlxDv*uۉD8Ε DKdTy{mֺ,(| #9]7}jEa\ig@D1\U1eo3PYroúk4Oa=Xki+=g\e~'&c77,PQҤ mM[P}=;o ~ko-y\s~@Huq8.Ĺ33L. zԔ)+TMSGGk]P|N@&` xTU5-wr|暲W_"6+] $Q 6 D` |v(`ǎob0tL&ѥ77[f4f4͆ pa4LJ ImD_@/s &;{Gb̘ BBݙnݜQ(,~bdg";&.{LA|9󨨭mՀwDR)BKO YB8=# bٰ<<8thyu4Ο`bҩ5>>J߼99w3f ֭ )?} bژ1{8x@{NOuӵ2yr{|رAlG:u~r #Gʕ*"#p[XZʶ~\JL-\x|DDrrbVD3~CHNی 9~@ R/ &Ɨ'pu}3?7XY6,ٷo#Fgy{` $WTh{ ww;ioVSSc^U*E.7Tgxq .ׁ~2`0!&$ mm6xm6x0f;;ې6++ Zx4Z2%%8ws$.u )+EUpT]e6rܝ@ cT zhZ fɒVi7cdGK `xhHzlh|P*Njj"""!11,d`0r8'Noy Tޘ^  =|#ii)*ŋN@\_͟?:11:''GrSn0g}ɀ͛6%p|&8R0[!INNfFz=cҥL2\Q`@RS `5pZ&N e~}:uuu888P\\¢EEqDE#.sL012r3/Wҥ"a4 vpčml.TCmWsl696Xcnp_Mr,V\)̞=7n%wh<9M̊ٵ:M;a6cڴ0"#j*zEBBB|AAyyyF|||!G)))!+++… uBAɻw:St޽qpz* ssg!7o֢6X|~~~ 2Ihmme۶m;;;Yl$=}?2}m)--eƍžBnjӾBNnk/>[淕fKcc())eeeݻbРA"++K8p@DGG׋%ŤI>/_ڵKdee={Vn-qss+>FdƨS' O_#"YLQQr|f͚YYY2n8 c){4#BtOO22&ryRSS::rժUߝ>}U!M038(Ϛ^bbݻv22qR]:aeeRhiZCe,!""VnJCCn̙AAARzzgΜ޽{Ltt(˫gmluE~~%wgOX[-J{|s$aa TW*BC{\.gѢEwu+Wqܩ!0gO[a7ZH)'wbEeuuC{KTͭ[#`ŊT腭-{h4reѳgO)%%[$z=[[[vIrS*6XXX?܎)bŻۅuu3 k׮5455u:pBJ͛Yq}u;v[EE---ۜu#G?~\]^^ !/B7WWc֭zRd?a6:33Sx{{ !Tkm׷c@&x.)B$3322_N$d'__% b(8QPP 44E.$7r֬Y㞘8u?<mpԨQÆ ݲe^R :.55wΝ鞞% ÇwSΝlmḿ&x&$)=Fj?SD׷455œhٶiӦUj4_KP|I/CB:OOϳ ,Pz^pMyy9vR;W^mټy檪?y!G $I!>>>3f\`ɓ'[5MS@@%Kh4>,I(If!?6B\. prrtVT222U`hdff !6661w<<<ދځ6$AqMҭ[P(9B>>>t.r ^cciI^յ)@PDzxx,~3A&IENDB`qmapshack-1.5.1/src/icons/48x48/Scale.png000644 001750 000144 00000001002 12574344255 020721 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsߔ%tEXtSoftwarewww.inkscape.org<IDATh;KAF,QV-KBԔ)GeVIv)^f`XQULdk5A !zQݻSpk ߛց㖁/ `M&XSzv :2mEW񓯮J͂O":M;#tZZYջgNNƎ"$Yk6g=7zyĹzJꭟu9=T%(krB__~{8pNth4ƽ=i7"~U#VkZ)HŀoƁ !Nl `M&XX)N ܖ߬k5A Yo\IENDB`qmapshack-1.5.1/src/icons/48x48/Progress.png000644 001750 000144 00000001212 12622430447 021472 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDAThMKQ8\d.Z] 6IJUm8p.j,saߡ>A>s[As_fnϜs{G9L-w  Y 4 g$l[~jۨd'>W7 %P Hxyy${l+]@;YE H6 9 H)h~Ν8D1 ]©sK.v2> \& Hcyd#pֆoV@6p#4F H6|n" cc\u!K/ClK5)c&݅$ ,m By(IPl3!z hF1DU U3!X e3!HmX[S&@7Q ^}@gO=^w7:gaNBχ9Q@Z X6㗀ަGVWs'ȟ:frퟗ}(Q_}9MsyTIENDB`qmapshack-1.5.1/src/icons/48x48/SaveGISAs.png000644 001750 000144 00000003732 12574344254 021432 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs @ @|K^tEXtSoftwarewww.inkscape.org<WIDATh՚PT?wy GBA,Vc#ҍIJ;L̤v4&4u&4:6CcL:X՚k #N_$ {}3~g޼{ss۷% uR+ 5h4c rؘ3d&CfLk5Bo2Ú( ҿ#Ѐ/_OG|x폫ksz8Z> _)v#]]H|mM˯‘B u-:;ƛ42%ws9D돻 X́q-Ǖ0PAhj 7gna1GXU4ZZLU}d9,Gx/Xt;V$ Zc@Z@M89`;q~El&0J:8f+@u>p<;!{ng  \G2huV".7cx&vKƣ׎*)p7l@5>q[({tpAcIJ9[1--3^M_Gg<L wF:phnʃ!ZGRTˀNp |O8 m|og.ߺ106}I@'M%0˹sv J sw|?ߛ>=8`Amf:9vN\-efKr+/Z=ۣm46 <?MFU 8 n,/HC_5_172I9񳀧HY^ Ƅ7d0XR5Oi}QcHK@}J'h^`.@&)25s3!>+T ֓ WޭR|ij:F嚂5GERJ.L> ~;}Ri#.ScM`;*f;_IENDB`qmapshack-1.5.1/src/icons/48x48/MimeDemVRT.png000644 001750 000144 00000003466 12574344245 021622 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org<IDATh{lS?Nl# &G3Ym)a Zk&кVP==lնZ :* (a$#o8Nb'`'Əm__`JJ|9s~\^Ȩ| "O]@t2Q' (+0͜z v<`,+׷4}@? #'է3!S$yѶ?6]k4z؈02]m}jxʢxNdnZz' :`2q@& 66qt?ڱjT@@KWKT_PU` /e;t_vUOsZ{ V' ' T ynArJ\##n/ЩHT*,V7Ab3fhљ[]䭢G˵j, kXO I05J?aUhJ.8\⢨* Xu{?N܂22I7ɣэu7oQ0=ˠKk(ÚgW'-o D7`=ۨ޸OLh̹W>'|qb&VV?.;7¢r92~B(dq5%YhM0~⠽x ZkhT^galVܳ-5]?m|ֳ^7rEW}3є !ჭ;+6;kΦC. XV6=Gi=Oa3/;EHx22|o32p^D>mdȝ5׶vYR@^W.03o>K?1tWxp~}ɘ4=&$%xaE%TyYFewֳj2f.0 dq{@&C9dFEҧwIw` ,HkLzj噸9G> ~]<|8ߟJH֪aжpIENDB`qmapshack-1.5.1/src/icons/48x48/WptProx.png000644 001750 000144 00000002475 12574344263 021333 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh͚AhUI&6Hm6 f<D<#`kUԞyjzEDA"(S\VdS[6Iٿ7˾};;of7}|{L G^`{ض#ٰ_3NM za1 e4pG#I~ CEZԮ|$A<QE R_pGE`xz"\ gO03{E`* <#Mb+]'S!?$?/?'Xv ( ף_Ϧ#` -_|q5#`ƞ`TA"ٓ,/s;x=%#b]sU<, :8--$qt0^.ОX/8oᚳq9LJ@0O9~PGF{l6Si 'V,W@A޹Ek ߢ63TOX! a;mP!['ig 8*$nOE C6Z'2SvtpBZK/}(`cu%CoVϞN"4CP({,oI\L=vb6)ՏO,3ёXf.9Ciɇ:c7ԾA 쀮poW.JX qF6U7$fZ'W;1aSUH7l^'%V3؋csI*Rt7$^`#fs81f[33'L: 1oL ~RSV13>1ؘb [Ff+-:Xհhnw}8]^U}y}WFn 7˂}q: +g|,|SƏq׬N1t Ew')-%Rh՟,6&>HMƊ6|Vq_IENDB`qmapshack-1.5.1/src/icons/48x48/2NavProject.png000644 001750 000144 00000003463 12574344232 022037 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs B*tEXtSoftwarewww.inkscape.org<IDATh{UϹOJ_BRlhH "5CDcF (HJ,Gm0jDB RWD[RKi8_wۅ7~{sf~g̜YXOईp,aUF+ִ6' ^1x=A-j(PO.m(yLc``+R ?PsKS'^5x0XS''S ;tNhvWVs9[@;'eN:^l!6\Lmcr+FJZʍn!a܏tpdwKP^5*v6ӒǭہF:T<זVʣ-8"vtc30_[0R b1#01uŠ@zLB7(17BDt{ogZRwwDĿug8(5]{,mGC"q6{Gv* gD8f Cr w/tpkĭFyN;(<UnP.Vxa96ML^nȈGafm5q44G7^[M) 37I 7F}ltS!J%b죔6s098 *)eL)漄/.@kL'dZ#v*ގ1w^^5sR mSAj"ԥ'K :éyZa67M}<)a,smB"/*+n70ȗ[ {JvkhDv<{k/"v5L/ И:+n`5i#oT[{eQ\,X531*ѕCLcp~7v|)ecYD/Lhl4QFTG))RJ?J)ݔRZ?(49 <?}>Rm)RHxcBiR͊#n4t|?5"yj[c3xDu\Ix(4Qx j]L|V;RHj'q^{=¢o ~԰$"R"GkpAa!z|-"n(ZFZ(Rg^6S7] /crDuw' , 7?mxLDDusDbً]}Ew|<]C-'"L)T+(ȮR4_TWL|*url5_0 ~u9Pq';ӎUGTTiEu ri))[>)$O`\1/WG\#_W"b{v6>l='Kc[uWVBox8,^Ƃ@J39O!-/hGAw`GDu_+&bP.Rm628si|y"{%XΔ+x'ƪj^F2# ^3H/ ~ .n?~bu\,ߌ^''a;҈-P/=ҼIENDB`qmapshack-1.5.1/src/icons/48x48/TextItalic.png000644 001750 000144 00000000755 12574344257 021764 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<jIDATh홽JADA E|C=L.@4D L{)`;ogXe1kcXW`hum I5˛d6vN&*w?(?H:vCGvR:* L" ZlGH:ph@njzA{W`/&)"WFpU@C;7ۯFv"Sfr?H s iYJ}y%gZS1[WM0t,cIa" z=;Cs{i"cw6ЅD 2s&H$"&W^Xd2/as?_&qu &@t캚z+(g cG.9VX׽PUUnݪ ?ȭ[& @Վz<\Bv{Azz>{\%#E$!!رJP/`Swع _h4C* vQ{jH p }@ l?AN>Lڇ~:гg[\S V*+frr dfY@Igx~$$rDT`ge7!GW_ό}ر@4u!yyHM1,'{|ˀIDgfH3YTW[E-b _Kr|Ηِ󟋈yEָIMKj?ZO5^F:m"O!- [ ˗g"~~^SR45 G:t3g &4 _Q=Hu E8Ns 8B+ED.<]nREWpҥE.f]Wnnoz;"";08yE?0`@'Qta֬hI|M*ބ(ʀr}Z7u8{5{]s5SM2KDpRR.9_7G `2nts+(0 MO}?߽{8cIlAΝu:3fL(k96RjMZuȤI=VkFD97l˥/J1|xZTlY)BNN w$)!-pXh0M֭ٽz TloҮ]+E&ٳӰXz6VPW{ˊJLAݻKF*41~ \sлw;30@L`wiZlNMBhz4hՃ n…{ lWڼpPg\ٛ B"ʕ2 w2abk2}z U7IL~IM&V2;~Itqq8"";DA8!"%w$yFk|+E\ߒ`(x}o`ɫSO@A&K[2і@ݹC2pPZ ~~[VkQ{W&p痱IENDB`qmapshack-1.5.1/src/icons/48x48/Cut.png000644 001750 000144 00000003753 12574344236 020443 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5"tEXtSoftwarewww.inkscape.org<hIDATh{lTgڻ6~`؉a^)" *8rK< Ai)TBVQ&Q5VA( JLH0!1%<1z k{p7ݙ|̙sgPUvs%AD D 7BRkvuafYy:yo]s>"2|uPd "O&4_>03"){&e0Y;_σ BAm 'x vݦb`̅*)c@1`lbpޓ!ZEd -oC8 0pE]劕({] fqBc 1CZ_ZutAj( R wmW \U## cT5٨9+{< Mi6]\0ZҀF/L;ek~ B+TT "0Z"y<_BAk+XwpSK4Xv:@ړ^{ݳ <[b7CRY&b.4d`rL1`<ѿo'֨9"/ ~Pjw Vy8#E-PAI`ˆ׍;~8w T#n.kUPaKCCմo ]?!^ISf?8w /$AqM8WBc%r;pXYٳyؽB:`I2"P5J@ W!z"?Uu]`q_N>&^|q7k kqJ]>x`nU-4iP(.QV6C]T{p: G QUMt8@ֲ4y≟[s3g5 NB5`h}g !\*W7~v IQѻ80Bb1'=ԴGb ,bpyJ5k;IDn𜁕pWK`e~ TN!g kKgn.OFKUM ߠ!8b0 OUDYiR:U7%;0h)H8]ԃBt?U5pzOX;.\k}_` >zȳ[x I))ȢE~{ӏcZyJD$< ] BQ-/ՎMGkv/QXPLm#nnpRRrW^3fbݺG(.>WJs,7ACgg }@eг)?,~~{怮.F/VxTE<X!O0[r \ ` g}s|tJ\PwI o/xD {u|fi*`0pn{~u^+q`ߕps@\ MX] ,X,žUj Z\ZB"˥' P ̝<Im{ .̔:6;:ffR8Ikq<;`?c>+Xf6MJR)9@pi!Vާhv <.TJ \+iY0 u)|o< ' @v ߀Z!J3[4>X o.\q|0l0+}?H{Z;%-p>=3%툓/= T^vASFIa[ܟ2@<*i6s1{ =4/V҂$ R, "RI2U'W`r%_I*|ɩ0M 7COkq{U 0K ׷/@PnK =TVcԿ.RRq0ؒ TMP&Aj ƞAyrdkle.:nxTmiKy(|1??yT͢g{m"TD[1;TV)d 6Njݳ@\ͨ|r]jaH}U",TuH9 F kiUgiCpS OEgUsC ׃l\b;{vdvuB 'F z7l <\ =*tp) xQvP:b8v#w XLTYy NVmz%V&C72_s_Hh+\jd7XT?ED3K{vrSBCjNV>g# qйe/ʐ肝ׁ?:y!*)S0v3s`P<l/.&1P5;Xlq.GLsD_C(Uz; GuL^jVosz#x@  q50:UȒQ48YxqgvO(W VռLXj ( ݓREa)Е@nS5c%ks"-GAV;GW_޷&)8j6\qStm ln3[$RʎwG9^][ 6[,Wۊ93&,7t=,Ra}v{864O+ȢPu,=F|jȗ]kLW5T"ut .rН15\L ܥ:SU,tZ]ռjm9sDV^ |Fw+sz}Ųf90W[=?3^MQ.]c?,JZU-{eCH0PWEߪjzƪM֮ 5>0v%A"v`>xT7 IS8Et@Sr`~*| \zygC5AHEZ&FSdd`v{{R@tXuY#iv`vX`(4#ԦEՊ`L0m`D [k` kTA΋rƲr b;r/deo= 2 x3zQT[ YJ^P5)lVNNT|4`߈V59j("әS@̤ VbÚ풨R5#Ս wWP5P{K1=U8]?ЏY,;RO!k>awRK6ZVjiܺƬ&7U:`Ņ}.w0VVT};]H˯TVg:r_]'t(ClNSgjemd?t;M>Ocu vF9_;efx.U5VH<{S={# 8KEXMhV/o*耍f_0. \~{~ Ԁu$vfTW5iˮo-~"?#Հ v_:>$t{͔9{0u2QIENDB`qmapshack-1.5.1/src/icons/48x48/AddWpt.png000644 001750 000144 00000002245 12574344234 021064 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsCtEXtSoftwarewww.inkscape.org<"IDAThՙ]lUoThX-ZhD#H4&k^%ywrg $$h1P4V wiNN;MNfgs,Jo<O'~E$";&mAu)r!뮀~J۪=(F7  ^{!W@D7<‚xٓx6m`?@d[2PrHG7?#r1D$3 ^ؒl2pVEd]a0ElCY7f9+t1 2]pD&`=y5$L@d`i{A?WhWN4+ LH1[uչ#Z/tjZ(` 8 &`!G?X[fXaHFj+Ⱥ15>QøCר֢|% ɻG)w ٚRX7I᠀AL<ߞm"196"(`aousP-'fA]hs<;MCLO96RgºHEB&f x */rJ4&|ͤDSYV]S-G‚Nmm$o`II7x  ((䜥`8 /Q} ( P2`\#To4V @DoTdz"0kmF|b#( \ȆN|2"\̆N|AlUu> D ?@wG_(2 ]v8[۠/z09N%,6`vG?e%\j24 *z=+2I_W/{tf"FOqk>_,LsoլRx: (y.ȆiVYu6r9qU0, (a|XIX~6 XX/`YI FO)}e{09ryWIENDB`qmapshack-1.5.1/src/icons/48x48/Undo.png000644 001750 000144 00000002532 12574344262 020606 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh[Uߚ@r)LT˵T "ܤU )" '**ZR! J$DC (h %ii;<̜9/{{ DB܏3ʮ6 uZ$Z3'0}˚DK"bk1aX3%5Dđ32M'B??"#b7FLvEp7Kqbkw?sx wf曝<ƾF"JGOB؄N~\0G[Yoя_RfUaVV=܎2vYg3vFE ֨:.u" ":,fV`cv<)K"37򹀈XD'egzx@YQyQf>ב"dr6wLoRœN7eGJ{XnI7v60?.{uWL^_2#Xe96VlPNgfig2si1i07>%)W=CJ;Ԕ"u̗3f6eV,%*SaRk|<fZPb2ey|f߀J1ʁ{_` 7&;,@%uVqJ sdc/(^n`$3śTMronR(3̳p8~_j;pRplee܆ p0Fސ+p ^eq+A2`,OꄳF2{˓d3 &ZJlH&»(k\_gz;rMS\G U{'qs7=P jx,3[ {0s&;f܎t!35.i;̉=%Qu3,e=u]zJ@,t-s*ڞ\ UzGDl^ ?z83߻IENDB`qmapshack-1.5.1/src/icons/48x48/Font.png000644 001750 000144 00000001444 12574344241 020605 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsߔ%tEXtSoftwarewww.inkscape.org<IDAThKhQhAF|KE݈;Qq+n EmhQA+*҅Bi ucZE,ZwS6w2wNsܙ$&Z!* )<0fH^I:UQq190ǀEF@ XC\%N^GWGJ "h?Y|X$h"@f2R $M}hesYo1I$}5e>5L3 ) #_q|+Q740; - #h `:ҭUB3%;-S"(JʐV&RhK<:EA?_֛lqU7OGaM9t %8r6/K(2/[T&c@ x28(9q`D,_Q, l/Rd S9wȿ2Z@)^Δ?v1>.TboT"@KZP;؆HƧ.wwe shuפ 9ZyZ"ŒvUoGN)[GLyut?K?z2iLe5Bb]hH"o5O<ž=U붩U&^si*(ؘ_vJQ܀2o(= 00|ͳ:.VؖVZU@(/"ڄ%[}ut:.3a,!!>8 iYfΠwYڵ.]'aX4bg3>6`Iz<~fLe9lѽ{W200?V߭V={`V7@LR!B/#,8,hBP B~; IENDB`qmapshack-1.5.1/src/icons/48x48/MimeVRT.png000644 001750 000144 00000005427 12574344246 021174 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs5tEXtSoftwarewww.inkscape.org< IDATh{XU?0000oàE.*iZ-Z힙֮t-rzj2ul5sKM/P " (}qf`0yoy~y{s~#<m%2; E`’ɸÜCin*em1clbvH0_;p!\} gOlMé#(:5FAG-ȚL$&׮ ڻll6!Hndպr #-k6Fc+֠ -<5xj8DɨG"V]. Zrx[>E]m# 4wU ԾeťCjya"5OA$[ :j*Oqd޷{X쥲0.*2F- %Ife`V?ɂ $N!t b!#[8USl!'~}Jwd2pynՖ؃1 <DFPXfo'{7@c%ɦp`{V:~A yh{N?"g⅟ -Mxu$+L` "ؐojAk$s"F<ʲÖP0$ Or)7IvUlVo` gO|Њ_pgO|LRq4(U9He.\,+^+z&C]߀.Wrfk1i;u2:6KyxN.eib'1mC WOd:NU@= cD"߼ X߰}U]{V3Pcka2kh'4 6*!yo>EH? ܍5Gr))eX, gXHlEAPX!0CʁE&lbԳELn=tH $<ȞZ e/d2PRk 'pR#xxURؕ !m@>OVVX˲絬\IuE!5i]_tm_~fGKؼj&3tb X@õJ!xhI{:\ C*sq(gR'#W6d"{f#uJܯ_i63y<. Kɼ7+*L!IH_p\vm"G7sz+}\T_MeY 1 qsYŸ:%o̱$A'6Cn>sT!kb嬲II' f@<Ӧq8oKQok{d7Ԯ|`ho}inli9߱bJ陽߀ :봆 ;7--Y|y;ڕ+ܙ6:g%$UTҚT'`=kW.X#G/Tyգf>#ff(=h>MҡwMͥ㌘%8Sr1 M_J@Xݸu9Gve.&9DJ&va6uS* U}4D ~A47^]dpXUGWRc#A4-Y|cڔ~:H ?=_`_ٸb*kXN҂@dVMiC!wLZj9Cin52ΝA9}-f0GЩNxXCMJJdh(:jOR}FiWMtJ'(\ F$c^}'9}/@,GPCx~y&06g?c?Qo#gq@ U)_ԩ$NYpUzw8Me3xE8B;6̧^>Zhn̆`LtˏajpӦҋ{ػmzr[þmo0`\|{XXǙ­.Fc+g 'o2rZC6n)ض~.&W3dBK|FIc%K_j,R?c߰mLF=%c~~ ^|d fDbk68F~$ R]tsVJ51ܕ+qsE&"f3|H?5%\8Ot&_,drW{CMe'۵Oc43Ҋ)N"of\y PydvTnTkn݁3Ƿ7G"ꋅvKOS;-ˏo*m0J0pz,/jIENDB`qmapshack-1.5.1/src/icons/48x48/ActCycle.png000644 001750 000144 00000003074 12616741641 021371 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 5tEXtSoftwarewww.inkscape.org<IDATh[lTU5RR Ѐ (HiCHԢDc|AH s<-qnxDi*"Z@hmt̞33CK2i?>ku#JoF z>2>2>2>2^@V&7 @u3m=DL\`d ϪV[k !tSw|U+C:?!TOD컒[].ã[CG̀alZ@) >åoe#U뢪%C1 R5/-@9Ϋ$ۈ%U)mwB#bzlA{="[r=cX|v@h~X 24/^ =@H 8sP.~eT]\{/Gh15diȪW]m; b*>*-@;<?HBH]t EU_4'|c@ĞS* #/ Wݹa { Ǔg3$F] g[Z`'ph|i_J$>>6썡9NeeK:|0E,ۀ (jQ@iN@|kȞ߱0 ,$Z?⎳T``=9:y-bg'8mě(U6y pg|h*s6|d #W0 cVHvsSU`=pj[  rocCmOdsg N@a9`H5*P]c+0xRb;H~s.w:TO/x򚪵tO5X u/N Meġ` > hYGƻ(0 $b7!0b^Ӎ('Vzo!*5c`Bl( 0%8k[aՠy&cxp@N]2H,G @̄mzI} DuKf 5f$ʁ_V:T W5i-9^n{\〩_gfq0 U"U%>LjJ~Ԍ7Eu{ŭS ?T_TCI nq3SIǃ@A.hAbCq:jTkVb)Nq'ԉ= ɮUIyCR}.\kFwY~n@ӍokYS@CR*Su8pt2N>M\fl ; !^R?[r}uH~\R nHrxHQ^U&I @w~;:wƸndN`ٳq(/ǀPT`t" حԣqu/ȿDwρLρLρL;?aIENDB`qmapshack-1.5.1/src/icons/48x48/CSrcSlope.png000644 001750 000144 00000002537 12621572226 021537 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs a aJ%tEXtSoftwarewww.inkscape.org<IDATh_Eݭm)EKAjBlb4F $KPBZtjdM1$6K"!-Yi{>mtΜ3g73gf~La SNO W{6ށfqv%{%coLH@(ƩP^&\V*^ޔ'B[`|S a}@xCkiqvg^M`Z9ړh3b(d}M|RY_.|%]Bomla$I5C0*nzUIǡlbV*a.qbt1Mݱ(@XGߋ{}-+L,Pj;KrVPC[b@u\ `ӻ~c5o-%PҤŽU2y]ĥ/dvFRʠq>6y8c lkBBmg q8៦pm@% އK7=Q^EL 'M.q%.#i7 `+ 1Ao>ƒV6|Vp:dr,!a 6Oh c){7V%{|j183PYNRmrC'~ƞΤ߃7>xH ! ~¿x6/jV˗T e:5A27sӱtؔD?!Gʫ SQ '0cߏjjF^rqt.3x.'9u\mխ/3gmѡ9('j/ QisHAuCG@c3}I~=pI*aw(d!c[${VF,>~1_#,/64Tw'y~;Μ$ǝp*<RIENDB`qmapshack-1.5.1/src/icons/48x48/Zoom.png000644 001750 000144 00000003370 12574344264 020630 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<uIDAThy]U:CE,B%E+@(S@1JDJ `I!! SD"2I[lz}nhmy%7=gZDfW?TAw߆[([,rRE,5+뽘2_7@DO\G~mkP㑚w@DT#hz^GpOʕ&@DLĭ8q F7,sxfqŘ P >=Y\ۓ?h1,| 1aPf|4W7H-x1m m|x,ϰ% +;%o1M`\3E7Xw˭O1:J^۔ЉZ,{U/,W]J<ڥ|us%϶1ZaeL;T1!,;]v[%:݁uR"Ȝ$#Yٹ.ic<$5Ek2֊#p$UxC @Qyq./ b bΚ-6:dU~i Ύn++ߍU6dq^'>0+"b#.RDKuR~qHYbo׼IG̼?R&OY@Wq!f399H,įjV,#,F̼ |X5+&'6+'|a3-;[6끸@D|kNq u~wPZIENDB`qmapshack-1.5.1/src/icons/48x48/NoFix.png000644 001750 000144 00000001673 12574344247 020734 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYszzzStEXtSoftwarewww.inkscape.org<8IDAThKq_~jK&BPq4& aau)/zB;5h7Oz!""P;xQ"tx:o{ r( mlDzv|5Ϟq[R~Ac >$-pP~lneGU"  }}xߏ毈Á'*^ ɤ L(Dk45Qy ̍œWdʣQhXYr33$҂WG>RD^.-,(z5"ɗ/| 51%Yr70u~>}*SRn5fU^tZcwȖzGGKzLI,.(v{)JE@fi8# ,Y9(Vk+$].E~uQSd<4Պ3A/Cn{АXcq!}TsVKΒzx HwxDQIq.pXkam S]]SY{<|zjm-T b#b>xw݄F/.o}Pfoj [X[Z{uu?=]) #0 #0 #ް|~lX9zsn`_S82IENDB`qmapshack-1.5.1/src/icons/48x48/UnLock.png000644 001750 000144 00000002745 12574344262 021102 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsеtEXtSoftwarewww.inkscape.org<bIDAThݘMlTUgcZ*BB$5RZ#"Z-jDѕpa ab66 Z%Pj m)Զvڙigi'/s==QU2 sOsUU5 ,UXPqu8 Mk?Y_bzD ߁',X bڀ_"9Z}Sgq{3l$jV7j|" " `lxقYQa/@%.,i^;p Zwך>X ,]% ">(bOs8~ ||ExXe]Uݓi;y!OzLAO>/E$|)elޟhbd?|{a5𩂞ݘh2 x~NGi:x#Q"ͅ2q#ݤw&3.v4BySD סX\gu tE}|Clsj1>x; ߩWZsS4åI= VHl|IAݕM8cG>zq hQ`%k3 pr S H>f80v(,t8E/<")))*jw*S qoHǝ@ xKg,hmДcPto(p&Bf"*<&I7C|n#`lp*TծU WIFa7p1Fjjj&h Ӈ{{m CYYMii)9#"Xy+퉩Ҷmn(b̫Aq37Ƽ<иcG-"E5UA#^ JvLVh0H`9SI~+}O7j@`z?Gm;ͼIENDB`qmapshack-1.5.1/src/icons/48x48/AddArea.png000644 001750 000144 00000004226 12574344232 021161 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYsG2QtEXtSoftwarewww.inkscape.org<IDATh{pU@JB78P4M[(/iE*V[3֊i}PQ%h[EPV0"}}隹skk$fL6px3ٌY դ)~LRo~@(iy$  O#.tu 6bEmA:hlIkeZ <#(T0P0=3i&\q·tsV7|ł!3 Rk6r2t˂ܤTV d>Vp`` Anp#ՂG_ < X'8&`y pxA:'(olN.wit5!%^u29\K݊t Ñ/M0a]yp f`G\h  p;]ܷ`T+ w+X"hH2W-pe,.M!AπɂmV  q+7AL'>~)#@I t 8鞙us>zpF#52೟Jֺ4I07+//>|N ɝ_C. oT9\ }ڻku8(qKW0eO |pn .\# BٍA`II'W]7LDHC[1@jV-8*Spa;J. <umb9;: @\,/;_ VL[9R^!j>E\9"8L]ǥe$ib  ;v)Yӵ0ae*L&w,`гeuq} +SA)ZA҅:lJtݖ2] [) dO}ldP]^dMH U:օ5t.;~Zo`;;YD9p N1UW,ׁH5^vZBf# ߉N`>0ޭ\B^7"ಁU@w`8v1l#_ L&nZ`/0,6IW`k3z`blrGlHİ@pOn |LH9h>ƘVQLV'{p%Rρʴ clFa@ l %6VTgUAv┱{}w4[E{O&H=Ntܿ7'ӻ+  ?XD2VQV, ,[;yH+7x*SxwMIe ͉E;{U&kQ*L_Z2ʀŢ)TLZ"-n21ϵ+' T[Y|YM4 f;ƕ@/lezۃ6[ɦ\WiLwA]dU.PԘ˖<։ʼä u.rQNGj:Bb*S ƱѦK:R`wH]oT@'*3Hg)&L=9TFpi`9p)0isOgU*`R0f50qLv\MS>0 6D`=Hn,`1<>P1ñ~!I61b_"-IB cʐJe͡ 6CzCV`&4)yT(ņX!Z+ rlOa{1HyVUԩL}بމtawG4`cæ 5pI=}a(|FXS@!GiuQW}N q:"PIENDB`qmapshack-1.5.1/src/icons/48x48/ActBike.png000644 001750 000144 00000002753 12616741641 021207 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs 5tEXtSoftwarewww.inkscape.org<hIDATh[VWb-mV lSC֨5KMLJprPSR_\웩7/*)N)U% V!ƒr|8g;]1j2k{]RDFW.ft3: ))utGAnǩ?s=yS1_1Ts{S\}bбDOjkp W8 m׶#ٜMo d_ݗG`<.LamD |96=; 2ue9n," qJy7ĝ&<\o`I~H)|O:1\Ф)Ipm)7~gp/`.Zb5>U]6M)}x@%b b ^s /%P_p8T{H+Ec=10_"2̮"N*E\A܏t-t=q#zھAEE yЫX9aHq\l1vߢ t6I>N,K~L)~ȞBk9">gx'א)t18у2~#6>D mW1x +zvuRzlG?OlY'4";[(^W+ceDJC/4YVEd;]ݗ"6}7L)/*v"meO=C{H)YN)i8xZ rl* YOk '"w)ڪR~#-d)=rNc~)zWŊfǷ\R'pV?Dd5W?&8}_uQI@Eo\͋U:܈j;\w0;oևS-֎\;IG*Jxi6gd 7+0-X][+>,z(pK㪈S߂kZ(}h@'};Rb5VD6;S>oj@u~5 -Ÿˁ~ڑ:RڼSik9fqmY?z"(JJw8 wΦǘF`\Stz5vWDGd/5^QT_Xv=~J͜Wz3&_$WDw?ҖZ^ bL&0}gc- .k-MG?& b"X2ش%RבDEl- &<ٜyc#TbQ)ZꦌTE{{pnշإŕV >Ww+HqBҳ_$9XKZA,Wԇcֺ췭5]h:}Ә!i4ftgQXIENDB`qmapshack-1.5.1/src/icons/48x48/Bubble.png000644 001750 000144 00000001615 12574344235 021075 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs <tEXtSoftwarewww.inkscape.org< IDATh1H[a#.v*(\c'PD!Tm'U(v+աH).Vc%t]HB`@)B$yH;ԄF}/OA8wz ^4;| B!x]Y@oo/ف "⶞Q^@UX]]E\n+1 N9B,J5rEZY6i}xPT<5gIY}qhherFfыGPpAֿIRu>mXV J5+p#ڨK766anttmmm+`| ]l6:,RQqgF'''[$yڨ_*Ü[T5{bN5@Ømwi]i (EԬJf*F@;R i~,L^H.k'q7Jq Z$?|qxx(X :^ mķ177+辑=5FH4tDW""su8r'L&#SSSvNn%^Hlnnb~~ޱ$dH$^dp|E ɧiONNofo5V!pX_Th:2=  Huf|;M3gA</;~4EÊIENDB`qmapshack-1.5.1/src/icons/48x48/AddTrk.png000644 001750 000144 00000002757 12574344233 021061 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs0&tEXtSoftwarewww.inkscape.org<lIDAThՙkUUkFglq|NRKA>!B R,C +zLf3HͰR1,'Rl9WϜ\?w_g5Io`ZStm/(,=%8%Pઽg ~ 'Fa\+᥂"U vD$`pDD^W`;#&6`v3eor`m`s!M{ࢠ}J!'ABo#ؖBn< j}\ VD;"%Oj`AJB40TДBij A}wHDMpRPO~us׆`lV bx17ڷ=Op(Da/#A@܏IpW(oxEN`UW#8pQ0,ؠ-{|&W,x($bN?=[@LɲC b>ʛ:}F=\(}T,  tp'F4pQ0*M?]ibH?8D4`9fgX>{hX=)4yBLQ3q*wCU{? H^qf(+Igߥg0+f}j]Adij`;Hn۔Rzc1Hn6sঈ)r  $r{-`[B&$>*$!ہOcr,$zKwf݀@EHB`vs?Y蚂i5 -(sJt;4?}.'6 NyVx'9)ҵЧ)/Y18#7 ۋѿ3Kx ¬:`-t>JG 5"7/0htp"5YxZ(l0, 9L 7ӛY̺}#}o)8L 07W1 uQdge~` I~{7S؉7PYc;ǣj2`Pz  )(Ҩ2Q)-+&wR?STG.݀wuVa-y`a0I;<@:.TH9jE}. ^T7?#|1Vg-"{we9g(u:| j it(B:7=ohѐN<4GV/ Ԋ=С=(p-/"%IENDB`qmapshack-1.5.1/src/icons/48x48/Copy.png000644 001750 000144 00000001211 12574344236 020605 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs^^8-tEXtSoftwarewww.inkscape.org<IDATh?hQ?g`:("R$ti  -Pp$ 8NEQ(t0 إDЭC'5CV@r$ϡ@Kx8;{,A>( lOgB@JIX]V)W(RgM3:1E4mzC#zNH^K2~k6;rR*;R\f ~`ii'+G"/),~s7_>;JN?mowזvERu#H@>Pb-.Xfsx^/ۭcpS\"Rq,"D$!"r/,IENDB`qmapshack-1.5.1/src/icons/48x48/TextLeft.png000644 001750 000144 00000000623 12574344260 021435 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs&:4tEXtSoftwarewww.inkscape.org<IDATh1jAQ,,2B&*HrKo / V 6yMR c`y/` D#ke5Ro>c{9[x-u|@O .4<j?_};ht _ڝ<;N ]hs ,B1ĐQk-C]H*BB,T `b B%֘[H](4*@1P u!Yd'g@"57CPIENDB`qmapshack-1.5.1/src/icons/48x48/PointShow.png000644 001750 000144 00000001545 12574344251 021634 0ustar00oeichlerusers000000 000000 PNG  IHDR00WsBIT|d pHYs;tEXtSoftwarewww.inkscape.org<IDATh=hAs@" HR"H.FAmDTMk%jg%$B T.+TD(b@SDxގn`}ǜVF!oY7F#@hfm!R) cvׁCQ9>ľ,TVw:ڧ%GWZC`]|Q+jI`A5q ؛p!N43cJ@1iر5% H1Ib8jjpJ~;ВQs3YǗ/i/5jJ0#pX^|Y;OFxXz*h TBA{Z:yqt۲?oqOQuEODZIAۣowč- PM ` 8Fqңߤ@>`TY7#NLY9Fszߗ_kjawެcp`DPU˂BU]' 1aiP,G-l"[ VƪƽPq m!CN')Yv"- ylIډF)DCx>h5 lO`~:6+G7q. 70ֵρ I-M#@hy7ZC-IENDB`qmapshack-1.5.1/src/icons/Check.svg000644 001750 000144 00000003772 12527654570 020145 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/makeicons000755 001750 000144 00000001626 12621572226 020272 0ustar00oeichlerusers000000 000000 #!/bin/bash function call_inkscape { # $1: width = height # $2: source file (*.svg) # $3: target file (*.png) echo -n "$1x$1 " inkscape -D -w $1 -h $1 $2 --export-png=$1x$1/$3 >/dev/null 2>&1 if [ ! $? -eq 0 ]; then echo -n "ERROR " fi } function convert { # $1: source file echo -n "generating icons for $1... " NAME=`echo $1 | sed -e 's/svg$/png/'` if [[ $1 =~ Act.*\.svg ]]; then call_inkscape 16 $1 $NAME fi call_inkscape 32 $1 $NAME call_inkscape 48 $1 $NAME echo "" } which inkscape >/dev/null 2>&1 if [ ! $? -eq 0 ]; then echo "this script requires inkscape, aborting..." fi if [ "$1" == "" ]; then echo "generating ALL icons..." for i in *.svg; do convert "$i" done else if [ -f $1 ]; then convert "$1" else echo "$1 does not exist or is no regular file, aborting..." fi fi qmapshack-1.5.1/src/icons/Zoom.svg000644 001750 000144 00000005104 12574344161 020035 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/UnLock.svg000644 001750 000144 00000006715 12527654570 020323 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Font.svg000644 001750 000144 00000006736 12527654570 020041 0ustar00oeichlerusers000000 000000 image/svg+xml T T qmapshack-1.5.1/src/icons/Error.svg000644 001750 000144 00000004562 12527654570 020217 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Reset.svg000644 001750 000144 00000005235 12527654570 020206 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TextCenter.svg000644 001750 000144 00000006550 12527654570 021212 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/AreaMove.svg000644 001750 000144 00000027176 12527654570 020633 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Track.svg000644 001750 000144 00000016305 12527654570 020170 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/icons.svg.save000644 001750 000144 00001700557 12527654570 021206 0ustar00oeichlerusers000000 000000 image/svg+xml 3D 2D X ! GPX Tip ? T T RMAP JNX IMG VRT MAP T qmapshack-1.5.1/src/icons/2NavProject.svg000644 001750 000144 00000010132 12527654570 021251 0ustar00oeichlerusers000000 000000 image/svg+xml 2NV qmapshack-1.5.1/src/icons/cache/minicon.svg000644 001750 000144 00000004166 12527654570 021625 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/greenpin.svg000644 001750 000144 00000014410 12527654570 021771 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/needs_maintenance.svg000644 001750 000144 00000013123 12527654570 023622 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/event.svg000644 001750 000144 00000027160 12527654570 021311 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/32x32/virtual.png000644 001750 000144 00000003015 12527654570 022415 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org<IDATXWmhSW~o4ަM? E(lE!eQժ0v`":Kcر?6m"tfcuZ6I7yc$6IS9=y~0h-`Bz} q&Fhtx~~_k"G/޽x<8PWWhZX g -Koss+,I Z??>/-iEEb4wцgϦU;33Qxdle orҥ _KoFSxa\ "l6ظ|ZTTh4~ZBI<f̲lf?zT-x I$qFebbBHԄSN-޻wϹ s}رح[|> O7 l8Nf||<(#lޢj5PHS|OQ Qr"`-$-Eh p- "[$Jo( B˪ODd5EA-.c /U 9|=lc]ĜU^0e#%UZl(,KAq%/ETՉy2,}5Ŭ̈R9@hp*Kv7@wΜG&/6Y5/qKW ÜNV>&|"GDy?)rɛ ΆXK\ąKaɔ@HdXSTnzKAޏV_n4|0][%ZQ`R2*\OFN&مoI:zsY?گvYi=z%?_&j7p= nYi؜jF}P(bttTw1Z[[ +W; i<-9ohh,--pYjjj>>tP\g/j[[݀^g a+muuuߟ?~ZsEEEmC8QUp४r?;44W~7-< |򷛛#@v?^`YY[Lu0wMIENDB`qmapshack-1.5.1/src/icons/cache/32x32/maxicon.png000644 001750 000144 00000000362 12527654570 022367 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsYtEXtSoftwarewww.inkscape.org<oIDATXױ 0 D!ؐ`#[ $"]YrOr,+Eج pWhAu\:}QU (۠ &kxv s@v@'o{w@skIENDB`qmapshack-1.5.1/src/icons/cache/32x32/restore.png000644 001750 000144 00000000422 12527654570 022411 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs66NQtEXtSoftwarewww.inkscape.org<IDATX1 A oe =B-DY/ )ɃTo! $Vi@ Dt]u  #Ǧ4TJ)/G Kk S0]ś{G 0j`ZN)IENDB`qmapshack-1.5.1/src/icons/cache/32x32/cito.png000644 001750 000144 00000004137 12527654570 021673 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsԑPtEXtSoftwarewww.inkscape.org<IDATX{pT?}{f`AFhlT"hAXp:ZюLiQ:-F:ZbZA w>=c<$s|νWH)΂\!oŪ*]peFIt6-mѽ;wDL0.@.YY3,Nv MAyRĞXKt+.g-2^GҤ;8 ȹB//l'$`k ?PMbHYAP5G6$s.y^LsVN!NT7\J|ue0vQ^>xSL8IfR(\濶޼2i]nx!k1i.xEۏ VR !awO'*b_Ն9ܽ-ޅ5A2߹CaݭF>@!KxQ;5*K8i'襮7b[5ښrb1:j%l ۛa6} Z^=UndL<Oîb0&ҷT 26Ҟ/.4zx!7I?`VʅijEbG};Z+h`O؊!t;kD5š!;V+03ic%u@˱]㯎 &3纵>:vp%\XS-!83q=v'>' *%1; ܀p*4>@ );QŇ$8c'^Dx;w Yyw"txfm!<vY5B3GnlBʾw|IoBo=\UT-bbAчR/txnJy;_)8ǨNfD_Kueڞ￙ 6PJ򟢯jo8S5]PPMa(j -(eóahJ6ԽhR>cBWI]+f?Z؏3X\VIENDB`qmapshack-1.5.1/src/icons/cache/32x32/event.png000644 001750 000144 00000002152 12527654570 022051 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsNtEXtSoftwarewww.inkscape.org<IDATX_hWe?{3ԉXԍdȬN"D"n !."o&#Đ.B`]̙Ks@Ul=b;cn }{y=d=W6QdgϷmE^i&f&<]c1,n2!k,RϦj3ZW`}ңjͱmaW5"RHvNYHc'ND y_HSJcx3 U:}~ _dM5]N/%Uտ>UqpZUo\u/J?WUcN)'wZN+g`{|gGRܙ6gz`{(H߭pM7YfCKa-vMulIw,ˣ1`uzS\\s=V־NJaZ/x(*,S%L? h# |=ƾrдxʣCDkfcWoGllG(.4@sB vQeF( 2+Ӗd  u ݖJ2z1NMCp1WacF/8&R>]%/?$ :n^t W fji1~ {1񜌉TXhJܚ)`f-Q ?+i7m!'-@D|qn˵H1;YB~ij$"-26su {%Z._FZ70/`^90_#8JF:u}rkGAg,6z,%!bžPu0b脥``au@IENDB`qmapshack-1.5.1/src/icons/cache/32x32/up_icon.png000644 001750 000144 00000000542 12527654570 022365 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs774tEXtSoftwarewww.inkscape.org<IDATX 0 @U:H'D٭tأTr/D E&$~*#"U}߰. " ևT4@|?`thf-PfS  ^fzgIYY!I!I!Oc}4-PH!BtBtBFBFBVBVyVkn@bB!~pELo5IENDB`qmapshack-1.5.1/src/icons/cache/32x32/star.png000644 001750 000144 00000002540 12527654570 021702 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsƺ5tEXtSoftwarewww.inkscape.org<IDATXL[UϡA[mhiQ-_)Mnn`-h8H`f-18 (l:qnR[8p1AM,0hGR#מw|ι!mZ-M_˸z19tr""(UJtSߏ9\u8W@ )IChN$WNȏS`@'NCD\1,v!!PlP+5ihy4""Rz]Lي "-Wa,0Td%|(6yJKԱ:Ц CBhɜCHīxiK g7_dz)"}|b2fMcc3SSS&:̙n¤˷J"|GQ<4ţh(O 7^% |+@fM"0 ",Ι2 1 1,掋TOD6yǍu"*^ J- 4QAv fc:wu!3ref;Օ|E6{m'{$򶕉>W%S:g}gg 7ˮʁ9XqIKC]&=GF&h?vg! "rg}*AB ],@˭k:SR"ڽKh( t=OEKk'kbxHOJz<4^dWkz ;;ޙakB[ai[W;_ rOI% h* #gc" :?# fGTx85%TGYѩ6)Ra:o^+ߚ}ͅ?G]׃W6<{ :[ܮ`OӾWi۰(JMlSs<0>o~o4Q{o uspݽ/w&n E}$3@r^(~`׉C!ڽ+AMI.[)Cñ=dZM4HoyAo(=U1d\Z9F߭LF']~xg(}aO2Y@RN3E]e`Jc'DZ^ #FYkcLCScƤHMڹBeE6!_)&S# c]\l+]bfYeee/47N@1ٱV,2oD||G.X5{W>R-~oc::x(`wXhj@6/پ/r͓RP`t:/?@W &8 )_H3cI qjqaWCĽUoVq˽o.BX-KpO/\S7G û'd*plpdq1X[uUHan.e bYQΌ[qD&A*2&41{NĦY [ǵbf+J `ccZvʱLk'.ltkCq!9rblY@)}1sUGÖ4'yw9SQ T<)_ը˲ 7xu33ZsI?JZicN&+g8y(  Z+ˍ:R FQsI83e߮s0x!h琏FΖIؖ#)qP(yި-JMMBM,J_ ZPgb?U c]\~ܗA'nL4 ݕ/[mV(|pVV3555;v$E 7X^4޴Sr9< }`ti$OzW80RkҺ`RM s jL oGІxd.AEslfqw.h=IENDB`qmapshack-1.5.1/src/icons/cache/32x32/minicon.png000644 001750 000144 00000000362 12527654570 022365 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsYtEXtSoftwarewww.inkscape.org<oIDATXױ 0 D!ؐ`#[ $"]YrOr,+Eج pWhAu\:}QU (۠ &kxv s@v@'o{w@skIENDB`qmapshack-1.5.1/src/icons/cache/32x32/treasure.png000644 001750 000144 00000003325 12527654570 022565 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org<RIDATXV{Tg;s絳3;;3ؔvyAB`hhcijXbl",m)I5es̾Rz/f9;{w?b0I7⋊;-P6?Nec8x_\N&O&rz90.tD8WnYs؄)Ab"{~cM!@D+N_7wGU`g9 ǐȔhR0^sW l wxR@ી7W>>Ӵ-Y" \ArH#7VSDX2D\.Z" nv߾.1S:΂8M#lݡTڼNmNyӶuPW@F?jSUgVs5MеwT\M}uPO#ԁ HOrwHI\_"rx4H@}duifI]Wl9jT +ٜvGpW rhTB]Om't޴ Sa&V1lY|;Thjm뛪rȗ,IJkY-x*Y:s!O}+|@D%?Po4XSk,piEI?INqp  YM⿺ym:,ճWͽN*oh:'ƀ7?_=0R9WRX " ;Ѥ[\}y_e1/}3f %s7c̚/% cl]Dҩ?MkjGiH` 8u>e9sPhb-c-4U W.=3btS_+w)k.]+vo :gWn[IENDB`qmapshack-1.5.1/src/icons/cache/32x32/log.png000644 001750 000144 00000003601 12527654570 021511 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs< tEXtSoftwarewww.inkscape.org<IDATX[lgfwmzI]m."V6nD / W<8!"I$6U#$Ж8-ЇHBBr`qKm/T^ggNfvv}+O:ٙ9ߙ8CDBX&rJyUY,EDRSI{`o7T5X> @DX9_[n,40D ?:u7Dw<̛Wֶ|yEӶ֚gϴUOH(@I`?Ч?3'Ns}2=Z}F׮-=0oX :25UUP,j%>jQCʾ F@DWJ$/{R@%W[ \lv@ 4Cee\"R<{VsF!2yWpf+,\tUN}W-s ~J "CȰn}҂j<˟GUdQ2{;>/A~H; yMSZj3g: M/l/J$(+%!UfUfQfTQfi3bh9W_52j`2kf 0,\8x!/DH۷TUE~(YowMO@E zEM )C@5Wk~4oi&dιeʔ@u !: .0WQT D$He;'&(+ ܉3 (Rn! `2> d)-hG ,JA[X<o` 3 l ?"A_:Egċ}W{͒Jep! Tc$WGX$aˮ]6N}ٛ/6I{;ŵk߽0z{#j Ғ…q ZjԤ<;,&{>b>xh˖" B?ir)2ÍM{`n9`˖!v~e Ь[Ȱo_zggPlOcbAss+W&l/,ɢ7l<+invF%JcSլpz{}xjÀ={L r=LQH'JKil~[PenĿ02$FkI%sw O1!"f-~ov+' %QTLzB2zto`M~W%z DG $M寶3vQKR7N]*zS)E-zh bo"yd8n ҆^@:w0^!a}F@gpdtD.BίO#,5[<V1Fܓy [1&v.'hg ,B [c{q]- MwAhh(Z-Vdgo<Ï祐K;\`EIɄNd2h49{ 7ཛྷ(*(vw:!/\JVEZJ"ߌf+9֭!zu6Qٰ`>yj@8jzV| ȌO-Wϊ3ETF=T<,Ǣus~zP(cskQS0-qpg3E奃Ah;v<0 \0@) yKŒs_Nwp'. ?n7mywTSBEэn6 Q()))2/oQ*dHI>\<%\asL&P`&#rg7.Ctt4 8f9zhv;mhhRTɤ "<σyaf0װ]-y,95QHz<܎W %qt:}1cƤdeeյy \.?X4S{zGu }f@brr/^7_Ju"oCuu5ҟ-=]pܹ;v$eff ZVV#<<<?. ̄e{aaI r9( zeZ?"YҵkZ|>~^tر7;v}9N޽{Cv[III;322Vm۶mTEii»;R ;w*-yVRRrnrCƿvqqq+rrrZ:::455FAφ!DAvvagΜD]]ZcUdd$ɵk׈r\ IENDB`qmapshack-1.5.1/src/icons/cache/32x32/halfstar.png000644 001750 000144 00000002550 12527654570 022536 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsktEXtSoftwarewww.inkscape.org<IDATXmlSU箮/wE][mG+{$.3Y%!5/MDMXL~˪i63pdOdGL4Ɓq{ڎ1֎II_9;ϹJ)Ru^oL' iX^ۢ;j &Վ5vVݞjZ[]۲f3gm6W:˲2ޒQvfZ h3 P[k=Poe( jl-!Q@NNc^lvVyh4X,'8Na!10i+ 0fN%< JT*y~2B@@TdgCQT@Tfd Y[[WG$`ׯwLM xE޽c.,N={4%z}>Q)J%'*(J*(*(lseUOC~~>RYI "DQ $I(((Lye׮)])6oR0aA 4yXBrss4sO *щl=nuV9@idS^ >o7ܼgX,IrBbU81ϟz?_}}}C$= ֶmύܿ'># סCN577.**br9VkR(/plgFc8T6rN[,cYII=Yw6zhH@ggI˿ڗƧ;=::*~:88țL&C رϯet~uf%ν{vhvdv9 HZvNa\]J\vIENDB`qmapshack-1.5.1/src/icons/cache/32x32/letterbox.png000644 001750 000144 00000004072 12527654570 022743 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsXtEXtSoftwarewww.inkscape.org<IDATX{PTrYX rUA.D4"*Ti E8XvfF#H6[Z4HP@l0YEx]?*"N̜?_ƍEvVx;Qk=w.w )+wszzso= N1( 㰷JMF\, /~ʭGM\Fc˦udUp&JvH3'"7v 8unu'ĉŝwax`Alqe]:Vt~^׺PƏf N|Μb@$I5<6%2ac1-\L}GCϺn`P/e`mUڡ|\\︹,zml:i\]{kZw-Żu񱱄NdYZfQW<>>\bRLZe˗ƕHF/ FȲŸ]_8yH(=322lذ@9rdYbbbŒi:'bԩDFccc݇|Uz.]Bqa!1k-\.9g9pH3,˒$I+f$**:؇^Gyޯݻt̨*v:[NgZFq Eff\WZ;ч+л@ɳ(= Ba?U\\U\\UllUSSɓUɒ YWP'1߸GU̍?poIO_멻a4}xsSRt㤤"IRzGg%*|sfAn hF=fG@];uPEFk;+VGt!<"ѿN#;;CefGK=4,XѣS_{"7 P~ ^I>SCط25 /[?8O P]]-FX, C)1immeT~ȠA0V $::zPqqqxzzb41 +++!''7΁:u*/듀bE!--Ļacc… v'N1pvI||<&Lxm1}tF}Xzٳg1Ldggτ?FM;Ҷm۞z@/ϣ!Ch"##ۓ%^o$I<3oHTWW[]pAr$ TBIENDB`qmapshack-1.5.1/src/icons/cache/32x32/dnf.png000644 001750 000144 00000003463 12527654570 021505 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org<IDATXklWwfvnxWl8aSڍMjaF%wɘn$_>pؾt:}s9K(G| ߎGhgG|^(U Ǜz^gxFB54QV^ȆOqo'ozh!Yư:;$ޛbldr f]%dY9[j~.{ X[v jqzi d-7E%m)v8ȸoj Wu~'^܅TUoGY'oo{C[` Dj|y?VJrL*coFxkd34{(&PT; ܝ15mEUQH;5=kh 0ݱePMP(&xJ> BZbv1, 55 fۅ nHY)vJ X֕K_(LE,;ܝDM;d4Hd͕[L |M%5{pIH/mpMEY%:04Nj$Hj3a,D̼=wG[p/+pw )1.$T0MZxP],&^0 PBOL4># e[3T|)hwMH\г?0l۽}Md^Ihyz`‘ܧ@ $KokgP#[(88̰"s4o]Oi6 e* <#7 ;cʰ ܥ )ed CꆯʚU9Jrdr4c{I_=GYuVó\;N? 0bk8x>cP}H zZyɕa]/tNKG rk]畍/7S'm>?ShJr Q℣A;$`q?v't-V$IENDB`qmapshack-1.5.1/src/icons/cache/32x32/pushpin.png000644 001750 000144 00000002270 12527654570 022417 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  OEtEXtSoftwarewww.inkscape.org<5IDATX_L[u??Zn˅4Ƥ[: F*y!1F4qouF,3d=4Q_䁨l.f`pĔs[hr=>gҖˀ{ϯ/D TZJ< n 0$".RʩEfZ T#p AH_Dd4ŧ q$"3ȔlbU/`eM*@Z Q>$q@sQ?A&@LA 2 O@BŘkR?솖8m˷qh#;DlUqRỡY(0gI ;=/pЯ[@Rg_GV:8g;ح+l6j!30cTB6(>AKCeL7(;LW_@U*-[:_P>t3Z廁8T[h֓` Gr",0UUcN){JDzUDN[x<fc jЪP]Phs$ `;fǐA)R[np߾}MMM<әYx= 4Pzo8WTTio8MB/1$2>""(p"RJO)4?66ƫ{Pc$pF ܹsK}}}@ Giiif/p\-ED2Pm۶Ħ*>11!pl%7;[lyxJaga5IENDB`qmapshack-1.5.1/src/icons/cache/32x32/unknown.png000644 001750 000144 00000003513 12527654570 022431 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsTKtEXtSoftwarewww.inkscape.org<IDATXWoLSY^KU-qGp)@la #@!F:X1&Lf]M&~3 ;Y?H;``-C8*KBőm>)}'9=;s﹄1\nmm=PWWeZ bHA55w+VtmmmKCCQѼr}$7wrB?ew%?,be% Cee JKKVStܾ}{ihhܹs'c9lGqVVE ??BR=Baa{D(,… c/^x<[hoo{NӺ~:]z" `(|4/TTI;<<|d{B'N8p8,~/@BJ(&QP* IyD̠t7((Dmopf9# :uRccUePJp۷o&tr@<̌_}>_5(-} 33YX !Ptwwu<ؔ0N)E<AN *BR>.Guu91:(Z05uU===M@oT!˲b ߆#(86 čP B!ǏSp.ZDjWLR6>LNE6>zzZ^iZAxh+,u]9AX Μhn)dYy>H(EB3 Bde)- (ЗIeYޓ>_KK%?=?%% M?;$IZ^Hҳ,g4=<{_Ly T7+9/J /S8Iv/}Wz|>ݟRJe_fx?Ѯ+M)!$뛐8D"/*FBN4:~f) W~lfK[7:?Z FtفHNփU7oN~#tsss555Gh<̹L{GƎ Ըlv\zgަlIS&##c7o0!u%쫩Jq(**YޑawCW,L&Sb9]{wKlTTY%Rss{n-( QlFѡ/h j< x<O 1IENDB`qmapshack-1.5.1/src/icons/cache/32x32/bluepin.png000644 001750 000144 00000002442 12527654570 022370 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs  OEtEXtSoftwarewww.inkscape.org<IDATX_lU?~m0Ɛ̸ͪ0g^4h[%i&." > xM$AH @ ' ]kF׵]ǿnsUN4ى8E# G'㪚,Ol])l]XkeH_bkheUHUm7UE3[K R=!`k33CE.T܉K[g=f-"bמj;Θ@U  )2'?ttRyqxŎt^g3kY([[B\hH4F,>pt "bbqVLꍼ~-2*^`}˲ldbRUyVv dn~TuW8x Is' F1LkW]̚uÒQ 2Pe[ Xif_p6T %Ɇ 2Ʉ'!VSR@sY)@sJK0,:Ȯ鬕(u>0ϕĄM5DQ[!Š HG!z"Ӭ?q$sKQB $8Nh溎o~hȨ +ٮcRՑ""˗/lӦMeӷKmӧ!u$N 6Bfۍal5MH{(ҫ=7T5 eND"RC"2(6I,H'C2$Et``g1 cւ GH3^L9 H'54bx Hwh׮]\8pLŒN4X<4+1$qHBE (vp8̡Cx|?9>bpi։f)t3_}}K[nDMLLm&CmC)`ZH$DbڿB 絧gwK|}1_8jxn hii̙3?\.WϽPSSwUUutt4qaŊq i(kY+W ~f{~ yvҥo3&rIENDB`qmapshack-1.5.1/src/icons/cache/32x32/waypoint-flag-red.png000644 001750 000144 00000001262 12527654570 024262 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs6VtEXtSoftwarewww.inkscape.org</IDATXӻkAf;s/8ba!VxKa-jaeqV`oeBPDQsF%q۟Ea I$~̲3Ì`fvL5uڱn0"7ǣb.@81Q`0{7 E$B;|z-J?Zߩ\.q>araq.("PD @p=xQ*=^?PBXT-+ijFc"D )a*EblLYBM& ь-! EL#`Qɀ'oXL0ķA;>(Z pu%h5̞ pp~!;l"1}{[%D睟hZ)At>3t~G x~f0XxW#)WZIBT=f6 "| [ݥR@ H) ӭc]Xrw*~˗\ d3 ɗ]hc c6n, s8XIENDB`qmapshack-1.5.1/src/icons/cache/32x32/write_note.png000644 001750 000144 00000001224 12527654570 023106 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs% tEXtSoftwarewww.inkscape.org<IDATX͗MhQْF-T(zԃP*{%GE4Q XԪA>J>6n7۝̏7-]:Z=*5Vf+&UnA%fAzOPTqi1Eoʐ0Q.<.dK e" dUieP$65 !/$L;'4 x$Ⲷܟ!%+f:C̥P.c"RR' G }'=r58@ۺ:T7>" i+£wރ tv=X2/yKma5qW} O> O75uy[ p$«Y^N/ׇQ`sLNwJ cm>{y3@<- 5 0jL 8uoC0 Kpy KzmqxmIENDB`qmapshack-1.5.1/src/icons/cache/32x32/mega.png000644 001750 000144 00000004044 12527654570 021643 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsY;tEXtSoftwarewww.inkscape.org<IDATXilTb %lb%RBH’F4UZ(jӦ-$)AP0$Ҙ/1cl7 ^f/緧.xjl=lRMϱmƷYPR?}EҳC\Z'פEQV#e5/`yX#V!8 B[/>pg1`ncmS !sd ~~铰( dg &9EFe2w$soF\i\yF5ïk=jķڸj.n~`SJ0]k{X#JRy5@gY-fu|v/kMF5|$i>)G 3)سRz y#m"Jɰ{VnSxRbcHq/ԌX81 Z]\mfU(s Ӓ( QAwa1I)GxyûxBr냓lwfR"gT\ epf6A4xސRXR6讄Uwn25Ó (3q\w][ߝ^T[HXYېdK`awzMpFxQBƠSz `w<0 `ֶxBw&kwO\u&͇E'L"A/lgxA*X.~^=YD7 xd3Qwy,{e%5F֋E[* Qh+"JBɎ~8~B6{a>u )B  vw"&azډwp+Ogu9!ZV,SgV#,MCY a 03r"ikY=Zb6h9A(Ku+f{oWK*VFǯ"W8נT j+L2K;"OG<y~'\6/HT|5]@S&z"T7L$0שvҁv^:I"*A`4aj}> -&A5RGE ఄL&%* BfiSEl@SLҠ)k`NnMA!x\hqV"6n1=ͣS ti(w:DEm6(-:p=/}¹2Ex 9,u,ևldYq&+0866?,SXXnqF\.l@IƈDˮ?^ephO44-`=fh(jJ,j7~qhَhv;|4fQZ%K)##d!V 2` ?-s;83g{񚙈D{ٳ96!''M)- .6ۨ=[ܣMq[曠IY᧣ ͕(Ǐ_%\!9B_;Xc2u ,\.1@ &z),0iRH)if3S &ZTd=q~y SO)"IgP(v\1z5K. ='hM A&j"<7 0챌gH9|4ESM\`WcX$4f4*&wHdNN$刉Bp9jWR 9<- $b RpAM?cD"Aʕ[ qGf!%RL3hA$e2bq~ٛX)}fg鑱LSS$%%vljjb'NכR\\ @{{;1~| NŒfO Xgveپ`&egON,~;vP[[KAAAȑ#0w\Z[[ٲMIL6c{z &]eWk\Y[om#r ^]v ˲8vkYڅK,fqlUUHs7$nr(L2+V œ9s0ի:<`;9_{-}׭JUCUQUB!ck{shy%WԶ6nݼy9s&}9]zz<.}iS{{ǮB!ck7[;%G;wf .\HOPSSKss3˗/'NsvkE]ݘzd֮M5>ْ,kUd鸑]Lcd G-/ vvxn_[׬\12~yW7DB?+S 7L¦Mώu͹nz1YPvNO;jfY3Ɂ7-[]4gp8\ЍC&z15K6oP!=z$ AD"=}}9~/ & !D (i" }=g~~6|c9nIӋ]􉤞4% bˍby뤞>A/7Bd:6*3X+fq tƹsUr i#4)Y~ W^5GGG|rn=55Ǐ u~Z7Y/ JLJOi#+ގeӦM466R__oH)~V|޸{EFQ^xA8}޸UVU~'ᲹV4Ԅ-87o㌎2==֭[ٹs)*69a$j+[Ngڊlٲ(WV+IV WXh4ۿ?~سgOQV</WU,)>>>N رcLLL՜bJjvs$Ass3PVVF<f+6dJ- MTU TUU;;;p/_dnZZZBd4U]FWI E7~BuN8Çijj*ʱX`r~2_^_ Fn>---4440;;;vLLLj`䷑\:Lw5UK\Tt;w֭[b1D"\%kh!9[s%[*ǞVB0^W.Buq)!IضmصkO<1{6+5gk8 @tuujfǓ3iZ(ǢGU^[Gwu@v{%J~ݻu̽|ExdUgda#1;;Ï=jڜ @IENDB`qmapshack-1.5.1/src/icons/cache/32x32/parking.png000644 001750 000144 00000003001 12527654570 022355 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsXtEXtSoftwarewww.inkscape.org<~IDATXWOLv'C8P dMp&ڥc*u;J[+ʤJ2A.S;K&QUJ;ԍՊ@#SKSRR8;0{PB $K{l~Qd*HqXFXYkYgg DRj/Yrʻ-o]4lZ3ϫפD$ R-nNZ ;K-EP&X/`,:UW,æ9}xv( ]k$&1lMM8^{s3d 7ǒv;&ǚH'YQuzO`D.TWGVіt; MfKa`% By?ĝߏ8B@;N3W7V@).OΛxl?<4ٔ0`jjj @yy9***P__ݾ=ڍSKһ7A!o@pO2xQXXH.]D{2x)B.ce0ո{nƲ766btt ȑ#=H޽{æӸp¦QcKaXYt!(-7 >+PZZ-v 0 s&A eeTa`W׾M߿Vц&@4=L)v'mI5{&7p!|+1`p$$N$µQa/ht, )vn7G:TUUm-(r^Muz_zzz󚚚1W"pRJ^: yyy8vXx+&^搛J&PżfYP1]ρ-5[躎09ѣG7-T/yquXˋXt.PPP>ZEx+XE RVBNNZ[[,a,:[hIB*)n0p욤guŦ廗߸f3d:MNTXuFfF04=ȇVJMN O&q)-zWHi)tp O< D*ݕW F@k>nʫFN ;}}~mݣf_sM涻)y᲼lmJ!Il6<#MhזRi{LفזQ\cȟL<Ha{Y<8>t)ΧzMa nF3+k<O^>]{ʖӅB4-AcbL-gHɼ> Xے~v< R"IENDB`qmapshack-1.5.1/src/icons/cache/32x32/traditional.png000644 001750 000144 00000003203 12527654570 023240 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsKtEXtSoftwarewww.inkscape.org<IDATX{PUƟs|^lU\\bk  fx ^2/IYL֌h%5eӔ2]&GoC!T,4.&B(,Bv9}oĿ02$FkI%sw O1!"f-~ov+' %QTLzB2zto`M~W%z DG $M寶3vQKR7N]*zS)E-zh bo"yd8n ҆^@:w0^!a}F@gpdtD.BίO#,5[<V1Fܓy [1&v.'hg ,B [c{q]- MwAhh(Z-Vdgo<Ï祐K;\`EIɄNd2h49{ 7ཛྷ(*(vw:!/\JVEZJ"ߌf+9֭!zu6Qٰ`>yj@8jzV| ȌO-Wϊ3ETF=T<,Ǣus~zP(cskQS0-qpg3E奃Ah;v<0 \0@) yKŒs_Nwp'. ?n7mywTSBEэn6 Q()))2/oQ*dHI>\<%\asL&P`&#rg7.Ctt4 8f9zhv;mhhRTɤ "<σyaf0װ]-y,95QHz<܎W %qt:}1cƤdeeյy \.?X4S{zGu }f@brr/^7_Ju"oCuu5ҟ-=]pܹ;v$eff ZVV#<<<?. ̄e{aaI r9( zeZ?"YҵkZ|>~^tر7;v}9N޽{Cv[III;322Vm۶mTEii»;R ;w*-yVRRrnrCƿvqqq+rrrZ:::455FAφ!DAvvagΜD]]ZcUdd$ɵk׈r\ IENDB`qmapshack-1.5.1/src/icons/cache/32x32/corrected.png000644 001750 000144 00000003715 12527654570 022710 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs ~tEXtSoftwarewww.inkscape.org<JIDATXkl1q8@,d>TeP%(a]F]`E`tTJVԮA$2I(d4:n!Hbb;N챎?9yJeen~=55@Q4{*nٳbŊP|" dc&0> >c%p >5C($&np GO»XF6>f+HQ:{Ñ(I;;fw1ܡBs>6ژ+P[[1==s˕4lRz]{;,~!D>&.W,DKyO_Ν;s]!K LFFv7,Zb<I"ʥ) /sp:Yv{^9s,bd{.kk[۟ݛ~+9x"(Q )e#hvhGtSD3rpFiM.AbFcʤ)뚚 BBi%sKexz5]1#[)ׯ_䃃O +SPR7mH^jB aM+11՛_N;JC=>JXN`%Br/B$o[!'%ѺGЙJ,=:n;v~QV@1x9PffϽL_]S{KGFVs…a3IH0>-DJuN<7⃃(CjTp;?1SR10{c?mkQZt]zŀI)s~X^[[;_JѣGկO]]N+ͪ$G'<](\uEg\څ8#H4.]{K>%ikcVlY3eu}ytÇ9AHE556S|LQ`vZ(-f`0<'H3^!B$WW_~;WW&Pi-k6l7 <j{b|ԥ-.ɺx<ԊBcPE{2''ϒjÑsǟ6uï22K0wӀ0$SSsYMc2i="nlQÓg+f?OGXlνэ᫿ӘZ=;Ϣ+*uIFFGUeUw0eJ®%H/db N%ݭvb}QUF`drn]cN#Y,#_G (WDi} /##cz 3]G@ UŶ?,!SQߟ H>4-= ^$_%PKTʷ_O`ڷG0`)EPM,QU@vKJ5n:BRRrL߃`W]Xi6AlJ %Es::LlE"=C(b\Cp}F/^X~jrz#RRypX)#ptERJQi*3F@?/Rʇ}Vd29ޮ IOuw_^toD(D>ǼM0]`-~,R>:u+v{j L&:TSs~X ~!c311/ʣX[>tfMp3>tBܦ?x;2~_4?0JVQ]LkaKxΰE#zaáںhdTS@!c|h;#?"Z@Z+}tmVK.EgjZ-Jk"[Ѽpw֯"JQ{P9b19#)(3gy/#<&tiN1Q oh<H䲆YT@QH(ů4έXZsֲ!D)@k)y18-ghNf k8 5d`pTrz姰1'ֱϡ\Sad$3$Q@}7ᚵk{I_JM|2׺7E'IWuII> @+h}@w|_PN_ 02W>8Y_? 0ؽA{iRFG&7Tp @h(E6j/ojxI2ѫ/ wZpB.۽sCtv(IENDB`qmapshack-1.5.1/src/icons/cache/32x32/needs_maintenance.png000644 001750 000144 00000001216 12527654570 024370 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org< IDATX׽kSQO%*)֗." b WA'W( "P"Ji6qu&WhCs~aP'VIENDB`qmapshack-1.5.1/src/icons/cache/32x32/found.png000644 001750 000144 00000003576 12527654570 022056 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org<IDATX]lgfwڤd[ KEC 6(|Y %/!I$6j#$Rh6 VR jU<D@SHĀ,Q[(~̎_2޹=3犪8*ڗ/^:W$ $\oISc(YZo5mQH`H$"wuC_v_zTՑ'xj}BɐT3$ř9˓C>/٩H$%q5OH4@Iplfwko,[&-[̻pT՚xw Kgj3?[㥖 O?]? ]9qHŊX KsbV@mg)qqB TU iHkOFjWvg_O{=SU٦?j7P ujShJ&'Umϧ>`l̢}|s9Ey'DZan\$T/ 4O2\IDbF]7BR#Xj!XiN1 Ʌ ȇ<[,h7} ,]RF:-Ldի-2pEDI&ʸ{7CUU^{UMUA(EwLBԘw4pTOE _TƐ8"ZmmRTMLD5%{"E#$3K c %uEQD"j 4(XN9x\&.Hh{(e8̛qAT&EA1x3id UJl"(&鋇{W7^ArH&#<<"'xFt/S,Y{-3__(ñ28 t/w8j _W+,d®]6Wj)!J '~/P`JpN2HscAIdZaxtش`ѢB~oLdW>ϰgmmZ>0wEPb?}T3ٰƍ:"K6Wy1vN D~OU@o 6 8zd*vg9 &=R ._  ᆱ~ ~uurz{NUUGzz8NOmQ8~䭷Lxa~ѹ1y; M"*M:pԮ]ߒ])sS1 ѴbR7(~3&f9Y4`<2k @4zBL1NӪZ7ICVp?Ú6 / /R*A5e 01 IZi4MeZ%UUL˲H2VVIENDB`qmapshack-1.5.1/src/icons/cache/32x32/star_empty.png000644 001750 000144 00000002233 12527654570 023117 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsktEXtSoftwarewww.inkscape.org<IDATX[H#g7NnVBl"jH$eiLԤٔj.V-ZEEоXؗ׶,B` 1V!>K biM5̦LYbX滜93|g>`u5۫%5IwRX7"pXlH$js0666T* ݭ( ÁF\׻n!T'MlcV]*UVIh*wύar Ns B?~۫z Xַjuq fh4@ (GZ^ikwBPYw%D"dL&󌢨)J_\\<:+ np`Eq0 m9|># q +(id25Kt6ei&ɴHd{crrݦj/6H$ aZr::;;B`pqww7 fl~M 3 P( ߋ㿰/W,7FS0633ĢH$**[[[zt:}J%FI X ߗ) NkWwbؓrqX˟+(P(V JZ[[[j0 ZL&h4]3Io677U 5H$/LH$~-5/tbVB/>|.kD"l6[R([|FGGGr )NN4-'VXZv @PKӽ1 #&K$_c 4ʾP:ێ'gxy<nwJ]]]$ApE' ( +9tͽ83??Q >}vvvړᐯFv+FFFsub|sޔӥc$IENDB`qmapshack-1.5.1/src/icons/cache/32x32/OCMLogo.png000644 001750 000144 00000003171 12527654570 022171 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs<tEXtSoftwarewww.inkscape.org<IDATX{l?`H<( KC\&RHkI4LHBb6f҇bRD:R5emAmPp5$fGq|3c^i)=Oq;R->X:7%xq법kuPowm`?kD#yon73>ϧNoy䢙;d/=?ZL/+v|-}閼>۞O`_v%%OLv#3++++^Se}g5sBxR|_iyKPJw+&2`~ggskG-[vt׮]#6m:z-"}BqU>@s'::^5"Tmm</_fɒ% np<~ܬ>YLR\oߎbl6SZZՊ餷t:X,ɜJq7fU YxԶoAT1/ckՅ_E`+~kxxÇs﷬4tx?uqARMr;ZG;٩^I1xF0j***qn7۶mPmmm kwvS_$"P(p^nXBγ à˗KffqwM[OV sqf 0 rss Bg~V XR ""Y#]79++ `*pRFP)u*bXlt׆aal6[W~E5 D".'9:FUX[ȬvYfvsX.tg!#zVvϷ!EعsҐyEt#'ĆR2&5L/*#=(NL@ˊHW#y͈v3w0ۧL٭,C]e;L{RD1`د(n@rGjvWy<)y.p8<@Gz.0L%]=Pu8 %ұCׯ7'=)[H pi^N(Ha(%:۔Ci;li.h#CZt}0?ykE|X {2v& mR8;viW)eps -Z xUWHDk{rt cӶD5^M]4'}ސ<@j}DY"R0FogG)u&+ڞLxءZ ED&H@"4 `8 z%.r q1G3wAup1qdR: óp[Gx3G@ܙ!xA]lG@exxe"X{adXpVx翘CIENDB`qmapshack-1.5.1/src/icons/cache/32x32/webcam.png000644 001750 000144 00000003527 12527654570 022175 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsTN0tEXtSoftwarewww.inkscape.org<IDATXklw޻޵k/~Įm$4J4mqkETJTUFjJ Rh6V@! Ƭ6>fg̽z ޏ39̽pQ:zڃnT"䩞+jB9g31b1ض<}@ ,DO(@uu.Y>@dm0B d2Auchh +x O˲|  }gvv6()AN_xqsBEQv*RC!c-SJ黄||)(877O)"Ư5666TVV*.+/zJ {ïsu}@WWW,4m -iV]lѿI[̙37na{+aJ=Y>< z$'0 law}enLJ%X$  GwކGƚDoo %bЌ]]/5 B& UYA7,錅C'PS"P퓧&QY˂(J)(h=9/ S!2 ZTOD}&G.q jW:~p~(z$˲f2$$hPQd `4뇭f18ڲ, UAWQ$lYIYɲ,Pdv^sнOAj~5SAԖe|S(i "&mEK~eAb0vq{^xUl 4i-_sޟ.es&5Rx|h-1;1n"ঁbŽMO2j8\?$O˷ m8\h?C;(@Y= xjU Clrj*0z= ,WRP 7*Eh[ml}Vh .\kƽ~+Zć8{E<0ՕiR0z5-ūa9Wh;[@)M2m ҺkMW^@`ƊwD_`=TAžwb_+u:`(MKPJ*8v<*,sI ,a$l|0&{8w]G /g [WZQQND>ۀO`'"&l tqFבF*rgPy9~t-k/))BC-D,O?M&C+UU(uEQy{$IV2Lf Rq8cLޤ5^_;B ,/󵕔Y:l{ ˲(JpȪGcr{p-b4k D\u8ŞRKqӤSEȒ A@iRL$t4Iz>J ! rZ**M1m"s5OТ1dnp^|y Gcjǫ*T'HyDc9H #^upy(`'ϯWx-6_Mի2&lX0&ѿ^#/?NdĦcz&bt7fmIMC!yXMoW6IENDB`qmapshack-1.5.1/src/icons/cache/32x32/down_icon.png000644 001750 000144 00000000646 12527654570 022715 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYs774tEXtSoftwarewww.inkscape.org<#IDATXэ@\!׊uE\ ؇H]fe%? ~u8@&t.@MuED D@czZatױ^**~7]f m0 B{(, TM|}NB>+P(ח*)q lTX_ؠ`L&*T0כ zQU0(+ z@A!V(C aЍ) #~ Cy)<o/dIENDB`qmapshack-1.5.1/src/icons/cache/32x32/earth.png000644 001750 000144 00000003300 12527654570 022027 0ustar00oeichlerusers000000 000000 PNG  IHDR szzsBIT|d pHYsF. tEXtSoftwarewww.inkscape.org<=IDATXlS?ٱ&!a $m6H *D(J uZU=?tkspuEyaWb&4_!y@'HD&v.v`OUsWayA3Y+JtZ@yކض8 (r~aH<aamй fՓϋٰ|:1/˲^ZJ:UHOn;w͋.^lS@a.l:럴}{Q@>OU߅y^Fo:lIqqκFR@9݇_ /yjRM6?qCe񫻿ɵ۳IT56/v˲^9A1jg6ȴPS㲩(7]ViY۪8S2АݼiSKM-2G5~Sիs; F`0Sþ;y>%sn TոWwhoo7HA m?(?ܱcG^cwݻkV m/Ft*yޓRʒH$2WB|~سgOu}E,#hޮ#S6@ss4G&''c!;v ye=111A8fvwwwpX{! PA8dK(UjY;iwN:et:~fSi[?)]]] !^iY7{zz؏a¶m5!D(ۏ=z!o! wIENDB`qmapshack-1.5.1/src/icons/cache/treasure.svg000644 001750 000144 00000056162 12527654570 022026 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/SearchIcon.svg000644 001750 000144 00000012212 12527654570 022176 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/OCMLogo.svg000644 001750 000144 00000211432 12527654570 021424 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/star_empty.svg000644 001750 000144 00000005353 12527654570 022357 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/dnf.svg000644 001750 000144 00000017731 12527654570 020742 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/DistIcon.svg000644 001750 000144 00000020744 12527654570 021705 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Recommended_at_night.svg000644 001750 000144 00000031670 12527654570 027152 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Special_Tool_Required.svg000755 001750 000144 00000041731 12527654570 027435 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Public_restrooms_nearby.svg000755 001750 000144 00000020620 12527654570 027742 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Medium_hike_(1km-10km).svg000755 001750 000144 00000013732 12527654570 027120 0ustar00oeichlerusers000000 000000 image/svg+xml <10km qmapshack-1.5.1/src/icons/cache/attributes/no_Medium_hike_(1km-10km).svg000755 001750 000144 00000015467 12527654570 026743 0ustar00oeichlerusers000000 000000 image/svg+xml <10km qmapshack-1.5.1/src/icons/cache/attributes/dis_Thorns.svg000755 001750 000144 00000017745 12527654570 024505 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Field_Puzzle.svg000755 001750 000144 00000627712 12527654570 025646 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Off-road_vehicles.svg000755 001750 000144 00000014575 12527654570 026566 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Access_or_parking_fee.svg000755 001750 000144 00000013024 12527654570 027445 0ustar00oeichlerusers000000 000000 image/svg+xml $ qmapshack-1.5.1/src/icons/cache/attributes/no_Night_Cache.svg000755 001750 000144 00000135605 12527654570 025215 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Fuel_Nearby.svg000755 001750 000144 00000014406 12527654570 025247 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Motorcycles.svg000755 001750 000144 00000020376 12527654570 025525 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Abandoned_Structure.svg000755 001750 000144 00000015557 12527654570 027162 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Truck_Driver_RV.svg000755 001750 000144 00000017236 12527654570 026235 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Difficult_climbing.svg000755 001750 000144 00000017545 12527654570 026640 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Abandoned_Structure.svg000755 001750 000144 00000016131 12527654570 027004 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Poison_plants.svg000755 001750 000144 00000022433 12527654570 026046 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Poison_plants.svg000755 001750 000144 00000020646 12527654570 026073 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Short_hike_(less_than_1km).svg000755 001750 000144 00000013315 12527654570 030266 0ustar00oeichlerusers000000 000000 image/svg+xml <1km qmapshack-1.5.1/src/icons/cache/attributes/yes_Picnic_tables_nearby.svg000755 001750 000144 00000012362 12527654570 027336 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Food_Nearby.svg000755 001750 000144 00000020376 12527654570 025432 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Telephone_nearby.svg000755 001750 000144 00000022315 12527654570 026521 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Wheelchair_accessible.svg000755 001750 000144 00000016433 12527654570 027306 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Recommended_for_kids.svg000755 001750 000144 00000034713 12527654570 027345 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Abandoned_mines.svg000755 001750 000144 00000015170 12527654570 026264 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Drinking_water_nearby.svg000755 001750 000144 00000034471 12527654570 027367 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Public_transportation.svg000755 001750 000144 00000016622 12527654570 027627 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Recommended_for_kids.svg000755 001750 000144 00000035407 12527654570 027162 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Horses.svg000755 001750 000144 00000033223 12527654570 024315 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Night_Cache.svg000755 001750 000144 00000134063 12527654570 025376 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Thorns.svg000755 001750 000144 00000020172 12527654570 024512 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Dangerous_area.svg000755 001750 000144 00000043603 12527654570 026160 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_UV_Light_Required.svg000755 001750 000144 00000013500 12527654570 026553 0ustar00oeichlerusers000000 000000 image/svg+xml UV qmapshack-1.5.1/src/icons/cache/attributes/dis_Drinking_water_nearby.svg000755 001750 000144 00000035053 12527654570 027527 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Long_Hike_(+10km).svg000755 001750 000144 00000016451 12527654570 026104 0ustar00oeichlerusers000000 000000 image/svg+xml >10km qmapshack-1.5.1/src/icons/cache/attributes/yes_Stealth_required.svg000755 001750 000144 00000022176 12527654570 026547 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Special_Tool_Required.svg000755 001750 000144 00000042213 12527654570 027452 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Camping_available.svg000755 001750 000144 00000014066 12527654570 026620 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Quads.svg000755 001750 000144 00000016334 12527654570 024317 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Truck_Driver_RV.svg000755 001750 000144 00000021231 12527654570 026244 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_May_require_swimming.svg000755 001750 000144 00000022064 12527654570 027433 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Recommended_at_night.svg000644 001750 000144 00000033576 12527654570 027345 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Dogs.svg000755 001750 000144 00000013130 12527654570 023741 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Food_Nearby.svg000755 001750 000144 00000021104 12527654570 025234 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Snowmobiles.svg000755 001750 000144 00000015631 12527654570 025521 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Abandoned_mines.svg000755 001750 000144 00000015043 12527654570 026304 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Campfires.svg000755 001750 000144 00000042232 12527654570 025147 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Recommended_at_night.svg000644 001750 000144 00000030640 12527654570 027311 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Parking_available.svg000755 001750 000144 00000013210 12527654570 026437 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/attributes/no_Snowmobiles.svg000755 001750 000144 00000016253 12527654570 025357 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Medium_hike_(1km-10km).svg000755 001750 000144 00000016461 12527654570 027101 0ustar00oeichlerusers000000 000000 image/svg+xml <10km qmapshack-1.5.1/src/icons/cache/attributes/yes_Takes_less_than_an_hour.svg000755 001750 000144 00000076014 12527654570 030065 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Dogs.svg000755 001750 000144 00000013505 12527654570 024112 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Picnic_tables_nearby.svg000755 001750 000144 00000013054 12527654570 027314 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Snowshoes.svg000755 001750 000144 00000020310 12527654570 025176 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Cross_Country_Skis.svg000755 001750 000144 00000016677 12527654570 027061 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Available_at_all_times.svg000755 001750 000144 00000013615 12527654570 027452 0ustar00oeichlerusers000000 000000 image/svg+xml 24/7 qmapshack-1.5.1/src/icons/cache/attributes/no_Poison_plants.svg000755 001750 000144 00000021767 12527654570 025714 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Stealth_required.svg000755 001750 000144 00000022305 12527654570 026355 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Park_and_Grab.svg000755 001750 000144 00000115610 12527654570 025525 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/attributes/yes_Horses.svg000755 001750 000144 00000032501 12527654570 024477 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Campfires.svg000755 001750 000144 00000042750 12527654570 024770 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Scuba_gear.svg000755 001750 000144 00000046514 12527654570 025257 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Recommended_for_kids.svg000755 001750 000144 00000035764 12527654570 027333 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Boat.svg000755 001750 000144 00000021355 12527654570 024105 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Boat.svg000755 001750 000144 00000020145 12527654570 024122 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Wheelchair_accessible.svg000755 001750 000144 00000015721 12527654570 027471 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Scenic_view.svg000755 001750 000144 00000014307 12527654570 025476 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Significant_Hike.svg000755 001750 000144 00000017012 12527654570 026411 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Difficult_climbing.svg000755 001750 000144 00000020161 12527654570 026767 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Stroller_accessible.svg000755 001750 000144 00000016170 12527654570 027202 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Quads.svg000755 001750 000144 00000017046 12527654570 024134 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Drinking_water_nearby.svg000755 001750 000144 00000033757 12527654570 027561 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Significant_Hike.svg000755 001750 000144 00000017427 12527654570 026260 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Stroller_accessible.svg000755 001750 000144 00000016112 12527654570 027217 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Long_Hike_(+10km).svg000755 001750 000144 00000015470 12527654570 025741 0ustar00oeichlerusers000000 000000 image/svg+xml >10km qmapshack-1.5.1/src/icons/cache/attributes/no_Motorcycles.svg000755 001750 000144 00000020015 12527654570 025350 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Truck_Driver_RV.svg000755 001750 000144 00000021744 12527654570 026071 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Wheelchair_accessible.svg000755 001750 000144 00000016012 12527654570 027442 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Fuel_Nearby.svg000755 001750 000144 00000015001 12527654570 025402 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Watch_for_livestock.svg000755 001750 000144 00000043257 12527654570 027245 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Horses.svg000755 001750 000144 00000032603 12527654570 024461 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Available_at_all_times.svg000755 001750 000144 00000012477 12527654570 027643 0ustar00oeichlerusers000000 000000 image/svg+xml 24/7 qmapshack-1.5.1/src/icons/cache/attributes/no_Short_hike_(less_than_1km).svg000755 001750 000144 00000015453 12527654570 030107 0ustar00oeichlerusers000000 000000 image/svg+xml <1km qmapshack-1.5.1/src/icons/cache/attributes/yes_Access_or_parking_fee.svg000755 001750 000144 00000011720 12527654570 027467 0ustar00oeichlerusers000000 000000 image/svg+xml $ qmapshack-1.5.1/src/icons/cache/attributes/yes_Scuba_gear.svg000755 001750 000144 00000046413 12527654570 025276 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Picnic_tables_nearby.svg000755 001750 000144 00000013476 12527654570 027161 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_May_require_wading.svg000755 001750 000144 00000035213 12527654570 027052 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Campfires.svg000755 001750 000144 00000042406 12527654570 025131 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Bicycles.svg000755 001750 000144 00000024515 12527654570 024756 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Telephone_nearby.svg000755 001750 000144 00000022447 12527654570 026506 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Dangerous_area.svg000755 001750 000144 00000044702 12527654570 026140 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Needs_maintenance.svg000644 001750 000144 00000014602 12527654570 026612 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Telephone_nearby.svg000755 001750 000144 00000023025 12527654570 026334 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Quads.svg000755 001750 000144 00000017430 12527654570 024274 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Scenic_view.svg000755 001750 000144 00000014400 12527654570 025447 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Cliff___falling_rocks.svg000755 001750 000144 00000025476 12527654570 027467 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Flashlight_required.svg000755 001750 000144 00000025446 12527654570 027233 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Takes_less_than_an_hour.svg000755 001750 000144 00000077331 12527654570 027704 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_May_require_swimming.svg000755 001750 000144 00000023274 12527654570 027416 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Available_at_all_times.svg000755 001750 000144 00000014206 12527654570 027612 0ustar00oeichlerusers000000 000000 image/svg+xml 24/7 qmapshack-1.5.1/src/icons/cache/attributes/dis_Public_restrooms_nearby.svg000755 001750 000144 00000021204 12527654570 030104 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Watch_for_livestock.svg000755 001750 000144 00000043377 12527654570 027227 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Off-road_vehicles.svg000755 001750 000144 00000015271 12527654570 026537 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Snowmobiles.svg000755 001750 000144 00000015537 12527654570 025547 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Takes_less_than_an_hour.svg000755 001750 000144 00000102210 12527654570 030030 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Cliff___falling_rocks.svg000755 001750 000144 00000025721 12527654570 027437 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_UV_Light_Required.svg000755 001750 000144 00000014753 12527654570 026545 0ustar00oeichlerusers000000 000000 image/svg+xml UV qmapshack-1.5.1/src/icons/cache/attributes/yes_Abandoned_Structure.svg000755 001750 000144 00000017445 12527654570 027201 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Wireless_Beacon.svg000644 001750 000144 00000017762 12527654570 026270 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Food_Nearby.svg000755 001750 000144 00000021475 12527654570 025412 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Off-road_vehicles.svg000755 001750 000144 00000015711 12527654570 026373 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Parking_available.svg000755 001750 000144 00000012626 12527654570 026614 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/attributes/yes_Ticks.svg000755 001750 000144 00000051236 12527654570 024317 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Public_restrooms_nearby.svg000755 001750 000144 00000020110 12527654570 030120 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Camping_available.svg000755 001750 000144 00000014602 12527654570 026430 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Climbing_gear.svg000755 001750 000144 00000017332 12527654570 025763 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Available_during_winter.svg000644 001750 000144 00000156010 12527654570 030032 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Dogs.svg000755 001750 000144 00000012473 12527654570 024136 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Hunting.svg000755 001750 000144 00000023207 12527654570 024653 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Long_Hike_(+10km).svg000755 001750 000144 00000013733 12527654570 026125 0ustar00oeichlerusers000000 000000 image/svg+xml >10km qmapshack-1.5.1/src/icons/cache/attributes/dis_Short_hike_(less_than_1km).svg000755 001750 000144 00000016044 12527654570 030247 0ustar00oeichlerusers000000 000000 image/svg+xml <1km qmapshack-1.5.1/src/icons/cache/attributes/yes_Park_and_Grab.svg000755 001750 000144 00000115402 12527654570 025710 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/attributes/dis_Flashlight_required.svg000755 001750 000144 00000026175 12527654570 027212 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Night_Cache.svg000755 001750 000144 00000133713 12527654570 025356 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Hunting.svg000755 001750 000144 00000023350 12527654570 024631 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Park_and_Grab.svg000755 001750 000144 00000114307 12527654570 025672 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/attributes/yes_Difficult_climbing.svg000755 001750 000144 00000017033 12527654570 027014 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Dangerous_Animals.svg000755 001750 000144 00000021527 12527654570 026614 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Bicycles.svg000755 001750 000144 00000023310 12527654570 024767 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Public_transportation.svg000755 001750 000144 00000020115 12527654570 027576 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Dangerous_Animals.svg000755 001750 000144 00000020401 12527654570 026623 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Field_Puzzle.svg000755 001750 000144 00000436300 12527654570 025614 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Stealth_required.svg000755 001750 000144 00000022621 12527654570 026521 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Bicycles.svg000755 001750 000144 00000024031 12527654570 024604 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Needs_maintenance.svg000644 001750 000144 00000015474 12527654570 026643 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Available_during_winter.svg000644 001750 000144 00000152715 12527654570 030063 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Significant_Hike.svg000755 001750 000144 00000016717 12527654570 026445 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Motorcycles.svg000755 001750 000144 00000017304 12527654570 025543 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Stroller_accessible.svg000755 001750 000144 00000016621 12527654570 027040 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Wireless_Beacon.svg000644 001750 000144 00000017700 12527654570 026301 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_May_require_wading.svg000755 001750 000144 00000033677 12527654570 027045 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Camping_available.svg000755 001750 000144 00000014160 12527654570 026572 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Fuel_Nearby.svg000755 001750 000144 00000013675 12527654570 025442 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Climbing_gear.svg000755 001750 000144 00000017425 12527654570 025745 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/no_Available_during_winter.svg000644 001750 000144 00000153402 12527654570 027671 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/dis_Cross_Country_Skis.svg000755 001750 000144 00000016766 12527654570 027037 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Parking_available.svg000755 001750 000144 00000012073 12527654570 026631 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/attributes/dis_Ticks.svg000755 001750 000144 00000041514 12527654570 024274 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/attributes/yes_Snowshoes.svg000755 001750 000144 00000020521 12527654570 025223 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/multi.svg000644 001750 000144 00000042250 12527654570 021317 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/wherigo.svg000644 001750 000144 00000013664 12527654570 021640 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/waypoint-flag-red.svg000644 001750 000144 00000012234 12527654570 023515 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/down_icon.svg000644 001750 000144 00000005141 12527654570 022142 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/corrected.svg000644 001750 000144 00000050174 12527654570 022143 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/virtual.svg000644 001750 000144 00000026617 12527654570 021664 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/bluepin.svg000644 001750 000144 00000014410 12527654570 021620 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/traditional.svg000644 001750 000144 00000022127 12527654570 022500 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/webcam.svg000644 001750 000144 00000216266 12527654570 021435 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/mega.svg000644 001750 000144 00000060001 12527654570 021070 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/parking.svg000644 001750 000144 00000015742 12527654570 021626 0ustar00oeichlerusers000000 000000 image/svg+xml P qmapshack-1.5.1/src/icons/cache/restore.svg000644 001750 000144 00000007515 12527654570 021655 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/halfstar.svg000644 001750 000144 00000010577 12527654570 022000 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/star.svg000644 001750 000144 00000006426 12527654570 021143 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/up_icon.svg000644 001750 000144 00000005145 12527654570 021623 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/ftf.svg000644 001750 000144 00000022430 12527654570 020742 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/COPYRIGHT000644 001750 000144 00000000244 12527654570 020734 0ustar00oeichlerusers000000 000000 All icons in this directory are from the Open Cache Manager project http://opencachemanage.sourceforge.net/ project has released files under Apache 2.0 lincense. qmapshack-1.5.1/src/icons/cache/earth.svg000644 001750 000144 00000245537 12527654570 021305 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/Apache-2.0000644 001750 000144 00000026136 12527654570 021052 0ustar00oeichlerusers000000 000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. qmapshack-1.5.1/src/icons/cache/trailhead.svg000644 001750 000144 00000036417 12527654570 022132 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/letterbox.svg000644 001750 000144 00000150735 12527654570 022205 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/OCMLogoSmall.svg000644 001750 000144 00000022127 12527654570 022416 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/log.svg000644 001750 000144 00000020256 12527654570 020750 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/maxicon.svg000644 001750 000144 00000004234 12527654570 021623 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/cito.svg000644 001750 000144 00000250031 12527654570 021121 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/other.svg000644 001750 000144 00000011106 12527654570 021302 0ustar00oeichlerusers000000 000000 image/svg+xml ? qmapshack-1.5.1/src/icons/cache/pushpin.svg000644 001750 000144 00000014165 12527654570 021657 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/write_note.svg000644 001750 000144 00000013466 12527654570 022353 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/found.svg000644 001750 000144 00000017130 12527654570 021277 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/cache/makeicons000755 001750 000144 00000000166 12527654570 021343 0ustar00oeichlerusers000000 000000 #!/bin/bash for i in *.svg; do inkscape -D -w 32 -h 32 $i --export-png=32x32/`echo $i | sed -e 's/svg$/png/'`; done qmapshack-1.5.1/src/icons/cache/unknown.svg000644 001750 000144 00000015340 12527654570 021664 0ustar00oeichlerusers000000 000000 image/svg+xml ? qmapshack-1.5.1/src/icons/Info.svg000644 001750 000144 00000005675 12527654570 020027 0ustar00oeichlerusers000000 000000 image/svg+xml ! qmapshack-1.5.1/src/icons/Right.svg000644 001750 000144 00000005644 12527654570 020205 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/TextUnderlined.svg000644 001750 000144 00000005213 12527654570 022056 0ustar00oeichlerusers000000 000000 image/svg+xml U qmapshack-1.5.1/src/icons/MimeIMG.svg000644 001750 000144 00000121202 12527654570 020341 0ustar00oeichlerusers000000 000000 image/svg+xml IMG qmapshack-1.5.1/src/icons/QMapShack.svg000644 001750 000144 00000123747 12527654570 020745 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/Copy.svg000644 001750 000144 00000005206 12527654570 020034 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/8x8/bullet_cyan.png000644 001750 000144 00000000323 12527654570 022032 0ustar00oeichlerusers000000 000000 PNG  IHDRRWsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<PIDAT 0D.R^SAvAA\>g Xh(PvsK2")#)U.J I lۿvkɉ+YIX|"IENDB`qmapshack-1.5.1/src/icons/8x8/bullet_dark_blue.png000644 001750 000144 00000000345 12527654570 023034 0ustar00oeichlerusers000000 000000 PNG  IHDRRWsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<bIDATA 0C@Ӳl|@b +Фi^zhH@Ι >0,ˎY}$ͬ"m*@ b)*'Ϸ>r IENDB`qmapshack-1.5.1/src/icons/8x8/bullet_dark_magenta.png000644 001750 000144 00000000355 12527654570 023522 0ustar00oeichlerusers000000 000000 PNG  IHDRRWsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<jIDAT}1 0\v`AؠU^fRXs_l`ҙp1U:ҙLl~]XZ۪7ٛRRSf{{nIIENDB`qmapshack-1.5.1/src/icons/8x8/bullet_dark_yellow.png000644 001750 000144 00000000355 12527654570 023421 0ustar00oeichlerusers000000 000000 PNG  IHDRRWsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<jIDAT}1 0\v`AؠU^fRXs_l`R:q2MYR:Llޏ]֪罔Ǫ7ٛRRSf{{)nw/xIENDB`qmapshack-1.5.1/src/icons/AddProject.svg000644 001750 000144 00000007125 12527654570 021143 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MoveArrow.svg000644 001750 000144 00000005520 12527654570 021042 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MimeWMTS.svg000644 001750 000144 00000125663 12527654570 020536 0ustar00oeichlerusers000000 000000 image/svg+xml WMTS qmapshack-1.5.1/src/icons/Combine.svg000644 001750 000144 00000010015 12527654570 020470 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/icons/MimeJNX.svg000644 001750 000144 00000121176 12527654570 020376 0ustar00oeichlerusers000000 000000 image/svg+xml JNX qmapshack-1.5.1/src/icons/Reverse.svg000644 001750 000144 00000004704 12527654570 020537 0ustar00oeichlerusers000000 000000 image/svg+xml qmapshack-1.5.1/src/locale/qmapshack_de.ts000644 001750 000144 00001122522 12624270553 021510 0ustar00oeichlerusers000000 000000 CAbout API Version %1 (expected %2) API Version %1 (erwartet wird %2) %1 (API V%2, expected V%3) %1 (API V%2, erwartet wird V%3) %1 (API V%2) CCanvas Workspace %1 Ansicht %1 View %1 Ansicht %1 CCommandProcessor Print debug output to console. Debug-Ausgabe in die Konsole drucken. Print debug output to logfile (temp. path). Debug-Ausgabe in Log-Datei drucken (Systemordner für temporäre Dateien). Do not show splash screen. Startbildschirm nicht anzeigen. File with QMapShack configuration. Datei mit QMapShack Einstellungen. File with qmapshark configuration. ??? qmapshack statt qmapshark im Ursprungstext, IMO Datei mit QMapShak Einstellungen. file Datei Files for future use. Dateien für den späteren Gebrauch. CDemList Deactivate Deaktivieren Activate Aktivieren CDemPathSetup Add or remove paths containing DEM data. There can be multiple files in a path but no sub-path is parsed. Supported formats are: %1 Hinzufügen oder Entfernen von Verzeichnissen mit DEM Daten. Im Verzeichnis können mehrere Dateien vorliegen. Allerdings wird kein Unterverzeichnis durchsucht. Unterstützte Formate sind: %1 Select DEM file path... Pfad für DEM Dateien wählen... CDemPropSetup <b>Grade %1</b> <b>Stufe %1</b> CDemVRT Error... Fehler... Failed to load file: %1 Die Datei konnte nicht geladen werden: %1 DEM must have one band with 16bit or 32bit data. DEM muss aus einem Satz mit 16 bit oder 32 bit Daten bestehen. DEM must have one band with 16 bit or 32 bit data. Das DEM muss aus einem Satz mit 16 bit oder 32 bit Daten bestehen. No georeference information found. Keine Georeferenzierung gefunden. CDetailsGeoCache none keiner ??? Searching for images... Suche nach Bildern... No images found Keine Bilder gefunden CDetailsOvlArea Edit name... Name bearbeiten... Enter new area name. Geben Sie einen neuen Namen für das Gebiet ein. Enter new waypoint name. Geben Sie einen neuen Namen für den Wegpunkt ein. <h4>Comment:</h4> <h4>Kommentar:</h4> <p>--- no comment ---</p> <p>--- kein Kommentar ---</p> <h4>Description:</h4> <h4>Beschreibung:</h4> <p>--- no description ---</p> <p>--- keine Beschreibung ---</p> CDetailsPrj none keine Build diary... Tagebuch erstellen... Abort Abbrechen <b>Summary over all tracks in project</b><br/> <b>Auswertung aller Tracks im Projekt</b><br/> <h2>Waypoints</h2> <h2>Wegpunkte</h2> Info Information Comment Kommentar <h2>Tracks</h2> <h2>Tracks</h2> <h2>Areas</h2> <h2>Gebiete</h2> You want to sort waypoints along a track, but you switched off track and waypoint correlation. Do you want to switch it on again? Sie wollen Wegpunkte entlang eines Tracks sortieren, aber Sie haben die Verknüpfung von Wegpunkten und Tracks ausgeschaltet. Wollen Sie sie wieder anschalten? Correlation... Verknüpfungen... Total Distance: Summe Entfernung: Total Ascend: Summe Anstieg: Total Descend: Summe Abstieg: Total Time: Summe Zeit: Total Time Moving: Summe Zeit in Bewegung: distance: %1%2 Entfernung: %1 %2 ascent: %1%2 Anstieg: %1 %2 descend: %1%2 Abstieg: %1 %2 <h2>Routes</h2> <h2>Routen</h2> Edit name... Name bearbeiten... Enter new project name. Geben Sie einen neuen Namen für das Projekt ein. Print Diary Tagebuch drucken Enter new waypoint name. Name eingeben. Edit keywords... Stichwörter bearbeiten... Enter keywords. Stichwörter eingeben. CDetailsRte Edit name... Name bearbeiten... Enter new route name. Geben Sie einen neuen Namen für die Route ein. CDetailsTrk Solid color Feste Farbe Reduce visible track points Sichtbare Trackpunkte reduzieren Change elevation of track points Höhe von Trackpunkten ändern Change timestamp of track points Zeitstempel von Trackpunkten ändern Cut track into pieces Track in Stücke teilen %1 %2 Edit name... Name bearbeiten... Enter new track name. Geben Sie einen neuen Namen für den Track ein. Reset activities... Aktivitäten zurücksetzen... This will remove all activities from the track. Proceed? Dies wird alle Aktivitäten vom Track entfernen. Fortfahren? None Keine <h4>Comment:</h4> <h4>Kommentar:</h4> <p>--- no comment ---</p> <p>--- kein Kommentar ---</p> <h4>Description:</h4> <h4>Beschreibung:</h4> <p>--- no description ---</p> <p>--- keine Beschreibung ---</p> CDetailsWpt no comment kein Kommentar vorhanden no description keine Beschreibung vorhanden <h4>Comment:</h4> <h4>Kommentar:</h4> <p>--- no comment ---</p> <p>--- kein Kommentar ---</p> <h4>Description:</h4> <h4>Beschreibung:</h4> <p>--- no description ---</p> <p>--- keine Beschreibung ---</p> Edit name... Name bearbeiten... Enter new waypoint name. Geben Sie einen neuen Namen für den Wegpunkt ein. Enter new elevation. Geben Sie eine neue Höhe ein. Enter new proximity range. Geben Sie einen neuen Abstandsalarm ein. CElevationDialog No DEM data found for that point. Keine Höhendaten für diesen Punkt gefunden. CGisListDB Lost & Found Verloren & Gefunden Database Datenbank Add Folder Ordner hinzufügen Delete Folder Ordner löschen Delete Löschen Add Database Datenbank hinzufügen Delete Item Element löschen Remove Database Datenbank entfernen Empty Leer Remove database... Datenbank entfernen... Do you really want to remove '%1' from the list? Wollen Sie '%1' wirklich aus der Liste entfernen? Do you realy want to remove '%1' from the list? Wollen Sie '%1' wirklich aus der Liste entfernen? Delete database folder... Datenbankordner löschen... Are you sure you want to delete "%1" from the database? Sind Sie sicher, dass Sie "%1" aus der Datenbank löschen wollen? Remove items... Element entfernen... Are you sure you want to delete all items from Lost&Found? This will remove them permanently. Sind Sie sicher, dass Sie alle Elemente in Verloren & Gefunden löschen wollen? Sie werden dauerhaft entfernt. Are you sure you want to delete all selected items from Lost&Found? This will remove them permanently. Sind Sie sicher, dass Sie alle ausgewählten Elemente in Verloren & Gefunden löschen wollen? Sie werden dauerhaft entfernt. CGisListWks Save Speichern Save As... Speichern unter... Edit.. Bearbeiten.. Update Project on Devices Aktualisiere das Projekt auf allen Geräten Close Schließen Update Project on Device Aktualisiere das Projekt auf dem Gerät Edit... Bearbeiten... Copy to... Kopieren nach... Show Bubble Infoblase anzeigen Move Waypoint Wegpunkt verschieben Route Instructions Navigationsanweisungen Calculate Route Route berechnen Reset Route Route zurücksetzen Edit Route Route bearbeiten Create Route Route erstellen Drop items... Elemente verwerfen... <b>Update devices</b><p>Update %1<br/>Please wait...</p> <b>Aktualisieren der Geräte</b><p>Aktualisiere %1<br/>Bitte warten...</p> Copy items... Elemente kopieren... Move Waypoint... Wegpunkt verschieben... Proj. Waypoint... Wegpunkt Projektion... Track Profile Trackprofil Send to Devices Ans Gerät senden Lock/Unlock Sperren/Entsperren Select Range Bereich wählen Edit Track Points Trackpunkte bearbeiten Reverse Track Track umkehren Combine Tracks Tracks verbinden Edit Area Points Gebietspunkte bearbeiten Delete Löschen Show on Map Auf der Karte anzeigen. Hide from Map Auf der Karte ausblenden. Saving workspace. Please wait. Arbeitsplatz speichern. Bitte warten. Loading workspace. Please wait. Arbeitsplatz laden. Bitte warten. Close all projects... Alle Projekte schließen... This will remove all projects from the workspace. Dies wird alle Projekte aus dem Arbeitsplatz entfernen. Add Empty Project Leeres Projekt hinzufügen New Project Neues Projekt CGisWidget Load project... Lade Projekt... The project "%1" is already in the workspace. Das Projekt "%1" ist schon im Arbeitsplatz geladen. Cut Track... Track teilen... Do you want to delete the original track? Wollen Sie den ursprünglichen Track löschen? CGrid [Grid: %1] [Gitter: %1] [Grid: %1%2%5 %3%4%5] [Gitter: %1%2%5 %3%4%5] [Grid: N %1m, E %2m] [Gitter: N %1m, E %2m] %1 %2 %1%2%5 %3%4%5 %1m, %2m %1 m, %2 m N %1m, E %2m N %1 m, E %2 m CHistoryListWidget Cut history Historie kürzen CImportDatabase Import QLandkarte Database QLandkarte Datenbank importieren Select source database... Quelldatenbank wählen... Select target database... Zieldatenbank wählen... Select source databse... Quelldatenbank wählen... Select target databse... Zieldatenbank wählen... CMainWindow Ele: %1%2 Höhe: %1%2 [Grid: %1] [Gitter: %1] Load GIS Data... GIS Daten laden... Select output file Ausgabedatei auswählen Select file to load Zu ladende Datei auswählen CMapIMG Failed ... Fehlgeschlagen... Unspecified Nicht angegeben French Französisch German Deutsch Dutch Niederländisch English Englisch Italian Italienisch Finnish Finnisch Swedish Schwedisch Spanish Spanisch Basque Baskisch Catalan Catalanisch Galician Galizisch Welsh Walisisch Gaelic Gälisch Danish Dänisch Norwegian Norwegisch Portuguese Portugiesisch Slovak Slovakisch Czech Tschechisch Croatian Kroatisch Hungarian Ungarisch Polish Polnisch Turkish Türkisch Greek Griechisch Slovenian Slowenisch Russian Russisch Estonian Estnisch Latvian Lettisch Romanian Rumänisch Albanian Albanisch Albanisch Bosnian Bosnisch Lithuanian Litauisch Serbian Serbisch Macedonian Makedonisch Bulgarian Bulgarisch Major highway Autobahn Principal highway Bundesstraße Other highway Schnellstraße Arterial road Fernstraße Collector road Sammelstraße Residential street Wohnstraße Alley/Private road Allee/Privatstraße Highway ramp, low speed Auffahrt (langsam) Highway ramp, high speed Auffahrt (schnell) Unpaved road Unbefestigte Straße Major highway connector Autobahnzubringer Roundabout Kreisverkehr Railroad Eisenbahn Shoreline Küstenlinie Trail Stream Bach Time zone Zeitzone Ferry Fähre State/province border Staats-/Landesgrenze County/parish border Kreis-/Gemeindegrenze International border Internationale Grenze River Fluss Minor land contour Höhenlinie klein Intermediate land contour Höhenlinie mittel Major land contour Höhenlinie groß Minor depth contour Tiefenlinie klein Intermediate depth contour Tiefenlinie mittel Major depth contour Tiefenlinie groß Intermittent stream Intermittierender Bach Airport runway Landebahn Pipeline Pipeline Powerline Stromleitung Marine boundary Meeresgrenze Hazard boundary Gefahrbereichgrenze Large urban area (&gt;200K) Großes Wohngebiet (&gt;200K) Small urban area (&lt;200K) Kleines Wohngebiet (&lt;200K) Rural housing area Ländliches Wohngebiet Military base Militärbasis Parking lot Parkplatz Parking garage Parkhaus Airport Flugplatz Shopping center Einkaufszentrum Marina Jachthafen University/College Universität/Hochschule Hospital Krankenhaus Industrial complex Industrie Reservation Schutzgebiet Man-made area Fabrikgelände Sports complex Sportanlage Golf course Golfplatz Cemetery Friedhof National park Nationalpark City park Stadtpark State park Forest Wald Ocean Ozean Blue (unknown) Sea Meer Large lake See Medium lake See Small lake See Major lake See Major River Strom Large River Fluss Medium River Fluss Small River Fluss Intermittent water Gewässer Wetland/Swamp Feuchtgebiet/Sumpf Glacier Gletscher Orchard/Plantation Obstgarten/Plantage Scrub Buschwerk Tundra Tundra Flat Ebene ??? Failed to read: Lesen fehlgeschlagen: Failed to open: Öffnen fehlgeschlagen: Bad file format: Falsches Format: Failed to read file structure: Lesen der Dateistruktur fehlgeschlagen: Loading %1 Lädt %1 User abort: Benutzerabbruch: File is NT format. QMapShack is unable to read map files with NT format: Die Datei hat das NT Format. QMapShack kann dieses Format nicht lesen: File contains locked / encypted data. Garmin does not want you to use this file with any other software than the one supplied by Garmin. Die Datei enthält verschlüsselte Daten. Garmin möchte nicht dass diese Datei mit einer anderen Software, als der von Garmin bereitgestellten, benutzt wird. Point of Interest Ort von Interesse Unknown Unbekannt Area Gebiet CMapList Deactivate Deaktivieren Activate Aktivieren Where do you want to store maps? Wo wollen Sie die Karten speichern? CMapMAP Failed ... Fehlgeschlagen... Failed to open: Öffnen fehlgeschlagen: Bad file format: Falsches Dateiformat: CMapPathSetup Add or remove paths containing maps. There can be multiple maps in a path but no sub-path is parsed. Supported formats are: %1 Hinzufügen oder Entfernen von Karten. In einem Verzeichnis können mehrere Karten liegen. Unterverzeichnisse werden jedoch nicht durchsucht. Unterstützte Formate sind: %1 Select map path... Kartenpfad wählen... Select root path... Hauptverzeichnis auswählen... CMapPropSetup Cache path... Cache Pfad... CMapRMAP Error... Fehler... This is not a TwoNav RMAP file. Das ist keine bekannte TwoNav RMAP Datei. Unknown sub-format. Unbekanntes Unterformat. Unknown version. Unbekannte Version. Failed to read reference point. Referenzpunkte konnten nicht gelesen werden. Unknown projection and datum (%1%2). Unbekannte Projektion und Datum (%1 %2). CMapTMS Error... Fehler... Failed to open %1 Öffnen fehlgeschlagen: %1 Failed to read: %1 line %2, column %3: %4 Lesen fehlgeschlagen: %1 Zeile %2, Spalte %3: %4 Layer %1 This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. Diese Karte benötigt OpenSSL. Aufgrund rechtlicher Beschränkungen in einigen Ländern, ist OpenSSL nicht Bestandteil von QMapShack. Sie können sich auf der <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> nach Binäries umsehen. Sie müssen die Dateien libeay32.dll and ssleay32.dll in das QMapShack Programmverzeichnis kopieren. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. Diese Karte benötigt OpenSSL. Aufgrund rechtlicher Beschränkungen in einigen Ländern, ist OpenSSL nicht Bestandteil von QMapShack. Sie können sich auf der <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> nach Binäries umsehen. --- All --- --- Alle --- <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 unerledigte Kacheln<br/> CMapVRT Error... Fehler... Failed to load file: %1 Die Datei konnte nicht geladen werden: %1 File must be 8 bit palette or gray indexed. Die Datei muss eine 8 bit Palette haben, oder Graustufen. No georeference information found. Keine Georeferenzierung gefunden. CMapVrtBuilder Build GDAL VRT GDAL VRT erstellen Select files... Dateien auswählen... Select target file... Zieldatei auswählen... !!! done !!! !!! erledigt !!! !!! failed !!! !!! fehlgeschlagen !!! CMapWMTS Error... Fehler... Failed to open %1 Öffnen fehlgeschlagen: %1 Failed to read: %1 line %2, column %3: %4 Lesen fehlgeschlagen: %1 Zeile %2, Spalte %3: %4 Failed to read: %1 Unknown structure. Lesen fehlgeschlagen: %1 Unbekannte Struktur. Unexpected service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Unerwarteter Dienst. '* WMTS 1.0.0' wird erwartet. '%1 %2' wurde gelesen. Unexpexted service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Unerwarteter Dienst. '* WMTS 1.0.0' wird erwartet. '%1 %2' wurde gelesen. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. Diese Karte benötigt OpenSSL. Aufgrund rechtlicher Beschränkungen in einigen Ländern, ist OpenSSL nicht Bestandteil von QMapShack. Sie können sich auf der <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> nach Binäries umsehen. Sie müssen die Dateien libeay32.dll and ssleay32.dll in das QMapShack Programmverzeichnis kopieren. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. Diese Karte benötigt OpenSSL. Aufgrund rechtlicher Beschränkungen in einigen Ländern, ist OpenSSL nicht Bestandteil von QMapShack. Sie können sich auf der <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> nach Binäries umsehen. --- All --- --- Alle --- <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 unerledigte Kacheln %1: %2 tiles pending %1: %2 unerledigte Kacheln Unexpexted service. 'OGC WMTS 1.0.0' is expected. '%1 %2' is read. Unerwarterter Dienst. 'OGC WMTS 1.0.0' Wird erwartet. '%1 %2' wird gelesen. No georeference information found. Keine Georeferenzierung gefunden. CMouseEditArea <b>Edit Area</b><br/>Select a corner point for more options.<br/> <b>Gebiet bearbeiten</b><br/>Wähle einen Eckpunkt für mehr Optionen.<br/> Area Gebiet <b>Edit Area</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Gebiet bearbeiten</b><br/>Wählen Sie mittels der Werkzeug-Buttons eine Funkion und einen Routing-Modus. Als nächstes wählen Sie einen Punkt auf der Linie. Es können nur Punkte geändert werden, die mit einem großen Quadrat markiert sind. Schwarze Punkte sind durchs Routing erzeugte Unterpunkte.<br/> CMouseEditLine Add points? Punkte hinzufügen? Add points to temporary line? Punkte zur temporären Linie hinzufügen? Warning! Warnung! This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Alle ursprünglichen Daten werden durch eine einfache Koordinatenlinie ersetzt. Alle anderen Daten sind dauerhaft verloren. CMouseEditRte <b>Edit Route Points</b><br/>Select a route point for more options.<br/> <b>Routenpunkte bearbeiten</b><br/>Wählen Sie einen Routenpunkt für mehr Optionen.<br/> Route Routen <b>Edit Route Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Routenpunkte bearbeiten</b><br/>Wählen Sie mittels der Werkzeug-Buttons eine Funkion und einen Routing-Modus. Als nächstes wählen Sie einen Punkt auf der Linie. Es können nur Punkte geändert werden, die mit einem großen Quadrat markiert sind. Schwarze Punkte sind durchs Routing erzeugte Unterpunkte.<br/> CMouseEditTrk <b>Edit Track Points</b><br/>Select a track point for more options.<br/> <b>Trackpunkte bearbeiten</b><br/>Wähle einen Trackpunkt für mehr Optionen.<br/> Track Track <b>Edit Track Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Trackpunkte bearbeiten</b><br/>Wählen Sie mittels der Werkzeug-Buttons eine Funkion und einen Routing-Modus. Als nächstes wählen Sie einen Punkt auf der Linie. Es können nur Punkte geändert werden, die mit einem großen Quadrat markiert sind. Schwarze Punkte sind durchs Routing erzeugte Unterpunkte.<br/> Warning! Warnung! This will replace all data of the original by a simple line of coordinates. All other data will be lost permanently. Alle ursprünglichen Daten werden durch eine einfache Koordinatenlinie ersetzt. Alle anderen Daten sind dauerhaft verloren. This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Alle ursprünglichen Daten werden durch eine einfache Koordinatenlinie ersetzt. Alle anderen Daten sind dauerhaft verloren. CMouseNormal Add Waypoint Wegpunkt hinzufügen Add Track Track hinzufügen Add Route Route hinzufügen Add Area Gebiet hinzufügen Copy position Position kopieren Copy position (Grid) Position kopieren (Gitter) CMousePrint <b>Save(Print) Map</b><br/>Select a rectangular area on the map. Use the left mouse button and move the mouse. Abort with a right click. Adjust the selection by point-click-move on the corners. Save/print the selection by a left click on the disc/printer icon in the center of the selection. <b>Karte speichern (drucken)</b><br/>Wählen Sie mit der linken Maustaste einen rechteckigen Bereich auf der Karte aus. Die Auswahl kann durch Verschieben der Eckpunkte mit der Maus angepasst werden. Abbruch mit Rechtsklick. Speichern/Drucken erfolgt durch Linksklick auf das entsprechende Symbol in der Mitte der Auswahl. CMouseRangeTrk <b>Select Range</b><br/>Select first track point. And then a second one.<br/> <b>Bereich wählen</b><br/>Wählen Sie einen ersten Punkt. Dann einen zweiten.<br/> CPhotoAlbum Select images... Bilder wählen... CPlotDistance distance [%1] Entf. [%1] time Uhrzeit time [h] Uhrzeit distance. [%1] Entf. [%1] CPlotProfile distance [%1] Entfernung [%1] time [h] Zeit [h] alt. [%1] Höhe [%1] CPlotSpeed distance [%1] Entfernung [%1] time [h] Zeit [h] speed. [%1] Geschw. [%1] CPrintDialog Print Map... Karte drucken... Save Map as Image... Karte als Bild speichern... Printer Properties... Drucker Eigenschaften... Pages: %1 x %2 Seiten: %1 x %2 Zoom with mouse wheel on map below to change resolution: %1x%2 pixel x: %3 m/px y: %4 m/px Zoomen Sie mit dem Mausrad auf der Karte unten, um die Auflösung zu ändern: %1x%2 Pixel x: %3 m/px y: %4 m/px Printing pages. Drucke Seiten. Save map... Karte speichern... CProgressDialog Elapsed time: %1 Verstrichene Zeit: %1 Elapsed time: %1 seconds. Verstrichene Zeit: %1 Sekunden. CProjWizard north Norden south Süden Error... Fehler... The value '%1' is not a valid coordinate system definition: %2 Die Eingabe: '%1' ist keine gültige Koordinatensystemdefinition: %2 CProjWpt Edit name... Name bearbeiten... Enter new waypoint name. Geben Sie einen neuen Namen für den Wegpunkt ein. CQlgtDb Migrating database from version 4 to 5. Datenbank von Version 4 nach 5 migrieren. Migrating database from version 5 to 6. Datenbank von Version 5 nach 6 migrieren. Migrating database from version 6 to 7. Datenbank von Version 6 nach 7 migrieren. Migrating database from version 7 to 8. Datenbank von Version 7 nach 8 migrieren. Migrating database from version 8 to 9. Datenbank von Version 8 nach 9 migrieren. Open database: %1 Öffne Datenbank: %1 Folders: %1 Ordner: %1 Tracks: %1 Tracks: %1 Routes: %1 (not supported yet) Routen: %1 (noch nicht unterstützt) Routes: %1 (Only the basic route will be copied) Routen: %1 (Es wird nur die Basisroute kopiert) Waypoints: %1 Wegpunkte: %1 Overlays: %1 (only area overlays will be converted to QMapShack) Overlays: %1 (Es werden nur Gebietsoverlays nach QMapShack konvertiert) Overlays: %1 (areas will be converted as areas, distance lines will be converted to tracks, all other overlay items will be lost) Overlays: %1 (Flächen werden als Flächen übernommen, Distanzlinien werden als Tracks übernommen, alle anderen Overlays gehen verloren) Diaries: %1 Tagebücher: %1 Map selections: %1 (can't be converted to QMapShack) Kartenselektionen: %1 (können nicht nach QMapShack konvertiert werden) ------ Start to convert database to %1------ ------ Konvertierung der Datenbank %1 beginnt ------ Failed to create target database. Erstellen der Zieldatenbank fehlgeschlagen. ------ Abort ------ ------ Abbrechen ------ ------ Done ------ ------ Fertig ------ Restore folders... Ordner wiederherstellen... Abort Abbrechen Imported %1 folders and %2 diaries Importiert wurden %1 Ordner und %2 Tagebücher Copy items... Elemente kopieren... Imported %1 tracks, %2 waypoints, %3 routes, %4 areas Importiert wurden %1 Tracks, %2 Wegpunkte, %3 Routen, %4 Gebiete Import folders... Importiere Ordner... Overlay of type '%1' cant be converted Das Overlay vom Typ '%1' kann nicht konvertiert werden CQmsDb Existing file... Vorhandene Datei... Remove existing %1? Entferne vorhandene %1? Remove existing file %1 Entferne vorhandene Datei %1 %1: drop item with QLGT DB ID %2 %1: verwerfe das Element mit der QLGT DB ID %2 CRouterMapQuest Fastest Schnellste Shortest Kürzeste Bicycle Fahrrad Pedestrian/pub. transp. Fußgänger/öffentl. Transport Pedestrian Fußgänger US English Englisch (USA) British English Englisch (Britisch) Danish Dänisch Dutch Niederländisch French Französisch German Deutsch Italian Italienisch Norwegian Norwegisch Spanish Spanisch Swedish Schwedisch mode "%1" Modus "%1" no highways keine Kraftfahrzeugstraßen no toll roads keine Mautstraßen no seasonal keine saisonalen Straßen no unpaved keine unbefestigten Straßen no ferry keine Fähren no crossing of country borders keine Überqueruung von Landesgrenzen <b>MapQuest</b><br/>Routing request sent to server. Please wait... <b>MapQuest</b><br/>Routinganforderung an den Server gesendet. Bitte warten... <b>MapQuest</b><br/>Bad response from server:<br/>%1 <b>MapQuest</b><br/>Schlechte Antwort vom Server:<br/>%1 <br/>Calculation time: %1s <br/>Berechnungszeit: %1 s CRouterRoutino Foot Fußgänger Horse Reiter Wheelchair Rollstuhl Bicycle Fahrrad Moped Moped Motorcycle Motorrad Motorcar Auto Goods LKW Shortest Kürzeste Found Routino with a wrong version. Expected %1 found %2 Falsche Routino Version gefunden. Erwartet wird %1, gefunden wurde %2 Quickest Schnellste English Englisch German Deutsch French Französisch Hungarian Ungarisch Dutch Niederländisch Russian Russisch Polish Polnisch A function was called without the database variable set. Eine Funktion wurde ohne gesetzte Datenbank aufgerufen. A function was called without the profile variable set. Eine Funktion wurde ohne gesetztes Profil aufgerufen. A function was called without the translation variable set. Eine Funktion wurde ohne gesetzte Sprache aufgerufen. The specified database to load did not exist. Die zu ladende vorgegebene Datenbank existiert nicht. The specified database could not be loaded. Die vorgegebene Datenbank konnte nicht geladen werden. The specified profiles XML file did not exist. Die vorgegebene XML Profildatei existiert nicht. The specified profiles XML file could not be loaded. Die vorgegebene XML Profildatei konnte nicht geladen werden. The specified translations XML file did not exist. Die vorgegebene XML Sprachdatei existiert nicht. The specified translations XML file could not be loaded. Die vorgegebene XML Profildatei konnte nicht geladen werden. The requested profile name does not exist in the loaded XML file. Den geforderten Profilnamen gibt es in der geladenen XML Datei nicht. The requested translation language does not exist in the loaded XML file. Die geforderte Sprache gibt es in der geladenen XML Datei nicht. There is no highway near the coordinates to place a waypoint. Es gibt keine Straße in der Nähe des zu plazierenden Wegpunktes. The profile and database do not work together. Profil und Datenbank funktionieren nicht zusammen. The profile being used has not been validated. Das zu benutzende Profil wurde nicht validiert. The user specified profile contained invalid data. Das vorgegebene Profil enthält ungültige Daten. The routing options specified are not consistent with each other. Die vorgebenen Routingoptionen passen nicht zusammen. There is a mismatch between the library and caller API version. Die Library und die API Version passen nicht zusammen. Route calculation was aborted by user. Die Routenberechnung wurde vom Benutzer abgebrochen. A route could not be found to waypoint %1. Es konnte keine Route zum Wegpunkt %1 gefunden werden. Unknown error: %1 Unbekannter Fehler: %1 profile "%1" Profil "%1" , mode "%1" , Modus "%1" Warning... Warnung... %1: Due to limitations in the Windows POSIX API Routino can't handle files larger than 4GB. %1: Aufgrund der Einschränkungen der Windows POSIX API können Dateien, die größer als 4GB sind, nicht bearbeitet werden. Calculate route with %1 Berechne Route mit %1 <br/>Calculation time: %1s <br/>Berechnungszeit: %1 s CRouterRoutinoPathSetup Add or remove paths containing Routino data. There can be multiple databases in a path but no sub-path is parsed. Pfade mit Routino Daten hinzufügen oder entfernen. In einem Pfad können mehrere Datenbanken sein. Teilpfade werden nicht geparst. Select routing data file path... Pfad für Routingdatendatei wählen... Select DEM file path... Pfad für DEM Dateien wählen... CRouterSetup Routino (offline) MapQuest (online) CRoutinoDatabaseBuilder Create Routino Database Routino Datenbank erstellen Select files... Dateien auswählen... Select target path... Zielpfad auswählen... !!! failed !!! !!! fehlgeschlagen !!! !!! done !!! !!! erledigt !!! CSearchGoogle Unknown response Unbekannte Antwort Error: Fehler: CSetupDB Setup database... Datenbank einrichten... Changes will become active after an application's restart. Änderungen werden nach Neustart der Anwendung aktiv. Select database path... Datenbankpfad w#ählen... CSetupDatabase Error... Fehler... There is already a database with name '%1' Es gibt schon eine Datenbank mit dem Namen '%1' New database... Neue Datenbank... Open database... Datenbank öffnen... CSetupWorkspace Setup database... Datenbank einrichten... Changes will become active after an application's restart. Änderungen werden erst nach Neustart der Anwendung aktiv. CTextEditWidget &Color... &Farbe... IAbout About.... Über.... <b>QMapShack</b>, Version TextLabel Bezeichnung Qt GDAL Proj4 Routino Routino Rainer Unseld French Französisch Czech Tschechisch Pavel Fric German Deutsch <b>Translation:</b> <b>Übersetzung:</b> Dutch Niederländisch Harrie Klomp <b>Binaries:</b> <b>Ausführbare Dateien:</b> <b>Contributors:</b> <b>Mitwirkende:</b> Christian Eichler (qms@christian-eichler.de) Translation: Übersetzung: Josef Latt Spanish Spanisch Jose Luis Domingo Lopez Ivo Kronenberg Helmut Schmidt Win64 OS X ...and thanks to all Linux binary maintainers for doing a great job. Special thanks to Dan Horák and Bas Couwenberg for showing presence on the mailing list to discuss distribution related topics. ... und Danke an alle Ersteller von ausführbaren Linux-Dateien für die gute Arbeit. Ganz besonderen Dank an Dan Horák und Bas Couwenberg für ihre Teilnahme an der Diskussion in der Mailingliste distributionsbezogene Punkte betreffend. Binaries: Ausführbare Dateien: This software is licensed under GPL3 or any later version Diese Software steht unter der GPL3 Lizenz (oder spätere Versionen) © 2014 Oliver Eichler (oliver.eichler@gmx.de) ICanvasSetup Setup Map Workspace... Kartenansicht einstellen... Setup Map View... Kartenansicht einstellen... Projection & Datum Projektion & Datum ... Scales Skalierung Logarithmic Logarithmisch Square (optimized for TMS and WTMS tiles) Quadratisch (optimal für TMS und WMTS Karten) ICombineTrk Combine Tracks... Tracks verbinden... ... ICoordFormatSetup Coordinate Format... Koordinatenformat... N48° 53.660 E013° 31.113 N48.8943° E013.51855° N48° 53' 39.6" E13° 31' 6.78" ICreateRouteFromWpt Create Route from Waypoints Route aus Wegpunkten erstellen ... ICutTrk Cut Track Track teilen Delete first part of the track and keep second one Löscht den ersten Teil des Tracks und behält den zweiten Keep both parts of the track Behält beide Teile des Tracks Keep first part of the track and delete second one Behält den ersten Teil des Tracks und löscht den zweiten Check this to store the result into a new track. If you keep both parts of the track you have to create new ones. If you want to keep just one half you can simply remove the points, or check this to create a new track. Wählen Sie das aus, wenn das Ergebnis ein neuer Track sein soll. Wenn Sie beide Teile des Tracks behalten wollen, müssen neue Tracks erstellt werden. Wenn Sie nur die eine Hälfte behalten wollen, dann können auch nur die Punkte gelöscht werden, oder wenn Sie das hier auswählen, ein neuer Track erstellt werden. Create a new track Neuen Track erstellen Create a clone Ein Abbild erstellen IDemPathSetup Setup DEM file pathss Pfad für DEM Dateien setzen Setup DEM file paths Pfad für DEM Dateien setzen ... - IDemPropSetup Form <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Ändert die Deckkraft der Karte</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Klicken, um die aktuelle Skalierung als minimale Skalierung zu benutzen.</p></body></html> ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Stellt den Skalierungsbereich ein, in dem die Karte sichtbar ist. Benutzen Sie die beiden Knöpfe links und rechts um die minimale und maximale Skalierung einzustellen.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Klicken um die aktuelle Skalierung als maximale Skalierung zu benutzen.</p></body></html> Hillshading Schummerung Slope Hangneigung ° > TextLabel Bezeichnung IDemsList Form To add files with elevation data use File->Setup DEM Paths. Um Dateien mit Höhendaten zuzufügen -> Menü Datei - DEM Verzeichnisse angeben. Use the context menu (right mouse button click on entry) to activate a file. Use drag-n-drop to move the activated file in the process order. Nutze das Kontextmenü (Klick mit rechter Maustaste auf Eintrag), um eine Datei zu aktivieren. Nutze Ziehen u. Ablegen um die aktivierte Datei in der Reihenfolge der Ansicht zu verschieben. Activate Aktivieren IDetailsGeoCache Dialog - about:blank Position: Position: Difficulty Schwierigkeit Terrain Gelände Update spoilers Spoiler erneut laden ... Hint: Hinweis: TextLabel Bezeichnung IDetailsOvlArea Dialog Dialog - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Der Wegpunkt wurde in QMapShack importiert und geändert. Die ursprünglichen Daten werden nicht mehr angezeigt. Näheres siehe Änderungshistorie.</p></body></html> Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. ... Color Farbe Border width Rahmenbreite Style Stil Opacity Deckkraft Info Info Points Punkte Position Position Hist. Historie IDetailsPrj Form - Sort By Time Sortieren nach Zeit Keep Order of Project Reihenfolge beibehalten Sort Along Track Sort. entl. d. Tracks Print diary Tagebuch drucken ... Keep order of project Reihenfolge beibehalten Sort by time Sortieren nach Zeit Sort along track (multiple) Sort. entl. d. Tracks (mehrfach) Sort along track (single) Sort. entl. d. Tracks (einmalig) Sort along track (with doubles) Sort. entl. d. Tracks (mehrfach) Sort along track (without doubles) Sort. entl. d. Tracks (einmalig) Rebuild diary. Tagebuch aktualisieren. Keywords: Stichwörter: IDetailsRte Info Info - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Der Wegpunkt wurde in QMapShack importiert und geändert. Die ursprünglichen Daten werden nicht mehr angezeigt. Näheres siehe Änderungshistorie.</p></body></html> Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. ... Points Punkte Position Position Hist. Historie IDetailsTrk Form - - Graph Control Grafikeinstellungen Profile Profil Speed Geschw. Progress Verlauf Track Track Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. ... <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Der Wegpunkt wurde in QMapShack importiert und geändert. Die ursprünglichen Daten werden nicht mehr angezeigt. Näheres siehe Änderungshistorie.</p></body></html> Style Stil from Data aus Daten Source Quelle Maximum Minimum Solid color Feste Farbe Graphs Grafiken Graph 3 Grafik 3 Graph 2 Grafik 2 Graph 1 Grafik 1 Activity Aktivität To differentiate the track statistics select an activity from the list for the complete track. Or select a part of the track to assign an activity. Um Trackstatistiken zu differenzieren, wählen Sie für den gesamten Track eine Aktivität aus der Liste. Oder wählen Sie einen Teil des Track aus und weisen diesem eine Aktivität zu. Points Punkte Time Zeit Ele. Höhe Delta Delta Dist. Entf. Slope Neigung Ascend Anstieg Descend Abstieg Position Position Info Info - Filter Filter Hist. Historie TextLabel Bezeichnung IDetailsWpt Dialog <html><head/><body><p>Edit comment.</p></body></html> <html><head/><body><p>Kommentar bearbeiten.</p></body></html> ... <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Der Wegpunkt wurde in QMapShack importiert und geändert. Die ursprünglichen Daten werden nicht mehr angezeigt. Näheres siehe Änderungshistorie.</p></body></html> <html><head/><body><p>Edit description.</p></body></html> <html><head/><body><p>Beschreibung bearbeiten.</p></body></html> Info Info Position: Position: - Ele. Höhe Proximity: Abstand: Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. Hist. Historie <html><head/><body><p>Read Only Mode</p></body></html> <html><head/><body><p>Schreibschutz</p></body></html> Add images. Bilder hinzufügen. Delete selected image. Ausgewähltes Bild löschen. Date/Time: Datum/Zeit: IElevationDialog Edit elevation... Höhe bearbeiten... Elevation Höhe - Get elevation from active digital elevation model. Höhe aus aktivem digitalen Höhenmodell entnehmen. ... IFilterDelete Form <b>Remove Track Points</b> <b>Trackpunkte entfernen</b> Remove all hidden track points permanently. Alle ausgeblendeten Trackpunkte werden dauerhaft entfernt. ... IFilterDouglasPeuker Form <b>Hide Points (Douglas Peuker)</b> <b>Trackpunkte ausblenden (Douglas Peuker)</b> Hide track points if the distance to a line between neighboring points is less than Trackpunkte werden ausgeblendet, wenn der Abstand zu einer Linie zwischen benachbarten Punkten kleiner ist als m m Apply filter now. Den Filter jetzt anwenden. ... IFilterInvalid Form Hide Invalid Points Ungültige Punkte ausblenden Hide points with invalid coordinates at the beginning of the track. Blendet Punkte mit ungültigen Koordinaten am Anfang vom Track aus. ... IFilterMedian Form <b>Smooth Profile (Median Method)</b> <b>Profil glätten (Median-Methode) </b> Smooth deviation of the track points elevation with a Median filter of size Glättet die Abweichung der Höhe von Trackpunkten mit einem Median-Filter der Größe points Pkt. ... IFilterNewDate Form <b>Change Time</b> <b>Zeit ändern</b> Change start of track to Ändert den Trackbeginn auf dd.MM.yy HH:mm:ss dd.MM.yy HH:mm:ss - ... IFilterObscureDate Form <b>Obscure Timestamps</b> <b>Zeitstempel verschleiern</b> Increase timestamp by Erhöht den Zeitstempel um sec. s with each track point. 0 sec. will remove timestamps. für jeden Trackpunkt. 0 s entfernt alle Zeitstempel. ... IFilterOffsetElevation Form <b>Offset Elevation</b> <b>Höhenversatz</b> Add offset of Fügt einen Versatz von to track points elevation. zur Höhe der Trackpunkte hinzu. ... IFilterReplaceElevation Form <b>Replace Elevation Data</b> <b>Höhendaten ersetzen</b> Replace elevation of track points with the values from loaded DEM files. Ersetzt die Höhendaten durch Daten aus den geladenen DEM Dateien. ... IFilterReset Form <b>Reset Hidden Track Points</b> <b>Ausgeblendete Trackpunkte wiederherstellen</b> Make all trackpoints visible again. Alle Trackpunkte werden wieder sichtbar. ... IFilterSpeed Form <b>Change Speed</b> <b>Geschwindigkeit ändern</b> Set speed to Ändert Geschwindigkeit auf km/h km/h ... IGisWidget Form State Status Name Name To add a database do a right click on the database list above. Um eine Datenbank hinzuzufügen rechtsklicken Sie oben auf die Datenbankliste. IGridSetup Setup Grid... Gitter einstellen... Projection Projektion restore default Grundeinstellung wiederherstellen ... Get projection from current map. Projektion aus der aktuellen Karte nehmen. projection wizzard Projektionshilfe Grid color Gitterfarbe setup grid color Gitterfarbe einstellen IImportDatabase Form Source Database: Quelldatenbank: - Start Starten ... Target Database: Zieldatenbank: IInputDialog Edit... Bearbeiten... TextLabel Bezeichnung ILinksDialog Links... Verknüpfungen... Type Typ Text Text Uri URI ... IMainWindow QMapShack File Datei View Ansicht Window Fenster ? ? Project Projekt Tool Werkzeug Maps Karten Dig. Elev. Model (DEM) Dig. Höhenmodell (DEM) Data Daten Add Map Workspace Ansicht hinzufügen Route Add Map View Kartenansicht hinzufügen Ctrl+T Show Scale Maßstab Setup Map Font Kartenfont einstellen Show Grid Gitter Ctrl+G Setup Grid Gitter einstellen Ctrl+Alt+G Flip Mouse Wheel Mausrad umdrehen Setup Map Paths Kartenverzeichnisse angeben POI Text POI Text Night / Day Nacht / Tag Map Tool Tip Kartentooltip Ctrl+I Setup DEM Paths DEM Verzeichnisse angeben About Über Help Hilfe Setup Map View Kartenansicht einstellen VRT Builder VRT Builder GUI front end to gdalbuildvrt Eine graphische Benutzerschnittstelle zu gdalbuildvrt Store Map View Kartenansicht speichern Write current active map and DEM list including the properties to a file Speichert die aktive Karten und DEM Dateien inklusive der Eigenschaften in einer Datei Load Map View Kartenansicht laden Restore view with active map and DEM list including the properties from a file Stellt die aktive Karten und DEM Dateien inklusive der Eigenschaften aus einer Datei wieder her Ext. Profile Erw. Profil Ctrl+E Close Schließen Ctrl+Q Clone Map View Kartenansicht klonen Ctrl+Shift+T Create Routino Database Routino Datenbank erstellen Save(Print) Map Screenshot Kartenansicht speichern(drucken) Print a selected area of the map Einen ausgewählten Bereich der Karte drucken Ctrl+P Setup Coord. Format Koordinatenformat einstellen Change the format coordinates are displayed Ändert das Format der angezeigten Koordianten Setup Map Workspace Arbeitsplatz einstellen Load GIS Data GIS Daten laden Load projects from file Lade Datei als Projekt Ctrl+L Save All GIS Data Alle GIS Daten speichern Save all projects in the workspace Alle Projekte die sich auf dem Arbeitsplatz befinden, speichern Ctrl+S Setup Time Zone Zeitzone einstellen Add empty project Leeres Projekt hinzufügen Search Google Mit Google suchen Close all projects Alle Projekte schließen F8 Setup Units Einheiten einstellen Setup Workspace Arbeitsplatz konfigurieren Setup save on exit. Speichert Einstellungen beim Beenden. Import Database from QLandkarte Datenbankimport aus QLandkarte Setup Database Datenbank einrichten Import Database Datenbank importieren Import QLandkarte GT database QLandkarte GT Datenbank importieren IMapList Form To add maps use File->Setup Map Pathss. Um Karten zuzufügen -> Menü Datei - Kartenverzeichnisse angeben. To add maps use File->Setup Map Paths. Um Karten zuzufügen: Menü - Datei - Kartenverzeichnisse angeben. Use the context menu (right mouse button click on entry) to activate a map. Use drag-n-drop to move the activated map in the draw order. Nutze das Kontextmenü ( Klick mit rechter Maustaste auf Eintrag), um eine Karte zu aktivieren. Nutze Ziehen u. Ablegen um die aktivierte Datei in der Reihenfolge der Ansicht zu verschieben. Help! I want maps! I don't want to read the documentation! Hilfe! Ich will Karten! Keine Lust die Anleitung zu lesen! Activate Aktivieren IMapPathSetup Setup map paths Kartenpfad einstellen Root path of tile cache for online maps: Hauptverzeichnis für den Kachelspeicher von Onlinekarten: ... Help! I want maps! I don't want to read the documentation! Hilfe! Ich will Karten! Keine Lust die Anleitung zu lesen! - IMapPropSetup Form <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Ändert die Transparenz der Karte</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Klicken um die aktuelle Skalierung als minimale Skalierung zu benutzen.</p></body></html> ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Stellt den Skalierungsbereich ein, in dem die Karte sichtbar ist. Benutzen Sie die beiden Knöpfe links und rechts um die minimale und maximale Skalierung einzustellen.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Klicken um die aktuelle Skalierung als maximale Skalierung zu benutzen.</p></body></html> Areas Gebiete Lines Linien Points Punkte - Cache Path Speicherpfad Cache Size (MB) Cache (MB) Expiration (Days) Verfallzeit (Tage) IMapVrtBuilder Form ... Select source files: Quelldateien auswählen: Target Filename: Zieldatei auswählen: - Start IMouseEditLine Add points? Punkte hinzufügen? Add points to temporary line? Punkte zur temporären Linie hinzufügen? Warning! Warnung! This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Alle ursprünglichen Daten werden durch eine einfache Koordinatenlinie ersetzt. Alle anderen Daten sind dauerhaft verloren. <b>New Line</b><br/>Move the mouse and use the left mouse button to drop points. When done use the right mouse button to stop.<br/> <b>Neue Linie</b><br/>Erstellen Sie Punkte durch Verschieben des Mauscursors und Drücken der linken Maustaste. Mit der rechten Maustaste beenden Sie den Vorgang.<br/> <b>Delete Point</b><br/>Move the mouse close to a point and press the left button to delete it.<br/> <b>Punkt löschen</b><br/>Bewegen Sie den Mauscursor nahe an einen Punkt und drücken Sie die linke Maustaste um ihn zu löschen. <br/> <b>Select Range of Points</b><br/>Left click on first point to start selection. Left click second point to complete selection and choose from options. Use the right mouse button to cancel.<br/> <b>Punktebereich auswählen</b><br/>Mit der linken Maustaste wählen Sie den ersten Punkt aus. Mit einem erneuten Drücken der linken Maustaste wählen Sie den zweiten Punkt aus und dann wählen Sie eine der Optionen. Zum Abbrechen nutzen Sie die rechte Maustaste.<br/> <b>Move Point</b><br/>Move the mouse close to a point and press the left button to make it stick to the cursor. Move the mouse to move the point. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Punkt verschieben</b><br/>Bewegen Sie den Mauscursor nahe an einen Punkt und drücken die linke Maustaste, um ihn mit dem Cursor zu fangen. Verschieben Sie den Punkt mit der Maus. Setzen Sie den Punkt mit einem Linksklick. Zum Abbrechen nutzen Sie die rechte Maustaste.<br/> <b>Add Point</b><br/>Move the mouse close to a line segment and press the left button to add a point. The point will stick to the cursor and you can move it. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Punkt hinzufügen</b><br/>Bewegen Sie den Mauscursor nahe an ein Liniensegment und drücken die linke Maustaste, um einen Punkt hinzuzufügen. Der Punkt wird durch den Cursor gefangen und kann verschoben werden. Setzen Sie den Punkt mit einem Linksklick. Zum Abbrechen nutzen Sie die rechte Maustaste.<br/> <b>No Routing</b><br/>All points will be connected with a straight line.<br/> <b>Kein Routing</b><br/>Alle Punkte werden mittles einer geraden Linie verbunden.<br/> <b>Auto Routing</b><br/>The current router setup is used to derive a route between points. <b>Note:</b> The selected router must be able to route on-the-fly. Offline routers usually can do, online routers can't.<br/> <b>Auto Routing</b><br/>Die aktuellen Router Einstellungen erstellen eine Route zwischen Punkten. <b>Hinweis:</b> Der gewählte Router muss schnell routen können. Offline Router können dies gewöhnlich, Online Router nicht.<br/> <b>Vector Routing</b><br/>Connect points with a line from a loaded vector map if possible.<br/> <b>Vektor Routing</b><br/>Verbindet Punkte mit einer Linie einer geladenen Vektorkarte, soweit möglich.<br/> <b>%1 Metrics</b> <b>%1maße</b> Distance: Entfernung: Ascend: Anstieg: Descend: Abstieg: <b>No Routing</b><br/>Connect points with a line from a loaded vector map if possible.<br/> <b>Keun Routing</b><br/>Verbindet Punkte mit einer Linie einer geladenen Vektorkarte, soweit möglich.<br/> IPhotoAlbum Form ... IPlot Reset Zoom Zoom zurücksetzen Stop Range Bereichsauswahl beenden Save... Speichern ... No or bad data. Keine oder schlechte Daten. Select output file Ausgabedatei auswählen IPositionDialog Position ... Position... Enter new position Neue Position eingeben Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Falsches Positionsformat. Muss entweder "[N|S] ddd mm.sss [W|E] ddd mm.sss" oder "[N|S] ddd.ddd [W|E] ddd.ddd" sein IPrintDialog Print map... Karte drucken... Save Speichern TextLabel Bezeichnung Print Drucken IProgressDialog Please wait... Bitte warten... TextLabel Bezeichnung IProjWizard Proj4 Wizzard Proj4 Wizard Mercator UTM zone Zone user defined Benutzer definiert Datum World Mercator (OSM) Result: Ergebnis: UPS North (North Pole) UPS Nord (Nordpol) UPS South (South Pole) UPS Süd (Südpol) Projection Projektion IProjWpt Waypoint Projection Wegpunkt Projektion ... - Clone waypoint and move by: Wegpunkt kopieren und verschieben um: m m ° IRouterMapQuest Form Lim. Access Eingeschr. Zugang Highways Autobahnen Toll Road Mautstraßen Seasonal saisonale Straßen Unpaved unbefestigte Straßen Ferry Fähren Language Sprache Country Border Landesgrenzen Profile Profil Avoid: Vermeide: <p>Directions Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> <p>Mit freundlicher Genehmigung von <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> IRouterRoutino Form Profile Profil Mode Modus Database Datenbank Add paths with Routino database. Fügt Pfade mit Routino Datenbanken hinzu. ... Language Sprache To use offline routing you need to define paths to local routing data. Use the setup tool button to register a path. Um Offline-Routing zu nutzen, müssen Pfade zu lokalen Routendaten definiert sein. Benutzen Sie den Werkzeugbutton, um einen Pfad zu registrieren. IRouterRoutinoPathSetup Setup Routino database... Routino Datenbank einrichten... ... - IRouterSetup Form IRoutinoDatabaseBuilder Form ... Select source files: Quelldateien auswählen: Start Starten Target Path: Zielpfad: - File Prefix Dateipräfix IScrOptEditLine Form Save to orignal Ins Original speichern Save as new Als neu speichern Abort Abbrechen Move points. (Ctrl+M) Punkte verschieben. (Ctrl+M) Ctrl+M Add new points. (Ctrl++) Punkte hinzufügen. (Ctrl++) Ctrl++ Select a range of points. (Ctrl+R) Einen Punktebereich wälen (Ctrl+R) Ctrl+R Delete a point. (Ctrl+D) Einen Punkt löschen (Ctrl+D) Ctrl+D No auto-routing or line snapping (Ctrl+O) Kein Auto-Routing oder Fangen an Linie (Ctrl+O) Ctrl+O Use auto-routing to between points. (Ctrl+A) Auto-Routing zwischen Punkten benutzen. (Ctrl+A) Ctrl+A Snap line along lines of a vector map. (Ctrl+V) Fängt die Linie entlang Linien einer Vektorkarte. (Ctrl+V) Move points. Punkte verschieben. ... Add new points. Punkte hinzufügen. Select a range of points. Einen Punktebereich wählen. Delete a point. Einen Punkt löschen. No auto-routing or line snapping Kein Auto-Routing oder Fangen an Linie 0 Use auto-routing to between points. Auto-Routing zwischen Punkten benutzen. A Snap line along lines of a vector map. Fängt die Linie längs Linien einer Vektorkarte. V Ctrl+V Undo last change Letzte Änderung rückgängig machen Redo last change Letzte Änderung wiederherstellen Redo next change Letzte Änderung wiederherstellen IScrOptOvlArea Form View details and edit. Details anzeigen und bearbeiten. ... Copy area into another project. Gebiet in ein anderes Projekt kopieren. Delete area from project. Gebiet aus einem Projekt entfernen. Edit shape of the area. Form eines Gebietes ändern. Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. TextLabel Bezeichnung IScrOptPoint Delete point. Punkt löschen. Select a range of points. Einen Punktebereich auswählen. Move selected point. Ausgewählten Punkt verschieben. Add points before the selected point. Punkte vor dem ausgewählten Punkt einfügen. Add points after the selected point. Punkte nach dem ausgewählten Punkt einfügen. IScrOptRange Delete selected range of points. Löscht gewählten Punktebereich. IScrOptRangeLine Form Delete all points between the first and last one. Alle Punkte zwischen dem ersten und dem letzten Punkt löschen. ... <html><head/><body><p>Calculate a route between the first and last selected point.</p></body></html> Caclculate a route between the first and last selected point. Eine Route zwischen dem ersten und dem letzten Punkt berechnen. IScrOptRangeTrk Form Hide all points. Blendet alle Punkte aus. Show all points. Blendet alle Punkte ein. Select an activity for the selected range. Wählen Sie eine Aktivität für den ausgewählten Bereich. Copy track points as new track. Kopiert Trackpunkte in neuen Track. Hide points. Pukte ausblenden. ... Show points. Punkte anzeigen. Copy track points to clipboard Trackpunkte in die Zwischenablage kopieren TextLabel Bezeichnung IScrOptRte Form <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Details anzeigen & Bearbeiten</p></body></html> View details and edit. Details anzeigen und bearbeiten. ... Copy route into another project. Kopiert die Route in ein anderes Projekt. Delete route from project. Route aus einem Projekt entfernen. Reset route calculation. Routenberechnung zurücksetzen. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Löschen</p></body></html> Calculate route. Route berechnen. Reset route caclulation. ??? calculation statt caclulation - ja :) Routenberechnung zurücksetzen. Move route points. Routenpunkte verschieben. Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. TextLabel Bezeichnung IScrOptTrk Form View details &amp; Edit properties of track. Detailansicht & Trackeigenschaften bearbeiten. Copy track into another project. Track in ein anderes Projekt kopieren. Delete Löschen Show on-screen profile and detailed information about points. Zeigt das Profil und detaillierte Informationen der Punkte. Cut track at selected point into two tracks. Teilt den Track am selektierten Punkt in zwei Teile. Edit position of track points. Position von Trackpunkten bearbeiten. Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. View details and edit properties of track. Details anzeigen und Trackeigenschaften bearbeiten. Delete track from project. Track aus einem Projekt entfernen. Select a range of points. Wähle einen Punktebereich. Reverse track. Track umkehren. Combine tracks. Tracks verbinden. Cut track at selected point. You can use this to: * remove bad points at the start or end of the track * use the track parts to plan a new tour * cut a long track into stages Zerteilt den Track am ausgewählten Punkt. Damit kann man: * schlechte Punkte am Anfang oder Ende eines Tracks entfernen * die Teile zum Planen neuer Touren verwenden * einen langen Track in Etappen zerteilen <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Details anzeigen &amp; Bearbeiten</p></body></html> <html><head/><body><p>View details &amp; Edit properties of track.</p></body></html> <html><head/><body><p>Detailansicht &amp; Trackeigenschaften bearbeiten..</p></body></html> ... <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Löschen</p></body></html> <html><head/><body><p>Show on-screen profile and detailed information about points.</p></body></html> <html><head/><body><p>Zeigt das Profil und detaillierte Informationen der Punkte.</p></body></html> <html><head/><body><p>Cut track at selected point into two tracks.</p></body></html> <html><head/><body><p>Teilt den Track am selektierten Punkt in zwei Teile.</p></body></html> <html><head/><body><p>Edit position of track points.</p></body></html> <html><head/><body><p>Bearbeiten der Position von Trackpunkten.</p></body></html> TextLabel Bezeichnung IScrOptWpt Form <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Details anzeigen &amp; Bearbeiten</p></body></html> View details and edit. Details anzeigen und editieren. ... Copy waypoint into another project. Wegpunkt in ein anderes Projekt kopieren. Delete waypoint from project. Wegpunkt aus einem Projekt entfernen. Show content as static bubble. Inhalt als statische Legende zeigen. Move waypoint to a new location. Wegpunkt an einen neuen Ort verschieben. Clone waypoint and move clone a given distance and angle. Wegpunkt klonen und um eine bestimmte Entfernung und einen bestimmten Winkel verschieben. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Löschen</p></body></html> Toggle read only mode. You have to open the lock to edit the item. Den Schreibschutz ändern. Das Schloss muss offen sein um das Element zu bearbeiten. <html><head/><body><p>Move waypoint to a new location.</p></body></html> <html><head/><body><p>Wegpunkt verschieben.</p></body></html> <html><head/><body><p>Clone waypoint and move clone a given distance and angle.</p></body></html> <html><head/><body><p>Wegpunkt kopieren und unter Angabe von Entfernung und Winkel verschieben.</p></body></html> TextLabel Bezeichnung ISelDevices Select devices... Geräte auswählen... ISelectActivity Activities... Aktivitäten... Select one: Wählen Sie eine aus: ISelectCopyAction Copy item... Element kopieren... Replace existing item Ersetzt vorhandenes Element TextLabel Bezeichnung Do not copy item Element nicht kopieren Create a clone Abbild erstellen Replace with: Ersetzen mit: Keep item: Element behalten: The clone's name will be appended with '_Clone' Der Name das Abbildes wird mit '_Klon' erweitert And for all other items, too. Auch für alle anderen Elemente anwenden. ISelectDBFolder Select Parent Folder... Übergeordneten Ordner auswählen... Name Name ISelectProjectDialog Dialog Dialog Select a project... Ein Projekt auswählen... Select project from list or enter new project name. Wähle ein Projekt aus der Liste oder gib einen neuen Projektnamen ein. New project's name Neuer Projektname New project is created as: Das neue Projekt wird erstellt als: *.qms *.gpx Database Datenbank ISelectSaveAction Copy item... Element kopieren... Replace existing item Ersetzt vorhandenes Element Replace with: Ersetzen mit: TextLabel Bezeichnung Do not replace item Das Element nicht ersetzen Use item: Element verwenden: And for all other items, too. Auch für alle anderen Elemente anwenden. ISetupDB Setup database... Datenbank einrichten... save workspace on exit, and every Ansicht beim Beenden speichern, und alle minutes Minuten Database path Datenbankpfad ISetupDatabase Add database... Datenbank hinzufügen... File Datei - Name Name Add new database. Fügt eine neue Datenbank hinzu. ... Open existing database. Öffnet eine vorhandene Datenbank. ISetupFolder Folder... Ordner... Name Name Database Folder... Datenbankordner... Folder name Ordnername Group Gruppe Project Projekt Other Sonstige ISetupNewWpt New Waypoint... Neuer Wegpunkt... Symbol Symbol ... Position Position Name Name Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Falsches Positionsformat. Muss entweder "[N|S] ddd mm.sss [W|E] ddd mm.sss" oder "[N|S] ddd.ddd [W|E] ddd.ddd" sein ISetupWorkspace Setup database... Datenbank einrichten... Setup workspace... Arbeitsplatz konfigurieren... save workspace on exit, and every Arbeitsplatz beim Beenden speichern, und alle minutes Minuten ITextEditWidget Edit text... Text bearbeiten... ... Undo Rückgängig Ctrl+Z Redo Wiederherstellen Ctrl+Shift+Z Cut Ausschneiden Ctrl+X Copy Kopieren Ctrl+C Paste Einfügen Ctrl+V Align Left Linksbündig Ctrl+L Align Right Rechtsbündig Ctrl+R Align Center Zentriert Ctrl+E Align Block Blocksatz Ctrl+J Underline Unterstreichen Ctrl+U Bold Fett Ctrl+B Italic Kursiv Ctrl+I ITimeZoneSetup Setup Time Zone ... Zeitzone einstellen... UTC Local Lokal Automatic Automatisch Print date/time in Datum/Uhrzeit in long format, or langem Format, oder short format kurzem Format ausgeben IToolShell Execution of external program `%1` failed: Ausführen des externen Programms `%1` ist fehlgeschlagen: Process cannot be started. Der Prozess konnte nicht gestartet werden. Make sure the required packages are installed, `%1` exists and is executable. Stellen Sie sicher, dass die erforderlichen Pakete installiert sind, `%1` existiert und ist ausführbar. External process crashed. Der externe Prozess ist abgestürzt. An unknown error occurred. Ein unbekannter Fehler ist aufgetreten. !!! failed !!! !!! fehlgeschlagen !!! IUnitsSetup Setup units... Einheiten einrichten... Metric metrisch <b>Note:</b> For some GUI elements changing the units will not take effect until you restart QMapShack. <b>Anmerkung:</b> Das Ändern der Einheiten wird bei einigen GUI-Elementen erst nach einem Neustart von QMapShack wirksam. Imperial imperial Nautic nautisch IWptIconDialog Icons... Symbole... QObject Error Fehler Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Falsches Positionsformat. Muss entweder "[N|S] ddd mm.sss [W|E] ddd mm.sss" oder "[N|S] ddd.ddd [W|E] ddd.ddd" sein. Position values out of bounds. Position außerhalb der gültigen Werte. Bad position format. Must be: [N|S] ddd mm.sss [W|E] ddd mm.sss Falsches Positionsformat. Richtig: [N|S] ddd mm.sss [W|E] ddd mm.sss Failed to read... Lesen fehlgeschlagen... Failed to read: %1 line %2, column %3: %4 Lesen fehlgeschlagen: %1 Zeile %2, Spalte %3: %4 Not a GPX file: Keine GPX Datei: Saving GIS data failed... Das Speichern der GIS Daten ist fehlgeschlagen... Filename: %1 Dateiname: %1 Waypoints: %1 Wegpunkte: %1 Tracks: %1 Tracks: %1 Routes: %1 Routen: %1 Areas: %1 Fläche: %1 Save project? Projekt speichern? The project "%1" was changed. Save befor closing it? Das Projekt "%1" wurde geändert. Speichern bevor es geschlossen wird? %1: Correlate tracks and waypoints. %1: Tracks und Wegpunkte verknüpfen. Abort Abbrechen Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project (%1) in the future? Hat das zu lange gedauert? Wollen Sie die Verknüpfung von Tracks und Wegpunkten auch in Zukunft für das Projekt (%1) überspringen? <h3>%1</h3>The project was changed. Save befor closing it? <h3>%1</h3>Das Projekt wurde geändert. Speichern bevor es geschlossen wird? <h3>%1</h3>The project was changed. Save before closing it? <h3>%1</h3>Das Projekt wurde geändert. Speichern, bevor es geschlossen wird? <h3>%1</h3>Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project in the future? <h3>%1</h3>Hat das zu lange gedauert? Wollen Sie die Verknüpfung von Tracks und Wegpunkten auch in Zukunft für dieses Projekt überspringen? Cancelled correlation... Verknüpfung abgebrochen... Canceled correlation... Verknüpfung abgebrochen... <br/> Filename: %1 <br/> Dateiname: %1 Waypoints: %1 Wegpunkte: %1 Tracks: %1 Tracks: %1 Routes: %1 Routen: %1 Areas: %1 Gebiete: %1 Are you sure you want to delete '%1' from project '%2'? Sind Sie sicher, dass Sie '%1' aus dem Projekt '%2' löschen wollen? Are you sure you want to delete '%1' from folder '%2'? Sind Sie sicher, dass Sie '%1' aus dem Ordner '%2' löschen wollen? Delete... Löschen... Failed to open... Öffnen fehlgeschlagen... Failed to open %1 Öffnen fehlgeschlagen: %1 Save GIS data to... GIS Daten speichern in... Save ... Speichern ... Abort save Speichern abbrechen File exists ... Datei existiert... The file exists and it has not been created by QMapShack. If you press 'yes' all data in this file will be lost. Even if this file contains GPX data and has been loaded by QMapShack, QMapShack might not be able to load and store all elements of this file. Those elements will be lost. I recommend to use another file. <b>Do you really want to overwrite the file?</b> Diese Datei wurde nicht mit QMapShack erstellt. Wenn Sie 'Ja' drücken werden alle Daten dieser Datei gelöscht. Selbst wenn diese Datei GPX Daten enthält und mit QMapShack geladen wurde, können nicht alle Elemente dieser Datei durch QMapShack geladen und gespeichert werden. Diese Elemente sind verloren. Ich empfehle die Nutzung einer anderen Datei. <b>Wollen Sie die Datei wirklich überschreiben?</b> Failed to create file '%1' Datei %1' konnte nicht erstellt werden Saveing GIS data failed... Speichern der GIS Daten fehlgeschlagen... Failed to write file '%1' Datei %1' konnte nicht gespeichert werden Changed trackpoints, sacrificed all previous data. Wegpunkte geändert, alle vorherigen Daten sind verloren. Length: %1 %2 Länge: %1 %2 , %1%2 %3, %4%5 %6 Time: %1 Gesamtzeit: %1 , Speed: %1 %2 , Geschw.: %1 %2 Moving: %1 Zeit in Bew.: %1 Start: %1 Beginn: %1 End: %1 Ende: %1 Points: %1 (%2) Punkte: %1 von %2 Ele.: %1 %2 Höhe: %1 %2 slope: %1%3 (%2%) , Steigung: %1%3 (%2%) ... and %1 tags not displayed ... und %1 Tags werden nicht angezeigt Ascend: %1%2 Anstieg: %1 %2 , %1%2 , %1 %2 Ascend: - Anstieg: - Descend: %1%2 Abstieg: %1 %2 Descend: - Abstieg: - Dist.: %1%2 Entf.: %1 %2 Time: %1%2 Zeit: %1 %2 Permanently removed points %1..%2 Punkte %1..%2 dauerhaft entfernt Changed activity to '%1' for complete track. Die Aktivität wurde für den gesamten Track auf '%1' geändert. Changed activity to '%1' for range(%2..%3). Die Aktivität wurde für den Bereich (%2..%3) auf '%1' geändert. slope: %1° (%2%) Neigung: %1° (%2%) Hide points. Punkte ausblenden. Show points. Punkte einblenden. slope: %1°(%2%) Neigung: %1°(%2%) speed: %1%2 , Geschw.: %1 %2 Ascend: %1%2 (%3%) Anstieg: %1 %2 (%3%) Ascend: - (-) Anstieg: - (-) Descend: %1%2 (%3%) , Abstieg: %1 %2 (%3%) Descend: - (-) , Abstieg: - (-) Dist.: %1%2 (%3%) Entf.: %1 %2 (%3%) Dist.: - (-) Entf.: - (-) Moving: %1%2 (%3%) , Zeit in Bew.: %1 %2 (%3%) Moving: - (-) , Zeit in Bew.: - (-) thin dünn normal normal wide weit strong stark _Clone _Klon Area: %1%2 Gebiet: %1 %2 Changed area shape. Gebietsform geändert. Changed name. Name geändert. Changed border width. Umrandungsbreite geändert. Changed fill pattern. Füllung geändert. Changed opacity. Durchsichtigkeit geändert. Changed comment. Kommentar geändert. Changed description. Beschreibung geändert. Changed links Geänderte Verknüpfungen Changed color Farbe geändert Edit name... Name bearbeiten... Enter new waypoint name. Geben Sie einen neuen Namen für den Wegpunkt ein. Elevation: %1 %2 Höhe: %1 %2 Proximity: %1 %2 Abstand: %1 %2 Changed name Name geändert Changed position Position geändert Changed elevation Höhe geändert Changed proximity Abstandsalarm geändert Changed icon Symbol geändert Changed images Bilder geändert Add image Bild hinzufügen Changed comment Kommentar geändert Changed description Beschreibung geändert Length: - Länge: - Time: %1 %2 Gesamtzeit: %1 %2 Time: %2 days %1 Gesamtzeit: %2 days %1 Time: - Gesamtzeit: - Last time routed:<br/>%1 Letzte Routenberechnung: <br/>%1 with %1 mit %1 Time: %1 Gesamtzeit: %1 Distance: %1 %2 Entfernung: %1 %2 Time: %1 Distance: %2 Zeit: %1 Entfernung: %2 Turn: %1 Bearing: %2 Abbiegen: %1 Richtung: %2 Calculation took %1 sec. Die Berechnung dauerte %1 Sek. Changed route points. Geänderte Routenpukte. Archived Archiviert Available Verfügbar Not Available Nicht verfügbar Warning... Warnung... This is a typ file with unknown polygon encoding. Please report! Dieser Dateityp hat eine unbekannte Polygon Kodierung. Bitte mitteilen! This is a typ file with unknown polyline encoding. Please report! Dieser Dateityp hat eine unbekannte Polyline Kodierung. Bitte mitteilen! Initial version. Erstversion. This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press'Ok'. Diese Element ist vermutlich schreibgeschützt, da nicht mit QMapShack erstellt. Normalerweise sollten importierte Daten nicht geändert werden. Wenn doch, drücken Sie 'OK'. <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press 'Ok'. <h3>%1</h3> Diese Element ist vermutlich schreibgeschützt, da nicht mit QMapShack erstellt. Normalerweise sollten importierte Daten nicht geändert werden. Wenn doch, drücken Sie 'OK'. Read Only Mode... Schreibgeschützt... <h4>Comment:</h4> <h4>Kommentar:</h4> <p>--- no comment ---</p> <p>--- kein ---</p> <h4>Description:</h4> <h4>Beschreibung:</h4> [no name] [kein Name] <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press'Ok'. <h3>%1</h3> Dieses Element ist vermutlich schreibgeschützt, da nicht mit QMapShack erstellt. Normalerweise sollten importierte Daten nicht geändert werden. Wenn doch, drücken Sie 'OK'. <p>--- no description ---</p> <p>--- keine ---</p> <h4>Links:</h4> <h4>Verknüpfungen:</h4> <p>--- no links ---</p> <p>--- keine ---</p> Enter new track name. Geben Sie einen neuen Namen für den Track ein. Enter new area name. Geben Sie einen neuen Namen für das Gebiet ein. All your data grouped by folders. Alle Daten nach Ordnern gruppiert. Database Datenbank Lost & Found Verloren & Gefunden Lost & Found (%1) Verloren & Gefunden (%1) Copy flag information from QLandkarte GT track Kopiert das Informationsflag aus dem QLandkarte GT Track Corrupt track ... Beschädigter Track ... Number of trackpoints is not equal the number of training data trackpoints. Anzahl der Trackpunkte entspricht nicht der Anzahl der Trackpunkte der Trainingsdaten. Number of trackpoints is not equal the number of extended data trackpoints. Anzahl der Trackpunkte entspricht nicht der Anzahl der erweiterten Trackpunkte. Number of trackpoints is not equal the number of shadow data trackpoints. Anzahl der Trackpunkte entspricht nicht der Anzahl der ausgeblendeten Trackpunkte. Hide points by Douglas Peuker algorithm (%1%2) Punkte ausblenden mit dem Douglas-Peuker Algorithmus (%1 %2) Hide points with invalid coordinates at the beginning of the track Punkte mit ungültigen Koordinaten wurden ausgeblendet. Reset all hidden track points to visible Alle verborgenen Trackpunkte zurücksetzen Permanently removed all hidden track points Alle verborgenen Trackpunkte wurden dauerhaft entfernt Smoothed profile with a Median filter of size %1 Mit einem Median-Filter der Größe %1 geglättetes Profil Replaced elevation data with data from DEM files. Höhendaten durch Daten von DEM Dateien ersetzt. Offset elevation data by %1%2. Versatz der Höhendaten um %1 %2. Changed start of track to %1. Trackanfang auf %1 verschoben. Remove timestamps. Zeitstempel entfernt. Set artificial timestamps with delta of %1 sec. Künstliche Zeitstempel mit einem Abstand von %1 Sek. gesetzt. Changed speed to %1%2. Geschwindigkeit auf %1 %2 geändert. Delete project... Projekt löschen... Do you really want to delete %1? Sind Sie sicher, dass sie %1? löschen wollen? Do you really want to delete %1 Sind Sie sicher, dass sie %1 löschen wollen? Error... Fehler... Failed to open %1. Die Datei %1 konnte nicht geöffnet werden. Only support lon/lat WGS 84 format. Es wird nur lon/lat WGS 84 als Format unterstützt. Failed to read data. Lesen der Daten fehlgeschlagen. Picture%1 Bild %1 There is another project with the same name. If you press 'ok' it will be removed and replaced. Es gibt schon ein Projekt mit dem selben Namen. Wenn Sie 'ok' drücken wird dieses entfernt und ersetzt. Enter new route name. Geben Sie einen neuen Namen für die Route ein. Foot Fußgänger Bicycle Fahrrad Motor Bike Motorrad Car Auto Cable Car Seilbahn Swim Schwimmen Ship Schiff Aeronautik Luftfahrt Aeronautics Aeronautik Distance: Entfernung: Ascend: Anstieg: Descend: Abstieg: Speed Moving: Geschwindigkeit in Bewegung: Speed Total: Geschwindigkeit insgesamt: Time Moving: Zeit in Bewegung: Time Total: Zeit insgesamt: Progress Verlauf time Uhrzeit distance [%1] Entfernung [%1] Slope (directed) Neigung (mit Vorzeichen) Speed Geschwindigkeit Elevation Höhe Heart Rate Pulsrate Cadence Trittfrequenz Air Temperature Lufttemperatur Water Temperature Wassertemperatur Depth Tiefe qmapshack-1.5.1/src/locale/qmapshack_es.ts000644 001750 000144 00001045614 12624270560 021533 0ustar00oeichlerusers000000 000000 CAbout %1 (API V%2, expected V%3) %1 (API V%2) CCanvas Workspace %1 Espacio de Trabajo %1 View %1 Vista %1 CCommandProcessor Print debug output to console. Print debug output to logfile (temp. path). Do not show splash screen. File with QMapShack configuration. file Files for future use. CDemList Deactivate Desactivar Activate Activar CDemPathSetup Add or remove paths containing DEM data. There can be multiple files in a path but no sub-path is parsed. Supported formats are: %1 Añadir o quitar rutas que contienen datos DEM. Puede haber múltiples archivos en una ruta, pero no se buscará en los subdirectorios. Los formatos soportados son: %1 Select DEM file path... Seleccione la ruta al archivo DEM... CDemPropSetup <b>Grade %1</b> Nivel %1 CDemVRT Error... Error... Failed to load file: %1 Fallo al cargar el archivo: %1 DEM must have one band with 16bit or 32bit data. El DEM debe tener una sola banda con datos de 16 o 32 bits. No georeference information found. No se encontró información de georreferenciación. CDetailsGeoCache none ninguno ??? Searching for images... Buscando imagenes... No images found No se encontraron imagenes CDetailsOvlArea Edit name... Editar nombre... Enter new area name. Introduzca el nombre del nuevo área. Enter new waypoint name. Editar el nombre del nuevo waypoint. <h4>Comment:</h4> <h4>Comentario:</h4> <p>--- no comment ---</p> <p>---sin comentario---</p> <h4>Description:</h4> <h4>Descripción:</h4> <p>--- no description ---</p> <p>---sin descripción---</p> CDetailsPrj none ninguna Build diary... Creando diario... Abort Cancelar <h2>Waypoints</h2> Waypoints Info Información Comment Comentario <h2>Tracks</h2> Tracks <h2>Areas</h2> Áreas You want to sort waypoints along a track, but you switched off track and waypoint correlation. Do you want to switch it on again? Correlation... <b>Summary over all tracks in project</b><br/> distance: %1%2 ascent: %1%2 descend: %1%2 <h2>Routes</h2> Edit name... Editar nombre... Enter new project name. Introducir nuevo nombre de proyecto. Edit keywords... Editar etiquetas... Enter keywords. Introducir etiquetas. Print Diary Imprimir Diario CDetailsRte Edit name... Editar nombre... Enter new route name. CDetailsTrk distance [%1] distancia [%1] speed. [%1] velocidad. [%1] time tiempo distance. [%1] distancia. [%1] Solid color Reduce visible track points Reducir puntos visibles del track Change elevation of track points Cambiar elevación de puntos del track Change timestamp of track points Cambiar fecha/hora de puntos del track Cut track into pieces Dividir track en partes %1 %2 Edit name... Editar nombre... Enter new track name. Introduzca el nombre del nuevo track. Reset activities... This will remove all activities from the track. Proceed? None <h4>Comment:</h4> <h4>Comentario:</h4> <p>--- no comment ---</p> <p>---sin comentario---</p> <h4>Description:</h4> <h4>Descripción:</h4> <p>--- no description ---</p> <p>---sin descripción---</p> CDetailsWpt <h4>Comment:</h4> <h4>Comentario:</h4> <p>--- no comment ---</p> <p>---sin-comentario---</p> <h4>Description:</h4> <h4>Descripción:</h4> <p>--- no description ---</p> <p>---sin descripción---</p> Edit name... Editar nombre... Enter new waypoint name. Introducir el nombre del nuevo waypoint. Enter new proximity range. Introduzca el nuevo valor de proximidad. CElevationDialog No DEM data found for that point. No se encontraron datos DEM para ese punto. CGisListDB Add Database Añadir Base de Datos Add Folder Añadir carpeta Delete Folder Eliminar Carpeta Delete Item Eliminar Elemento Remove Database Quitar Base de Datos Empty Vacío Remove database... Quitar Base de Datos... Do you really want to remove '%1' from the list? Do you realy want to remove '%1' from the list? ¿Desea realmente quitar '%1' de la lista? Delete database folder... Eliminar Carpeta de la Base de Datos... Are you sure you want to delete "%1" from the database? ¿Desea realmente eliminar '%1' de la Base de Datos? Remove items... Eliminar elementos... Are you sure you want to delete all items from Lost&Found? This will remove them permanently. ¿Desea realmente eliminar todos los elementos de 'Objetos Perdidos'? Se eliminarán definitivamente. Are you sure you want to delete all selected items from Lost&Found? This will remove them permanently. ¿Desea realmente eliminar todos los elementos.seleccionados de 'Objetos Perdidos'? Se eliminarán definitivamente. CGisListWks Save Guardar Save As... Guardar Como... Edit.. Editar.. Update Project on Devices Actualizar Proyecto en Dispositivos Close Cerrar Update Project on Device Actualizar Proyecto en Dsipositivo Edit... Editar... Copy to... Copiar a... Show Bubble Move Waypoint Mover Waypoint Proj. Waypoint... Proyectar Waypoint... Route Instructions Calculate Route Reset Route Edit Route Create Route Drop items... <b>Update devices</b><p>Update %1<br/>Please wait...</p> <b>Actualizar dispositivos</b><p>Actualizar %1<br/>Por favor espere...</p> Copy items... Copia elementos.... Track Profile Perfil del Track Show on Map Hide from Map Send to Devices Select Range Seleccionar Rango Edit Track Points Editar Puntos del Track Reverse Track Invertir Track Combine Tracks Combinar Tracks Edit Area Points Editar Puntos del Área Delete Borrar Saving workspace. Please wait. Guardando espacio de trabajo. Por favor espere. Loading workspace. Please wait. Cargando espacio de trabajo. Por favor espere. Close all projects... Cerrar todos los proyectos... This will remove all projects from the workspace. Esto quitará.todos los proyectos.del espacio de trabajo. CGisWidget Load project... The project "%1" is already in the workspace. Cut Track... Partir Track... Do you want to delete the original track? ¿Desea borrar el track original? CGrid [Grid: %1] [Malla: %1] [Grid: %1%2%5 %3%4%5] [Malla: %1%2%5 %3%4%5] [Grid: N %1m, E %2m] [Malla: N %1m, E %2m] %1 %2 %1%2%5 %3%4%5 %1m, %2m N %1m, E %2m CHistoryListWidget Cut history Cortar historial CImportDatabase Import QLandkarte Database Importar Base de Datos de Qlandkarte Select source database... Seleccionar origen de base de datos... Select target database... Seleecionar destino de base de datos... CMainWindow Ele: %1%2 Alt: %1%2 [Grid: %1] Load GIS Data... Cargar Datos GIS... Select output file Select file to load CMapIMG Failed ... Falló ... Unspecified No especificado French Francés German Alemán Dutch Holandés English Inglés Italian Italiano Finnish Finés Swedish Sueco Spanish Español Basque Euskera Catalan Catalán Galician Gallego Welsh Galés Gaelic Gaélico Danish Danés Norwegian Noruego Portuguese Portugués Slovak Eslovaco Czech Checo Croatian Croata Hungarian Húngaro Polish Polaco Turkish Turco Greek Griego Slovenian Esloveno Russian Ruso Estonian Estonio Latvian Letón Romanian Rumano Albanian Albanés Bosnian Bosnio Lithuanian Lituano Serbian Serbio Macedonian Macedonio Bulgarian Búlgaro Major highway Autovía Primaria Principal highway Autovía secundaria Other highway Otras autovías Arterial road Carretera principal Collector road Carretera secundaria Residential street Calle residencial Alley/Private road Callejón/Carretera privada Highway ramp, low speed Acceso a autopista, baja velocidad Highway ramp, high speed Acceso a autopista, alta velocidad Unpaved road Carretera sin asfaltar Major highway connector Conexión con autovía principal Roundabout Rotonda Railroad Ferrocarril Shoreline Línea de costa Trail Sendero Stream Arroyo Time zone Zona horaria Ferry Ferry State/province border Frontera de estado/provincia County/parish border Frontera de condado/término municipal International border Frontera internacional River Río Minor land contour Curva altimétrica menor Intermediate land contour Curva altimétrica intermedia Major land contour Curva altimétrica principal Minor depth contour Curva batimétrica menor Intermediate depth contour Curva batimétrica intermedia Major depth contour Curva batimétrica principal Intermittent stream Curso intermitente Airport runway Pista de aterrizaje Pipeline Tubería Powerline Línea eléctrica Marine boundary Límite marítimo Hazard boundary Límite de peligro Large urban area (&gt;200K) Área urbana grande (&gt;200K) Small urban area (&lt;200K) Área urbana pequeña (&lt;200K) Rural housing area Área de alojamienos rurales Military base Base militar Parking lot Aparcamiento Parking garage Garaje Airport Aeropuerto Shopping center Centro comercial Marina Puerto deportivo University/College Universidad/Facultad Hospital Hospital Industrial complex Complejo industrial Reservation Reserva natural Man-made area Área creada por el hombre Sports complex Complejo deportivo Golf course Recorrido de golf Cemetery Cementerio National park Parque nacional City park Parque urbano State park Parque regional Forest Bosque Ocean Océano Blue (unknown) Azul (desconocido) Sea Mar Large lake Lago grande Medium lake Lago mediano Small lake Lago pequeño Major lake Lago principal Major River Río Principal Large River Río Grande Medium River Río Mediano Small River Río Pequeño Intermittent water Agua intermitente Wetland/Swamp Marisma/Ciénaga Glacier Glaciar Orchard/Plantation Invernadero/Plantación Scrub Monte bajo Tundra Tundra Flat Llanura ??? Failed to read: Fallo al leer: Failed to open: Fallo al abrir: Bad file format: Formato de archivo incorrecto: Failed to read file structure: Fallo al leer la estructura del archivo: Loading %1 Cargando %1 User abort: Abortado por el usuario: File is NT format. QMapShack is unable to read map files with NT format: El archivo está en formato NT. QMapShack no puede leer archivos en formato NT: File contains locked / encypted data. Garmin does not want you to use this file with any other software than the one supplied by Garmin. El archivo contiene datos bloqueados/cifrados. Garmin no quiere que use este archivo con otros programas distintos a los que ellos proporcionan. Point of Interest Punto de Interés Unknown Desconocido Area Área CMapList Deactivate Desactivar Activate Activar Where do you want to store maps? CMapMAP Failed ... Falló... Failed to open: Fallo al abrir: Bad file format: Formato de archivo incorrecto: CMapPathSetup Add or remove paths containing maps. There can be multiple maps in a path but no sub-path is parsed. Supported formats are: %1 Añada o elimine rutas que contengan mapas. Puede haber múltiples mapas en una ruta, pero no se buscará en subdirectorios. Los formatos soportados son: %1 Select map path... Selecciona la ruta del mapa... Select root path... CMapPropSetup Cache path... Ruta de la caché... CMapRMAP Error... Error... This is not a TwoNav RMAP file. Éste no es un archivo en formato TwoNav RMAP. Unknown sub-format. Sub-formato desconocido. Unknown version. Versión desconocida. Failed to read reference point. Fallo al leer el punto de referencia. Unknown projection and datum (%1%2). Proyección y datum desconocidos (%1%2). CMapTMS Error... Error... Failed to open %1 Fallo al abrir %1 Failed to read: %1 line %2, column %3: %4 Fallo al leer: %1 línea %2, columna %3. %4 Layer %1 Capa %1 This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 teselas pendientes<br/> CMapVRT Error... Error... Failed to load file: %1 Fallo al leer el archivo: %1 File must be 8 bit palette or gray indexed. El archivo debe ser con paleta de 8 bits o escala de grises indexada. No georeference information found. No se encontró información de georreferenciación. CMapVrtBuilder Build GDAL VRT Crear GDAL VRT Select files... Seleccionar ficheros de origen... Select target file... Seleccionar fichero de destino... !!! done !!! !!! failed !!! !!! fallo !!! CMapWMTS Error... Error... Failed to open %1 Fallo al abrir %1 Failed to read: %1 line %2, column %3: %4 Fallo al leer: %1 línea %2, columna %3. %4 Failed to read: %1 Unknown structure. Fallo al leer: %1 Estructura desconocida. Unexpected service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Unexpexted service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Servicio no esperado. se esperaba'* WMTS 1.0.0'. Se leyó '%1 %2'. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. No georeference information found. No se encontró información de georreferenciación. <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 teselas pendientes<br/> CMouseEditArea <b>Edit Area</b><br/>Select a corner point for more options.<br/> <b>Editar Área</b><br/>Seleccione un punto de las esquinas para más información.<br/> Area Área <b>Edit Area</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> CMouseEditRte Route <b>Edit Route Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> CMouseEditTrk <b>Edit Track Points</b><br/>Select a track point for more options.<br/> <b>Editar Puntos del Track</b><br/>Selecciones un punto del track para más opciones.<br/> Track <b>Edit Track Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> Warning! ¡Cuidado! This will replace all data of the original by a simple line of coordinates. All other data will be lost permanently. This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Esto sustituirá todos los datos del original con una simple línea de coordenadas. Todos los demás datos se perderán definitivamente. CMouseNormal Add Waypoint Añadir Waypoint Add Track Añadir Track Add Route Add Area Añadir Área Copy position Copiar posición Copy position (Grid) CMousePrint <b>Save(Print) Map</b><br/>Select a rectangular area on the map. Use the left mouse button and move the mouse. Abort with a right click. Adjust the selection by point-click-move on the corners. Save/print the selection by a left click on the disc/printer icon in the center of the selection. CMouseRangeTrk <b>Select Range</b><br/>Select first track point. And then a second one.<br/> <b>Seleccione Rango</b><br/>En el track, seleccione el primer punto, y otro más a continuación.<br/> CPhotoAlbum Select images... Seleccionar imagenes... CPlotDistance distance [%1] distancia [%1] time tiempo time [h] tiempo [h] distance. [%1] distancia. [%1] CPlotProfile distance [%1] distancia [%1] time [h] tiempo [h] alt. [%1] alt. [%1] CPlotSpeed distance [%1] distancia [%1] time [h] tiempo [h] speed. [%1] velocidad. [%1] CPrintDialog Print Map... Save Map as Image... Printer Properties... Pages: %1 x %2 Zoom with mouse wheel on map below to change resolution: %1x%2 pixel x: %3 m/px y: %4 m/px Printing pages. Save map... CProgressDialog Elapsed time: %1 Elapsed time: %1 seconds. CProjWizard north norte south sur Error... Error... The value '%1' is not a valid coordinate system definition: %2 El valor '%1' no es una definición de sistema de coordenadas válido: %2 CProjWpt Edit name... Editar nombre... Enter new waypoint name. Introduzca el nuevo nombre del waypoint. CQlgtDb Migrating database from version 4 to 5. Convirtiendo base de datos de version 4 a 5. Migrating database from version 5 to 6. Convirtiendo base de datos de version 5 a 6. Migrating database from version 6 to 7. Convirtiendo base de datos de version 6 a 7. Migrating database from version 7 to 8. Convirtiendo base de datos de version 7 a 8. Migrating database from version 8 to 9. Convirtiendo base de datos de version 8 a 9. Open database: %1 Abrir base de datos:%1 Folders: %1 Carpetas: %1 Tracks: %1 Tracks: %1 Routes: %1 (Only the basic route will be copied) Routes: %1 (Only the basic route will be copied) Waypoints: %1 Waypoints: %1 Overlays: %1 (only area overlays will be converted to QMapShack) Superposiciones: %1 (solo las superposiciones de área se copiaran a QMapShack) Overlays: %1 (areas will be converted as areas, distance lines will be converted to tracks, all other overlay items will be lost) Diaries: %1 Diarios: %1 Map selections: %1 (can't be converted to QMapShack) Selecciones de mapa: %1 (NO pueden convertirse a QMapShack) ------ Start to convert database to %1------ ------ Comenzar a convertir base de datos a %1------ Failed to create target database. Fallo al crear la base de datos de destino. ------ Abort ------ ------ Abortar ------ ------ Done ------ ------ Hecho ------ Restore folders... Restaurar carpetas... Abort Abortar Imported %1 folders and %2 diaries Importadas %1 carpetas y %2 diarios Copy items... Copia elementos.... Imported %1 tracks, %2 waypoints, %3 routes, %4 areas Importado: %1 tracks, %2 waypoints, %3 routes, %4 areas Import folders... Importar carpetas... Overlay of type '%1' cant be converted No puede convertirse superposición del tipo '%1' CQmsDb Existing file... Remove existing %1? Remove existing file %1 %1: drop item with QLGT DB ID %2 CRouterMapQuest Fastest Shortest Bicycle Pedestrian US English British English Danish Danés Dutch Holandés French Francés German Alemán Italian Italiano Norwegian Noruego Spanish Español Swedish Sueco mode "%1" no highways no toll roads no seasonal no unpaved no ferry no crossing of country borders <b>MapQuest</b><br/>Routing request sent to server. Please wait... <b>MapQuest</b><br/>Bad response from server:<br/>%1 <br/>Calculation time: %1s CRouterRoutino Foot Horse Wheelchair Bicycle Moped Motorcycle Motorcar Goods Shortest Found Routino with a wrong version. Expected %1 found %2 Quickest English Inglés German Alemán French Francés Hungarian Húngaro Dutch Holandés Russian Ruso Polish Polaco A function was called without the database variable set. A function was called without the profile variable set. A function was called without the translation variable set. The specified database to load did not exist. The specified database could not be loaded. The specified profiles XML file did not exist. The specified profiles XML file could not be loaded. The specified translations XML file did not exist. The specified translations XML file could not be loaded. The requested profile name does not exist in the loaded XML file. The requested translation language does not exist in the loaded XML file. There is no highway near the coordinates to place a waypoint. The profile and database do not work together. The profile being used has not been validated. The user specified profile contained invalid data. The routing options specified are not consistent with each other. There is a mismatch between the library and caller API version. Route calculation was aborted by user. A route could not be found to waypoint %1. Unknown error: %1 profile "%1" , mode "%1" Warning... Aviso... %1: Due to limitations in the Windows POSIX API Routino can't handle files larger than 4GB. Calculate route with %1 <br/>Calculation time: %1s CRouterRoutinoPathSetup Add or remove paths containing Routino data. There can be multiple databases in a path but no sub-path is parsed. Select routing data file path... Select DEM file path... Seleccione la ruta al archivo DEM... CRouterSetup Routino (offline) MapQuest (online) CRoutinoDatabaseBuilder Create Routino Database Select files... Seleccionar ficheros de origen... Select target path... !!! done !!! !!! failed !!! !!! fallo !!! CSearchGoogle Unknown response Respuesta desconocida Error: Error: CSetupDB Setup database... Configurar la base de datos... Changes will become active after an application's restart. Los cambios surtirán efecto tras reiniciar la aplicación. Select database path... Seleccione la ruta a la base de datos... CSetupDatabase Error... Error... There is already a database with name '%1' Ya existe una base de datos con el nombre '%1' New database... Nueva base de datos... Open database... Abrir base de datos... CSetupWorkspace Setup database... Configurar la base de datos... Changes will become active after an application's restart. Los cambios surtirán efecto tras reiniciar la aplicación. CTextEditWidget &Color... &Color... IAbout About.... Acerca de... <b>QMapShack</b>, Version <b>QMapShack</b>, Versión TextLabel Qt Qt GDAL GDAL Proj4 Proj4 Routino Rainer Unseld French Francés Czech Checo Pavel Fric German Alemán <b>Translation:</b> Dutch Holandés Harrie Klomp <b>Binaries:</b> <b>Contributors:</b> Josef Latt Spanish Español Jose Luis Domingo Lopez Ivo Kronenberg Helmut Schmidt Win64 OS X ...and thanks to all Linux binary maintainers for doing a great job. Special thanks to Dan Horák and Bas Couwenberg for showing presence on the mailing list to discuss distribution related topics. Christian Eichler (qms@christian-eichler.de) This software is licensed under GPL3 or any later version © 2014 Oliver Eichler (oliver.eichler@gmx.de) ICanvasSetup Setup Map Workspace... Configurar Espacio de Trabajo del Mapa... Setup Map View... Configurar Vista de Mapa... Projection & Datum Proyección & Datum ... ... Scales Logarithmic Square (optimized for TMS and WTMS tiles) ICombineTrk Combine Tracks... Combinar Tracks... ... ... ICoordFormatSetup Coordinate Format... N48° 53.660 E013° 31.113 N48.8943° E013.51855° N48° 53' 39.6" E13° 31' 6.78" ICreateRouteFromWpt Create Route from Waypoints ... ... ICutTrk Cut Track Delete first part of the track and keep second one Keep both parts of the track Keep first part of the track and delete second one Check this to store the result into a new track. If you keep both parts of the track you have to create new ones. If you want to keep just one half you can simply remove the points, or check this to create a new track. Create a new track Create a clone Crear un duplicado IDemPathSetup Setup DEM file pathss Configurar las rutas a los archivos DEM Setup DEM file paths ... ... - - IDemPropSetup Form <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Cambiar la opacidad del mapa</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Pulse para usar la escala actual como la escala mínima a la que mostrar el mapa.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Controle el rango de escalas para las cuales desea que se muestre el mapa. Use los dos botones a izquierda y derecha para definir la escala actual como bien la escala mínima o máxima para el mapa.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Pulse para usar la escala actual como la escala máxima a la que mostrar el mapa</p></body></html> Hillshading Sombreado Slope Pendiente ° º > TextLabel IDemsList Form To add files with elevation data use File->Setup DEM Paths. Para añadir archivos con información de elevación use Archivo->Configurar Rutas a los DEM. Use the context menu (right mouse button click on entry) to activate a file. Use drag-n-drop to move the activated file in the process order. Use el menú contextual (botón derecho del ratón y seleccione) para activar un archivo. Use arrastrar y soltar para mover el archivo activado en el orden de procesamiento. Activate Activar IDetailsGeoCache Dialog - - about:blank about:blank Position: Posición: Difficulty Dificultad Terrain Terreno Update spoilers ... ... Hint: Consejo: TextLabel IDetailsOvlArea Dialog - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>El waypoint se importó en QMapShack y ha sido modificado, por lo que ya no muestra los datos originales. Por favor consulte los cambios en el histórico. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. ... ... Color Color Border width Ancho del borde Style Estilo Opacity Opacidad Info Información Points Puntos Position Posición Hist. Historial IDetailsPrj Form - - Sort By Time Ordenar por Fecha/Hora Keep Order of Project Mantener orden del proyecto Print diary Imprimir diario ... ... Keep order of project Sort by time Sort along track (multiple) Sort along track (single) Rebuild diary. Recargar diario. Keywords: Etiquetas: IDetailsRte Info Información - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>El waypoint se importó en QMapShack y ha sido modificado, por lo que ya no muestra los datos originales. Por favor consulte los cambios en el histórico. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. ... ... Hist. Historial IDetailsTrk Form - - - - Profile Perfil Speed Velocidad Progress Progreso Toggle read only mode. You have to open the lock to edit the item. ... ... <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>El waypoint se importó en QMapShack y ha sido modificado, por lo que ya no muestra los datos originales. Por favor consulte los cambios en el histórico. </p></body></html> Style Estilo from Data Source Maximum Minimum Solid color Graphs Graph 3 Graph 2 Graph 1 Activity To differentiate the track statistics select an activity from the list for the complete track. Or select a part of the track to assign an activity. Points Puntos Time Tiempo Ele. Altitud Delta Delta Dist. Distancia Slope Pendiente Ascend Ascenso Descend Descenso Position Posición Info Información - - Filter Filtro Hist. Historial IDetailsWpt Dialog Toggle read only mode. You have to open the lock to edit the item. ... ... Position: Posición: Info Información - - Ele. Proximity: Proximidad: <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>El waypoint se importó en QMapShack y ha sido modificado, por lo que ya no muestra los datos originales. Por favor consulte los cambios en el histórico. </p></body></html> Hist. Historial <html><head/><body><p>Read Only Mode</p></body></html> <html><head/><body><p>Modo Sólo Lectura</p></body></html> Add images. Añadir imagenes. Delete selected image. Eliminar imagen seleccionada. Date/Time: Fecha/Hora: IElevationDialog Edit elevation... Editar altitud... Elevation Altitud - - Get elevation from active digital elevation model. Obtener la altitud desde el DEM activo. ... ... IFilterDelete Form <b>Remove Track Points</b> <b>Eliminar puntos del Track</b> Remove all hidden track points permanently. Eliminar todos los puntos ocultos del track definitavamente. ... ... IFilterDouglasPeuker Form <b>Hide Points (Douglas Peuker)</b> <b>Ocultar Puntos (Douglas Peuker)</b> Hide track points if the distance to a line between neighboring points is less than Ocultar puntos del track si la distancia a una linea entre puntos vecinos es menor que m m Apply filter now. Aplicar filtro ahora. ... ... IFilterInvalid Form Hide Invalid Points Hide points with invalid coordinates at the beginning of the track. ... ... IFilterMedian Form <b>Smooth Profile (Median Method)</b> <b>Suavizar Perfil (Mediana)</b> Smooth deviation of the track points elevation with a Median filter of size Suavizar el desvio de la elevación de los puntos del track con la Mediana de points puntos ... ... IFilterNewDate Form <b>Change Time</b> <b>Cambiar Fecha/hora</b> Change start of track to Modificar el inicio del track a dd.MM.yy HH:mm:ss - - ... ... IFilterObscureDate Form <b>Obscure Timestamps</b> <b>Ocultar Marcas de tiempo</b> Increase timestamp by Incrementar marca de tiempo en sec. with each track point. 0 sec. will remove timestamps. con cada punto del track. ( 0 sec. eliminará las marcas de tiempo). ... ... IFilterOffsetElevation Form <b>Offset Elevation</b> <b>Desplazar Elevación</b> Add offset of Añadir corrección de to track points elevation. a la elevación de los puntos del track. ... ... IFilterReplaceElevation Form <b>Replace Elevation Data</b> <b>Reemplazar Datos de Elevación</b> Replace elevation of track points with the values from loaded DEM files. Reemplazar elevación de los puntos del track utilizando los valores del fichero DEM cargado. ... ... IFilterReset Form <b>Reset Hidden Track Points</b> <b>Restaurar Puntos Ocultos del Track</b> Make all trackpoints visible again. Hacer visibles de nuevo todos los puntos del track. ... ... IFilterSpeed Form <b>Change Speed</b> <b>Modificar Velocidad</b> Set speed to Ajustar velocidad a km/h ... ... IGisWidget Form Name Nombre To add a database do a right click on the database list above. Para añadir una base de datos haga click-derecho en el espacio superior. IGridSetup Setup Grid... Configurar Malla... Projection Proyección restore default restaurar predeterminado ... ... Get projection from current map. Obtener la proyección desde el mapa actual. projection wizzard asistente de proyección Grid color Color de la malla setup grid color configurar el color de la malla IImportDatabase Form Source Database: Origen de Base de datos: - - ... ... Target Database: Destino de Base de Datos: Start Comenzar IInputDialog Edit... Editar... TextLabel ILinksDialog Links... Links... Type Tipo Text Texto Uri Uri ... ... IMainWindow QMapShack QMapShack File Archivo View Ver Window Ventana ? ? Project Proyecto Tool Herramientas Maps Mapas Dig. Elev. Model (DEM) Modelo Digital del Terreno (DEM) Data Datos Add Map Workspace Añadir Espacio de Trabajo de Mapa Route Add Map View Añadir Vista de Mapa Ctrl+T Ctrl+T Show Scale Mostrar Escala Setup Map Font Configurar Fuente del Mapa Show Grid Mostrar Malla Ctrl+G Ctrl+G Setup Grid Configurar Malla Ctrl+Alt+G Ctrl+Alt+G Flip Mouse Wheel Invertir la Rueda del Ratón Setup Map Paths Configurar Rutas de Mapas POI Text Texto del POI Night / Day Noche / Día Map Tool Tip Mostrar Tooltips en los Mapas Ctrl+I Ctrl+I Setup DEM Paths Configurar Rutas a los DEM About Acerca de Help Ayuda Setup Map View Configurar Vista de Mapa VRT Builder Asistente VRT GUI front end to gdalbuildvrt Store Map View Write current active map and DEM list including the properties to a file Load Map View Restore view with active map and DEM list including the properties from a file Ext. Profile Ctrl+E Ctrl+E Close Cerrar Ctrl+Q Clone Map View Ctrl+Shift+T Create Routino Database Save(Print) Map Screenshot Print a selected area of the map Ctrl+P Setup Coord. Format Change the format coordinates are displayed Setup Map Workspace Configurar Espacio de Trabajo de Mapas Load GIS Data Cargar Datos GIS Load projects from file Cargar proyectos desde archivo Ctrl+L Ctrl+L Save All GIS Data Guardar Todos los Datos GIS Save all projects in the workspace Guardar todos los proyectos del espacio de trabajo Ctrl+S Ctrl+S Setup Time Zone Configurar Zona Horaria Add empty project Añadir proyecto vacío Search Google Buscar en Google Close all projects Cerrar todos los proyectos F8 Setup Units Configurar Unidades Setup Workspace Configurar Espacio de trabajo Setup save on exit. Import Database from QLandkarte Importar base de datos de Qlandkarte Import QLandkarte GT database Setup Database Configurar Base de Datos IMapList Form To add maps use File->Setup Map Paths. Para añadir mapas use Archivo->Configurar Rutas de Mapas. Use the context menu (right mouse button click on entry) to activate a map. Use drag-n-drop to move the activated map in the draw order. Use el menú contextual (botón derecho del ratón, y seleccione) para activar un mapa. Use arrastrar y soltar para mover el mapa activado en el orden de dibujado. Help! I want maps! I don't want to read the documentation! Activate Activar IMapPathSetup Setup map paths Configurar rutas de mapas Root path of tile cache for online maps: ... ... Help! I want maps! I don't want to read the documentation! - - IMapPropSetup Form <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Cambiar la opacidad del mapa</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Pulse para usar la escala actual como la escala mínima a la que mostrar el mapa.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Controle el rango de escalas para las cuales desea que se muestre el mapa. Use los dos botones a izquierda y derecha para definir la escala actual como bien la escala mínima o máxima para el mapa.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Pulse para usar la escala actual como la escala máxima a la que mostrar el mapa.</p></body></html> Areas Áreas Lines Líneas Points Puntos - - Cache Path Cache Size (MB) Tamaño de Caché (MiB) Expiration (Days) Caducidad (Días) IMapVrtBuilder Form ... ... Select source files: Seleccionar ficheros de origen: Target Filename: Fichero de destino: - - Start Comenzar IMouseEditLine Add points? ¿Añadir puntos? Add points to temporary line? ¿Añadir puntos a la línea temporal? Warning! ¡Cuidado! This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Esto sustituirá todos los datos del original con una simple línea de coordenadas. Todos los demás datos se perderán definitivamente. <b>New Line</b><br/>Move the mouse and use the left mouse button to drop points. When done use the right mouse button to stop.<br/> <b>Delete Point</b><br/>Move the mouse close to a point and press the left button to delete it.<br/> <b>Select Range of Points</b><br/>Left click on first point to start selection. Left click second point to complete selection and choose from options. Use the right mouse button to cancel.<br/> <b>Move Point</b><br/>Move the mouse close to a point and press the left button to make it stick to the cursor. Move the mouse to move the point. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Add Point</b><br/>Move the mouse close to a line segment and press the left button to add a point. The point will stick to the cursor and you can move it. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>No Routing</b><br/>All points will be connected with a straight line.<br/> <b>Auto Routing</b><br/>The current router setup is used to derive a route between points. <b>Note:</b> The selected router must be able to route on-the-fly. Offline routers usually can do, online routers can't.<br/> <b>Vector Routing</b><br/>Connect points with a line from a loaded vector map if possible.<br/> <b>%1 Metrics</b> Distance: Ascend: Descend: IPhotoAlbum Form ... ... IPlot Reset Zoom Stop Range Save... No or bad data. Datos incorrectos o inexistentes. Select output file IPositionDialog Position ... Posición ... Enter new position Introduzca la nueva posición Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Formato de posición incorrecto. Debe ser: "[N|S] ggg mm.sss [W|E] ggg mm.sss" o "[N|S] ggg.ggg [W|E] ggg.ggg" IPrintDialog Print map... Save Guardar TextLabel Print IProgressDialog Please wait... TextLabel IProjWizard Proj4 Wizzard Asistente de Proj4 Mercator Mercator UTM UTM zone zona user defined definido por el usuario Datum Datum World Mercator (OSM) World Mercator (OSM) Result: Resultado: UPS North (North Pole) UPS Norte (Polo Norte) UPS South (South Pole) UPS Sur (Polo Sur) Projection Proyección IProjWpt Waypoint Projection Proyección del Waypoint ... ... - - Clone waypoint and move by: Clonar el waypoint y moverlo: m m ° º IRouterMapQuest Form Highways Seasonal Language Country Border Profile Perfil Avoid: Ferry Ferry Toll Road Unpaved <p>Directions Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> IRouterRoutino Form Profile Perfil Mode Database Add paths with Routino database. ... ... Language To use offline routing you need to define paths to local routing data. Use the setup tool button to register a path. IRouterRoutinoPathSetup Setup Routino database... ... ... - - IRouterSetup Form IRoutinoDatabaseBuilder Form ... ... Select source files: Seleccionar ficheros de origen: Start Comenzar Target Path: - - File Prefix IScrOptEditLine Form Save to orignal Guardar al original Save as new Guardar como nuevo Abort Abortar Move points. (Ctrl+M) Ctrl+M Add new points. (Ctrl++) Ctrl++ Select a range of points. (Ctrl+R) Ctrl+R Ctrl+R Delete a point. (Ctrl+D) Ctrl+D No auto-routing or line snapping (Ctrl+O) Ctrl+O Use auto-routing to between points. (Ctrl+A) Ctrl+A Snap line along lines of a vector map. (Ctrl+V) ... ... 0 A V Ctrl+V Ctrl+V Undo last change Redo last change IScrOptOvlArea Form View details and edit. ... ... Copy area into another project. Copiar área en otro proyecto. Delete area from project. Edit shape of the area. TextLabel IScrOptPoint Delete point. Borrar punto. Select a range of points. Selecciones un rango de puntos. Move selected point. Mover el punto seleccionado. Add points before the selected point. Añadir puntos antes del seleccionado. Add points after the selected point. Añadir puntos después del seleccionado. ... ... IScrOptRange Delete selected range of points. Borrar el rango de puntos seleccionado. ... ... IScrOptRangeLine Form Delete all points between the first and last one. ... ... <html><head/><body><p>Calculate a route between the first and last selected point.</p></body></html> IScrOptRangeTrk Form Hide all points. Ocultar todos los puntos. ... ... Show all points. Mostrar todos los puntos. Select an activity for the selected range. Copy track points as new track. Copiar los puntos del track como un nuevo track. TextLabel IScrOptRte Form <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Ver detalles &amp; Editar</p></body></html> ... ... Copy route into another project. Copiar ruta en otro proyecto. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Borrar</p></body></html> View details and edit. Delete route from project. Calculate route. Reset route calculation. Move route points. TextLabel IScrOptTrk Form View details &amp; Edit properties of track. Ver detalles / Editar las propiedades del track. Delete Eliminar Show on-screen profile and detailed information about points. Mostrar el perfil en pantalla e información detallada de los puntos. Cut track at selected point into two tracks. Partir el track en dos en el punto seleccionado. Edit position of track points. Editar la posición de los puntos del track. View details and edit properties of track. ... ... Copy track into another project. Copiar track en otro proyecto. Delete track from project. Select a range of points. Seleccionar un rango de puntos. Reverse track. Invertir track. Combine tracks. Combinar tracks. Cut track at selected point. You can use this to: * remove bad points at the start or end of the track * use the track parts to plan a new tour * cut a long track into stages TextLabel IScrOptWpt Form <html><head/><body><p>View details &amp; Edit</p></body></html> Ver detalles / Editar View details and edit. ... ... Copy waypoint into another project. <html><head/><body><p>Copiar waypoint en otro proyecto.</p></body></html> Delete waypoint from project. Show content as static bubble. Move waypoint to a new location. Clone waypoint and move clone a given distance and angle. <html><head/><body><p>Delete</p></body></html> Eliminar <html><head/><body><p>Move waypoint to a new location.</p></body></html> <html><head/><body><p>Mover el waypoint a una nueva ubicación.</p></body></html> <html><head/><body><p>Clone waypoint and move clone a given distance and angle.</p></body></html> <html><head/><body><p>Clonar el waypoibt y moverlo una cierta distancia y ángulo.</p></body></html> TextLabel ISelDevices Select devices... ISelectActivity Activities... Select one: ISelectCopyAction Copy item... Copiar elemento... Replace existing item Sustituir el elemento existente TextLabel Do not copy item NO copiar el elemento Create a clone Crear un duplicado Replace with: Sustituir por: Keep item: Mantener: The clone's name will be appended with '_Clone' Se añadirá el sufijo '_Clone' al nombre del duplicado And for all other items, too. Hacer igual para todos los elementos. ISelectDBFolder Select Parent Folder... Name Nombre ISelectProjectDialog Select a project... Seleccionar un proyecto... Select project from list or enter new project name. Seleccione un proyecto de la lista, o introduzca un nuevo nombre de proyecto. New project's name New project is created as: El nuevo proyecto se creó como: *.qms *.qms *.gpx *.gpx Database ISelectSaveAction Copy item... Copiar elemento... Replace existing item Sustituir el elemento existente Replace with: Sustituir por: TextLabel Do not replace item Use item: And for all other items, too. Hacer igual para todos los elementos. ISetupDB Setup database... Configurar la base de datos... save workspace on exit, and every guardar el espacio de trabajo al salir, y cada minutes minutos Database path Ruta de la base de datos ... ... - - ISetupDatabase Add database... Añadir Base de Datos... File Archivo - - Name Nombre Add new database. Añadir nueva base de datos. ... ... Open existing database. Abrir base de datos existente. ISetupFolder Folder... Carpeta... Name Nombre Database Folder... Folder name Group Grupo Project Proyecto Other Otros ISetupNewWpt New Waypoint... Symbol ... ... Position Posición Name Nombre Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Formato de posición incorrecto. Debe ser: "[N|S] ggg mm.sss [W|E] ggg mm.sss" o "[N|S] ggg.ggg [W|E] ggg.ggg" ISetupWorkspace Setup database... Configurar la base de datos... Setup workspace... Configurar espacio de trabajo... save workspace on exit, and every guardar el espacio de trabajo al salir, y cada minutes minutos ITextEditWidget Edit text... Editar texto... ... ... Undo Deshacer Ctrl+Z Ctrl+Z Redo Rehacer Ctrl+Shift+Z Ctrl+Shift+Z Cut Cortar Ctrl+X Ctrl+X Copy Copiar Ctrl+C Ctrl+C Paste Pegar Ctrl+V Ctrl+V Align Left Alinear a la Izquierda Ctrl+L Ctrl+L Align Right Alinear a la Derecha Ctrl+R Ctrl+R Align Center Alinear al Centro Ctrl+E Ctrl+E Align Block Alinear Bloque Ctrl+J Ctrl+J Underline Subrayar Ctrl+U Ctrl+U Bold Negrita Ctrl+B Ctrl+B Italic Cursiva Ctrl+I Ctrl+I ITimeZoneSetup Setup Time Zone ... Configurar la Zona Horaria... UTC UTC Local Local Automatic Automático Print date/time in long format, or short format IToolShell Execution of external program `%1` failed: Process cannot be started. Make sure the required packages are installed, `%1` exists and is executable. External process crashed. An unknown error occurred. !!! failed !!! !!! fallo !!! IUnitsSetup Setup units... Configurar unidades... Metric Métrico <b>Note:</b> For some GUI elements changing the units will not take effect until you restart QMapShack. Imperial Imperial Nautic Naútico IWptIconDialog Icons... Iconos... QObject Error Error Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Formato de coordenadas incorrecto. Debe ser: "[N|S] ggg mm.sss [W|E] ggg mm.sss" o "[N|S] ggg.ggg [W|E] ggg.ggg" Position values out of bounds. Valores de posición fuera del límite. Bad position format. Must be: [N|S] ddd mm.sss [W|E] ddd mm.sss Formato de posición incorrecto. Debe ser: "[N|S] ggg mm.sss [W|E] ggg mm.sss" Failed to read... Fallo al leer... Failed to read: %1 line %2, column %3: %4 Fallo al leer: %1 línea %2, columna %3. %4 Not a GPX file: No es un archivo GPX: Saving GIS data failed... Filename: %1 Nombre del archivo: %1 Waypoints: %1 Waypoints: %1 Tracks: %1 Tracks: %1 Routes: %1 Rutas: %1 Areas: %1 Áreas: %1 Save project? %1: Correlate tracks and waypoints. <h3>%1</h3>The project was changed. Save before closing it? <h3>%1</h3>Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project in the future? Canceled correlation... <br/> Filename: %1 Waypoints: %1 Tracks: %1 Routes: %1 Areas: %1 Are you sure you want to delete '%1' from project '%2'? ¿Desea realmente eleiminar '%1' del proyecto '%2'? Are you sure you want to delete '%1' from folder '%2'? ¿Desea realmente eleiminar '%1' de la carpeta '%2'? Delete... Borrar... Failed to open... Fallo al abrir... Failed to open %1 Fallo al abrir %1 Save GIS data to... Guardar los datos GIS en... Save ... Guardar... Abort save Cancelar guardar File exists ... El archivo ya existe ... The file exists and it has not been created by QMapShack. If you press 'yes' all data in this file will be lost. Even if this file contains GPX data and has been loaded by QMapShack, QMapShack might not be able to load and store all elements of this file. Those elements will be lost. I recommend to use another file. <b>Do you really want to overwrite the file?</b> El archivo ya existe y no lo ha creado QMapShack. Si pulsa 'sí' todos los datos de este archivo se perderán. Incluso si el archivo contiene datos GPX y QMapShack lo ha leído, QMapShack podría no ser capaz de leer y almacenar todos los elementos en el archivo, y aquellos que no haya leído se perderán. Se le recomienda usar otro archivo distinto. <b>¿Quiere realmente sobrescribir el archivo</b> Failed to create file '%1' Fallo al crear el archivo '%1' Saveing GIS data failed... Fallo al guardar los datos GIS... Failed to write file '%1' Fallo al escribir en el archivo '%1' Length: %1 %2 Longitud: %1 %2 , %1%2 %3, %4%5 %6 , %1%2 %3, %4%5 %6 Time: %1 Tiempo: %1 , Speed: %1 %2 , Velocidad: %1 %2 Moving: %1 En movimiento: %1 Start: %1 Comienzo: %1 End: %1 Final: %1 Points: %1 (%2) Puntos: %1 (%2) Ele.: %1 %2 Altitud: %1 %2 slope: %1%3 (%2%) pendiente: %1%3 (%2%) speed: %1%2 velocidad: %1%2 ... and %1 tags not displayed Ascend: %1%2 (%3%) Ascenso: %1%2 (%3%) Ascend: - (-) Ascenso: - (-) Descend: - (-) Descenso: - (-) Moving: - (-) En movimiento: - (-) Ascend: %1%2 , %1%2 Ascend: - Descend: %1%2 Descend: - Dist.: %1%2 Time: %1%2 Permanently removed points %1..%2 Hide points. Ocultar puntos. Show points. Mostrar puntos. Changed activity to '%1' for complete track. Changed activity to '%1' for range(%2..%3). Descend: %1%2 (%3%) Descenso: %1%2 (%3%) Changed trackpoints, sacrificed all previous data. Se cambiaron los puntos del track, y descartados todos los datos previos. Dist.: %1%2 (%3%) Distancia: %1%2 (%3%) Dist.: - (-) Dist.: - (-) Moving: %1%2 (%3%) En movimiento: %1%2 (%3%) thin fino normal normal wide ancho strong intenso _Clone Area: %1%2 Changed area shape. Se cambió la forma del área. Changed name. Se cambió el nombre. Changed border width. Se cambió la anchura del borde. Changed fill pattern. Se cambió el patró de relleno. Changed opacity. Se cambió la opacidad. Changed comment. Se cambió el comentario. Changed description. Se cambió la descripción. Changed links Se cambió el enlace Changed color Se cambió el color Elevation: %1 %2 Altitud: %1 %2 Proximity: %1 %2 Proximidad: %1 %2 Changed name Se cambió el nombre Edit name... Editar nombre... Enter new waypoint name. Introduzca el nuevo nombre del waypoint. Changed position Se cambió la posición Changed elevation Se cambió la altitud Changed proximity Se cambió la proximidad Changed icon Se cambió el icono Changed images Se cambió la imagen Add image Añadir Imagen Changed comment Se cambió el comentario Changed description Se cambió la descripción Length: - Time: %1 %2 Tiempo: %1 %2 Distance: %1 %2 Time: - Last time routed:<br/>%1 with %1 Changed route points. Archived Archivado Available Disponible Not Available No Disponible Warning... Aviso... This is a typ file with unknown polygon encoding. Please report! Este es un fichero TYP con una codificación de polígonos desconocida. ¡Por favor repórtelo! This is a typ file with unknown polyline encoding. Please report! Este es un fichero TYP con una codificación de polilíneas desconocida. ¡Por favor repórtelo! Initial version. Versión inicial This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press'Ok'. Este elemento probablemente sea de sólo lectura por no haber sido creado por QMapShack. Habitualmente no deseará cambiar los datos importados. De lo contrario, es correcto pulsar 'OK'. <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press 'Ok'. Read Only Mode... Modo Sólo Lectura... <h4>Comment:</h4> <h4>Comentario:</h4> <p>--- no comment ---</p> <p>---sin comentario---</p> <h4>Description:</h4> <h4>Descripción:</h4> [no name] <p>--- no description ---</p> <p>---sin descripción---</p> <h4>Links:</h4> <p>--- no links ---</p> <p>--- sin links ---</p> Enter new track name. Introduzca el nombre del nuevo track. Enter new area name. Introduzca el nombre del nuevo área. All your data grouped by folders. Todos tus datos agrupados en carpetas. Lost & Found Objetos Perdidos Lost & Found (%1) Objetos Perdidos (%1) Copy flag information from QLandkarte GT track Corrupt track ... Track corrupto... Number of trackpoints is not equal the number of training data trackpoints. Number of trackpoints is not equal the number of extended data trackpoints. Number of trackpoints is not equal the number of shadow data trackpoints. Hide points by Douglas Peuker algorithm (%1%2) Ocultar puntos con algoritmo Douglas Peuker (%1%2) Hide points with invalid coordinates at the beginning of the track Reset all hidden track points to visible Restaurados todos los puntos a visible Permanently removed all hidden track points Elminados definitivamente todos los puntos ocultos del track Smoothed profile with a Median filter of size %1 Pefil suavizado con Mediana de %1 puntos Replaced elevation data with data from DEM files. Datos de elevación sustituidos por valores de fichero DEM. Offset elevation data by %1%2. Elevación desplazada %1%2. Changed start of track to %1. Cambiado el inicio de track a %1. Remove timestamps. Eliminadas las marcas de tiempo. Set artificial timestamps with delta of %1 sec. Marcas de tiempo ficticias con incremento de %1 sec. Changed speed to %1%2. Velocidad modificada a %1%2. Delete project... Eliminar Proyecto... Do you really want to delete %1? ¿Desea realmente eliminar %1? Error... Error... Failed to open %1. Fallo al abrir %1. Only support lon/lat WGS 84 format. Solamente soporta formato lon/lat WGS84. Failed to read data. Fallo al leer los datos. Picture%1 There is another project with the same name. If you press 'ok' it will be removed and replaced. Enter new route name. Foot Bicycle Motor Bike Car Cable Car Swim Ship Aeronautik Aeronautics Distance: Ascend: Descend: Speed Moving: Speed Total: Time Moving: Time Total: Progress Progreso time tiempo distance [%1] distancia [%1] Slope (directed) Speed Velocidad Elevation Altitud Heart Rate Cadence Air Temperature Water Temperature Depth qmapshack-1.5.1/src/locale/qmapshack_nl.ts000644 001750 000144 00001001604 12624270550 021523 0ustar00oeichlerusers000000 000000 CAbout %1 (API V%2, expected V%3) %1 (API V%2, verwachte V%3) %1 (API V%2) %1 (API V%2) CCanvas View %1 Venster %1 CCommandProcessor Print debug output to console. Toon debug resultaat op scherm. Print debug output to logfile (temp. path). Sla debug resultaat op als bestand (tijdelijk psd). Do not show splash screen. Toon geen splash scherm. File with QMapShack configuration. Bestand met QMapShack. configuratie. file bestand Files for future use. Bestanden voor toekomstig gebruik. CDemList Deactivate Deactiveer Activate Activeer CDemPathSetup Add or remove paths containing DEM data. There can be multiple files in a path but no sub-path is parsed. Supported formats are: %1 Maak of verwijder mappen naar DEM gegevens.-Er kunnen meerdere bestanden in een map zijn maar geen submappen Ondersteunende formaten zijn: %1 Select DEM file path... Selecteer map met DEM bestanden... CDemPropSetup <b>Grade %1</b> <b>Graad %1</b> CDemVRT Error... Fout... Failed to load file: %1 Kan bestand %1 niet laden DEM must have one band with 16bit or 32bit data. DEM moet 16 bit of 32 bit gegevens bevatten. No georeference information found. Geen geografische gegevens gevonden. CDetailsGeoCache none geen ??? ??? Searching for images... Zoeken naar afbeeldingen... No images found Geen afbeeldingen gevonden CDetailsOvlArea Edit name... Bewerk naam... Enter new area name. Geef gebied een nieuwe naam. CDetailsPrj You want to sort waypoints along a track, but you switched off track and waypoint correlation. Do you want to switch it on again? Om wayponts van een track te sorteren dient de correlatie van track en waypoints ingeschakeld zijn.. Moet dit weer ingeschakeld worden? Correlation... Correlatie... none geen Build diary... Dagboek maken... <b>Summary over all tracks in project</b><br/> <b>Overzicht van alle tracks in project</b><br/> <h2>Waypoints</h2> <h2>Waypoints</h2> Info Info Comment Notitie <h2>Tracks</h2> <h2>Tracks</h2> distance: %1%2 afstand:-%1%2 ascent: %1%2 Stijging: %1%2 descend: %1%2 daling: %1%2 <h2>Areas</h2> <h2>Gebieden</h2> <h2>Routes</h2> <h2>Routes</h2> Edit name... Bewerk naam... Enter new project name. Geef project een nieuwe naam. Edit keywords... Bewerkt sleutelwoorden... Enter keywords. Geef sleutelwoorden. Print Diary Dagboek afdrukken CDetailsRte Edit name... Bewerk naam... Enter new route name. Geef proute een nieuwe naam. CDetailsTrk distance [%1] afstand [%1] speed. [%1] snelheid. [%1] time tijd distance. [%1] afstand. [%1] Solid color Vaste kleur Reduce visible track points Reduceer zichtbare trackpunten Change elevation of track points Verander hoogte van trackpunten Change timestamp of track points Verander tijdstempels van trackpunten Cut track into pieces Knip track in delen %1 %2 %1 %2 Edit name... Bewerk naam... Enter new track name. Geef track nieuwe naam. Reset activities... Herstel activiteiten... This will remove all activities from the track. Proceed? Dit zal alle activiteiten uit de track verwijderen. Doorgaan? None Geen CDetailsWpt Edit name... Bewerk naam... Enter new waypoint name. Geef waypoint nieuwe naam. Enter new proximity range. Geef nieuwe afstand in voor nabijheid. CElevationDialog No DEM data found for that point. Geen OEM gegevens gevonden voor dit punt. CGisListDB Add Database Database toevoegen Add Folder Map toevoegen Delete Folder Map verwijderen Delete Item Remove Database Database verwijderen Empty Leeg Remove database... Database verwijderen... Do you really want to remove '%1' from the list? Delete database folder... Verwijder database map... Are you sure you want to delete "%1" from the database? Remove items... Are you sure you want to delete all items from Lost&Found? This will remove them permanently. Are you sure you want to delete all selected items from Lost&Found? This will remove them permanently. CGisListWks Edit.. Bewerken.. Show on Map Toon op kaart Hide from Map Toon niet op kaart Save Opslaan Save As... Opslaan als... Send to Devices Verzend naar GPS Close Sluiten Update Project on Device Update project op GPS Delete Verwijder Edit... Bewerken... Copy to... Kopieer naar... Track Profile Track profiel Select Range Selecteer afstand Edit Track Points Trackpunten bewerken Reverse Track Track omdraaien Combine Tracks Combineer tracks Show Bubble Toon ballon Move Waypoint Verplaats waypoint Proj. Waypoint... Projecteer waypoint... Route Instructions Route instructies Calculate Route Route berekenen Reset Route Route omkeren Edit Route Bewerk route Edit Area Points Bewerk gebied punten Create Route Maak route Drop items... <b>Update devices</b><p>Update %1<br/>Please wait...</p> <b>Updaten GPS</b><p>Updaten %1<br/>Moment geduld...</p> Saving workspace. Please wait. Werkruimte opslaan. Moment geduld. Loading workspace. Please wait. Laden werkruimte. Moment geduld. Close all projects... Sluit alle projecten... This will remove all projects from the workspace. Dit zal alle projecten uit de werkruimte verwijderen. Copy items... CGisWidget Load project... Project laden... The project "%1" is already in the workspace. Het project "%1" is al geopend. Cut Track... Track knippen... Do you want to delete the original track? Moet de orginele track verwijderd worden? CGrid %1 %2 %1 %2 %1%2%5 %3%4%5 %1%2%5 %3%4%5 %1m, %2m %1m, %2m N %1m, E %2m N %1m, O %2m CHistoryListWidget Cut history Historie wissen CImportDatabase Import QLandkarte Database QLandkarte database importeren Select source database... Bron database selecteren... Select target database... Doel database selecteren... CMainWindow Ele: %1%2 Hoogte: %1%2 [Grid: %1] [Raster: %1] Load GIS Data... GIS gegevens laden... Select output file Selecteer bestand Select file to load Selecteer bestand CMapIMG Failed ... Mislukt... Unspecified Ongespecificeerd French Frans German Duits Dutch Nederlands English Engels Italian Italiaans Finnish Fins Swedish Zweeds Spanish Spaans Basque Baskisch Catalan Catalaans Galician Galicisch Welsh Wels Gaelic Gaelisch Danish Deens Norwegian Noors Portuguese Portugees Slovak Slowaaks Czech Tsjechisch Croatian Kroatisch Hungarian Hongaars Polish Pools Turkish Turks Greek Grieks Slovenian Sloveens Russian Russisch Estonian Ests Latvian Lets Romanian Roemeens Albanian Albanisch Bosnian Bosnisch Lithuanian Litouws Serbian Servisch Macedonian Macedonisch Bulgarian Bulgaars Major highway Belangrijke snelweg Principal highway Gewone snelweg Other highway Andere snelweg Arterial road Uitvalsweg Collector road Verzamelweg Residential street Woonstraat Alley/Private road Laan/privéweg Highway ramp, low speed Snelweg oprit, langzame snelheid Highway ramp, high speed Snelweg oprit, hoge snelheid Unpaved road Onverharde weg Major highway connector Belangrijke snelwegknooppunt Roundabout Rotonde Railroad Spoorlijn Shoreline Kustlijn Trail Pad Stream Stroom Time zone Tijdzone Ferry Veerdienst State/province border Staat/provinciegrens County/parish border Provincie/gemeentegrens International border Internationale grens River Rivier Minor land contour Intermediate land contour Major land contour Minor depth contour Intermediate depth contour Major depth contour Intermittent stream Intermitterende beek Airport runway Landingsbaan Pipeline Pijplijn Powerline Hoogspanningsleiding Marine boundary Zeegrens Hazard boundary Gevaarlijke grens Large urban area (&gt;200K) Groot bevolkt gebied (&gt;200K) Small urban area (&lt;200K) Klein bevolt gebied (&lt;200K) Rural housing area Landelijk woongebied Military base Militaire basis Parking lot Parkeerterrein Parking garage Parkeergarage Airport Vliegveld Shopping center Winkelcentrum Marina Jachthaven University/College Universiteit/College Hospital Ziekenhuis Industrial complex Industrie Reservation Reservaat Man-made area Gemaakt gebied Sports complex Sprtcomplex Golf course Golfbaan Cemetery Begraafplaats National park Nationaal park City park Stadspark State park Staatspark Forest Bos Ocean Oceaan Blue (unknown) Blauw (onbekend Sea Zee Large lake Groot meer Medium lake Middelmatig meer Small lake Klein meer Major lake Belangrijk meer Major River Belangrijke rivier Large River Groot rivier Medium River Middelmatig rivier Small River Klein rivier Intermittent water Intermitterende water Wetland/Swamp Moeras Glacier Gletsjer Orchard/Plantation Boomgaard/Plantage Scrub Struikgewas Tundra Toendra Flat Vlak ??? ??? Failed to read: Lezen mislukt: Failed to open: Openen mislukt: Bad file format: Verkeerd bestandsformaat: Failed to read file structure: Lezen bestandsstructuur mislukt: Loading %1 Laden %1 User abort: Afgebroken door gebruiker: File is NT format. QMapShack is unable to read map files with NT format: File contains locked / encypted data. Garmin does not want you to use this file with any other software than the one supplied by Garmin. Point of Interest Unknown Onbekend Area Gebied CMapList Deactivate Deactiveer Activate Activeer Where do you want to store maps? Waar moeten de kaarten opgeslagen worden? CMapMAP Failed ... Failed to open: Openen mislukt: Bad file format: Verkeerd bestandsformaat: CMapPathSetup Add or remove paths containing maps. There can be multiple maps in a path but no sub-path is parsed. Supported formats are: %1 Maak of verwijder mappen naar kaarten.-Er kunnen meerdere kaarten in een map zijn maar geen submappen Ondersteunende formaten zijn: %1 Select map path... Select root path... CMapRMAP Error... Fout... This is not a TwoNav RMAP file. Dit is geen TwoNav RMAP bestand. Unknown sub-format. Onbekend sub formaat. Unknown version. Onbekende versie. Failed to read reference point. Kan geen referentiepunten lezen. Unknown projection and datum (%1%2). Onbekende projectie en datum (%1%2). CMapTMS Error... Fout... Failed to open %1 Openen mislukt-%1 Failed to read: %1 line %2, column %3: %4 Lezen mislukt: %1 lijn %2, kolom %3: %4 Layer %1 Laag-%1 This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. <b>%1</b>: %2 tiles pending<br/> CMapVRT Error... Fout... Failed to load file: %1 Bestand laden mislukt: %1 File must be 8 bit palette or gray indexed. Bestand moet 8 bit kleur of grijs geindexeerd zijn. No georeference information found. Geen geografische gegevens gevonden. CMapVrtBuilder Build GDAL VRT Select files... Selecteer bestanden... Select target file... Selecteer doel bestand... !!! done !!! !!! klaar !!! CMapWMTS Error... Fout... Failed to open %1 Openen mislukt-%1 Failed to read: %1 line %2, column %3: %4 Lezen mislukt: %1 lijn %2, kolom %3: %4 Failed to read: %1 Unknown structure. Lezen mislukt: %1 Onbekende structuur. Unexpected service. '* WMTS 1.0.0' is expected. '%1 %2' is read. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. No georeference information found. Geen geografische gegevens gevonden. <b>%1</b>: %2 tiles pending<br/> CMouseEditArea Area Gebied <b>Edit Area</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Bewerk gebied</b><br/>Selecteer een functie en een routeoptie via de gereedschapknoppen. Selecteer dan een punt op de lijn. Alleen zwarte vierkante punten kunnen gewijzigd worden. De ronde zwarte subpunten niet.<br/> CMouseEditRte Route Route <b>Edit Route Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Bewerk route punten</b><br/>Selecteer een functie en een routeoptie via de gereedschapknoppen. Selecteer dan een punt op de lijn. Alleen zwarte vierkante punten kunnen gewijzigd worden. De ronde zwarte subpunten niet.<br/> CMouseEditTrk Track Track <b>Edit Track Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Bewerk track punten</b><br/>Selecteer een functie en een routeoptie via de gereedschapknoppen. Selecteer dan een punt op de lijn. Alleen zwarte vierkante punten kunnen gewijzigd worden. De ronde zwarte subpunten niet.<br/> Warning! Waarschuwing! This will replace all data of the original by a simple line of coordinates. All other data will be lost permanently. CMouseNormal Add Waypoint Maak waypoint Add Track Maak track Add Route Maak route Add Area Maak gebied Copy position Kopieer positie Copy position (Grid) Kopieer positie (Raster) CMousePrint <b>Save(Print) Map</b><br/>Select a rectangular area on the map. Use the left mouse button and move the mouse. Abort with a right click. Adjust the selection by point-click-move on the corners. Save/print the selection by a left click on the disc/printer icon in the center of the selection. <b>Opslaan of afdrukken kaart</b><br>Selecteer een rechthoekig deel van de kaart. Gebruik de linkertoets van de muis en beweeg deze. Annuleer door een rechter klik. Stel in door een hoek met linker muisknop te bewegen. Opslaan of afdrukken kan door op het icoon te klikken in het midden van de geselecteerde kaart. CMouseRangeTrk <b>Select Range</b><br/>Select first track point. And then a second one.<br/> CPhotoAlbum Select images... Selecteer afbeeldingen... CPlotDistance distance [%1] afstand [%1] time tijd distance. [%1] afstand. [%1] CPlotProfile distance [%1] afstand [%1] time [h] tijd [u] alt. [%1] hoogte [%1] CPlotSpeed distance [%1] afstand [%1] time [h] tijd [u] speed. [%1] snelheid. [%1] CPrintDialog Print Map... Kaart afdrukken... Save Map as Image... Sla kaart op als afbeelding... Printer Properties... Printer instellingen... Pages: %1 x %2 Pagina's: %1 x %2 Zoom with mouse wheel on map below to change resolution: %1x%2 pixel x: %3 m/px y: %4 m/px Zoom met muiswiel op kaart om resulotie te veranderen: %1x%2 pixel x: %3 m/px y: %4 m/px Printing pages. Pagina's afdrukken. Save map... Kaart opslaan... CProgressDialog Elapsed time: %1 Verstreken tijd: %1 Elapsed time: %1 seconds. Verstreken tijd: %1 seconden. CProjWizard north noord south zuid Error... Fout... The value '%1' is not a valid coordinate system definition: %2 De waarde '%1' is geen geldig coordinaat definitie: %2 CProjWpt Edit name... Bewerk naam... Enter new waypoint name. Geef waypoint een nieuwe naam. CQlgtDb Migrating database from version 4 to 5. Migreren database van versie 4 naar 5. Migrating database from version 5 to 6. Migreren database van versie 5 naar 6. Migrating database from version 6 to 7. Migreren database van versie 6 naar 7. Migrating database from version 7 to 8. Migreren database van versie 7 naar 8. Migrating database from version 8 to 9. Migreren database van versie 8 naar 9. Open database: %1 Open database: %1 Folders: %1 Mappen: %1 Tracks: %1 Tracks: %1 Routes: %1 (Only the basic route will be copied) Waypoints: %1 Waypoints: %1 Overlays: %1 (areas will be converted as areas, distance lines will be converted to tracks, all other overlay items will be lost) Diaries: %1 Map selections: %1 (can't be converted to QMapShack) ------ Start to convert database to %1------ Failed to create target database. ------ Abort ------ ------ Geannuleerd ------ ------ Done ------ ------ Klaar ------ Restore folders... Imported %1 folders and %2 diaries Copy items... Imported %1 tracks, %2 waypoints, %3 routes, %4 areas Geimporteerd %1 tracks, %2 waypoints, %3 routes, %4 gebieden Import folders... Overlay of type '%1' cant be converted CQmsDb Existing file... Remove existing %1? Remove existing file %1 %1: drop item with QLGT DB ID %2 CRouterMapQuest Fastest Snelst Shortest Kortst Bicycle Fiets Pedestrian Wandelen US English VS Engels British English Brits Engels Danish Deens Dutch Nederlands French Frans German Duits Italian Italiaans Norwegian Noors Spanish Spaans Swedish Zweeds mode "%1" no highways geen snelwegen no toll roads geen tolwegen no seasonal geen seizoenswegen no unpaved onverhard no ferry geen veer no crossing of country borders geen grensovergangen <b>MapQuest</b><br/>Routing request sent to server. Please wait... <b>MapQuest</b><br/>Route aanvraag naar server gezonden. Moment geduld... <b>MapQuest</b><br/>Bad response from server:<br/>%1 <b>MapQuest</b><br/>Geen reactie van server:<br/>%1 <br/>Calculation time: %1s <br/>Berekenen tijd: %1s CRouterRoutino Warning... Waarschuwing... Found Routino with a wrong version. Expected %1 found %2 Routini gevonden met verkeerde versie. Verwachtte %1 gevonden %2 Shortest Kortst Quickest Snelst Foot Wandelen Horse Paard Wheelchair Rolstoel Bicycle Fiets Moped Brommer Motorcycle Motorfiets Motorcar Auto Goods Goederen English Engels German Duits French Frans Hungarian Hongaars Dutch Nederlands Russian Russisch Polish Pools A function was called without the database variable set. A function was called without the profile variable set. A function was called without the translation variable set. The specified database to load did not exist. The specified database could not be loaded. The specified profiles XML file did not exist. The specified profiles XML file could not be loaded. The specified translations XML file did not exist. The specified translations XML file could not be loaded. The requested profile name does not exist in the loaded XML file. The requested translation language does not exist in the loaded XML file. There is no highway near the coordinates to place a waypoint. The profile and database do not work together. The profile being used has not been validated. The user specified profile contained invalid data. The routing options specified are not consistent with each other. There is a mismatch between the library and caller API version. Route calculation was aborted by user. Berekening route was geanuleerd door gebruiker. A route could not be found to waypoint %1. Er kon geen route gevonden worden naar waypoint %1. Unknown error: %1 Onbekende fout: %1 profile "%1" Profiel "%1" , mode "%1" %1: Due to limitations in the Windows POSIX API Routino can't handle files larger than 4GB. Calculate route with %1 Bereken route met %1 <br/>Calculation time: %1s <br/>Berekenen tijd: %1s CRouterRoutinoPathSetup Add or remove paths containing Routino data. There can be multiple databases in a path but no sub-path is parsed. Select routing data file path... CRouterSetup Routino (offline) Routino (offline) MapQuest (online) MapQuest (online) CRoutinoDatabaseBuilder Create Routino Database Maak Routino database Select files... Selecteer bestanden... Select target path... Selecteer doel map... !!! done !!! !!! klaar !!! CSearchGoogle Unknown response Onbekende reactie Error: Fout: CSetupDatabase Error... Fout... There is already a database with name '%1' Er is al een database met naam '%1' New database... Nieuwe database... Open database... Open database... CSetupWorkspace Setup database... Instelling database... Changes will become active after an application's restart. Veranderingen zullen na herstart toegepast worden. CTextEditWidget &Color... &Kleur... IAbout About.... Over... <b>QMapShack</b>, Version <b>QMapShack<b>, Versie TextLabel TextLabel Qt Qt GDAL GDAL Proj4 Proj4 Routino Routino Rainer Unseld Rainer Unseld French Frans Czech Tsjechisch Pavel Fric Pavel Fric German Duits <b>Translation:</b> <b>Vertaling:</b> Dutch Nederlands Harrie Klomp <b>Binaries:</b> <b>Binaries::</b> <b>Contributors:</b> <b>Medewerkerss:</b> Christian Eichler (qms@christian-eichler.de) Christian Eichler (qms@christian-eichler.de) Translation: Vertaling: Josef Latt Josef Latt Spanish Spaans Jose Luis Domingo Lopez Jose Luis Domingo Lopez Ivo Kronenberg Ivo Kronenberg Helmut Schmidt Helmut Schmidt Win64 Win64 OS X OS X ...and thanks to all Linux binary maintainers for doing a great job. Special thanks to Dan Horák and Bas Couwenberg for showing presence on the mailing list to discuss distribution related topics. ..en een dank naar alle Linux binarie beheerders voor het geleverde werk. Speciale dank aan Dan Horák en Bas Couwenberg voor het tonen van hun aanwezigheid op de mailinglijst om de distributie-gerelateerde onderwerpen te bespreken. Binaries: Binaries: This software is licensed under GPL3 or any later version Deze software is gelicenseerd onder GPL3 of latere versies © 2014 Oliver Eichler (oliver.eichler@gmx.de) © 2014 Oliver Eichler (oliver.eichler@gmx.de) ICanvasSetup Setup Map View... Kaartinstellingen... Projection & Datum Projectie & Datum ... ... Scales Schalen Logarithmic Logarithmisch Square (optimized for TMS and WTMS tiles) Vierkant (optimaal voor TMS en WTMS delen) ICombineTrk Combine Tracks... Combineer tracks... ... ... ICoordFormatSetup Coordinate Format... Coördinaat formaat... N48° 53.660 E013° 31.113 N48° 53.660 O013° 31.113 N48.8943° E013.51855° N48.8943° O013.51855° N48° 53' 39.6" E13° 31' 6.78" N48° 53' 39.6" O13° 31' 6.78" ICreateRouteFromWpt Create Route from Waypoints Maak route van waypoints ... ... ICutTrk Cut Track Track knippen Delete first part of the track and keep second one Verwijder eerste deel van track en bewaar tweede deel Keep both parts of the track Bewaar beide delen van track Keep first part of the track and delete second one Bewaar eerste deel van track en verwijder tweede deel Check this to store the result into a new track. If you keep both parts of the track you have to create new ones. If you want to keep just one half you can simply remove the points, or check this to create a new track. Create a new track Maak een nieuwe track IDemPathSetup Setup DEM file paths Map met DEM bestanden instellen ... ... - - IDemPropSetup Form Formulier <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Verander transparantie van kaart</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> Hillshading Slope Helling ° ° > > TextLabel Benaming IDemsList Form Formulier To add files with elevation data use File->Setup DEM Paths. Use the context menu (right mouse button click on entry) to activate a file. Use drag-n-drop to move the activated file in the process order. Activate Activeer IDetailsGeoCache Dialog Dialoog Position: Positie: - - Difficulty Moeilijkheid Terrain Terrein Update spoilers Spoilers bijwerken ... ... about:blank Over:leeg Hint: Hint: TextLabel Benaming IDetailsOvlArea Dialog Dialoog - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Het waypoint was geimporteerd naar QMapShack en is aangepast. Het bevat geen orginele gegevens meer. Bekijk historie voor veranderingen. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Schrijfbeveiliging ingeschakeld. Klik op het slot om te kunnen bewerken. ... ... Color Kleur Border width Lijndikte Style Stijl Opacity Transparantie Info Info Points Punten Position Positie Hist. Hist. IDetailsPrj Form Keywords: Sleutelwoorden: - - Keep order of project Behoud volgorde van project Sort by time Sorteer op tijd Sort along track (multiple) Sorteer langs route (multi) Sort along track (single) Sorteer langs route (enkel) ... ... Print diary Dagboek afdrukken Rebuild diary. IDetailsRte Info Info - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Het waypoint was geimporteerd naar QMapShack en is aangepast. Het bevat geen orginele gegevens meer. Bekijk historie voor veranderingen. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Schrijfbeveiliging ingeschakeld. Klik op het slot om te kunnen bewerken. ... ... Hist. Hist. IDetailsTrk Form Formulier - - - - Graph Control Grafieken Profile Profiel Speed Snelheid Track Track Toggle read only mode. You have to open the lock to edit the item. Schrijfbeveiliging ingeschakeld. Klik op het slot om te kunnen bewerken. ... ... <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Het waypoint was geimporteerd naar QMapShack en is aangepast. Het bevat geen orginele gegevens meer. Bekijk historie voor veranderingen. </p></body></html> - - Info Info Style Stijl from Data uit gegevens Source Bron Maximum Maximum Minimum Minimum Solid color Vaste kleur Graphs Grafieken Graph 3 Grafiek 3 Graph 2 Grafiek 2 Graph 1 Grafiek 1 Activity Activiteit To differentiate the track statistics select an activity from the list for the complete track. Or select a part of the track to assign an activity. Om de track statistieken te onderscheiden selecteer een activiteit uit de lijst voor de volledige track. Of selecteer een deel van de track om een activieteit toe te kennen. Points Punten Time Tijd Ele. Hoogte. Delta Delta Dist. Afstand. Slope Helling Ascend Stijging Descend Daling Position Positie Filter Filter Hist. Hist. IDetailsWpt Dialog Dialoog Info Info Position: Positie: - - Ele. Hoogte. Proximity: Nabijheid: <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Het waypoint was geimporteerd naar QMapShack en is aangepast. Het bevat geen orginele gegevens meer. Bekijk historie voor veranderingen. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Schrijfbeveiliging ingeschakeld. Klik op het slot om te kunnen bewerken. ... ... Date/Time: Datum/Tijd: Add images. Afbeelding toevoegen. Delete selected image. Verwijder geselecteerde afbeelding. Hist. Hist. IElevationDialog Edit elevation... Bewerk hoogte... Elevation Hoogte - - Get elevation from active digital elevation model. Neem hoogte gegevens over van DEM (digital elevation model). ... ... IFilterDelete Form Formulier <b>Remove Track Points</b> <b>Verwijder trackpunten</b> Remove all hidden track points permanently. Verwijder permanent alle verborgen trackpunten. ... ... IFilterDouglasPeuker Form Formulier <b>Hide Points (Douglas Peuker)</b> <b>Verberg punten (Douglas Peuker)</b> Hide track points if the distance to a line between neighboring points is less than Verberg trackpunten wanneer de afstand tussen naaste punten minder is dan m m Apply filter now. Nu filter toepassen. ... ... IFilterInvalid Form Formulier Hide Invalid Points Verberg ongeldige punten Hide points with invalid coordinates at the beginning of the track. Verberg punten met ongeldige coördinaten naar het begin van de track. ... ... IFilterMedian Form Formulier <b>Smooth Profile (Median Method)</b> <b>Vloeiend profiel (Mediaans methode)</b> Smooth deviation of the track points elevation with a Median filter of size Vloeiend hoogteverschil van trackpunten-maken met een Mediaans filter van points punten ... ... IFilterNewDate Form Formulier <b>Change Time</b> <b>Verander tijd</b> Change start of track to Verander start van track naar dd.MM.yy HH:mm:ss dd.MM.yy UU:mm:ss - - ... ... IFilterObscureDate Form Formulier <b>Obscure Timestamps</b> <b>Onduidelijke tijdstempels</b> Increase timestamp by Verhoog tijdstempels met sec. sec. with each track point. 0 sec. will remove timestamps. voor elk trackpunt. 0 sec. zal tijdstempels verwijderen. ... ... IFilterOffsetElevation Form Formulier <b>Offset Elevation</b> Add offset of to track points elevation. ... ... IFilterReplaceElevation Form Formulier <b>Replace Elevation Data</b> <b>Vervang hoogtegegevens</b> Replace elevation of track points with the values from loaded DEM files. Vervang hoogte van trackpunten met gegevens van geladen DEM bestand. ... ... IFilterReset Form Formulier <b>Reset Hidden Track Points</b> <b>Herstel verborgen trackpunten</b> Make all trackpoints visible again. Maak alle trackpunten weer zichtbaar. ... ... IFilterSpeed Form Formulier <b>Change Speed</b> <b>Verander snelheid</b> Set speed to Zet snelheid op km/h km/u ... ... IGisWidget Form Formulier Name Naam To add a database do a right click on the database list above. IGridSetup Setup Grid... Raster instelling... Projection Projectie restore default standaardinstellingen herstellen ... ... Get projection from current map. Neem projectie van huidige kaart. projection wizzard projectie wizzard Grid color Rasterkleur setup grid color Instelling rasterkleur IImportDatabase Form Formulier ... ... Source Database: Bron database: - - Target Database: Doel database: Start Start IInputDialog Edit... Bewerken... TextLabel Benaming ILinksDialog Links... Links... Type Soort Text Tekst Uri URL ... ... IMainWindow QMapShack QMapShack File Bestand View Instellingen Window Venster ? ? Project Project Tool Extra Maps Kaarten Dig. Elev. Model (DEM) Dig. Elev. Model (DEM) Data Gegevens Route Route Add Map View Nieuw venster Ctrl+T Ctrl+T Show Scale Toon schaal Setup Map Font Lettertype kaart instellen Show Grid Toon raster Ctrl+G Ctrl+G Setup Grid Raster instellen Ctrl+Alt+G Ctrl+Alt+G Flip Mouse Wheel Draai muiswiel om Setup Map Paths Map met kaarten instellen POI Text Night / Day Dag/Nacht Map Tool Tip Ctrl+I Ctrl+I Setup DEM Paths Map DEM instellen About Over Help Help Setup Map View Kaartinstellingen Load GIS Data GIS gegevens laden Load projects from file Project laden uit bestand Ctrl+L Ctrl+L Save All GIS Data GIS gegevens opslaan Save all projects in the workspace Sla alle projecten op in werkruimte Ctrl+S Ctrl+S Setup Time Zone Tijdzone instellen Add empty project Nieuw leeg project Search Google Zoeken Google Close all projects Sluit alle projecten F8 F8 Setup Units Eenheden instellen Setup Workspace Werkruimte instellen Setup save on exit. Oplaan bij afsluiten instellen. Import Database from QLandkarte Database van QLandkarte importeren Import QLandkarte GT database QLandkarte database importeren VRT Builder GUI front end to gdalbuildvrt Store Map View Kaart opslaan Write current active map and DEM list including the properties to a file Load Map View Kaart laden Restore view with active map and DEM list including the properties from a file Ext. Profile Ctrl+E Ctrl+E Close Sluiten Ctrl+Q Ctrl+Q Clone Map View Kloon venster Ctrl+Shift+T Ctrl+Shift+T Create Routino Database Maak Routino database Save(Print) Map Screenshot Kaartdeel opslaan/afdrukken Print a selected area of the map Geselecteerde deel van kaart afdrukken Ctrl+P Ctrl+P Setup Coord. Format Coördinaat formaat instellen Change the format coordinates are displayed Getoonde coördinaat formaat aanpassen IMapList Form Formulier To add maps use File->Setup Map Paths. Om kaarten toe te voegen ->Map met kaarten instellen. Use the context menu (right mouse button click on entry) to activate a map. Use drag-n-drop to move the activated map in the draw order. Help! I want maps! I don't want to read the documentation! Help! Ik wil kaarten! Ik wil de documentatie niet lezen! Activate Activeer IMapPathSetup Setup map paths Map met kaarten instellen Root path of tile cache for online maps: Hoofdmap voor oplslaan van online kaarten: - - ... ... Help! I want maps! I don't want to read the documentation! Help! Ik wil kaarten! Ik wil de documentatie niet lezen! IMapPropSetup Form Formulier <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Verander transparantie van kaart</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> Areas Gebieden Lines Lijnen Points Punten Cache Size (MB) Ceheugen grootte Expiration (Days) Vervaltijd (dagen) - - Cache Path Cachemap IMapVrtBuilder Form Formulier ... ... Select source files: Selecteer bronbestand: Target Filename: Doel bestandsnaam: - - Start Start IMouseEditLine <b>New Line</b><br/>Move the mouse and use the left mouse button to drop points. When done use the right mouse button to stop.<br/> <b>Nieuwe lijn</b><br/>Verplaats de muis en gebruik linker muisknop om een punt te plaatsen. Gebruik rechter muisknop om te stoppen.<br/> <b>Delete Point</b><br/>Move the mouse close to a point and press the left button to delete it.<br/> <b>Select Range of Points</b><br/>Left click on first point to start selection. Left click second point to complete selection and choose from options. Use the right mouse button to cancel.<br/> <b>Move Point</b><br/>Move the mouse close to a point and press the left button to make it stick to the cursor. Move the mouse to move the point. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Add Point</b><br/>Move the mouse close to a line segment and press the left button to add a point. The point will stick to the cursor and you can move it. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>No Routing</b><br/>All points will be connected with a straight line.<br/> <b>Auto Routing</b><br/>The current router setup is used to derive a route between points. <b>Note:</b> The selected router must be able to route on-the-fly. Offline routers usually can do, online routers can't.<br/> <b>Vector Routing</b><br/>Connect points with a line from a loaded vector map if possible.<br/> <b>%1 Metrics</b> <b>%1 gegevens</b> Distance: Afstand: Ascend: Stijging: Descend: Daling: IPhotoAlbum Form Formulier ... ... IPlot Reset Zoom Stop Range Save... Opslaan... No or bad data. Geen of ontbrekende gegevens. Select output file Selecteer bestand IPositionDialog Position ... Positie... Enter new position Geef nieuwe positie op Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Verkeerde invoer. Moet zijn: "[N|Z] ddd mm.sss [W|O] ddd mm.sss" of "[N|Z] ddd.ddd [W|O] ddd.ddd" IPrintDialog Print map... Kaart afdrukken... Save Opslaan TextLabel Benaming Print Afdrukken IProgressDialog Please wait... Moment geduld... TextLabel Benaming IProjWizard Proj4 Wizzard Proj4 instelling Mercator Mercator UTM UTM zone zone user defined gebruikersinstelling Datum Datum World Mercator (OSM) Wereld Mercator (OSM) Result: Resultaat: UPS North (North Pole) UPS noord (Noordpool) UPS South (South Pole) UPS zuid (Zuidpool) Projection Projectie IProjWpt Waypoint Projection Waypoint projectie ... ... - - Clone waypoint and move by: Kopieer waypoint en verplaats naar: m m ° ° IRouterMapQuest Form Formulier Highways Snelwegen Seasonal Seizoenswegen Language Taal Country Border Landgrenzen Profile Profiel Avoid: Vermijd: Ferry Veerboten Toll Road Tolwegen Unpaved Onverhard <p>Directions Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> <p>Routebeschrijving afkomstig van <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> IRouterRoutino Form Formulier Profile Profiel Mode Modus Database Database Add paths with Routino database. ... ... Language Taal To use offline routing you need to define paths to local routing data. Use the setup tool button to register a path. IRouterRoutinoPathSetup Setup Routino database... Routino database instellen... ... ... - - IRouterSetup Form Formulier IRoutinoDatabaseBuilder Form Formulier ... ... Select source files: Selecteer bronbestand: Start Start Target Path: Doelmap: - - File Prefix IScrOptEditLine Form Formulier Save to orignal Opslaan als origineel Save as new Opslaan als nieuw Abort Annuleren Move points. (Ctrl+M) Verplaats punten. (Ctrl+M) ... ... Ctrl+M Ctrl+M Add new points. (Ctrl++) Maak nieuwe punten. (Ctrl++) Ctrl++ Ctrl++ Select a range of points. (Ctrl+R) Selecteer een reeks punten. (Ctrl+R) Ctrl+R Ctrl+R Delete a point. (Ctrl+D) Verwijder een punt. (Ctrl+D) Ctrl+D Ctrl+D No auto-routing or line snapping (Ctrl+O) 0 O Ctrl+O Ctrl+O Use auto-routing to between points. (Ctrl+A) A A Ctrl+A Ctrl+A Snap line along lines of a vector map. (Ctrl+V) V V Ctrl+V Ctrl+V Undo last change Redo last change IScrOptOvlArea Form Formulier View details and edit. Bekijk details en bewerk. ... ... Copy area into another project. Kopieer gebied in ander project. Delete area from project. Verwijder gebied uit project. Edit shape of the area. Verander vorm van het gebied. TextLabel TextLabel IScrOptRangeLine Form Formulier Delete all points between the first and last one. Verwijder alle punten tussen eerste en laatste punt. ... ... <html><head/><body><p>Calculate a route between the first and last selected point.</p></body></html> Caclculate a route between the first and last selected point. Bereken een route tussen eerste en laatste gecelecteerde punt. IScrOptRangeTrk Form Formulier Hide all points. Verberg alle punten. ... ... Show all points. Toon alle punten. Select an activity for the selected range. Selecteer een activiteit voor geselecteerde lengte. Copy track points as new track. Kopieer trackpunten naar een nieuwe track. TextLabel Benaming IScrOptRte Form Formulier View details and edit. Bekijk details en bewerk. ... ... Copy route into another project. Kopieer route in een ander project. Delete route from project. Verwijder route uit project. Calculate route. Bereken route. Reset route calculation. Herstel route berekening. Move route points. Verplaats route punten. TextLabel Benaming IScrOptTrk Form Formulier View details and edit properties of track. Bekijk details en bewerk eigenschappen van de track. ... ... Copy track into another project. Kopieer track in een ander project. Delete track from project. Verwijder track uit project. Show on-screen profile and detailed information about points. Toon op scherm het profiel en gedetaileerde informatie van de punten. Select a range of points. Selecteer een reeks punten. Edit position of track points. Bewerk positie van trackpunten. Reverse track. Draai trackrichting. Combine tracks. Combineer tracks. Cut track at selected point. You can use this to: * remove bad points at the start or end of the track * use the track parts to plan a new tour * cut a long track into stages Knip track op geselecteerde punt. Dit kan ook gebruikt worden om: * ongeldige punten aan start of eind van track te verwijderen * trackdelen te gebruiken voor nieuwe track * een lange track in etappes te verdelen Cut track at selected point into two tracks. Knip track op geselecteerde punt in 2 tracks. TextLabel Benaming IScrOptWpt Form Formulier View details and edit. Bekijk details en bewerk. ... ... Copy waypoint into another project. Kopieer waypoint in een ander project. Delete waypoint from project. Verwijder waypoint uit project. Show content as static bubble. Toon gegevens in een statische ballon. Move waypoint to a new location. Verplaats waypoint naar een nieuwe locatie. Clone waypoint and move clone a given distance and angle. Kloon waypoint en verplaats deze naar aangegeven afstand en hoek. TextLabel Benaming ISelDevices Select devices... Selecteer GPS... ISelectActivity Activities... Activiteiten... Select one: Selecteer: ISelectCopyAction Copy item... Replace existing item TextLabel Benaming Do not copy item Create a clone Maak een kloon Replace with: Keep item: The clone's name will be appended with '_Clone' De naam van de kloon zal aangeduid worden als '_Kloon' And for all other items, too. ISelectDBFolder Select Parent Folder... Name Naam ISelectProjectDialog Select a project... Selecteer een project... Select project from list or enter new project name. Selecteer project uit lijst of maak nieuw project. New project's name Naam nieuw project New project is created as: Nieuw project wordt gemaakt als: *.qms *.qms *.gpx *.gpx Database Database ISelectSaveAction Copy item... Replace existing item Replace with: TextLabel Benaming Do not replace item Use item: And for all other items, too. ISetupDatabase Add database... Database toevoegen... File Bestand - - Name Naam Add new database. Maak nieuwe database. ... ... Open existing database. Open bestaande database. ISetupFolder Database Folder... Databasemap... Folder name Mapnaam Group Groep Project Project Other Anders ISetupNewWpt New Waypoint... Nieuw waypoint... Symbol Symbool ... ... Position Positie Name Naam Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Verkeerde invoer. Moet zijn: "[N|S] ddd mm.sss [W|E] ddd mm.sss" of "[N|S] ddd.ddd [W|E] ddd.ddd" ISetupWorkspace Setup workspace... Instelling werkruimte... save workspace on exit, and every sla werkruimte op bij afsluiten en elke minutes minuten ITextEditWidget Edit text... Bewerk tekst... ... ... Undo Ongedaan maken Ctrl+Z Ctrl+Z Redo Opnieuw Ctrl+Shift+Z Ctrl+Shift+Z Cut Knippen Ctrl+X Ctrl+X Copy Kopiëren Ctrl+C Ctrl+C Paste Plakken Ctrl+V Ctrl+V Align Left Links uitlijnen Ctrl+L Ctrl+L Align Right Rechts uitlijnen Ctrl+R Ctrl+R Align Center Gecentreerd Ctrl+E Ctrl+E Align Block Uitgevuld Ctrl+J Ctrl+J Underline Onderstrepen Ctrl+U Ctrl+U Bold Vet Ctrl+B Ctrl+B Italic Cursief Ctrl+I Ctrl+I ITimeZoneSetup Setup Time Zone ... Tijdzone instellen... UTC UTC Local Lokaal Automatic Automatisch Print date/time in Toon datum/tijd als long format, or lang formaat, of short format kort formaat IToolShell Execution of external program `%1` failed: Process cannot be started. Make sure the required packages are installed, `%1` exists and is executable. External process crashed. An unknown error occurred. !!! failed !!! !!! Mislukt !!! IUnitsSetup Setup units... Eenheden instellen... Metric Metrisch <b>Note:</b> For some GUI elements changing the units will not take effect until you restart QMapShack. <b>Notitie:</b>Na het wijzigingen van de eenheden is het nodig om QMapShack opnieuw op te starten. Imperial Imperial Nautic Nautisch IWptIconDialog Icons... Iconen... QObject Picture%1 Afbeelding%1 There is another project with the same name. If you press 'ok' it will be removed and replaced. Are you sure you want to delete '%1' from folder '%2'? Delete... Verwijder... Delete project... Project verwijderen... Do you really want to delete %1? All your data grouped by folders. Lost & Found (%1) Lost & Found Save GIS data to... Save ... Opslaan... Failed to open... Openen mislukt... Failed to open %1 Openen mislukt-%1 Failed to read... Lezen mislukt... Failed to read: %1 line %2, column %3: %4 Lezen mislukt: %1 lijn %2, kolom %3: %4 Not a GPX file: Geen GPX bestand: File exists ... Bestand bestaat al... The file exists and it has not been created by QMapShack. If you press 'yes' all data in this file will be lost. Even if this file contains GPX data and has been loaded by QMapShack, QMapShack might not be able to load and store all elements of this file. Those elements will be lost. I recommend to use another file. <b>Do you really want to overwrite the file?</b> Failed to create file '%1' Failed to write file '%1' Saving GIS data failed... Saveing GIS data failed... Opslaan GIS gegevens mislukt... Archived Gearchiveerd Available Beschikbaar Not Available Niet beschikbaar [no name] [geen naam] Initial version. Orginele versie. <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press 'Ok'. <h3>%1<h3>Dit element is waarschijnlijk alleen lezen omdat dit niet gemaakt is in QMapShack. Normaal hoeven geen gegevens aangepast te worden na het importeren. Om toch aan te passen druk dan op 'OK'. Read Only Mode... Alleen lezen modus... <h4>Description:</h4> <h4>Beschrijving:</h4> <p>--- no description ---</p> <p>--- geen beschrijving ---</p> <h4>Comment:</h4> <h4>Notitie:</h4> <p>--- no comment ---</p> <p>--- geen notitie ---</p> <h4>Links:</h4> <h4>Links:</h4> <p>--- no links ---</p> <p>--- geen links ---</p> thin dun normal normaal wide breed strong vet _Clone _Kloon Area: %1%2 Gebied: %1%2 Changed area shape. Vorm gebied aangepast. Changed name. Naam aangepast. Changed border width. Lijndikte aangepast. Changed fill pattern. Changed opacity. Transparantie aangepast. Changed comment. Notitie aangepast. Changed description. Beschrijving aangepast. Changed links Link aangepast Changed color Kleur aangepast Save project? Project opslaan? <h3>%1</h3>The project was changed. Save before closing it? <h3>%1</h3>Het project is veranderd. Voor het sluiten opslaan? %1: Correlate tracks and waypoints. <h3>%1</h3>Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project in the future? Canceled correlation... <br/> Filename: %1 <br/> Bestandsnaam: %1 Waypoints: %1 Waypoints: %1 Tracks: %1 Tracks: %1 Routes: %1 Routes: %1 Areas: %1 Gebieden: %1 Are you sure you want to delete '%1' from project '%2'? Edit name... Bewerk naam... Enter new route name. Geef route een nieuwe naam. Changed comment Notitie aangepast Changed description Beschrijving aangepast Length: %1 %2 Lengte: %1 %2 Length: - Lengte: - Time: %1 %2 Tijd: %1 %2 Time: - Tijd: - Last time routed:<br/>%1 with %1 met %1 Distance: %1 %2 Afstand: %1 %2 Changed route points. Routepunten aangepast. Error... Fout... Failed to open %1. Kan %1 niet openen. Only support lon/lat WGS 84 format. Alleen lon/lat WGS 84 formaat word ondersteunt. Failed to read data. Lezen gegevens mislukt. Foot Wandelen Bicycle Fiets Motor Bike Motorfiets Car Auto Cable Car Kabelbaan Swim Zwemmen Ship Boot Aeronautik Aeronautics Vliegtuig Distance: Afstand: Ascend: Stijging: Descend: Daling: Speed Moving: Bewogen snelheid: Speed Total: Totale snelheid: Time Moving: Bewogen tijd: Time Total: Totale tijd: Changed trackpoints, sacrificed all previous data. , %1%2 %3, %4%5 %6 , %1%2 %3, %4%5 %6 Time: %1 Tijd: %1 , Speed: %1 %2 , Snelheid: %1 %2 Moving: %1 Beweging: %1 Start: %1 Start: %1 End: %1 Einde: %1 Points: %1 (%2) Punten: %1 (%2) Ele.: %1 %2 Hoogte.: %1 %2 slope: %1%3 (%2%) helling: %1%3 (%2%) speed: %1%2 snelheid: %1%2 ... and %1 tags not displayed Ascend: %1%2 (%3%) Stijging: %1%2 (%3%) Ascend: - (-) Stijging: - (-) Descend: %1%2 (%3%) Daling: %1%2 (%3%) Descend: - (-) Daling: - (-) Dist.: %1%2 (%3%) Afstand.: %1%2 (%3%) Dist.: - (-) Afstand.: - (-) Moving: %1%2 (%3%) Beweging: %1%2 (%3%) Moving: - (-) Beweging: - (-) Ascend: %1%2 Stijging: %1%2 , %1%2 , %1%2 Ascend: - Stijging: - Descend: %1%2 Daling: %1%2 Descend: - Daling: - Dist.: %1%2 Afstand.: %1%2 Time: %1%2 Tijd: %1%2 Enter new track name. Geef track nieuwe naam. Permanently removed points %1..%2 Permanent verwijderde punten %1..%2 Hide points. Verberg punten. Show points. Toon punten. Changed name Naam aangepast Changed activity to '%1' for complete track. Changed activity to '%1' for range(%2..%3). Hide points by Douglas Peuker algorithm (%1%2) Verberg alle punten via Douglas Peuker algoritme (%1%2) Hide points with invalid coordinates at the beginning of the track Reset all hidden track points to visible Verander alle verborgen trackpunten naar zichtbaar Permanently removed all hidden track points Alle verborgen trackpunten permanent verwijderd Smoothed profile with a Median filter of size %1 Replaced elevation data with data from DEM files. Offset elevation data by %1%2. Changed start of track to %1. Start van track veranderd naar %1. Remove timestamps. Verwijder tijdstempels. Set artificial timestamps with delta of %1 sec. Changed speed to %1%2. Snelheid veranderd naar %1%2. Elevation: %1 %2 Hoogte: %1 %2 Proximity: %1 %2 Changed position Positie aangepast Changed elevation Hoogte aangepast Changed proximity Changed icon Pictogram aangepast Changed images Afbeelding aangepast Add image Afbeelding toevoegen Warning... Waarschuwing... This is a typ file with unknown polygon encoding. Please report! This is a typ file with unknown polyline encoding. Please report! Enter new area name. Geef gebied een nieuwe naam. Copy flag information from QLandkarte GT track Corrupt track ... Number of trackpoints is not equal the number of training data trackpoints. Number of trackpoints is not equal the number of extended data trackpoints. Number of trackpoints is not equal the number of shadow data trackpoints. Error Fout Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Position values out of bounds. Progress Voortgang time tijd distance [%1] afstand [%1] Slope (directed) Helling (getekend) Speed Snelheid Elevation Hoogte Heart Rate Hartslag Cadence Cadans Air Temperature Luchttemperatuur Water Temperature Watertemperatuur Depth Diepte qmapshack-1.5.1/src/locale/qmapshack_fr.ts000644 001750 000144 00001047220 12624270566 021534 0ustar00oeichlerusers000000 000000 CAbout %1 (API V%2, expected V%3) %1 (API V%2) CCanvas View %1 Vue %1 CCommandProcessor Print debug output to console. Print debug output to logfile (temp. path). Do not show splash screen. File with QMapShack configuration. file Files for future use. CDemList Deactivate Désactiver Activate Activer CDemPathSetup Add or remove paths containing DEM data. There can be multiple files in a path but no sub-path is parsed. Supported formats are: %1 Ajoutez ou retirez des répertoires qui contiennent des données DEM. Il peut y avoir plusieurs fichiers dans un répertoire mais les sous-répertoires ne sont pas prises en compte. Select DEM file path... Sélectionnez le répertoire qui contient les fichiers DEM CDemPropSetup <b>Grade %1</b> <b>Niveau %1</b> CDemVRT Error... Erreur... Failed to load file: %1 DEM must have one band with 16bit or 32bit data. Le DEM doit contenir un seule bande avec des données en 16 ou 32 bits. No georeference information found. Aucune information de géoréférencement trouvé CDetailsGeoCache none ...indice? aucun ??? Searching for images... Recherche d'images... No images found Aucune image trouvée CDetailsOvlArea Edit name... Éditer le nom... Enter new area name. Entrez le nom de la nouvelle surface. Enter new waypoint name. Saisir le nouveau nom du waypoint. CDetailsPrj none aucun Build diary... Créer le journal... Abort Annuler <h2>Waypoints</h2> <h2>Waypoints</h2> Info Information Comment Commentaire <h2>Tracks</h2> <h2>Traces</h2> <h2>Areas</h2> <h2>Surfaces</h2> You want to sort waypoints along a track, but you switched off track and waypoint correlation. Do you want to switch it on again? Correlation... <b>Summary over all tracks in project</b><br/> distance: %1%2 distance : %1%2 ascent: %1%2 montée : %1%2 descend: %1%2 descente : %1%2 <h2>Routes</h2> Edit name... Éditer le nom... Enter new project name. Entrez le nom du nouveau projet. Edit keywords... Éditer les mots-clés... Enter keywords. Saisir les mots-clés. Print Diary Imprimer le journal CDetailsRte Edit name... Éditer le nom... Enter new route name. Entrez le nom de la nouvelle route. CDetailsTrk speed. [%1] vitesse [%1] time durée Solid color Reduce visible track points Réduire les points visibles de la trace Change elevation of track points Modifier l'altitude des points de la trace Change timestamp of track points Modifier l'horodatage des points de la trace Cut track into pieces Découper la trace %1 %2 Edit name... Éditer le nom... Enter new track name. Entrez le nom de la nouvelle trace. Reset activities... This will remove all activities from the track. Proceed? None CDetailsWpt Edit name... Éditer le nom... Enter new waypoint name. Entrez le nom du nouveau waypoint. Enter new proximity range. Entrez le rayon de l'alarme de proximité. CElevationDialog No DEM data found for that point. Pas de données DEM disponibles pour ce point. CGisListDB Add Database Ajouter une base de données Add Folder Ajouter un dossier Delete Folder Supprimer le dossier Delete Item Supprimer un élément Remove Database Enlever la base de données Empty Vider Remove database... Enlever la base de données Do you really want to remove '%1' from the list? Do you realy want to remove '%1' from the list? Êtes-vous sûr de vouloir enlever '%1' de la liste ? Delete database folder... Supprimer la base de données... Are you sure you want to delete "%1" from the database? Êtes-vous sûr de vouloir enlever '%1' de la liste ? Remove items... Supprimer les éléments... Are you sure you want to delete all items from Lost&Found? This will remove them permanently. Êtes-vous sûr de vouloir supprimer tous les éléments dans Perdu & Trouvé ? Les éléments seront supprimés définitivement. Are you sure you want to delete all selected items from Lost&Found? This will remove them permanently. Êtes-vous sûr de vouloir supprimer les éléments sélectionnés dans Perdu & Trouvé ? Les éléments seront supprimés définitivement. CGisListWks Edit.. Éditer.. Show on Map Hide from Map Save As... Enregistrer sous... Save Enregistrer Update Project on Devices Mettre à jour le projet sur tous les appareils Send to Devices Envoyer vers les appareils Close Fermer Update Project on Device Mettre à jour le projet sur l'appareil Delete Supprimer Edit... Éditer... Copy to... Copier vers... Track Profile Profile de la trace Select Range Sélectionner des points Edit Track Points Éditer les points de la trace Reverse Track Inverser la trace Combine Tracks Joindre des traces Show Bubble Afficher la bulle Move Waypoint Déplacer le waypoint Proj. Waypoint... Projection du waypoint... Route Instructions Calculate Route Calculer l'itinéraire Reset Route Réinitialiser la route Edit Route Éditer la route Edit Area Points Éditer les points de la surface Create Route Drop items... Saving workspace. Please wait. Sauvegarde de l'espace de travail. Patientez. Loading workspace. Please wait. Chargement de l'espace de travail. Patientez. Close all projects... Fermer tous les projets... This will remove all projects from the workspace. Ceci enlevera tous les projets de l'espace de travail. Copy items... Copier les éléments... <b>Update devices</b><p>Update %1<br/>Please wait...</p> <b>Mise à jour des appareils</b><p>Mise à jour de %1<br/>Patientez...</p> CGisWidget Load project... The project "%1" is already in the workspace. Cut Track... Couper la trace... Do you want to delete the original track? Voulez-vous supprimer la trace originale? CGrid [Grid: %1%2%5 %3%4%5] [Grille : %1%2%5 %3%4%5] [Grid: N %1m, E %2m] [Grille : N %1m, E %2m] %1 %2 %1%2%5 %3%4%5 %1m, %2m N %1m, E %2m CHistoryListWidget Cut history Tronquer l'historique CImportDatabase Import QLandkarte Database Importer une base de données QLandkarte Select source database... Choisissez la base de données à importer... Select target database... Choisissez la base de données cible... CMainWindow Ele: %1%2 Altitude: %1%2 [Grid: %1] Load GIS Data... Charger des données SIG... Select output file Sélectionner le fichier de sortie Select file to load Sélectionner le fichier à charger CMapIMG Failed ... Échec... Unspecified Non défini French Français German Deutsch Dutch Néerlandais English Anglais Italian Italien Finnish Finlandais Swedish Suédois Spanish Espagnol Basque Catalan Catalan Galician Galicien Welsh Gallois Gaelic Gaëlic Danish Danois Norwegian Norvégien Portuguese Portugais Slovak Slovaque Czech Tchèque Croatian Croate Hungarian Hongrois Polish Polonais Turkish Turque Greek Grèc Slovenian Slovène Russian Russe Estonian Estonien Latvian Letton Romanian Roumain Albanian Albanais Bosnian Bosnien Lithuanian Lituanien Serbian Serbe Macedonian Macédonien Bulgarian Bulgare Major highway Route majeure Principal highway Route principale Other highway Autre route Arterial road Artère urbaine Collector road Rue principale Residential street Rue résidentielle Alley/Private road Ruelle/Route privée Highway ramp, low speed Bretelle d'accès, basse vitesse Highway ramp, high speed Bretelle d'accès, grande vitesse Unpaved road Route non bitumé Major highway connector Bretelle majeure Roundabout Rond-point Railroad Voie ferrée Shoreline Ligne côtière Trail Sentier Stream Ruisseau Time zone Fuseau horaire Ferry Bac State/province border Frontière de province County/parish border Frontière de canton International border Frontière internationale River Rivière Minor land contour Courbe de niveau mineure Intermediate land contour Courbe de niveau intermédiaire Major land contour Courbe de niveau majeure Minor depth contour Courbe isobathe mineure Intermediate depth contour Courbe isobathe intermédiaire Major depth contour Courbe isobathe majeure Intermittent stream Ruisseau intermittent Airport runway Tarmac Pipeline Oléoduc Powerline Ligne à haute tension Marine boundary Frontière maritime Hazard boundary Limite de zone à risque Large urban area (&gt;200K) Grande agglomération urbaine (&gt;200K) Small urban area (&lt;200K) Petite agglomération urbaine (&lt;200K) Rural housing area Zone résidentielle rurale Military base Base militaire Parking lot Parking Parking garage Parking couvert Airport Aéroport Shopping center Centre commercial Marina Marina University/College Université Hospital Hôpital Industrial complex Complexe industriel Reservation Reserve naturelle Man-made area Zone industrielle Sports complex Complexe sportif Golf course Golf Cemetery Cimetière National park Parc national City park Parc urbain State park Parc régional Forest Forêt Ocean Océan Blue (unknown) Sea Mer Large lake Grand lac Medium lake Lac moyen Small lake Petit lac Major lake Lac majeur Major River Rivière majeure Large River Grande rivière Medium River Rivière moyenne Small River Petite rivière Intermittent water Cours d'eau intermittent Wetland/Swamp Marais Glacier Glacier Orchard/Plantation Verger Scrub Broussaille Tundra Tundra Flat Plaine ??? Failed to read: Erreur de lecture : Failed to open: Échec d'ouverture : Bad file format: Format de fichier invalide : Failed to read file structure: Erreur de lecture de la structure du fichier : Loading %1 Chargement de %1 User abort: Interruption par l'utilisateur : File is NT format. QMapShack is unable to read map files with NT format: Le fichier est au format NT. QMapShack ne peut pas lire des fichiers au format NT : File contains locked / encypted data. Garmin does not want you to use this file with any other software than the one supplied by Garmin. Le fichier contient des données verrouillées / cryptées. Garmin ne vous autorise pas d'utiliser ce fichier avec un logiciel non fourni par Garmin. Point of Interest Point d'intérêt Unknown Inconnu Area Surface CMapList Deactivate Désactiver Activate Activer Where do you want to store maps? Où voulez vous enregistrer les cartes ? CMapMAP Failed ... Échec... Failed to open: Échec d'ouverture : Bad file format: Format de fichier invalide : CMapPathSetup Add or remove paths containing maps. There can be multiple maps in a path but no sub-path is parsed. Supported formats are: %1 Ajoutez ou retirez des répertoires qui contiennent des cartes. Il peut y avoir plusieurs cartes dans un répertoire mais les sous-répertoires ne sont pas prises en compte. Les formats acceptés sont : %1 Select map path... Choisissez un répertoire... Select root path... Sélectionner le répertoire racine... CMapPropSetup Cache path... Répertoire de cache... CMapRMAP Error... Erreur... This is not a TwoNav RMAP file. Ceci n'est pas un fichier TwoNav RMAP. Unknown sub-format. Sous-format inconnu. Unknown version. Version inconnue. Failed to read reference point. Impossible de lire les points de référence. Unknown projection and datum (%1%2). Projection et date inconnus (%1%2). CMapTMS Error... Erreur... Failed to open %1 Impossible d'ouvrir : Failed to read: %1 line %2, column %3: %4 Impossible de lire: %1 ligne %2, colonne %3: %4 Layer %1 Calque %1 This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 tuiles à charger<br/> CMapVRT Error... Erreur... Failed to load file: %1 Impossible de charger le fichier: %1 File must be 8 bit palette or gray indexed. Le fichier doit avoir une palette à 8 bits ou être en niveaux de gris. No georeference information found. Aucune information de géoréférencement trouvé CMapVrtBuilder Build GDAL VRT Générer le VRT GDAL Select files... Sélectionnez les fichiers... Select target file... Sélectionnez le fichier à créer... !!! done !!! !!! failed !!! !!! échec !!! CMapWMTS Error... Erreur... Failed to open %1 Impossible d'ouvrir %1 Failed to read: %1 line %2, column %3: %4 Impossible de lire: %1 ligne %2, colonne %3: %4 Failed to read: %1 Unknown structure. Impossible de lire: %1 Structure inconnue. Unexpected service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Unexpexted service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Service inconnu. 'Attendu : * WMTS 1.0.0', lu : '%1 %2. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. No georeference information found. Aucune information de géoréférencement trouvé <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 tuiles à charger<br/> CMouseEditArea <b>Edit Area</b><br/>Select a corner point for more options.<br/> <b>Éditez la surface</b><br/>Sélectionnez un point d'angle pour voir plus d'options. Area Surface <b>Edit Area</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Éditer la surface</b><br/>Choisissez und fonction et un mode de calcul d'itinéraire à l'aide des boutons d'outils. Ensuite, sélectionnez un point de la ligne. Seulement les points marqués d'un carré large peuvet être modifiés. Les points noirs sont des sous-points introduits par le calcul d'itinéraire.<br/> CMouseEditRte Route Calcul d'itinéraire <b>Edit Route Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Éditer les points de la route</b><br/>Choisissez une fonction et un mode de calcul d'itinéraire à l'aide des boutons d'outils. Ensuite, sélectionnez un point de la ligne. Seul les points marqués d'un carré large peuvent être modifiés. Les points noirs sont des sous-points introduits par le calcul d'itinéraire.<br/> CMouseEditTrk <b>Edit Track Points</b><br/>Select a track point for more options.<br/> <b>Éditer les points de la trace</b><br/>Sélectionner un point de la trace pour voir plus d'options.<br/> Track Trace <b>Edit Track Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Éditer les points de la trace</b><br/>Choisissez une fonction et un mode de calcul d'itinéraire à l'aide des boutons d'outils. Ensuite, sélectionnez un point de la ligne. Seul les points marqués d'un carré large peuvent être modifiés. Les points noirs sont des sous-points introduits par le calcul d'itinéraire.<br/> Warning! Avertissement ! This will replace all data of the original by a simple line of coordinates. All other data will be lost permanently. This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Ceci remplacera les données de l'original par une ligne simple de coordonnées. Toutes les autres données seront définitivement perdues. CMouseNormal Add Waypoint Ajouter un waypoint Add Track Ajouter une trace Add Route Ajouter une route Add Area Ajouter une surface Copy position Copier la position Copy position (Grid) CMousePrint <b>Save(Print) Map</b><br/>Select a rectangular area on the map. Use the left mouse button and move the mouse. Abort with a right click. Adjust the selection by point-click-move on the corners. Save/print the selection by a left click on the disc/printer icon in the center of the selection. CMouseRangeTrk <b>Select Range</b><br/>Select first track point. And then a second one.<br/> <b>Sélectionner une séquence de points</b><br/>Sélectionner le premier point, puis un deuxième. CPhotoAlbum Select images... Sélectionner les images... CPlotDistance time durée CPlotProfile distance [%1] time [h] durée [h] alt. [%1] Alt. [%1] CPlotSpeed time [h] durée [h] speed. [%1] vitesse [%1] CPrintDialog Print Map... Save Map as Image... Printer Properties... Pages: %1 x %2 Zoom with mouse wheel on map below to change resolution: %1x%2 pixel x: %3 m/px y: %4 m/px Printing pages. Save map... CProgressDialog Elapsed time: %1 Elapsed time: %1 seconds. CProjWizard north nord south sud Error... Erreur... The value '%1' is not a valid coordinate system definition: %2 La valeur '%1' n'est pas une définition de système de coordonnées: %2 CProjWpt Edit name... Éditer le nom... Enter new waypoint name. Entrez le nom du nouveau waypoint. CQlgtDb Migrating database from version 4 to 5. Migration de la base de données de version 4 en version 5. Migrating database from version 5 to 6. Migration de la base de données de version 5 en version 6. Migrating database from version 6 to 7. Migration de la base de données de version 6 en version 7. Migrating database from version 7 to 8. Migration de la base de données de version 7 en version 8. Migrating database from version 8 to 9. Migration de la base de données de version 8 en version 9. Open database: %1 Ouvrir la base de données: %1 Folders: %1 Répertoires: %1 Tracks: %1 Traces: %1 Routes: %1 (Only the basic route will be copied) Routes: %1 (seulement la route de base sera copiée) Waypoints: %1 Waypoints: %1 Overlays: %1 (only area overlays will be converted to QMapShack) Overlays: %1 (Seulements les overlays de type surface seront convertis vers QMapShack) Overlays: %1 (areas will be converted as areas, distance lines will be converted to tracks, all other overlay items will be lost) Overlays : %1 (les surfaces seront convertis en surfaces, les lignes de distance en traces, tous les autres overlays seront perdus) Diaries: %1 Journaux: %1 Map selections: %1 (can't be converted to QMapShack) Sélections de carte: %1 (ne peuvent pas être convertis) ------ Start to convert database to %1------ ------ La conversion de base de donnée vers %1 commence ------ Failed to create target database. Erreur lors de la création de la base de données cible. ------ Abort ------ ------ Annuler ------ ------ Done ------ ------ Terminé ------ Restore folders... Restaurer ls dossiers... Abort Annuler Imported %1 folders and %2 diaries %1 dossiers et %2 journaux ont été importés Copy items... Copier les éléments... Imported %1 tracks, %2 waypoints, %3 routes, %4 areas %1 traces, %2 waypoints, %3 routes et %4 surfaces ont été importés Import folders... Importer les dossiers... Overlay of type '%1' cant be converted Overlay de type '%1' ne peut pas être converti CQmsDb Existing file... Le fichier existe... Remove existing %1? Supprimer %1 qui existe ? Remove existing file %1 Supprimer le fichier existant %1 ? %1: drop item with QLGT DB ID %2 %1: poubellise l'élément avec l'identifiant QLGT DB %2 CRouterMapQuest Fastest Shortest le plus court Bicycle Vélo Pedestrian US English British English Danish Danois Dutch Néerlandais French Français German Deutsch Italian Italien Norwegian Norvégien Spanish Espagnol Swedish Suédois mode "%1" no highways no toll roads no seasonal no unpaved no ferry no crossing of country borders <b>MapQuest</b><br/>Routing request sent to server. Please wait... <b>MapQuest</b><br/>Bad response from server:<br/>%1 <br/>Calculation time: %1s CRouterRoutino Foot à pied Horse à cheval Wheelchair en fauteuil roulant Bicycle Vélo Moped Cyclomoteur Motorcycle Moto Motorcar Voiture Goods Camion Shortest le plus court Found Routino with a wrong version. Expected %1 found %2 Quickest le plus rapide English Anglais German Deutsch French Français Hungarian Hongrois Dutch Néerlandais Russian Russe Polish Polonais A function was called without the database variable set. A function was called without the profile variable set. A function was called without the translation variable set. The specified database to load did not exist. The specified database could not be loaded. The specified profiles XML file did not exist. The specified profiles XML file could not be loaded. The specified translations XML file did not exist. The specified translations XML file could not be loaded. The requested profile name does not exist in the loaded XML file. The requested translation language does not exist in the loaded XML file. There is no highway near the coordinates to place a waypoint. The profile and database do not work together. The profile being used has not been validated. The user specified profile contained invalid data. The routing options specified are not consistent with each other. There is a mismatch between the library and caller API version. Route calculation was aborted by user. A route could not be found to waypoint %1. Unknown error: %1 profile "%1" profile "%1" , mode "%1" , mode "%1" Warning... Avertissement... %1: Due to limitations in the Windows POSIX API Routino can't handle files larger than 4GB. Calculate route with %1 <br/>Calculation time: %1s CRouterRoutinoPathSetup Add or remove paths containing Routino data. There can be multiple databases in a path but no sub-path is parsed. Ajouter ou enlever des répertoires contenant des données Routino. Il peut y avoir plusieurs bases de données dans un répertoire mais les sous-répertoires ne sont pas pris en compte. Select routing data file path... Sélectionner un répertoire de données de calcul d'itinéraire... Select DEM file path... Sélectionnez le répertoire qui contient les fichiers DEM CRouterSetup Routino (offline) Routino (hors ligne) MapQuest (online) MapQuest (en ligne) CRoutinoDatabaseBuilder Create Routino Database Select files... Sélectionnez les fichiers... Select target path... !!! done !!! !!! failed !!! !!! échec !!! CSearchGoogle Unknown response Réponse inconnue Error: Erreur: CSetupDatabase Error... Erreur... There is already a database with name '%1' Une database de ce nom existe déjà : %1 New database... Nouvelle base de données... Open database... Ouvrir la base de données... CSetupWorkspace Setup database... Mise en place la base de données... Changes will become active after an application's restart. Les modifications prennent effet après un redémarrage de l'application. CTextEditWidget &Color... &Couleur... IAbout About.... À propos... <b>QMapShack</b>, Version TextLabel Libellé Qt GDAL Proj4 Routino Rainer Unseld French Français Czech Tchèque Pavel Fric German Deutsch <b>Translation:</b> Dutch Néerlandais Harrie Klomp <b>Binaries:</b> <b>Contributors:</b> Josef Latt Spanish Espagnol Jose Luis Domingo Lopez Ivo Kronenberg Helmut Schmidt Win64 OS X ...and thanks to all Linux binary maintainers for doing a great job. Special thanks to Dan Horák and Bas Couwenberg for showing presence on the mailing list to discuss distribution related topics. Christian Eichler (qms@christian-eichler.de) This software is licensed under GPL3 or any later version Ce logiciel est distribué sous les termes de la licence GPL3 ou toute version utérieure © 2014 Oliver Eichler (oliver.eichler@gmx.de) ICanvasSetup Setup Map View... Configurer la vue cartographique... Projection & Datum Projection et date ... Scales Échelles Logarithmic Logarithmique Square (optimized for TMS and WTMS tiles) Carrée (optimisée pour tuiles TMS et WTMS) ICombineTrk Combine Tracks... Joindre des traces ... ICoordFormatSetup Coordinate Format... N48° 53.660 E013° 31.113 N48.8943° E013.51855° N48° 53' 39.6" E13° 31' 6.78" ICreateRouteFromWpt Create Route from Waypoints ... ... ICutTrk Cut Track Delete first part of the track and keep second one Keep both parts of the track Keep first part of the track and delete second one Check this to store the result into a new track. If you keep both parts of the track you have to create new ones. If you want to keep just one half you can simply remove the points, or check this to create a new track. Create a new track Create a clone Dupliquer l'élément IDemPathSetup Setup DEM file pathss Configurer les répertoires DEM Setup DEM file paths ... - IDemPropSetup Form <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Régler la transparence du calque DEM</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Cliquez pour utilser l'échelle courante comme échelle minimale d'affichage de la carte.</p></body></html> ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Définissez la plage d'échelle pour l'affichage de la carte. Utilisez les deux boutons à droite et à gauche pour définir l'échelle courante comme échelle maximale ou minimale.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Cliquez pour utilser l'échelle courante comme échelle maximale d'affichage de la carte.</p></body></html> Hillshading Relief ombré Slope Pente ° > TextLabel Libellé IDemsList Form To add files with elevation data use File->Setup DEM Paths. Pour ajouter des fichiers DEM cliquez sur Fichier -> Configurer les répertoires DEM. Use the context menu (right mouse button click on entry) to activate a file. Use drag-n-drop to move the activated file in the process order. Utiliser le menu de contexte (clic droite sur le fichier) pour activer un fichier. Utilisez glisser-déposer pour changer la position du fichier dans la liste. Activate Activer IDetailsGeoCache Dialog Dialogue Position: - Difficulty Difficulté Terrain Update spoilers Mise à jour des spoilers ... about:blank Hint: Indice: TextLabel Libellé IDetailsOvlArea Dialog Dialogue - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Le waypoint a été importé dans QMapShack et a été modifié. Il ne représente plus les données originales. Veuillez consulter l'historique pour voir les modifications. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Inverser le mode lecture seule. Ouvrez le cadenas pour pouvoir éditer l'objet. ... Color Couleur Border width Largeur de la bordure Style Opacity Opacité Info Information Points Position Hist. IDetailsPrj Form Keywords: Mots-clés: - Sort along track (multiple) Trier le long de la trace (multiple) Sort along track (single) Trier le long de la trace (individuel) Sort By Time Trier par date Keep Order of Project Conserver l'ordre du projet Print diary Imprimer le journal ... Keep order of project Garder l'ordre du projet Sort by time Trier par ordre chronolgique Rebuild diary. Restaurer le journal... IDetailsRte Info Information - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Le waypoint a été importé dans QMapShack et a été modifié. Il ne représente plus les données originales. Veuillez consulter l'historique pour voir les modifications. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Inverser le mode lecture seule. Ouvrez le cadenas pour pouvoir éditer l'objet. ... ... Hist. IDetailsTrk Form - - Info ... Graph Control Contrôle du graphe Profile Speed Vitesse Progress Progrès Track Trace Toggle read only mode. You have to open the lock to edit the item. Inverser le mode lecture seule. Ouvrez le cadenas pour pouvoir éditer l'objet. <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Le waypoint a été importé dans QMapShack et a été modifié. Il ne représente plus les données originales. Veuillez consulter l'historique pour voir les modifications. </p></body></html> - Style from Data Source Maximum Minimum Solid color Graphs Graph 3 Graph 2 Graph 1 Activity To differentiate the track statistics select an activity from the list for the complete track. Or select a part of the track to assign an activity. Points Time Durée Ele. Alt. Delta Dist. Slope Pente Ascend Montée Descend Descente Position Filter Filtre Hist. IDetailsWpt Dialog Dialogue Info Information Position: - Ele. Alt. Proximity: Proximité: Date/Time: Date/heure Hist. <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Le waypoint a été importé dans QMapShack et a été modifié. Il ne représente plus les données originales. Veuillez consulter l'historique pour voir les modifications. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Inverser le mode lecture seule. Ouvrez le cadenas pour pouvoir éditer l'objet. <html><head/><body><p>Read Only Mode</p></body></html> <html><head/><body><p>Mode lecture seule</p></body></html> ... Add images. Ajouter des images Delete selected image. Supprimer l'image sélectionnée IElevationDialog Edit elevation... Modifier l'altitude... Elevation Altitude - Get elevation from active digital elevation model. Prendre l'altitude du modèle numérique de terrain (DEM) ... IFilterDelete Form <b>Remove Track Points</b> <b>Supprimer des points de trace Remove all hidden track points permanently. Supprimer définitivement tous les points cachés. ... IFilterDouglasPeuker Form <b>Hide Points (Douglas Peuker)</b> <b>Cacher des points (Douglas Peuker)</b> Hide track points if the distance to a line between neighboring points is less than Cacher les points dont la distance de la ligne entre les points voisinants est inférieur à m Apply filter now. Appliquer le filtre maintenant. ... IFilterInvalid Form Hide Invalid Points Hide points with invalid coordinates at the beginning of the track. ... ... IFilterMedian Form <b>Smooth Profile (Median Method)</b> <b>Lisser le profile (méthode médian)</b> Smooth deviation of the track points elevation with a Median filter of size Lisse le profile avec un filtre médian de dimension points points ... IFilterNewDate Form <b>Change Time</b> <b>Modifier la date et l'heure Change start of track to Date et heure du début de la trace dd.MM.yy HH:mm:ss - - ... IFilterObscureDate Form <b>Obscure Timestamps</b> <b>Camoufler l'horodatage</b> Increase timestamp by Incrementer l'horodatage de sec. sec. with each track point. 0 sec. will remove timestamps. pour chaque point de la trace. O sec. supprimera l'horodatage. ... IFilterOffsetElevation Form <b>Offset Elevation</b> <b>Décalage de l'altitude</b> Add offset of Ajouter un décalage de to track points elevation. à l'altitude des points ... IFilterReplaceElevation Form <b>Replace Elevation Data</b> <b>Remplacer l'altitude</b> Replace elevation of track points with the values from loaded DEM files. Remplacer l'altitude des points par les données DEM ... IFilterReset Form <b>Reset Hidden Track Points</b> <b>Restaurer les points cachés</b> Make all trackpoints visible again. Rendre visible les points cachés ... IFilterSpeed Form <b>Change Speed</b> <b>Modifier la vitesse</b> Set speed to Mettre la vitesse à km/h ... IGisWidget Form Name Nom To add a database do a right click on the database list above. Pour ajouter une base de données cliquez droit sur la liste ci-dessus IGridSetup Setup Grid... Configurer la grille... Projection Projection restore default Remise à zéro ... Get projection from current map. Utiliser la projection de la carte courante projection wizzard Assitant de projection Grid color Couleur de la grille setup grid color Choisir la couleur de la grille IImportDatabase Form ... Source Database: Base de donnée source - Target Database: Base de donnée cible Start Démarrer IInputDialog Edit... Éditer... TextLabel Libellé ILinksDialog Links... Liens... Type Type Text Texte Uri ... IMainWindow QMapShack QMapShack File Fichier View Vue Window Fenêtre ? ? Project Projet Tool Outils Maps Cartes Dig. Elev. Model (DEM) Modèle numérique de terrain (DEM) Data Données Route Calcul d'itinéraire Add Map View Ajouter une vue cartographique Ctrl+T Show Scale Afficher l'échelle Setup Map Font Configurer la police de la carte Show Grid Afficher la grille Ctrl+G Setup Grid Configurer la grille Ctrl+Alt+G Flip Mouse Wheel Inverser le sens de la molette de souris Setup Map Paths Configurer les répertoires des cartes POI Text Libellés des points d'interêt Night / Day Jour / Nuit Map Tool Tip Infobulles sur la carte Ctrl+I Setup DEM Paths Configurer les répertoires DEM About À propos Help Aide Setup Map View Configurer la vue cartographique Load GIS Data Charger des données SIG... Load projects from file Charger un fichier projet Ctrl+L Save All GIS Data Enregistrer toutes les données SIG Save all projects in the workspace Enregistrer tous les projets de l'espace de travail Ctrl+S Setup Time Zone Configurer le fuseau horaire Add empty project Ajouter un projet vide Search Google Recherche Google Close all projects Fermer tous les projets F8 Setup Units Configurer les unités Setup Workspace Configurer l'espace de travail Setup save on exit. Sauvegarde de la configuration en quittant Import Database from QLandkarte Importer une base de données QLandkarte Import QLandkarte GT database Importer une base de données QLandkarte GT VRT Builder Générateur de VRT GUI front end to gdalbuildvrt Interface utilisateur pour gdalbuildvrt Store Map View Enregistrer une vue cartographique Write current active map and DEM list including the properties to a file Sauvegarder les cartes et calques DEM actives et leurs paramètres dans un fichier Load Map View Charger une vue cartographique Restore view with active map and DEM list including the properties from a file Rétablir une vue avec les cartes et calques DEM et leurs paramètres à partir d'un fichier Ext. Profile Profile externe Ctrl+E Close Fermer Ctrl+Q Clone Map View Dupliquer une vue cartographique Ctrl+Shift+T Create Routino Database Save(Print) Map Screenshot Print a selected area of the map Ctrl+P Setup Coord. Format Change the format coordinates are displayed IMapList Form To add maps use File->Setup Map Paths. Pour ajouter des cartes cliquez sur Fichier -> Configurer les répertoires des cartes. Use the context menu (right mouse button click on entry) to activate a map. Use drag-n-drop to move the activated map in the draw order. Utiliser le menu de contexte (clic droite sur la carte) pour activer une carte. Utilisez glisser-déposer pour changer la position de la carte dans la liste. Help! I want maps! I don't want to read the documentation! Au secours ! Je veux des cartes ! Je n'ai pas envie de lire la documentation ! Activate Activer IMapPathSetup Setup map paths Configurer les répertoires des cartes Root path of tile cache for online maps: Répertoire racine du cache de tuiles pour les cartes en ligne : ... ... Help! I want maps! I don't want to read the documentation! Au secours ! Je veux des cartes ! Je n'ai pas envie de lire la documentation ! - - IMapPropSetup Form <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Régler la transparence de la carte</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Cliquez pour utilser l'échelle courante comme échelle minimale d'affichage de la carte.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Définissez la plage d'échelle pour l'affichage de la carte. Utilisez les deux boutons à droite et à gauche pour définir l'échelle courante comme échelle maximale ou minimale.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Cliquez pour utilser l'échelle courante comme échelle maximale d'affichage de la carte.</p></body></html> Areas Surfaces Lines Lignes Points Points - - Cache Path Répertoire du cache Cache Size (MB) Taille du cache (MO) Expiration (Days) Durée du cache (jours) IMapVrtBuilder Form ... ... Select source files: Sélectionnez les fichiers source: Target Filename: Fichier cible: - - Start Démarrer IMouseEditLine Add points? Ajouter les points ? Add points to temporary line? Ajouter les points à la ligne temporaire ? Warning! Avertissement ! This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Ceci remplacera les données de l'original par une ligne simple de coordonnées. Toutes les autres données seront définitivement perdues. <b>New Line</b><br/>Move the mouse and use the left mouse button to drop points. When done use the right mouse button to stop.<br/> <b>Nouvelle ligne</b><br/>Déplacez la souris et utilisez le bouton gauche pour ajouter des points. Cliquez droit pour terminer.<br/> <b>Delete Point</b><br/>Move the mouse close to a point and press the left button to delete it.<br/> <b>Supprimer un point</b><br/>Approchez le pointeur du point et cliquez gauche pour le supprimer.<br/> <b>Select Range of Points</b><br/>Left click on first point to start selection. Left click second point to complete selection and choose from options. Use the right mouse button to cancel.<br/> <b>Sélectionner une série de points</b><br/> Pour commencer la sélection, cliquez gauche sur le premier point. Cliquez gauche sur le sur le deuxième point pour terminer la sélection et choisissez une option. Utilisez le bouton droit pour annuler.<br/> <b>Move Point</b><br/>Move the mouse close to a point and press the left button to make it stick to the cursor. Move the mouse to move the point. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Déplacer un point</b><br/>Approchez le pointeur près d'un point et cliquez gauche pour le coller au pointeur. Déplacez le point avec la souris. Déposez le point avec un clic gauche. Utilisez le bouton droit pour annuler.<br/> <b>Add Point</b><br/>Move the mouse close to a line segment and press the left button to add a point. The point will stick to the cursor and you can move it. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Ajouter un point</b><br/>Placez le pointeur de la souris près d'un segment de ligne et cliquez gauche pour ajouter un point. Le nouveau point va coller au pointeur et vous pouvez le déplacer. Déposez le point par un clic gauche. Utilisez le clic droit pour annuler.<br/> <b>No Routing</b><br/>All points will be connected with a straight line.<br/> <b>Aucun calcul d'itinéraire</b><br/>Les points seront connectés par une ligne droite.<br/> <b>Auto Routing</b><br/>The current router setup is used to derive a route between points. <b>Note:</b> The selected router must be able to route on-the-fly. Offline routers usually can do, online routers can't.<br/> <b>Calcul d'itinéraire automatique</b><br/>Les paramètres de calcul d'itinéraire actuels serviront pour calculer un tracé entre les points. <b>Remarque :</b>Le routeur sélectionné doit être capable de faire le calcul à la volée. Généralement, les routeurs hors ligne en sont capables, les routeurs en ligne ne le sont pas.<br/> <b>Vector Routing</b><br/>Connect points with a line from a loaded vector map if possible.<br/> <b>Calcul d'itinéraire vecteur</b><br/>Connecter les points avec une ligne basée sur une carte vecteur active, si possible.<br/> <b>%1 Metrics</b> Distance: Ascend: Descend: IPhotoAlbum Form ... IPlot Reset Zoom Stop Range tbc: range Terminer la série Save... Enregistrer... No or bad data. Aucune donnée ou données invalides. Select output file Sélectionner le fichier de sortie IPositionDialog Position ... Enter new position Saisissez la nouvelle position Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Mauvais format de position. Formats valides: "[N|S] ddd mm.sss [W|E] ddd mm.sss" ou "[N|S] ddd.ddd [W|E] ddd.ddd" IPrintDialog Print map... Save Enregistrer TextLabel Libellé Print IProgressDialog Please wait... TextLabel Libellé IProjWizard Proj4 Wizzard Mercator UTM zone user defined personnalisé Datum Date World Mercator (OSM) Result: Resultat: UPS North (North Pole) UPS Nord (pôle nord) UPS South (South Pole) UPS Sud (pôle sud) Projection IProjWpt Waypoint Projection Dupliquer un waypoint ... - Clone waypoint and move by: Dupliquer le waypoint et déplacer de: m ° IRouterMapQuest Form Highways Seasonal Language Country Border Profile Profile Avoid: Ferry Bac Toll Road Unpaved <p>Directions Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> IRouterRoutino Form Profile Profile Mode Mode Database Base de données Add paths with Routino database. Ajouter des répertoires qui contiennent des bases de données Routino. ... ... Language To use offline routing you need to define paths to local routing data. Use the setup tool button to register a path. tbc: setup tool button Pour pouvoir utiliser le calcul d'itinéraire hors ligne, vous devez spécifier les répertoires qui contiennent les données de calcul d'itinéraire locales. Utilisez le bouton de configuration pour ajouter des répertoires. IRouterRoutinoPathSetup Setup Routino database... Configurez la base de données Routino... ... ... - - IRouterSetup Form IRoutinoDatabaseBuilder Form ... ... Select source files: Sélectionnez les fichiers source: Start Démarrer Target Path: - - File Prefix IScrOptEditLine Form Save to orignal Enregistrer Save as new Enregistrer sous... Abort Annuler Move points. (Ctrl+M) Ctrl+M Add new points. (Ctrl++) Ctrl++ Select a range of points. (Ctrl+R) Ctrl+R Delete a point. (Ctrl+D) Ctrl+D No auto-routing or line snapping (Ctrl+O) Ctrl+O Use auto-routing to between points. (Ctrl+A) Ctrl+A Snap line along lines of a vector map. (Ctrl+V) Move points. Déplacer des points. ... ... Add new points. Ajouter des points. Select a range of points. Sélectionner une série de points. Delete a point. Supprimer un point. No auto-routing or line snapping tbc: snapping -> verrouillage (used in Garmin device UI) Aucun calcul d'itinéraire automatique ou verrouillage 0 Use auto-routing to between points. Utiliser le calcul d'itinéraire automatique entre deux points. A Snap line along lines of a vector map. Verrouiller la ligne aux lignes d'une carte vecteur. V Ctrl+V Undo last change Annuler la dernière modification Redo last change Rétablir la dernière modification IScrOptOvlArea Form View details and edit. Voir les détails et éditer. ... Copy area into another project. Copier la surface dans un autre projet. Delete area from project. Supprimer la surface du projet. Edit shape of the area. Modifier la forme de la surface. TextLabel Libellé IScrOptPoint Delete point. Supprimer le point Select a range of points. Sélectionner une séquence de points Move selected point. Déplacer le point Add points before the selected point. Ajouter des points avant le point sélectionné Add points after the selected point. Ajouter des points après le point sélectionné IScrOptRange Delete selected range of points. Supprimer une séquence de points IScrOptRangeLine Form Delete all points between the first and last one. Supprimer tous les points entre le premier et le dernier point. ... ... <html><head/><body><p>Calculate a route between the first and last selected point.</p></body></html> Caclculate a route between the first and last selected point. Calcluler un itinéraire entre le premier et le dernier point sélectionné. IScrOptRangeTrk Form Hide all points. Cacher tous les points. ... Show all points. Afficher tous les points. Select an activity for the selected range. Copy track points as new track. Créer une nouvelle trace à partir des points sélectionnés TextLabel Libellé IScrOptRte Form <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Afficher et éditer les détails</p></body></html> ... Copy route into another project. Copier la route dans un autre projet. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Supprimer</p></body></html> View details and edit. Voir les détails et éditer. Delete route from project. Supprimer la route du projet. Calculate route. Calculer la route. Reset route calculation. Reset route caclulation. Réinitialiser la route. Move route points. Déplacer les points de la route. TextLabel Libellé IScrOptTrk Form View details &amp; Edit properties of track. Afficher et éditer les caractéristiques de la trace. ... Copy track into another project. Copier la trace dans un autre projet Delete Supprimer View details and edit properties of track. Voir les détails et éditer les paramètres de la trace. Delete track from project. Supprimer la trace du projet. Show on-screen profile and detailed information about points. Afficher le profile d'altitude et des informations detaillées sur les points Select a range of points. Sélectionner une séquence de points. Edit position of track points. Modifier la position des points de la trace Reverse track. Inverser la trace Combine tracks. Joindre des traces Cut track at selected point. You can use this to: * remove bad points at the start or end of the track * use the track parts to plan a new tour * cut a long track into stages Cut track at selected point into two tracks. Couper la trace en deux au point sélectionné TextLabel Libellé IScrOptWpt Form <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Afficher et éditer les détails</p></body></html> View details and edit. Voir les détails et éditer. ... Copy waypoint into another project. Copier le waypoint dans un autre projet. Delete waypoint from project. tbc: waypoint Supprimer le waypoint du projet. Show content as static bubble. Afficher le contenu comme bulle statique. Move waypoint to a new location. Déplacer le waypoint. Clone waypoint and move clone a given distance and angle. Dupliquer le waypoint et déplacer la copie d'une distance et d'un angle défini. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Supprimer</p></body></html> <html><head/><body><p>Move waypoint to a new location.</p></body></html> <html><head/><body><p>Déplacer le waypoint à une nouvelle position.</p></body></html> <html><head/><body><p>Clone waypoint and move clone a given distance and angle.</p></body></html> <html><head/><body><p>Dupliquer le waypoint et déplacer la copie par une distance et un angle donnés .</p></body></html> TextLabel Libellé ISelDevices Select devices... Sélectionner les appareils... ISelectActivity Activities... Select one: ISelectCopyAction Copy item... Copier un élément... Replace existing item Remplacer l'élément existant TextLabel Libellé Do not copy item Ne pas copier l'élément Create a clone Dupliquer l'élément Replace with: Remplacer par: Keep item: Conserver l'élément: The clone's name will be appended with '_Clone' Le nom de la copie aura le suffixe '_Clone' And for all other items, too. et pour tous les autres éléments ISelectDBFolder Select Parent Folder... Sélectionner le répertoire parent... Name Nom ISelectProjectDialog Select a project... Choisissez un projet... Select project from list or enter new project name. Choisissez un projet dans la liste ou entrez le nom d'un nouveau projet. New project's name Nom du nouveau projet New project is created as: Le nouveau projet sera de type: *.qms *.gpx Database Base de données ISelectSaveAction Copy item... Copier un élément... Replace existing item Remplacer l'élément existant Replace with: Remplacer par: TextLabel Do not replace item Ne pas remplacer l'élément Use item: Utiliser l'élément: And for all other items, too. et pour tous les autres éléments ISetupDatabase Add database... Ajouter une base de données... File Fichier - Name Nom Add new database. Ajouter une nouvelle base de données. ... Open existing database. Ouvrir une base de données existante. ISetupFolder Folder... Dossier... Name Nom Database Folder... Dossier de base donnée... Folder name Nom du dossier Group Groupe Project Projet Other Autre ISetupNewWpt New Waypoint... Nouveau waypoint... Symbol Symbole ... ... Position Position Name Nom Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Mauvais format de position. Formats valides: "[N|S] ddd mm.sss [W|E] ddd mm.sss" ou "[N|S] ddd.ddd [W|E] ddd.ddd" ISetupWorkspace Setup workspace... Configurer l'espace de travail... save workspace on exit, and every Sauvegarde de l'espace de travail en quittant et toutes les minutes ITextEditWidget Edit text... Éditer le texte... ... Undo Annuler Ctrl+Z Redo Répéter Ctrl+Shift+Z Cut Couper Ctrl+X Copy Copier Ctrl+C Paste Coller Ctrl+V Align Left Aligné à gauche Ctrl+L Align Right Aligné à droite Ctrl+R Align Center Centré Ctrl+E Align Block Justifié Ctrl+J Underline Soulignage Ctrl+U Bold Gras Ctrl+B Italic Italique Ctrl+I ITimeZoneSetup Setup Time Zone ... Configurer le fuseau horaire UTC UTC Local Local Automatic Automatique Print date/time in Afficher la date au format long format, or long short format court IToolShell Execution of external program `%1` failed: Process cannot be started. Make sure the required packages are installed, `%1` exists and is executable. External process crashed. An unknown error occurred. !!! failed !!! !!! échec !!! IUnitsSetup Setup units... Configurer les unités Metric Métrique <b>Note:</b> For some GUI elements changing the units will not take effect until you restart QMapShack. Imperial Impérial Nautic Nautique IWptIconDialog Icons... Icônes QObject Picture%1 Image%1 Error Erreur Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Mauvais format de position. Formats valides: "[N|S] ddd mm.sss [W|E] ddd mm.sss" ou "[N|S] ddd.ddd [W|E] ddd.ddd" Position values out of bounds. Valeurs de la position hors de la plage autorisée. Are you sure you want to delete '%1' from folder '%2'? Êtes-vous sûr de vouloir supprimer %1 du dossier %2? Delete... Supprimer... Delete project... Supprimer le projet Do you really want to delete %1? Êtes-vous sûr de vouloir supprimer %1? All your data grouped by folders. Vos données groupées par dossiers. Lost & Found (%1) Perdu & Trouvé (%1) Lost & Found Perdu & Trouvé Save GIS data to... Enregistrer les données SIG dans... Save ... Enregistrer... Abort save Annuler l'enregistrement Failed to open... Impossible d'ouvrir... Failed to open %1 Impossible d'ouvrir %1 Failed to read... Impossible de lire... Failed to read: %1 line %2, column %3: %4 Impossible de lire: %1 ligne %2, colonne %3: %4 Not a GPX file: N'est pas un fichier GPX: File exists ... Le fichier existe... The file exists and it has not been created by QMapShack. If you press 'yes' all data in this file will be lost. Even if this file contains GPX data and has been loaded by QMapShack, QMapShack might not be able to load and store all elements of this file. Those elements will be lost. I recommend to use another file. <b>Do you really want to overwrite the file?</b> Le fichier existe et n'a pas été créé par QMapShack. Si vous cliquez sur 'oui' tous les données de ce fichier seront perdues. Même si ce fichier contient des données GPX et sera ouvert par QMapShack certains éléments de ce fichier ne pourront pas être lus ou enregistrés. Ces élements seront perdus. Il est conseillé d'utiliser un autre fichier.<b>Voulez-vous vraiment écraser ce fichier ?</b> Failed to create file '%1' Impossible de créer le fichier: '%1' Failed to write file '%1' Impossible d'écrire le fichier: '%1' Saving GIS data failed... Saveing GIS data failed... Impossible d'enregistrer les données SIG... Archived Archivé Available Disponible Not Available Non disponible Initial version. Version initiale This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press'Ok'. Cet élément est probablement en lecture seule parce qu'il n'a pas été créé avec QMapShack. Normelement, vous ne devriez pas modifier des données importées. Mais si vous êtes sûr de ce que vous faites, cliquez sur 'OK' <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press 'Ok'. Read Only Mode... Mode lecture seule <h4>Comment:</h4> <h4>Commentaire:</h4> <p>--- no comment ---</p> <p>--- pas de commentaire ---</p> <h4>Description:</h4> [no name] <p>--- no description ---</p> <p>--- pas de description ---</p> <h4>Links:</h4> <h4>Liens:</h4> <p>--- no links ---</p> <p>--- pas de liens ---</p> thin fine normal normal wide large strong épaisse _Clone _Clone Area: %1%2 Surface: %1%2 Changed area shape. Changed name. Nom modifié... Changed border width. Largeur de la bordure modifiée... Changed fill pattern. Remplissage modifié... Changed opacity. Opacité modifiée... Changed comment. Commentaire modifié... Changed description. Description modifiée... Changed links Liens modifiés... Changed color Couleur modifiée... Save project? Enregistrer le projet ? The project "%1" was changed. Save before closing it? Le projet "%1" a été modifié. Enregistrer avant de le fermer ? %1: Correlate tracks and waypoints. Abort Annuler <h3>%1</h3>The project was changed. Save before closing it? <h3>%1</h3>Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project in the future? Canceled correlation... <br/> Filename: %1 <br/> Nom de fichier: %1 Waypoints: %1 Waypoints: %1 Tracks: %1 Traces: %1 Routes: %1 Routes: %1 Areas: %1 Surfaces: %1 Are you sure you want to delete '%1' from project '%2'? Êtes-vous sûr de vouloir supprimer %1 du projet '%2'? Changed comment Commentaire modifié Changed description Description modifiée Length: - Longueur: - Time: %1 %2 Durée: %1 %2 Distance: %1 %2 Time: %2 days %1 Durée : %2 jours %1 Time: - tbc: heure/temps/durée Heure : - Last time routed:<br/>%1 tbc Date du dernier calcul d'itinéraire : <br/>%1 with %1 avec %1 Changed route points. Points de route modifiés. Error... Erreur... Failed to open %1. Impossible d'ouvrir %1 Only support lon/lat WGS 84 format. Le seul format lon/lat autorisé est WGS 84 Failed to read data. Impossible de lire les données. Changed trackpoints, sacrificed all previous data. Points de la trace modifiés, Les données antérieures sont perdues. Length: %1 %2 Longueur: %1 %2 , %1%2 %3, %4%5 %6 Time: %1 Durée: %1 , Speed: %1 %2 , vitesse %1 %2 Moving: %1 Déplacement: %1 Start: %1 Début: %1 End: %1 Fin: %1 Points: %1 (%2) Ele.: %1 %2 Altitude: %1 %2 slope: %1%3 (%2%) Pente: %1%3(%2%) speed: %1%2 Vitesse: %1 %2 ... and %1 tags not displayed Ascend: %1%2 (%3%) Montée: %1%2 (%3%) Ascend: - (-) Montées: - (-) Descend: %1%2 (%3%) Descente: %1%2 (%3%) Descend: - (-) Descente: - (-) Dist.: %1%2 (%3%) Dist.: %1%2 (%3%) Dist.: - (-) Dist.: - (-) Moving: %1%2 (%3%) En mouvement: %1%2 (%3%) Moving: - (-) En mouvement: - (-) Ascend: %1%2 Montée : %1%2 , %1%2 Ascend: - Montée : - Descend: %1%2 Descente : %1%2 Descend: - Descente : - Dist.: %1%2 Dist.: %1%2 Time: %1%2 Temps : %1%2 Permanently removed points %1..%2 Changed activity to '%1' for complete track. Changed activity to '%1' for range(%2..%3). Edit name... Éditer le nom... Enter new track name. Entrez le nom de la nouvelle trace. Hide points. Cacher des points. Show points. Afficher les points. Changed name Nom modifié Hide points by Douglas Peuker algorithm (%1%2) Cacher des points avec l'algorithme Douglas Peuker (%1%2) Hide points with invalid coordinates at the beginning of the track Reset all hidden track points to visible Restaurer les points cachés Permanently removed all hidden track points Supprimer définitivement tous les points cachés Smoothed profile with a Median filter of size %1 Profile lissé avec un filtre médian de dimension %1 Replaced elevation data with data from DEM files. Les altitudes ont été remplacées par les données DEM Offset elevation data by %1%2. Décaler les altitudes de %1%2. Changed start of track to %1. Début de la trace modifié à %1. Remove timestamps. Horodatage Supprimé. Set artificial timestamps with delta of %1 sec. Horodatage artificiel ajouté avec un décalage de %1 sec. Changed speed to %1%2. Vitesse modifiée à %1%2. Enter new waypoint name. Saisir le nouveau nom du waypoint. Elevation: %1 %2 Altitude: %1 %2 Proximity: %1 %2 Proximité: %1 %2 Changed position Position modifiée Changed elevation Altitude modifiée Changed proximity Rayon de proximité modifié Changed icon Icône modifié Changed images Images modifiées Add image Ajouter une image Warning... Avertissement... This is a typ file with unknown polygon encoding. Please report! Ceci est un fichier type avec un encodage de polygone inconnu. Veuillez signaler ce problème. This is a typ file with unknown polyline encoding. Please report! Ceci est un fichier type avec un encodage de polyligne inconnu. Veuillez signaler ce problème. Enter new area name. Entrez le nom de la nouvelle surface. Copy flag information from QLandkarte GT track Copier la balise d'information de la trace QLandkarte GT Corrupt track ... Trac invalide... Number of trackpoints is not equal the number of training data trackpoints. Le nombre de points de trace ne correspond pas au nombre de points de trace de training. Number of trackpoints is not equal the number of extended data trackpoints. Le nombre de points de trace ne correspond pas au nombre de points de trace étendus. Number of trackpoints is not equal the number of shadow data trackpoints. Le nombre de points de trace ne correspond pas au nombre des points cachés There is another project with the same name. If you press 'ok' it will be removed and replaced. Un autre projet du même nom existe déjà. Si vous confirmez, il sera supprimé et remplacé. Enter new route name. Entrez le nom de la nouvelle route. Foot à pied Bicycle Vélo Motor Bike Car Cable Car Swim Ship Aeronautik Aeronautics Distance: Ascend: Descend: Speed Moving: Speed Total: Time Moving: Time Total: Progress Progrès time durée distance [%1] Slope (directed) Speed Vitesse Elevation Altitude Heart Rate Cadence Air Temperature Water Temperature Depth qmapshack-1.5.1/src/locale/qmapshack_cs.ts000644 001750 000144 00001074261 12624270571 021533 0ustar00oeichlerusers000000 000000 CAbout API Version %1 (expected %2) Verze API %1 (očekávána %2) %1 (API V%2, expected V%3) %1 (API verze %2, očekávána verze %3) %1 (API V%2) %1 (API verze %2) CCanvas Workspace %1 Pohled %1 View %1 Pohled %1 CCommandProcessor Print debug output to console. Zobrazit výstup ladění v konzoli. Print debug output to logfile (temp. path). Uložit výstup ladění do souboru se zápisem (cesta temp). Do not show splash screen. Neukazovat uvítací obrazovku. File with QMapShack configuration. Soubor s nastavením pro QMapShack. file Soubor Files for future use. Soubory pro pozdější potřebu. CDemList Deactivate Vypnout Activate Zapnout CDemPathSetup Add or remove paths containing DEM data. There can be multiple files in a path but no sub-path is parsed. Supported formats are: %1 Přidat nebo odstranit cesty obsahující data DEM. V cestě může být více souborů, ale žádná podcesta není zpracována. Podporovanými formáty jsou: %1 Select DEM file path... Vybrat cestu k souboru DEM... CDemPropSetup <b>Grade %1</b> <b>Stupeň %1</b> CDemVRT Error... Chyba... Failed to load file: %1 Nepodařilo se nahrát soubor %1 DEM must have one band with 16bit or 32bit data. DEM musí mít jedno pásmo s 16bitovými nebo 32bitovými daty. No georeference information found. Nenalezeny žádné údaje o vyjádření prostorových vztahů. CDetailsGeoCache none žádné ??? ? Searching for images... Hledají se obrázky... No images found Nebyly nalezeny žádné obrázky CDetailsOvlArea Edit name... Upravit název... Enter new area name. Zadat název nové oblasti. Enter new waypoint name. Zadat nový název cestovního bodu. <h4>Comment:</h4> <h4>Poznámka:</h4> <p>--- no comment ---</p> <p>--- žádná poznámka ---</p> <h4>Description:</h4> <h4>Popis:</h4> <p>--- no description ---</p> <p>--- žádný popis ---</p> CDetailsPrj none žádné Build diary... Sestavit deník... Abort Zrušit <h2>Waypoints</h2> <h2>Cestovní body</h2> Info Informace Comment Poznámka <h2>Tracks</h2> <h2>Stopy</h2> <h2>Areas</h2> <h2>Oblasti</h2> You want to sort waypoints along a track, but you switched off track and waypoint correlation. Do you want to switch it on again? Chcete třídit cestovní body podél cesty, ale vypnul jste svázání cestovních bodů a cest (dání do vzájemného vztahu). Chcete je opět zapnout? Correlation... Svázání... <b>Summary over all tracks in project</b><br/> <b>Přehled všech stop v projektu</b><br/> distance: %1%2 Vzdálenost: %1%2 ascent: %1%2 Stoupání: %1%2 descend: %1%2 Klesání: %1%2 <h2>Routes</h2> <h2>Cesty</h2> Edit name... Upravit název... Enter new project name. Zadejte název projektu. Edit keywords... Upravit klíčová slova... Enter keywords. Zadejte klíčová slova. Print Diary Tisk deníku CDetailsRte Edit name... Upravit název... Enter new route name. Zadat nový název pro cestu. CDetailsTrk distance [%1] Vzdálenost [%1] speed. [%1] Rychlost [%1] time Čas distance. [%1] Vzdálenost [%1] Solid color Reduce visible track points Omezit počet viditelných bodů stopy Change elevation of track points Změnit informace o výškách bodů stopy Change timestamp of track points Změnit časová razítka bodů stopy Cut track into pieces Rozkrájet stopu na kusy %1 %2 %1 %2 Edit name... Upravit název... Enter new track name. Zadat název nové stopy. Reset activities... Nastavit činnosti znovu... This will remove all activities from the track. Proceed? Tímto budou všechny činnosti odstraněny ze stopy. Pokračovat? None Žádné <h4>Comment:</h4> <h4>Poznámka:</h4> <p>--- no comment ---</p> <p>--- žádná poznámka ---</p> <h4>Description:</h4> <h4>Popis:</h4> <p>--- no description ---</p> <p>--- žádný popis ---</p> CDetailsWpt <h4>Comment:</h4> <h4>Poznámka:</h4> <p>--- no comment ---</p> <p>--- žádná poznámka ---</p> <h4>Description:</h4> <h4>Popis:</h4> <p>--- no description ---</p> <p>--- žádný popis ---</p> Edit name... Upravit název... Enter new waypoint name. Zadat nový cestovní bod. Enter new elevation. Zadat novou výšku. Enter new proximity range. Zadejte nový poplach kvůli odstupu. CElevationDialog No DEM data found for that point. Pro tento bod nebyla nalezena žádná data DEM. CGisListDB Add Database Přidat databázi Add Folder Přidat složku Delete Folder Smazat složku Delete Item Smazat prvek Remove Database Odstranit databázi Empty Prázdný Remove database... Odstranit databázi... Do you really want to remove '%1' from the list? Do you realy want to remove '%1' from the list? Opravdu chcete '%1' odstranit ze seznamu? Delete database folder... Smazat složku s databází... Are you sure you want to delete "%1" from the database? Opravdu chcete "%1" odstranit z databáze? Remove items... Odstranit prvky... Are you sure you want to delete all items from Lost&Found? This will remove them permanently. Opravdu chcete smazat všechny prvky ze ztracených a nalezených? Tím budou trvale odstraněny. Are you sure you want to delete all selected items from Lost&Found? This will remove them permanently. Opravdu chcete smazat všechny vybrané prvky ze ztracených a nalezených? Tím budou trvale odstraněny. CGisListWks Save As... Uložit jako... Save Uložit Edit.. Upravit... Update Project on Devices Aktualizovat projekt na zařízeních Close Zavřít Update Project on Device Aktualizovat projekt na zařízení Edit... Upravit... Copy to... Kopírovat do... Show Bubble Ukázat bublinu Move Waypoint Přesunout cestovní bod Proj. Waypoint... Promítnutí cestovního bodu... Route Instructions Pokyny pro cestu Calculate Route Spočítat cestu Reset Route Nastavit cestu znovu Edit Route Upravit cestu Create Route Vytvořit cestu Drop items... Zahodit prvky... <b>Update devices</b><p>Update %1<br/>Please wait...</p> <b>Aktualizovat zařízení</b><p>Aktualizovat %1<br/>Počkejte, prosím...</p> Copy items... Kopírovat prvky... Track Profile Profil stopy Show on Map Ukázat na mapě Hide from Map Skrýt v mapě Send to Devices Poslat do zařízení Select Range Vybrat rozsah Edit Track Points Upravit body stopy Reverse Track Obrátit stopu Combine Tracks Spojit stopy Edit Area Points Upravit body oblasti Delete Smazat Saving workspace. Please wait. Ukládá se pracovní prostor. Počkejte, prosím. Loading workspace. Please wait. Nahrává se pracovní prostor. Počkejte, prosím. Close all projects... Zavřít všechny projekty... This will remove all projects from the workspace. Tímto budou všechny projekty odstraněny z pracovního prostoru. CGisWidget Load project... Nahrát projekt... The project "%1" is already in the workspace. Projekt "%1" je již náhrán do pracovního prostoru. Cut Track... Rozkrajet stopu... Do you want to delete the original track? Opravdu chcete smazat původní stopu? CGrid [Grid: %1] [Mřížka: %1] [Grid: %1%2%5 %3%4%5] [Mřížka: %1%2%5 %3%4%5] [Grid: N %1m, E %2m] [Mřížka: S %1m, V %2m] %1 %2 %1 %2 %1%2%5 %3%4%5 %1%2%5 %3%4%5 %1m, %2m %1m, %2m N %1m, E %2m N %1 m, E %2 m CHistoryListWidget Cut history Vyjmout historii CImportDatabase Import QLandkarte Database Zavést databázi QLandkarte Select source database... Vybrat zdrojovou databázi... Select target database... Vybrat cílovou databázi... CMainWindow Ele: %1%2 Výška: %1%2 [Grid: %1] [Mřížka: %1] Load GIS Data... Nahrát data GIS... Select output file Vybrat výstupní soubor Select file to load Vybrat soubor k nahrání CMapIMG Failed ... Nepodařilo se... Unspecified Neurčeno French Francouzský German Německý Dutch Holandský English Anglický Italian Italský Finnish Finský Swedish Švédský Spanish Španělský Basque Baskický Catalan Katalánský Galician Galicijský Welsh Velšský Gaelic Gaelský Danish Dánský Norwegian Norský Portuguese Portugalský Slovak Slovenský Czech Český Croatian Chorvatský Hungarian Maďarský Polish Polský Turkish Turecký Greek Řecký Slovenian Slovinský Russian Ruský Estonian Estonský Latvian Lotyšský Romanian Rumunský Albanian Albánský Bosnian Bosenský Lithuanian Litevský Serbian Srbský Macedonian Makedonský Bulgarian Bulharský Major highway Dálnice Principal highway Silnice první třídy Other highway Jiné rychlostní silnice Arterial road Rychlostní silnice Collector road Státní silnice Residential street Silnice v obytné oblasti Alley/Private road Soukromá cesta Highway ramp, low speed Nájezd na dálnici/sjezd z dálnice Highway ramp, high speed Nájezd na dálnici/sjezd z dálnice Unpaved road Neasfaltovaná cesta Major highway connector Dalniční přivaděč Roundabout Kruhový objezd Railroad Železnice, koleje Shoreline Břeh Trail Cesta Stream Proud Time zone Časové pásmo Ferry Přívoz State/province border Státní/Zemská hranice County/parish border Krajská/Obecní hranice International border Mezinárodní hranice River Řeka Minor land contour Malá vrstevnice Intermediate land contour Střední vrstevnice Major land contour Velká vrstevnice Minor depth contour Malá hloubková čára Intermediate depth contour Střední hloubková čára Major depth contour Velká hloubková čára Intermittent stream Přerušovaný potok (Wadi) Airport runway Přistávací dráha Pipeline Dálkové potrubí Powerline Elektrické vedení Marine boundary Hranice moře Hazard boundary Nebezpečná hranice Large urban area (&gt;200K) Velkoměstská oblast (&gt;200 000) Small urban area (&lt;200K) Maloměstská oblast (&gt;200 000) Rural housing area Městská obytná oblast Military base Vojenská základna Parking lot Parkoviště Parking garage Parkovací budova Airport Letiště Shopping center Nákupní středisko Marina Přístav University/College Univerzita/Vysoká škola Hospital Nemocnice Industrial complex Průmyslový celek Reservation Chráněné území Man-made area Zástavba Sports complex Oblast pro tělesné činnosti Golf course Golfové hřiště Cemetery Hřbitov National park Národní park City park Městské sady State park Státní park Forest Les Ocean Oceán Blue (unknown) Modrá (neznámé) Sea Moře Large lake Velké jezero Medium lake Střední jezero Small lake Malé jezero Major lake Velmi velké jezero Major River Veletok Large River Velká řeka Medium River Střední řeka Small River Malá řeka Intermittent water Přerušovaná voda Wetland/Swamp Močál/Bažina Glacier Ledovec Orchard/Plantation Sad/Plantáž Scrub Křoví Tundra Tundra Flat Rovina ??? ??? Failed to read: Nepodařilo se přečíst: Failed to open: Nepodařilo se otevřít: Bad file format: Špatný formát souboru: Failed to read file structure: Nepodařilo se přečíst stavbu souboru: Loading %1 Nahrává se %1 User abort: Zrušeno uživatelem: File is NT format. QMapShack is unable to read map files with NT format: Soubor je ve formátu NT. QMapShack nedokáže číst mapové soubory ve formátu NT: File contains locked / encypted data. Garmin does not want you to use this file with any other software than the one supplied by Garmin. Soubor obsahuje zamknutá/zašifrovaná data. Garmin nechce, aby byl tento soubor použit s jiným programem než dodaným Garminem. Point of Interest Podivuhodnost Unknown Neznámý Area Oblast CMapList Deactivate Vypnout Activate Zapnout Where do you want to store maps? Kde chcete ukládat mapy? CMapMAP Failed ... Nepodařilo se... Failed to open: Nepodařilo se otevřít: Bad file format: Špatný formát souboru: CMapPathSetup Add or remove paths containing maps. There can be multiple maps in a path but no sub-path is parsed. Supported formats are: %1 Přidat nebo odstranit cesty obsahující mapy. V cestě může být více map, ale žádná podcesta není zpracována. Podporovanými formáty jsou: %1 Select map path... Vybrat cestu k mapě... Select root path... Vybrat cestu ke kořeni... CMapPropSetup Cache path... Cesta k vyrovnávací paměti... CMapRMAP Error... Chyba... This is not a TwoNav RMAP file. Toto není soubor TwoNav RMAP Unknown sub-format. Neznámý podformát. Unknown version. Neznámá verze Failed to read reference point. Nepodařilo se přečíst referenční bod. Unknown projection and datum (%1%2). Neznámé promítání a datum (%1%2). CMapTMS Error... Chyba... Failed to open %1 Nepodařilo se otevřít %1 Failed to read: %1 line %2, column %3: %4 Chyba při čtení: %1 Řádek %2, Sloupec %3: %4 Layer %1 Vrstva %1 This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. Tato mapa vyžaduje podporu pro OpenSSL. Nicméně kvůli právním omezením v některých zemích není OpenSSL k QMapShack přibaleno. Můžete se podívat na <a href='https://www.openssl.org/community/binaries.html'>stránky OpenSSL</a>, kde najdete spustitelné soubory. Musíte zkopírovat libeay32.dll a ssleay32.dll do adresáře s programem QMapShack. <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 dlaždic čeká<br/> CMapVRT Error... Chyba... Failed to load file: %1 Nepodařilo se nahrát soubor %1 File must be 8 bit palette or gray indexed. Soubor musí mít 8 bitovou barevnou paletu nebo být v odstínech šedi. No georeference information found. Nenalezeny žádné údaje o vyjádření prostorových vztahů. CMapVrtBuilder Build GDAL VRT Sestavit GDAL VRT Select files... Vybrat soubory... Select target file... Vybrat cílový soubor... !!! done !!! Hotovo! !!! failed !!! Nepodařilo se! CMapWMTS Error... Chyba... Failed to open %1 Nepodařilo se otevřít %1 Failed to read: %1 line %2, column %3: %4 Chyba při čtení: %1 Řádek %2, Sloupec %3: %4 Failed to read: %1 Unknown structure. Chyba při čtení: %1 Neznámá stavba. Unexpected service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Unexpexted service. '* WMTS 1.0.0' is expected. '%1 %2' is read. Neočekávaná služba. Očekáváno '* WMTS 1.0.0'. Přečteno '%1 %2'. This map requires OpenSSL support. However due to legal restrictions in some countries OpenSSL is not packaged with QMapShack. You can have a look at the <a href='https://www.openssl.org/community/binaries.html'>OpenSSL Homepage</a> for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory. Tato mapa vyžaduje podporu pro OpenSSL. Nicméně kvůli právním omezením v některých zemích není OpenSSL k QMapShack přibaleno. Můžete se podívat na <a href='https://www.openssl.org/community/binaries.html'>stránky OpenSSL</a>, kde najdete spustitelné soubory. Musíte zkopírovat libeay32.dll a ssleay32.dll do adresáře s programem QMapShack. No georeference information found. Nenalezeny žádné údaje o soustavě souřadnic. --- All --- --- Vše --- <b>%1</b>: %2 tiles pending<br/> <b>%1</b>: %2 dlaždic čeká<br/> CMouseEditArea <b>Edit Area</b><br/>Select a corner point for more options.<br/> <b>Upravit oblast</b><br/>Vyberte rohový bod pro více voleb.<br/> Area Oblast <b>Edit Area</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Upravit oblast</b><br/>Vyberte funkci a režim stanovení směru cesty přes nástrojová tlačítka. Dále vyberte bod na čáře. Lze měnit pouze body označené velkým čtverečkem. Černé body jsou podbody vytvořenými při stanovení směru cesty.<br/> CMouseEditRte Route Cesta <b>Edit Route Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Upravit body cesty</b><br/>Vyberte funkci a režim stanovení směru cesty přes nástrojová tlačítka. Dále vyberte bod na čáře. Lze měnit pouze body označené velkým čtverečkem. Černé body jsou podbody vytvořenými při stanovení směru cesty.<br/> CMouseEditTrk <b>Edit Track Points</b><br/>Select a track point for more options.<br/> <b>Upravit body stopy</b><br/>Vyberte bod stopy pro více voleb.<br/> Track Stopa <b>Edit Track Points</b><br/>Select a function and a routing mode via the tool buttons. Next select a point of the line. Only points marked with a large square can be changed. The ones with a black dot are subpoints introduced by routing.<br/> <b>Upravit body cesty</b><br/>Vyberte funkci a režim stanovení směru cesty přes nástrojová tlačítka. Dále vyberte bod na čáře. Lze měnit pouze body označené velkým čtverečkem. Černé body jsou podbody vytvořenými při stanovení směru cesty.<br/> Warning! Varování! This will replace all data of the original by a simple line of coordinates. All other data will be lost permanently. This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Tímto budou všechny původní údaje nahrazeny jednoduchou čárou souřadnic. Všechna ostatní data budou trvale ztracena. CMouseNormal Add Waypoint Přidat cestovní bod Add Track Přidat stopu Add Route Přidat cestu Add Area Přidat oblast Copy position Kopírovat polohu Copy position (Grid) Kopírovat polohu (mřížka) CMousePrint <b>Save(Print) Map</b><br/>Select a rectangular area on the map. Use the left mouse button and move the mouse. Abort with a right click. Adjust the selection by point-click-move on the corners. Save/print the selection by a left click on the disc/printer icon in the center of the selection. <b>Uložit mapu (vytisknout)</b><br/>Vyberte na mapě obdélníkovou oblast. použijte levé tlačítko myši a pohybujte myší. Zrušte výběr klepnutím pravým tlačítkem myši. Výběr lze upravit posunutím rohových bodů myší. Uložení/Vytištění následuje po klepnutí levým tlačítkem myši na odpovídající symbol uprostřed výběru. CMouseRangeTrk <b>Select Range</b><br/>Select first track point. And then a second one.<br/> <b>Vybrat rozsah</b><br/>Vyberte první bod stopy. A potom druhý.<br/> CPhotoAlbum Select images... Vybrat obrázky... CPlotDistance distance [%1] Vzdálenost [%1] time Čas time [h] Čas [h] distance. [%1] Vzdálenost [%1] CPlotProfile distance [%1] Vzdálenost [%1] time [h] Čas [h] alt. [%1] Výška [%1] CPlotSpeed distance [%1] Vzdálenost [%1] time [h] Čas [h] speed. [%1] Rychlost [%1] CPrintDialog Print Map... Tisk mapy... Save Map as Image... Uložit mapu jako obrázek... Printer Properties... Vlastnosti tiskárny... Pages: %1 x %2 Strany: %1 x %2 Zoom with mouse wheel on map below to change resolution: %1x%2 pixel x: %3 m/px y: %4 m/px Přibližujte a oddalujte pomocí kolečka myši na mapě dole pro změnu rozlišení: %1x%2 pixel x: %3 m/px y: %4 m/px Printing pages. Probíhá tisk stran. Save map... Uložit mapu... CProgressDialog Elapsed time: %1 Uplynulý čas: %1 Elapsed time: %1 seconds. Uplynulý čas: %1 sekund. CProjWizard north Sever south Jih Error... Chyba... The value '%1' is not a valid coordinate system definition: %2 Zadání: '%1' není platným vymezením soustavy souřadnic %2 CProjWpt Edit name... Upravit název... Enter new waypoint name. Zadat nový název cestovního bodu. CQlgtDb Migrating database from version 4 to 5. Přestěhovat databázi z verze 4 na verzi 5. Migrating database from version 5 to 6. Přestěhovat databázi z verze 5 na verzi 6. Migrating database from version 6 to 7. Přestěhovat databázi z verze 6 na verzi 7. Migrating database from version 7 to 8. Přestěhovat databázi z verze 7 na verzi 8. Migrating database from version 8 to 9. Přestěhovat databázi z verze 8 na verzi 9. Open database: %1 Otevřít databázi: %1 Folders: %1 Složky: %1 Tracks: %1 Stopy: %1 Routes: %1 (Only the basic route will be copied) Cesty: %1 (Bude koírována pouze základní cesta) Waypoints: %1 Cestovní body: %1 Overlays: %1 (only area overlays will be converted to QMapShack) Překrytí: %1 (pouze překrytí oblasti budou převedena do QMapShack) Overlays: %1 (areas will be converted as areas, distance lines will be converted to tracks, all other overlay items will be lost) Překrytí: %1 (oblasti budou převáděny jako oblasti, vzdálenostní čáry budou převedeny do stop, všechny ostatní překrývající prvky budou ztraceny) Diaries: %1 Deníky: %1 Map selections: %1 (can't be converted to QMapShack) Výběry map: %1 (nelze převést do QMapShack) ------ Start to convert database to %1------ ------ Začít převádět databázi do %1------ Failed to create target database. Nepodařilo se vytvořit cílovou databázi. ------ Abort ------ ------ Zrušit ------ ------ Done ------ ------ Hotovo ------ Restore folders... Obnovit složky... Abort Zrušit Imported %1 folders and %2 diaries Zavedeno %1 složek a %2 deníků Copy items... Kopírovat prvky... Imported %1 tracks, %2 waypoints, %3 routes, %4 areas Zavedeno %1 stop, %2 cestovních bodů, %3 cest, %4 oblastí Import folders... Zavést složky... Overlay of type '%1' cant be converted Překrytí typu '%1' nelze převést CQmsDb Existing file... Stávající soubor... Remove existing %1? Odstranit stávající %1? Remove existing file %1 Odstranit stávající soubor %1 %1: drop item with QLGT DB ID %2 %1: Zahodit prvek s ID DB QLGT %2 CRouterMapQuest Fastest Nejrychlejší Shortest Nejkratší Bicycle Jízdní kolo Pedestrian Chodec US English Angličtina (USA) British English Angličtina (britská) Danish Dánský Dutch Holandský French Francouzský German Německý Italian Italský Norwegian Norský Spanish Španělský Swedish Švédský mode "%1" Režim "%1" no highways Žádné rychlostní silnice no toll roads Žádné silnice s mýtem no seasonal Žádné sezonní silnice no unpaved Žádné nezpevněné silnice no ferry Žádné přívozy no crossing of country borders Žádný přechod zemské hranice <b>MapQuest</b><br/>Routing request sent to server. Please wait... <b>MapQuest</b><br/>Požadavek na stanovením cesty poslán serveru. Počkejte, prosím... <b>MapQuest</b><br/>Bad response from server:<br/>%1 <b>MapQuest</b><br/>Špatná odpověď od serveru:<br/>%1 <br/>Calculation time: %1s <br/>Doba výpočtu: %1 s CRouterRoutino Foot Chodec Horse Jezdec Wheelchair Invalidní vozík Bicycle Jízdní kolo Moped Moped Motorcycle Motorka Motorcar Automobil Goods Nákladní automobil Shortest Nejkratší Found Routino with a wrong version. Expected %1 found %2 Nalezeno Routino s nesprávnou verzí. Očekávána %1, nalezena %2 Quickest Nejrychlejší English Anglický German Německý French Francouzský Hungarian Maďarský Dutch Holandský Russian Ruský Polish Polský A function was called without the database variable set. Byla zavolána funkce, aniž by byla nastavena proměnná databáze. A function was called without the profile variable set. Byla zavolána funkce, aniž by byla nastavena proměnná profilu. A function was called without the translation variable set. Byla zavolána funkce, aniž by byla nastavena proměnná jazyka. The specified database to load did not exist. Databáze zadaná k nahrání neexistuje. The specified database could not be loaded. Zadanou databázi se nepodařilo nahrát. The specified profiles XML file did not exist. Zadaný soubor s profilem XML neexistuje. The specified profiles XML file could not be loaded. Zadaný soubor s profilem XML se nepodařilo nahrát. The specified translations XML file did not exist. Zadaný soubor s jazykem XML neexistuje. The specified translations XML file could not be loaded. Zadaný soubor s jazykem XML se nepodařilo nahrát. The requested profile name does not exist in the loaded XML file. Požadovaný název profilu v nahraném souboru XML není. The requested translation language does not exist in the loaded XML file. Požadovaný jazyk v nahraném souboru XML není. There is no highway near the coordinates to place a waypoint. V blízkosti souřadnick umístění cestovního bodu není žádná silnice. The profile and database do not work together. Profil a databáze dohromady nepracují. The profile being used has not been validated. Používaný profil nebyl schválen. The user specified profile contained invalid data. Uživatelem stanovený profil obsahoval neplatná data. The routing options specified are not consistent with each other. Zadané volby pro stanovení cesty si vzájemně neodpovídají. There is a mismatch between the library and caller API version. Knihovna a verze API si vzájemně neodpovídají. Route calculation was aborted by user. Výpočet cesty byl přerušen uživatelem. A route could not be found to waypoint %1. Nepodařilo se najít žádnou cestu k cestovnímu bodu %1. Unknown error: %1 Neznámá chyba: %1 profile "%1" Profil "%1" , mode "%1" , režim "%1" Warning... Varování... %1: Due to limitations in the Windows POSIX API Routino can't handle files larger than 4GB. %1: Kvůli omezením Windows POSIX API Routino nedokáže zpracovat soubory větší než 4 GB. Calculate route with %1 Spočítat cestu s %1 <br/>Calculation time: %1s <br/>Doba výpočtu: %1 s CRouterRoutinoPathSetup Add or remove paths containing Routino data. There can be multiple databases in a path but no sub-path is parsed. Přidat nebo odstranit cesty obsahující data Routino. V cestě může být více databází, ale žádná podcesta není zpracována. Select routing data file path... Vybrat cestu k souboru s daty se stanovením cesty... Select DEM file path... Vybrat cestu k souboru DEM... CRouterSetup Routino (offline) Routino (nepřipojeno) MapQuest (online) MapQuest (nepřipojeno) CRoutinoDatabaseBuilder Create Routino Database Vytvořit databázi Routino Select files... Vybrat soubory... Select target path... Vybrat cílovou cestu... !!! done !!! Hotovo! !!! failed !!! Nepodařilo se! CSearchGoogle Unknown response Neznámá odpověď Error: Chyba: CSetupDB Setup database... Nastavit databázi... Changes will become active after an application's restart. Změny budou uvedeny v činnost po opětovném spuštění programu. Select database path... Vybrat cestu k databázi... CSetupDatabase Error... Chyba... There is already a database with name '%1' Již je databáze s názvem '%1' New database... Nová databáze... Open database... Otevřít databázi... CSetupWorkspace Setup database... Nastavit databázi... Changes will become active after an application's restart. Změny budou uvedeny v činnost po opětovném spuštění programu. CTextEditWidget &Color... B&arva... IAbout About.... O programu... <b>QMapShack</b>, Version <b>QMapShack</b>, verze TextLabel Textový štítek Qt Qt GDAL GDAL Proj4 Proj4 Routino Routino Rainer Unseld Rainer Unseld French Francouzština Czech Čeština Pavel Fric Pavel Fric German Němčina <b>Translation:</b> Dutch Holandský Harrie Klomp <b>Binaries:</b> <b>Contributors:</b> Christian Eichler (qms@christian-eichler.de) Translation: Překlad: Josef Latt Josef Latt Spanish Španělština Jose Luis Domingo Lopez Jose Luis Domingo Lopez Ivo Kronenberg Ivo Kronenberg Helmut Schmidt Helmut Schmidt Win64 Win64 OS X OS X ...and thanks to all Linux binary maintainers for doing a great job. Special thanks to Dan Horák and Bas Couwenberg for showing presence on the mailing list to discuss distribution related topics. ... a poděkování všem tvůrcům spustitelných souborů pro Linux za jejich dobrou práci. Zvláštní poděkování Danovi Horákovi a Basi Couwenbergovi za účast v diskuzi v poštovním seznamu. Binaries: Spustitelné soubory: This software is licensed under GPL3 or any later version Tento program je licencován pod GPL3 nebo kteroukoli pozdější verzí © 2014 Oliver Eichler (oliver.eichler@gmx.de) © 2014 Oliver Eichler (oliver.eichler@gmx.de) ICanvasSetup Setup Map Workspace... Nastavit pohled na mapu... Setup Map View... Nastavit pohled na mapu... Projection & Datum Promítání a datum ... ... Scales Měřítka Logarithmic Logaritmické Square (optimized for TMS and WTMS tiles) Čtvereční (vyladěné pro dlaždice TMS a WTMS) ICombineTrk Combine Tracks... Spojit stopy... ... ... ICoordFormatSetup Coordinate Format... Formát souřadnic... N48° 53.660 E013° 31.113 N48° 53.660 E013° 31.113 N48.8943° E013.51855° N48.8943° E013.51855° N48° 53' 39.6" E13° 31' 6.78" N48° 53' 39.6" E13° 31' 6.78" ICreateRouteFromWpt Create Route from Waypoints Vytvořit cestu z cestovních bodů ... ... ICutTrk Cut Track Delete first part of the track and keep second one Keep both parts of the track Keep first part of the track and delete second one Check this to store the result into a new track. If you keep both parts of the track you have to create new ones. If you want to keep just one half you can simply remove the points, or check this to create a new track. Create a new track Create a clone Vytvořit klon IDemPathSetup Setup DEM file pathss Stanovit cesty k souborům DEM Setup DEM file paths ... ... - - IDemPropSetup Form Formulář <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Změnit neprůhlednost mapy</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Klepněte pro použití nynějšího měřítka jako nejmenšího měřítka pro zobrazení mapy.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Je zobrazeno ovládání rozmezí měřítka mapy. Použijte tlačítka vlevo a vpravo pro stanovení skutečného měřítka jako buď nejmenšího nebo největšího měřítka.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Klepněte pro použití nynějšího měřítka jako nejmenšího měřítka pro zobrazení mapy.</p></body></html> Hillshading Stínování kopců Slope Sklon ° ° > TextLabel Textový štítek IDemsList Form Formulář To add files with elevation data use File->Setup DEM Paths. Pro přidání souborů s údaji o výšce použijte Soubor → Nastavit cesty k DEM. Use the context menu (right mouse button click on entry) to activate a file. Use drag-n-drop to move the activated file in the process order. Použijte související nabídku (klepnutí pravým tlačítkem myši na položku) pro zapnutí souboru. Použijte přetažení a upuštění pro posunutí zapnutého souboru v pořadí procesů. Activate Zapnout IDetailsGeoCache Dialog Dialog - - about:blank o:prázdný Position: Poloha: Difficulty Obtížnost Terrain Terén Update spoilers Nahrát spoiler znovu ... ... Hint: Rada: TextLabel Textový štítek IDetailsOvlArea Dialog Dialog - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Cestovní bod byl zaveden do QMapShacku a byl změněn. Už neukazuje původní data. Prohlédněte si, prosím, historii kvůli změnám. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Přepnout režim pouze pro čtení. Musíte otevřít zámek, abyste mohl prvek upravovat. ... ... Color Barva Border width Šířka okraje Style Styl Opacity Neprůhlednost Info Informace Points Body Position Poloha Hist. Hist. IDetailsPrj Form Formulář - - Keep Order of Project Zachovat pořadí projektu Sort Along Track Třídit podle stopy Print diary Tisk deníku ... ... Keep order of project Zachovat pořadí projektu Sort by time Třídit podle času Sort along track (multiple) Třídit podle stopy (vícenásobně) Sort along track (single) Třídit podle stopy (jednorázový) Rebuild diary. Sestavit deník znovu. Keywords: Klíčová slova: IDetailsRte Info Informace - - <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Cestovní bod byl zaveden do QMapShacku a byl změněn. Už neukazuje původní data. Prohlédněte si, prosím, historii kvůli změnám. </p></body></html> Toggle read only mode. You have to open the lock to edit the item. Přepnout režim pouze pro čtení. Musíte otevřít zámek, abyste mohl prvek upravovat. ... ... Hist. Hist. IDetailsTrk Form Formulář - - - - Graph Control Ovládání grafu Profile Profil Speed Rychlost Progress Postup Track Stopa Toggle read only mode. You have to open the lock to edit the item. Přepnout režim pouze pro čtení. Musíte otevřít zámek, abyste mohl prvek upravovat. ... ... <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Cestovní bod byl zaveden do QMapShacku a byl změněn. Už neukazuje původní data. Prohlédněte si, prosím, historii kvůli změnám. </p></body></html> Style Styl from Data Source Maximum Minimum Solid color Graphs Graph 3 Graph 2 Graph 1 Activity Činnost To differentiate the track statistics select an activity from the list for the complete track. Or select a part of the track to assign an activity. K rozlišení statistik stop zvolte v seznamu činnost pro celou stopu. Nebo vyberte část stopy a přiřaďte jí činnost. Points Body Time Čas Ele. Výška Delta Rozdíl Dist. Vzdál. Slope Sklon Ascend Stoupání Descend Klesání Position Poloha Info Informace - - Filter Filtr Hist. Historie IDetailsWpt Dialog Dialog Toggle read only mode. You have to open the lock to edit the item. Přepnout režim pouze pro čtení. Musíte otevřít zámek, abyste mohl prvek upravovat. ... ... Position: Poloha: Info Informace - - Ele. Výška Proximity: Blízkost: <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> <html><head/><body><p>Cestovní bod byl zaveden do QMapShacku a byl změněn. Už neukazuje původní data. Prohlédněte si, prosím, historii kvůli změnám. </p></body></html> Hist. Historie <html><head/><body><p>Read Only Mode</p></body></html> <html><head/><body><p>Režim pouze pro čtení</p></body></html> Add images. Přidat obrázky. Delete selected image. Smazat vybraný obrázek. Date/Time: Datum/Čas IElevationDialog Edit elevation... Upravit výšku... Elevation Výška - - Get elevation from active digital elevation model. Získat výšku z činného digitálního výškového modelu. ... ... IFilterDelete Form Formulář <b>Remove Track Points</b> <b>Odstranit body stopy</b> Remove all hidden track points permanently. Odstranit trvale všechny skryté body stopy. ... ... IFilterDouglasPeuker Form Formulář <b>Hide Points (Douglas Peuker)</b> <b>Skrýt cestovní body (Douglas Peuker)</b> Hide track points if the distance to a line between neighboring points is less than Skrýt body stopy, když je vzdálenost k čáře mezi sousedícími body méně než m m Apply filter now. Použít filtr nyní. ... ... IFilterInvalid Form Formulář Hide Invalid Points Hide points with invalid coordinates at the beginning of the track. ... ... IFilterMedian Form Formulář <b>Smooth Profile (Median Method)</b> <b>Vyhladit profil (Metoda střední hodnoty)</b> Smooth deviation of the track points elevation with a Median filter of size Zmenšit odchylku výšky bodů stopy pomocí filtru střední hodnoty o velikosti points Body ... ... IFilterNewDate Form Formulář <b>Change Time</b> <b>Změnit čas</b> Change start of track to Změnit začáteční čas stopy na dd.MM.yy HH:mm:ss dd.MM.yy HH:mm:ss - - ... ... IFilterObscureDate Form Formulář <b>Obscure Timestamps</b> <b>Zastřít časová razítka</b> Increase timestamp by Zvětšit časové razítko o sec. s with each track point. 0 sec. will remove timestamps. pro každý cestovní bod. 0 s odstraní všechna časová razítka. ... ... IFilterOffsetElevation Form Formulář <b>Offset Elevation</b> <b>Vyrovnat výšku</b> Add offset of Přidat posun to track points elevation. ke každé výšce bodu stopy. ... ... IFilterReplaceElevation Form Formulář <b>Replace Elevation Data</b> <b>Nahradit data výšky</b> Replace elevation of track points with the values from loaded DEM files. Nahradit výšková data bodů stopy daty z nahraných souborů s digitálními výškovými modely. ... ... IFilterReset Form Formulář <b>Reset Hidden Track Points</b> <b>Nastavit znovu skryté body stopy</b> Make all trackpoints visible again. Udělat všechny body stopy znovu viditelnými. ... ... IFilterSpeed Form Formulář <b>Change Speed</b> <b>Změnit rychlost</b> Set speed to Změnit rychlost na km/h km/h ... ... IGisWidget Form Formulář State Stav Name Název To add a database do a right click on the database list above. Pro přidání databáze klepněte pravým tlačítkem myši na seznam s databázemi výše. IGridSetup Setup Grid... Nastavení mřížky... Projection Promítání restore default Obnovit výchozí ... ... Get projection from current map. Promítání převzít z nynější mapy. projection wizzard Průvodce pro promítání Grid color Barva mřížky setup grid color Nastavení barvy mřížky IImportDatabase Form Formulář Source Database: Zdrojová databáze: - - ... ... Target Database: Cílová databáze: Start Spustit IInputDialog Edit... Upravit... TextLabel Textový štítek ILinksDialog Links... Odkazy... Type Typ Text Text Uri URI ... ... IMainWindow QMapShack QMapShack File Soubor View Pohled Window Okno ? Nápověda Project Projekt Tool Nástroj Maps Mapy Dig. Elev. Model (DEM) Digitální výškový model (DEM) Data Data Add Map Workspace Přidat pohled na mapu Ctrl+T Ctrl+T Show Scale Ukázat měřítko Setup Map Font Nastavit písmo mapy Show Grid Ukázat mřížku Ctrl+G Ctrl+G Setup Grid Nastavit mřížku Ctrl+Alt+G Ctrl+Alt+G Flip Mouse Wheel Obrátit kolečko myši Setup Map Paths Nastavit cesty k mapám POI Text Text POI Night / Day Noc/Den Map Tool Tip Rada k nástroji pro mapu Setup DEM Paths Nastavit cesty k DEM About O programu Help Nápověda Setup Map Workspace Nastavit pohled na mapu Route Cesta Add Map View Přidat pohled na mapu Ctrl+I Ctrl+I Setup Map View Nastavit pohled na mapu Load GIS Data Nahrát data GIS Load projects from file Nahrát projekty ze souboru Ctrl+L Ctrl+L Save All GIS Data Uložit všechna data GIS Save all projects in the workspace Uložit všechny projekty nacházející se v pracovním prostoru Ctrl+S Ctrl+S Setup Time Zone Nastavit časové pásmo Add empty project Přidat prázdný projekt Search Google Hledat pomocí Google Close all projects Zavřít všechny projekty F8 F8 Setup Units Nastavit jednotky Setup Workspace Nastavit pracovní prostor Setup save on exit. Nastavit uložení při ukončení. Import Database from QLandkarte Zavést databázi z QLandkarte Import QLandkarte GT database Zavést databázi GT QLandkarte VRT Builder Sestavovač VRT GUI front end to gdalbuildvrt Rozhraní pro gdalbuildvrt Store Map View Uložit pohled na mapu Write current active map and DEM list including the properties to a file Zapsat nynější činnou mapu a seznam DEM včetně vlastností do souboru Load Map View Nahrát pohled na mapu Restore view with active map and DEM list including the properties from a file Obnovit pohled s činnou mapou a seznam DEM včetně vlastností ze souboru Ext. Profile Ext. Profil Ctrl+E Ctrl+E Close Zavřít Ctrl+Q Ctrl+Q Clone Map View Klonovat pohled na mapu Ctrl+Shift+T Ctrl+Shift+T Create Routino Database Vytvořit databázi Routino Save(Print) Map Screenshot Uložit (vytisknout) snímek obrazovky s mapou Print a selected area of the map Vytisknout vybranou oblast mapy Ctrl+P Ctrl+P Setup Coord. Format Nastavit formát souřadnic Change the format coordinates are displayed Změnit formát, v němž jsou souřadnice zobrazeny Setup Database Nastavit databázi IMapList Form Formulář To add maps use File->Setup Map Pathss. Pro přidání map použijte Soubor → Nastavit cesty k mapám. To add maps use File->Setup Map Paths. Pro přidání map použijte Soubor → Nastavit cesty k mapám. Use the context menu (right mouse button click on entry) to activate a map. Use drag-n-drop to move the activated map in the draw order. Použijte související nabídku (klepnutí pravým tlačítkem myši na položku) pro zapnutí mapy. Použijte přetažení a upuštění pro posunutí zapnuté mapy v pořadí kreslení. Help! I want maps! I don't want to read the documentation! Pomoc! Chci mapy! Nechce se mi číst dokumentaci! Activate Zapnout IMapPathSetup Setup map paths Nastavit cesty k mapám Root path of tile cache for online maps: Kořenová cesta (root) vyrovnávací paměti dlaždic pro internetové mapy: ... ... Help! I want maps! I don't want to read the documentation! Pomoc! Chci mapy! Nechce se mi číst dokumentaci! - - IMapPropSetup Form Formulář <html><head/><body><p>Change opacity of map</p></body></html> <html><head/><body><p>Změnit neprůhlednost mapy</p></body></html> <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> <html><head/><body><p>Klepněte pro použití nynějšího měřítka jako nejmenšího měřítka pro zobrazení mapy.</p></body></html> ... ... <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> <html><head/><body><p>Je zobrazeno ovládání rozmezí měřítka mapy. Použijte tlačítka vlevo a vpravo pro stanovení skutečného měřítka jako buď nejmenšího nebo největšího měřítka.</p></body></html> <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> <html><head/><body><p>Klepněte pro použití nynějšího měřítka jako nejmenšího měřítka pro zobrazení mapy.</p></body></html> Areas Oblasti Lines Čáry Points Body - - Cache Path Cesta k vyrovnávací paměti Cache Size (MB) Velikost vyrovnávací paměti (MB) Expiration (Days) Datum vypršení (dny) IMapVrtBuilder Form Formulář ... ... Select source files: Vybrat zdrojové soubory: Target Filename: Název cílového souboru: - - Start Spustit IMouseEditLine Add points? Přidat body? Add points to temporary line? Přidat body do dočasné čáry? Warning! Varování! This will replace all data of the orignal by a simple line of coordinates. All other data will be lost permanently. Tímto budou všechny původní údaje nahrazeny jednoduchou čárou souřadnic. Všechna ostatní data budou trvale ztracena. <b>New Line</b><br/>Move the mouse and use the left mouse button to drop points. When done use the right mouse button to stop.<br/> <b>Nová čára</b><br/>Posuňte ukazovátko myši a použijte levé tlačítko myši k upuštění bodů. Až to bude uděláno, použijte pravé tlačítko myši k zastavení.<br/> <b>Delete Point</b><br/>Move the mouse close to a point and press the left button to delete it.<br/> <b>Smazat bod</b><br/>Přesuňte ukazovátko myši blízko k bodu a stiskněte levé tlačítko myši pro jeho smazání.<br/> <b>Select Range of Points</b><br/>Left click on first point to start selection. Left click second point to complete selection and choose from options. Use the right mouse button to cancel.<br/> <b>Upravit rozsah bodů</b><br/>Klepněte levým tlačítkem myši na první bod pro započetí výběru. Klepněte levým tlačítkem myši na druhý bod pro dokončení výběru a vyberte z voleb. Použijte pravé tlačítko myši pro zrušení.<br/> <b>Move Point</b><br/>Move the mouse close to a point and press the left button to make it stick to the cursor. Move the mouse to move the point. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Přesunout bod</b><br/>Přesuňte ukazovátko myši blízko k bodu a stiskněte levé tlačítko myši pro jeho držení v blízkosti ukazovátka. Posuňte myší pro přesunutí bodu. Upustěte bod klepnutím levým tlačítkem myši. Použijte pravé tlačítko myši pro zrušení.<br/> <b>Add Point</b><br/>Move the mouse close to a line segment and press the left button to add a point. The point will stick to the cursor and you can move it. Drop the point by a left click. Use the right mouse button to cancel.<br/> <b>Přidat bod</b><br/>Přesuňte ukazovátko myši blízko k čáře a stiskněte levé tlačítko myši pro přidání bodu. Bod přilne k ukazovátku a vy jím budete moci pohnout. Bod upustěte klepnutím levým tlačítkem myši. Použijte pravé tlačítko myši pro zrušení.<br/> <b>No Routing</b><br/>All points will be connected with a straight line.<br/> <b>Žádné stanovení cesty</b><br/>Všechny body budou spojeny přímou čarou.<br/> <b>Auto Routing</b><br/>The current router setup is used to derive a route between points. <b>Note:</b> The selected router must be able to route on-the-fly. Offline routers usually can do, online routers can't.<br/> <b>Automatické stanovení cesty</b><br/>Nynější nastavení směrování je používáno k vytváření cesty mezi body. <b>Poznámka:</b> Vybraný směrovač musí být schopen rychle směrovat (za běhu). Směrovače nepřipojené k internetu toto obvykle dokáží, směrovače připojené k internetu to běžně neumí.<br/> <b>Vector Routing</b><br/>Connect points with a line from a loaded vector map if possible.<br/> <b>Vektorové stanovení cesty</b><br/>Spojuje body čárou z nahrané vektorové mapy, je-li to možné.<br/> <b>%1 Metrics</b> <b>%1 metrika</b> Distance: Vzdálenost: Ascend: Stoupání: Descend: Klesání: IPhotoAlbum Form Formulář ... ... IPlot Reset Zoom Nastavit znovu zvětšení Stop Range Ukončit výběr oblasti Save... Uložit... No or bad data. Žádné nebo špatné údaje. Select output file Vybrat výstupní soubor IPositionDialog Position ... Poloha... Enter new position Zadat novou polohu Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Špatný polohový formát. Musí být: "[N|S] ddd mm.sss [W|E] ddd mm.sss" nebo "[N|S] ddd.ddd [W|E] ddd.ddd" IPrintDialog Print map... Tisk mapy... Save Uložit TextLabel Textový štítek Print Tisk IProgressDialog Please wait... Počkejte, prosím... TextLabel Textový štítek IProjWizard Proj4 Wizzard Průvodce pro Proj4 Mercator Mercatorovo zobrazení UTM UTM zone Pásmo user defined Stanoveno uživatelem Datum Datum World Mercator (OSM) Světový Mercator (OSM) Result: Výsledek: UPS North (North Pole) UPS Sever (Severní pól) UPS South (South Pole) UPS Jih (Jižní pól) Projection Promítání IProjWpt Waypoint Projection Promítnutí cestovního bodu ... ... - - Clone waypoint and move by: Klonovat cestovní bod a posunout o: m m ° ° IRouterMapQuest Form Formulář Highways Rychlostní silnice Seasonal Sezonní silnice Language Jazyk Country Border Zemské hranice Profile Profil Avoid: Vyhnout se: Ferry Přívoz Toll Road Silnice s mýtem Unpaved Nezpevněné silnice <p>Directions Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> <p>S přátelským povolením od <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> t.b.d t.b.d IRouterRoutino Form Formulář Profile Profil Mode Režim Database Databáze Add paths with Routino database. Přidat cestu pomocí databáze Routino. ... ... Language Jazyk To use offline routing you need to define paths to local routing data. Use the setup tool button to register a path. Pro použití stanovení cesty bez připojení k internetu je potřeba stanovit cesty k místním datům se stanovením cest. Použijte nástrojové tlačítko pro nastavení k zaregistrování cesty. IRouterRoutinoPathSetup Setup Routino database... Nastavit databázi Routino... ... ... - - IRouterSetup Form Formulář IRoutinoDatabaseBuilder Form Formulář ... ... Select source files: Vybrat zdrojové soubory: Start Spustit Target Path: Cílová cesta: - - File Prefix Předpona souboru IScrOptEditLine Form Formulář Save to orignal Uložit do předlohy Save as new Uložit jako nový Abort Přerušit Move points. (Ctrl+M) Přesunout body. (Ctrl+M) Ctrl+M Ctrl+M Add new points. (Ctrl++) Přidat nové body. (Ctrl++) Ctrl++ Ctrl++ Select a range of points. (Ctrl+R) Vybrat rozsah bodů. (Ctrl+R) Ctrl+R Ctrl+R Delete a point. (Ctrl+D) Smazat bod. (Ctrl+D) Ctrl+D Ctrl+D No auto-routing or line snapping (Ctrl+O) Žádné automatické stanovení cesty nebo přichycení k čáře (Ctrl+O) Ctrl+O Ctrl+O Use auto-routing to between points. (Ctrl+A) Použít automatické stanovení cesty mezi body. (Ctrl+A) Ctrl+A Ctrl+A Snap line along lines of a vector map. (Ctrl+V) Přichytit čáru podél čáry vektorové mapy. (Ctrl+V) Move points. Přesunout body. ... ... Add new points. Přidat nové body. Select a range of points. Vybrat rozsah bodů. Delete a point. Smazat bod. No auto-routing or line snapping Žádné automatické stanovení cesty nebo přichycení k čáře 0 0 Use auto-routing to between points. Použít automatické stanovení cesty mezi body. A A Snap line along lines of a vector map. Přichytit čáru podél čáry vektorové mapy. V V Ctrl+V Ctrl+V Undo last change Vrátit poslední změnu zpět Redo last change Obnovit poslední změnu IScrOptOvlArea Form Formulář View details and edit. Zobrazit podrobnosti a upravit. ... ... Copy area into another project. Kopírovat oblast do dalšího projektu. Delete area from project. Smazat oblast z projektu. Edit shape of the area. Upravit tvar oblasti. TextLabel Textový štítek IScrOptPoint Form Formulář Delete point. Smazat bod. Select a range of points. Vybrat rozsah bodů. Move selected point. Přesunout vybraný bod. Add points before the selected point. Přidat body před vybraný bod. Add points after the selected point. Přidat body po vybraném bodu. ... ... IScrOptRange Form Formulář Delete selected range of points. Smazat vybraný rozsah bodů. ... ... IScrOptRangeLine Form Formulář Delete all points between the first and last one. Smazat všechny body mezi prvním a posledním bodem. ... ... <html><head/><body><p>Calculate a route between the first and last selected point.</p></body></html> Caclculate a route between the first and last selected point. Vypočítat cestu mezi prvním a posledním vybraným bodem. IScrOptRangeTrk Form Formulář Hide all points. Skrýt všechny body. ... ... Show all points. Ukázat všechny body. Select an activity for the selected range. Vybrat činnost pro vybranou oblast. Copy track points as new track. Kopírovat body stopy jako novou stopu. TextLabel Textový štítek IScrOptRte Form Formulář <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Zobrazit podrobnosti a upravit</p></body></html> ... ... Copy route into another project. Kopírovat cestu do dalšího projektu. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Smazat</p></body></html> View details and edit. Zobrazit podrobnosti a upravit. Delete route from project. Smazat cestu z projektu. Calculate route. Spočítat cestu. Reset route calculation. Vynulovat spočítání cesty. Move route points. Přesunout body cesty. TextLabel Textový štítek IScrOptTrk Form Formulář View details &amp; Edit properties of track. Zobrazit podrobnosti a upravit vlastnosti stopy. Copy track into another project. Kopírovat stopu do dalšího projektu. Delete Smazat Show on-screen profile and detailed information about points. Ukázat promítnutý profil a podrobné údaje o bodech. Cut track at selected point into two tracks. Vyjmout stopu na vybraném bodu do dvou stop. Edit position of track points. Upravit polohu bodů stopy. View details and edit properties of track. Zobrazit podrobnosti a upravit vlastnosti stopy. Delete track from project. Smazat stopu z projektu. Select a range of points. Vybrat rozsah bodů. Reverse track. Obrátit stopu. Combine tracks. Spojit stopy. Cut track at selected point. You can use this to: * remove bad points at the start or end of the track * use the track parts to plan a new tour * cut a long track into stages <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Zobrazit podrobnosti a upravit</p></body></html> ... ... <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Smazat</p></body></html> TextLabel Textový štítek IScrOptWpt Form Formulář <html><head/><body><p>View details &amp; Edit</p></body></html> <html><head/><body><p>Zobrazit podrobnosti a upravit</p></body></html> View details and edit. Zobrazit podrobnosti a upravit. ... ... Copy waypoint into another project. Kopírovat cestovní bod do dalšího projektu. Delete waypoint from project. Smazat cestovní bod z projektu. Show content as static bubble. Ukázat obsah stálé vysvětlivky. Move waypoint to a new location. Přesunout cestovní bod do nového umístění. Clone waypoint and move clone a given distance and angle. Klonovat cestovní bod a kopii přesunout o danou vzdálenost a ve stanoveném úhlu. <html><head/><body><p>Delete</p></body></html> <html><head/><body><p>Smazat</p></body></html> <html><head/><body><p>Move waypoint to a new location.</p></body></html> <html><head/><body><p>Přesunout cestovní bod do nového umístění.</p></body></html> <html><head/><body><p>Clone waypoint and move clone a given distance and angle.</p></body></html> <html><head/><body><p>Klonovat cestovní bod a přesunout kopii o danou vzdálenost a ve stanoveném úhlu.</p></body></html> TextLabel Textový štítek ISelDevices Select devices... Vybrat zařízení... ISelectActivity Activities... Činnosti... Select one: Vyberte jednu: ISelectCopyAction Copy item... Kopírovat prvek... Replace existing item Nahradit stávající prvek TextLabel Textový štítek Do not copy item Nekopírovat prvek Create a clone Vytvořit klon Replace with: Nahradit: Keep item: Zachovat prvek: The clone's name will be appended with '_Clone' Název klonu bude rozšířen o '_Klon' And for all other items, too. A také pro všechny další prvky. ISelectDBFolder Select Parent Folder... Vybrat nadřazenou složku... Name Název ISelectProjectDialog Dialog Dialog Select a project... Vybrat projekt... Select project from list or enter new project name. Vybrat projekt ze seznamu nebo zadat nový název projektu. New project's name Nový název projektu New project is created as: Nový projekt je vytvořen jako: *.qms *.qms *.gpx *.gpx Database Databáze ISelectSaveAction Copy item... Kopírovat prvek... Replace existing item Nahradit stávající prvek Replace with: Nahradit: TextLabel Textový štítek Do not replace item Nenahrazovat prvek Use item: Použít prvek: And for all other items, too. A také pro všechny další prvky. ISetupDB Setup database... Nastavit databázi... save workspace on exit, and every Uložit pracovní oblast při ukončení, a každých minutes minut Database path Cesta k databázi ... ... - - ISetupDatabase Add database... Přidat databázi... File Soubor - - Name Název Add new database. Přidat novou databázi. ... ... Open existing database. Otevřít stávající databázi. ISetupFolder Folder... Složka... Name Název Database Folder... Složka s databází... Folder name Název složky Group Skupina Project Projekt Other Jiné ISetupNewWpt New Waypoint... Nový cestovní bod... Symbol Symbol ... ... Position Poloha Name Název Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Špatný polohový formát. Musí být: "[N|S] ddd mm.sss [W|E] ddd mm.sss" nebo "[N|S] ddd.ddd [W|E] ddd.ddd" ISetupWorkspace Setup database... Nastavit databázi... Setup workspace... Nastavit pracovní prostor... save workspace on exit, and every Uložit pracovní prostor při ukončení, a každých minutes minut ITextEditWidget Form Formulář Edit text... Upravit text... ... ... Undo Zpět Ctrl+Z Ctrl+Z Redo Znovu Ctrl+Shift+Z Ctrl+Shift+Z Cut Vyjmout Ctrl+X Ctrl+X Copy Kopírovat Ctrl+C Ctrl+C Paste Vložit Ctrl+V Ctrl+V Align Left Zarovnat vlevo Ctrl+L Ctrl+L Align Right Zarovnat vpravo Ctrl+R Ctrl+R Align Center Zarovnat na střed Ctrl+E Ctrl+E Align Block Zarovnat do bloku Ctrl+J Ctrl+J Underline Podtržení Ctrl+U Ctrl+U Bold Tučné Ctrl+B Ctrl+B Italic Kurzíva Ctrl+I Ctrl+I ITimeZoneSetup Setup Time Zone ... Nastavit časové pásmo... UTC Světový čas (UTC) Local Místní Automatic Automaticky Print date/time in Datum/Čas v long format, or dlouhém formátu nebo short format v krátkém formátu IToolShell Execution of external program `%1` failed: Process cannot be started. Make sure the required packages are installed, `%1` exists and is executable. External process crashed. An unknown error occurred. !!! failed !!! Nepodařilo se! IUnitsSetup Setup units... Nastavit jednotky... Metric Metrické <b>Note:</b> For some GUI elements changing the units will not take effect until you restart QMapShack. Imperial Anglické Nautic Nautické IWptIconDialog Icons... Ikony... QObject Error Chyba Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Špatný polohový formát. Musí být: "[N|S] ddd mm.sss [W|E] ddd mm.sss" nebo "[N|S] ddd.ddd [W|E] ddd.ddd" Position values out of bounds. Polohy mimo platné hodnoty. Bad position format. Must be: [N|S] ddd mm.sss [W|E] ddd mm.sss Špatný polohový formát. Musí být: "[N|S] ddd mm.sss [W|E] ddd mm.sss" Failed to read... Nepodařilo se přečíst... Failed to read: %1 line %2, column %3: %4 Chyba při čtení: %1 Řádek %2, Sloupec %3: %4 Not a GPX file: Není souborem GPX: Saving GIS data failed... Filename: %1 Název souboru: %1 Waypoints: %1 Cestovní body: %1 Tracks: %1 Stopy: %1 Routes: %1 Cesty: %1 Areas: %1 Oblasti: %1 Save project? Uložit projekt? The project "%1" was changed. Save before closing it? Projekt "%1" byl změněn. Uložit jej před zavřením? %1: Correlate tracks and waypoints. %1: Svázat stopy a cestovní body. Abort Přerušit Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project (%1) in the future? Trvalo to moc dlouho? Chcete svázání stop a cestovních bodů pro tento projekt (%1) přeskočit i v budoucnu? <h3>%1</h3>The project was changed. Save before closing it? <h3>%1</h3>Projekt byl změněn. Uložit jej před zavřením? <h3>%1</h3>Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project in the future? <h3>%1</h3>Trvalo to moc dlouho? Chcete svázání stop a cestovních bodů pro tento projekt přeskočit i v budoucnu? Cancelled correlation... Svázání zrušeno... Canceled correlation... <br/> Filename: %1 <br/> Název souboru: %1 Waypoints: %1 Cestovní body: %1 Tracks: %1 Stopy: %1 Routes: %1 Cesty: %1 Areas: %1 Oblasti: %1 Are you sure you want to delete '%1' from project '%2'? Jste si jistý, že chcete smazat '%1' z projektu '%2'? Are you sure you want to delete '%1' from folder '%2'? Jste si jistý, že chcete smazat '%1' ze složky '%2'? Delete... Smazat... Failed to open... Nepodařilo se otevřít... Failed to open %1 Nepodařilo se otevřít %1 Save GIS data to... Uložit data GIS do... Save ... Uložit... Abort save Zrušit ukládání File exists ... Soubor existuje... The file exists and it has not been created by QMapShack. If you press 'yes' all data in this file will be lost. Even if this file contains GPX data and has been loaded by QMapShack, QMapShack might not be able to load and store all elements of this file. Those elements will be lost. I recommend to use another file. <b>Do you really want to overwrite the file?</b> Soubor existuje a nebyl vytvořen programem QMapShack. Pokud stisknete Ano, budou všechna data v tomto souboru ztracena. I když by tento soubor obsahoval data GPX a byl nahrán programem QMapShack, QMapShack nemusí být schopen nahrát a uložit všechny prvky tohoto souboru. Tyto prvky budou ztraceny. Doporučuje se použít jiný soubor. <b>Opravdu chcete soubor přepsat?</b> Saveing GIS data failed... Nepodařilo se uložit data GIS... Failed to create file '%1' Nepodařilo se vytvořit soubor '%1' Failed to write file '%1' Nepodařilo se zapsat soubor '%1' Initial version. Počáteční verze. This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press'Ok'. Tento prvek je pravděpodobně pouze pro čtení, protože nebyl vytvořen v programu QMapShack. Obvykle nemáte zájem měnit zavedená data. Ale pokud si myslíte, že je to tentokrát na místě, stiskněte OK. <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press 'Ok'. Read Only Mode... Režim pouze pro čtení... <h4>Comment:</h4> <h4>Poznámka:</h4> <p>--- no comment ---</p> <p>--- žádná poznámka ---</p> <h4>Description:</h4> <h4>Popis:</h4> [no name] [žádný název] <h3>%1</h3> This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press'Ok'. <h3>%1</h3> Tento prvek je pravděpodobně pouze pro čtení, protože nebyl vytvořen v programu QMapShack. Obvykle nemáte zájem měnit zavedená data. Ale pokud si myslíte, že je to tentokrát na místě, stiskněte OK. <p>--- no description ---</p> <p>--- žádný popis ---</p> <h4>Links:</h4> <h4>Odkazy:</h4> <p>--- no links ---</p> <p>--- žádné odkazy ---</p> Length: %1 %2 Délka: %1 %2 , %1%2 %3, %4%5 %6 , %1%2 %3, %4%5 %6 Time: %1 Čas: %1 , Speed: %1 %2 , Rychlost: %1 %2 Moving: %1 Pohyb: %1 Start: %1 Začátek: %1 End: %1 Konec: %1 Points: %1 (%2) Body: %1 (%2) Ele.: %1 %2 Výška: %1 %2 slope: %1%3 (%2%) sklon: %1%3 (%2%) ... and %1 tags not displayed Ascend: %1%2 Stoupání: %1%2 , %1%2 , %1%2 Ascend: - Stoupání: - Descend: %1%2 Klesání: %1%2 Descend: - Klesání: - Dist.: %1%2 Vzdál.: %1%2 Time: %1%2 Čas: %1%2 Permanently removed points %1..%2 Hide points. Skrýt body. Show points. Ukázat body. Changed activity to '%1' for complete track. Činnost byla pro celou stopu změněna na '%1'. Changed activity to '%1' for range(%2..%3). Činnost byla změněna pro oblast (%2..%3) na '%1'. slope: %1°(%2%) sklon: %1°(%2%) speed: %1%2 rychlost: %1%2 Ascend: %1%2 (%3%) Stoupání: %1%2 (%3%) Ascend: - (-) Stoupání: - (-) Descend: - (-) Klesání: - (-) Moving: - (-) Pohyb: - (-) Descend: %1%2 (%3%) Klesání: %1%2 (%3%) Changed trackpoints, sacrificed all previous data. Změněny body stop. Obětována veškerá předchozí data. slope: %1° (%2%) sklon: %1° (%2%) Dist.: %1%2 (%3%) Vzdálenost: %1%2 (%3%) Dist.: - (-) Vzdálenost: - (-) Moving: %1%2 (%3%) Pohyb: %1%2 (%3%) thin Tenký normal Obvyklý wide Široký strong Silný _Clone _Klon Area: %1%2 Oblast: %1%2 Changed area shape. Změněn tvar oblasti. Changed name. Změněn název. Changed border width. Změněna šířka okraje. Changed fill pattern. Změněn vzor výplně. Changed opacity. Změněna neprůhlednost. Changed comment. Změněna poznámka. Changed description. Změněn popis. Changed links Změněné odkazy Changed color Změněná barva Elevation: %1 %2 Výška: %1 %2 Proximity: %1 %2 Blízkost: %1 %2 Changed name Změněný název Edit name... Upravit název... Enter new waypoint name. Zadat nový název pro cestovní bod. Changed position Změněná poloha Changed elevation Změněná výška Changed proximity Změněná blízkost Changed icon Změněná ikona Changed images Změněné obrázky Add image Přidat obrázek Changed comment Změněná poznámka Changed description Změněný popis Length: - Délka: - Time: %1 %2 Čas: %1 %2 Distance: %1 %2 Vzdálenost: %1 %2 Time: %2 days %1 Výsledný čas: %2 dnů %1 Time: - Výsledný čas: - Last time routed:<br/>%1 Poslední spočítání cesty:<br/>%1 with %1 s %1 Calculation took %1 sec. Výpočet trval %1 s. Changed route points. Změněné body cesty. Archived Archivováno Available Dostupné Not Available Nedostupné Warning... Varování... This is a typ file with unknown polygon encoding. Please report! Toto je souborový typ s neznámým druhem mnohoúhelníku. Nahlašte to, prosím! This is a typ file with unknown polyline encoding. Please report! Toto je souborový typ s neznámým druhem čáry. Nahlašte to, prosím! Enter new track name. Zadat název nové stopy. Enter new area name. Zadat název nové oblasti. All your data grouped by folders. Všechna data seskupená podle složek. Lost & Found Ztraceno a nalezeno Lost & Found (%1) Ztraceno a nalezeno (%1) Copy flag information from QLandkarte GT track Kopírovat informační příznak ze stopy QLandkarte GT Corrupt track ... Poškozená stopa... Number of trackpoints is not equal the number of training data trackpoints. Počet bodů stopy neodpovídá počtu bodů stopy cvičebních dat. Number of trackpoints is not equal the number of extended data trackpoints. Počet bodů stopy neodpovídá počtu rozšířených bodů stopy. Number of trackpoints is not equal the number of shadow data trackpoints. Počet bodů stopy neodpovídá počtu stínových bodů stopy. Hide points by Douglas Peuker algorithm (%1%2) Skrýt body pomocí algoritmu Douglas Peuker (%1%2) Hide points with invalid coordinates at the beginning of the track Reset all hidden track points to visible Nastavit znovu všechny skryté body stopy na viditelné Permanently removed all hidden track points Všechny skryté body stopy odstraněny trvale Smoothed profile with a Median filter of size %1 Profil vyhlazen středovým filtrem o velikosti %1 Replaced elevation data with data from DEM files. Výšková data nahrazena daty ze souborů s digitálním výškovým modelem. Offset elevation data by %1%2. Výšková data posunuta o %1%2. Changed start of track to %1. Začátek stopy změněn na %1. Remove timestamps. Odstranit časová razítka. Set artificial timestamps with delta of %1 sec. Uměle utvořená časová razítka nastavena s odstupem %1 s. Changed speed to %1%2. Rychlost změněna na %1%2. Delete project... Smazat projekt... Do you really want to delete %1? Opravdu chcete smazat %1? Error... Chyba... Failed to open %1. Nepodařilo se otevřít %1. Only support lon/lat WGS 84 format. Jako formát je podporován jen lon/lat WGS 84. Failed to read data. Nepodařilo se přečíst data. Picture%1 Obrázek %1 There is another project with the same name. If you press 'ok' it will be removed and replaced. Je jiný projekt se stejným názvem. Pokud stisknete OK, bude odstraněn a nahrazen. Enter new route name. Zadat nový název pro cestu. Foot Chodec Bicycle Jízdní kolo Motor Bike Motocykl Car Auto Cable Car Lanovka Swim Plavání Ship Loď Aeronautik Vzduchoplavba Aeronautics Distance: Vzdálenost: Ascend: Stoupání: Descend: Klesání: Speed Moving: Rychlost při pohybu: Speed Total: Celková rychlost: Time Moving: Čas při pohybu: Time Total: Celkový čas: Progress Postup time Čas distance [%1] Vzdálenost [%1] Slope (directed) Speed Rychlost Elevation Výška Heart Rate Cadence Air Temperature Water Temperature Depth qmapshack-1.5.1/src/tool/CImportDatabase.cpp000644 001750 000144 00000006601 12622435717 021745 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CSettings.h" #include "qlgt/CQlgtDb.h" #include "tool/CImportDatabase.h" #include CImportDatabase::CImportDatabase(QWidget *parent) : QWidget(parent) { setupUi(this); setObjectName(tr("Import QLandkarte Database")); SETTINGS; labelSource->setText(cfg.value("ConvertDB/source","-").toString()); labelTarget->setText(cfg.value("ConvertDB/target","-").toString()); textBrowser->setFont(QFont("Courier",10)); connect(toolSelectSource, SIGNAL(clicked()), this, SLOT(slotSelectSource())); connect(toolSelectTarget, SIGNAL(clicked()), this, SLOT(slotSelectTarget())); connect(pushStart, SIGNAL(clicked()), this, SLOT(slotStart())); pushStart->setEnabled(false); if(QFile::exists(labelSource->text())) { pushStart->setEnabled(true); dbQlgt = new CQlgtDb(labelSource->text(), this); } } CImportDatabase::~CImportDatabase() { SETTINGS; cfg.setValue("ConvertDB/source", labelSource->text()); cfg.setValue("ConvertDB/target", labelTarget->text()); } void CImportDatabase::stdOut(const QString& str) { textBrowser->setTextColor(Qt::black); textBrowser->append(str); } void CImportDatabase::stdErr(const QString& str) { textBrowser->setTextColor(Qt::red); textBrowser->append(str); } void CImportDatabase::slotSelectSource() { SETTINGS; QString path = cfg.value("ConvertDB/sourcePath",QDir::homePath()).toString(); QString filename = QFileDialog::getOpenFileName(this, tr("Select source database..."), path, "QLandkarte Database (*.db)"); if(filename.isEmpty()) { return; } QFileInfo fi(filename); cfg.setValue("ConvertDB/sourcePath", fi.absolutePath()); labelSource->setText(filename); delete dbQlgt; textBrowser->clear(); dbQlgt = new CQlgtDb(filename, this); pushStart->setEnabled(true); } void CImportDatabase::slotSelectTarget() { SETTINGS; QString path = cfg.value("Path/target",QDir::homePath()).toString(); QString filename = QFileDialog::getSaveFileName(this, tr("Select target database..."), path, "QMapShack Database (*.db)"); if(filename.isEmpty()) { return; } QFileInfo fi(filename); cfg.setValue("Path/target", fi.absolutePath()); if(fi.suffix().toLower() != "db") { filename += ".db"; } labelTarget->setText(filename); } void CImportDatabase::slotStart() { pushStart->setEnabled(false); dbQlgt->start(labelTarget->text()); pushStart->setEnabled(true); } qmapshack-1.5.1/src/tool/CMapVrtBuilder.cpp000644 001750 000144 00000006326 12622435717 021572 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMapVrtBuilder.h" #include "helpers/CSettings.h" #include CMapVrtBuilder::CMapVrtBuilder(QWidget *parent) : IToolShell(textBrowser, parent) { setupUi(this); setObjectName(tr("Build GDAL VRT")); connect(toolSourceFiles, SIGNAL(clicked()), this, SLOT(slotSelectSourceFiles())); connect(toolTargetFile, SIGNAL(clicked()), this, SLOT(slotSelectTargetFile())); connect(pushStart, SIGNAL(clicked()), this, SLOT(slotStart())); pushStart->setDisabled(true); } CMapVrtBuilder::~CMapVrtBuilder() { } void CMapVrtBuilder::slotSelectSourceFiles() { SETTINGS; QString path = cfg.value("VrtBuilder/sourcePath",QDir::homePath()).toString(); QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files..."), path); if(files.isEmpty()) { return; } QFileInfo fi(files.first()); path = fi.absolutePath(); cfg.setValue("VrtBuilder/sourcePath", path); listWidget->clear(); foreach(const QString &file, files) { new QListWidgetItem(QIcon("://icons/32x32/Map.png"), file, listWidget); } enableStartButton(); } void CMapVrtBuilder::slotSelectTargetFile() { SETTINGS; QString path = cfg.value("VrtBuilder/targetPath",QDir::homePath()).toString(); QString file = QFileDialog::getSaveFileName(this, tr("Select target file..."), path); if(file.isEmpty()) { return; } QFileInfo fi(file); path = fi.absolutePath(); cfg.setValue("VrtBuilder/targetPath", path); if(fi.suffix().toLower() != "vrt") { file += ".vrt"; } labelTargetFilename->setText(file); enableStartButton(); } void CMapVrtBuilder::enableStartButton() { pushStart->setEnabled(listWidget->count() > 0 && labelTargetFilename->text() != "-"); } void CMapVrtBuilder::slotStart() { pushStart->setDisabled(true); QStringList args; args << labelTargetFilename->text(); foreach(const QListWidgetItem * item, listWidget->findItems("*", Qt::MatchWildcard)) { args << item->text(); } stdOut("gdalbuildvrt " + args.join(" ") + "\n"); cmd.start("gdalbuildvrt", args); } void CMapVrtBuilder::finished(int exitCode, QProcess::ExitStatus status) { textBrowser->setTextColor(Qt::darkGreen); textBrowser->append(tr("!!! done !!!\n")); pushStart->setEnabled(true); return; } qmapshack-1.5.1/src/tool/IRoutinoDatabaseBuilder.ui000644 001750 000144 00000006214 12543505714 023277 0ustar00oeichlerusers000000 000000 IRoutinoDatabaseBuilder 0 0 988 712 Form ... :/icons/32x32/PathGreen.png:/icons/32x32/PathGreen.png 22 22 Select source files: Start ... :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png 22 22 0 0 Target Path: - File Prefix textBrowser pushStart listWidget qmapshack-1.5.1/src/tool/CRoutinoDatabaseBuilder.h000644 001750 000144 00000003215 12622435722 023100 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CROUTINODATABASEBUILDER_H #define CROUTINODATABASEBUILDER_H #include "tool/IToolShell.h" #include "ui_IRoutinoDatabaseBuilder.h" #include class CRoutinoDatabaseBuilder : public IToolShell, private Ui::IRoutinoDatabaseBuilder { Q_OBJECT public: CRoutinoDatabaseBuilder(QWidget * parent); virtual ~CRoutinoDatabaseBuilder(); private slots: void slotSelectSourceFiles(); void slotSelectTargetPath(); void slotStart(); void enabelStartButton(); private: void finished(int exitCode, QProcess::ExitStatus status); bool first = false; bool tainted = false; bool last = false; QStringList sourceFiles; QString targetPrefix; QString targetPath; }; #endif //CROUTINODATABASEBUILDER_H qmapshack-1.5.1/src/tool/CRoutinoDatabaseBuilder.cpp000644 001750 000144 00000011625 12622435717 023443 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CAppSetup.h" #include "helpers/CSettings.h" #include "tool/CRoutinoDatabaseBuilder.h" #include CRoutinoDatabaseBuilder::CRoutinoDatabaseBuilder(QWidget * parent) : IToolShell(textBrowser, parent) { setupUi(this); setObjectName(tr("Create Routino Database")); connect(toolSourceFiles, SIGNAL(clicked()), this, SLOT(slotSelectSourceFiles())); connect(toolTargetPath, SIGNAL(clicked()), this, SLOT(slotSelectTargetPath())); connect(pushStart, SIGNAL(clicked()), this, SLOT(slotStart())); connect(lineTargetPrefix, SIGNAL(textChanged(QString)), this, SLOT(enabelStartButton())); pushStart->setDisabled(true); SETTINGS; QString path = cfg.value("RoutinoDatabaseBuilder/targetPath",QDir::homePath()).toString(); labelTargetPath->setText(path); } CRoutinoDatabaseBuilder::~CRoutinoDatabaseBuilder() { } void CRoutinoDatabaseBuilder::slotSelectSourceFiles() { SETTINGS; QString path = cfg.value("RoutinoDatabaseBuilder/sourcePath",QDir::homePath()).toString(); QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files..."), path, "OSM Database (*.pbf)"); if(files.isEmpty()) { return; } QFileInfo fi(files.first()); path = fi.absolutePath(); cfg.setValue("RoutinoDatabaseBuilder/sourcePath", path); listWidget->clear(); foreach(const QString &file, files) { new QListWidgetItem(QIcon("://icons/32x32/Map.png"), file, listWidget); } enabelStartButton(); } void CRoutinoDatabaseBuilder::slotSelectTargetPath() { SETTINGS; QString path = cfg.value("RoutinoDatabaseBuilder/targetPath",QDir::homePath()).toString(); path = QFileDialog::getExistingDirectory(this, tr("Select target path..."), path); if(path.isEmpty()) { return; } cfg.setValue("RoutinoDatabaseBuilder/targetPath", path); labelTargetPath->setText(path); enabelStartButton(); } void CRoutinoDatabaseBuilder::enabelStartButton() { pushStart->setDisabled(true); if(listWidget->count() == 0) { return; } if(labelTargetPath->text() == "-") { return; } if(lineTargetPrefix->text().isEmpty()) { return; } pushStart->setEnabled(true); } void CRoutinoDatabaseBuilder::slotStart() { pushStart->setDisabled(true); sourceFiles.clear(); foreach(const QListWidgetItem * item, listWidget->findItems("*", Qt::MatchWildcard)) { sourceFiles << item->text(); } targetPrefix = lineTargetPrefix->text(); targetPath = labelTargetPath->text(); first = true; last = false; textBrowser->clear(); slotFinished(0,QProcess::NormalExit); } void CRoutinoDatabaseBuilder::finished(int exitCode, QProcess::ExitStatus status) { if(last) { textBrowser->setTextColor(Qt::darkGreen); textBrowser->append(tr("!!! done !!!\n")); pushStart->setEnabled(true); return; } CAppSetup* instance = CAppSetup::getPlattformInstance(); if(sourceFiles.isEmpty()) { QStringList args; args << QString("--dir=%1").arg(targetPath); args << QString("--prefix=%1").arg(targetPrefix); args << QString("--tagging=%1").arg(instance->routinoPath("tagging.xml")); args << "--process-only"; stdOut("planetsplitter " + args.join(" ") + "\n"); cmd.start("planetsplitter", args); last = true; } else { QStringList args; args << QString("--dir=%1").arg(targetPath); args << QString("--prefix=%1").arg(targetPrefix); args << QString("--tagging=%1").arg(instance->routinoPath("tagging.xml")); if(first) { first = false; args << "--parse-only"; } else { args << "--parse-only" << "--append"; } args << sourceFiles.first(); sourceFiles.pop_front(); stdOut("planetsplitter " + args.join(" ") + "\n"); cmd.start("planetsplitter", args); } } qmapshack-1.5.1/src/tool/CImportDatabase.h000644 001750 000144 00000002656 12622435722 021414 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CIMPORTDATABASE_H #define CIMPORTDATABASE_H #include "ui_IImportDatabase.h" #include #include class CQlgtDb; class CImportDatabase : public QWidget, private Ui::IImportDatabase { Q_OBJECT public: CImportDatabase(QWidget * parent); virtual ~CImportDatabase(); void stdOut(const QString& str); void stdErr(const QString& str); private slots: void slotSelectSource(); void slotSelectTarget(); void slotStart(); private: QPointer dbQlgt; }; #endif //CIMPORTDATABASE_H qmapshack-1.5.1/src/tool/IMapVrtBuilder.ui000644 001750 000144 00000005445 12543474466 021442 0ustar00oeichlerusers000000 000000 IMapVrtBuilder 0 0 980 616 Form ... :/icons/32x32/PathGreen.png:/icons/32x32/PathGreen.png 22 22 Select source files: ... :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png 22 22 0 0 Target Filename: - Start qmapshack-1.5.1/src/tool/IToolShell.h000644 001750 000144 00000003212 12622435722 020415 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef ITOOLSHELL_H #define ITOOLSHELL_H #include #include class QTextBrowser; class IToolShell : public QWidget { Q_OBJECT public: IToolShell(QTextBrowser *&textBrowser, QWidget *parent); virtual ~IToolShell(); protected slots: void slotStderr(); void slotStdout(); void slotError(QProcess::ProcessError error); virtual void slotFinished(int exitCode, QProcess::ExitStatus status); protected: virtual void finished(int exitCode, QProcess::ExitStatus status) = 0; void setOutputBrowser(QTextBrowser * textBrowser); void stdOut(const QString& str, bool gui = false); void stdErr(const QString& str, bool gui = false); QProcess cmd; QTextBrowser *& text; }; #endif //ITOOLSHELL_H qmapshack-1.5.1/src/tool/IImportDatabase.ui000644 001750 000144 00000005764 12543474406 021617 0ustar00oeichlerusers000000 000000 IImportDatabase 0 0 400 300 Form ... :/icons/32x32/PathGreen.png:/icons/32x32/PathGreen.png 22 22 0 0 Source Database: - ... :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png 22 22 0 0 Target Database: - Start qmapshack-1.5.1/src/tool/IToolShell.cpp000644 001750 000144 00000010565 12622435717 020765 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "IToolShell.h" #include IToolShell::IToolShell(QTextBrowser *&textBrowser, QWidget * parent) : QWidget(parent) , text(textBrowser) { connect(&cmd, SIGNAL(readyReadStandardError()), this, SLOT(slotStderr())); connect(&cmd, SIGNAL(readyReadStandardOutput()), this, SLOT(slotStdout())); connect(&cmd, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotFinished(int, QProcess::ExitStatus))); connect(&cmd, SIGNAL(error(QProcess::ProcessError)), this, SLOT(slotError(QProcess::ProcessError))); } IToolShell::~IToolShell() { } void IToolShell::setOutputBrowser(QTextBrowser * text) { } void IToolShell::slotError(QProcess::ProcessError error) { text->setTextColor(Qt::red); text->insertPlainText(QString(tr("Execution of external program `%1` failed: ")).arg(cmd.program())); switch(error) { case QProcess::FailedToStart: text->insertPlainText(QString(tr("Process cannot be started.\n"))); text->insertPlainText(QString(tr("Make sure the required packages are installed, `%1` exists and is executable.\n")).arg(cmd.program())); break; case QProcess::Crashed: text->insertPlainText(QString(tr("External process crashed.\n"))); break; default: text->insertPlainText(QString(tr("An unknown error occurred.\n"))); break; } } void IToolShell::slotStderr() { QString str; text->setTextColor(Qt::red); str = cmd.readAllStandardError(); if(str[0] == '\r') { #ifdef WIN32 if(str.contains("\n")) { text->insertPlainText("\n"); } else #endif // WIN32 { text->moveCursor( QTextCursor::End, QTextCursor::MoveAnchor ); text->moveCursor( QTextCursor::StartOfLine, QTextCursor::MoveAnchor ); text->moveCursor( QTextCursor::End, QTextCursor::KeepAnchor ); text->textCursor().removeSelectedText(); } #ifdef WIN32 str = str.split("\r").last().remove("\r").remove("\n"); #else str = str.split("\r").last(); #endif } text->insertPlainText(str); text->verticalScrollBar()->setValue(text->verticalScrollBar()->maximum()); } void IToolShell::slotStdout() { QString str; text->setTextColor(Qt::blue); str = cmd.readAllStandardOutput(); if(str[0] == '\r') { #ifdef WIN32 if(str.contains("\n")) { text->insertPlainText("\n"); } else #endif // WIN32 { text->moveCursor( QTextCursor::End, QTextCursor::MoveAnchor ); text->moveCursor( QTextCursor::StartOfLine, QTextCursor::MoveAnchor ); text->moveCursor( QTextCursor::End, QTextCursor::KeepAnchor ); text->textCursor().removeSelectedText(); } #ifdef WIN32 str = str.split("\r").last().remove("\r").remove("\n"); #else str = str.split("\r").last(); #endif } text->insertPlainText(str); text->verticalScrollBar()->setValue(text->verticalScrollBar()->maximum()); } void IToolShell::stdOut(const QString& str, bool gui) { text->setTextColor(Qt::black); text->append(str); } void IToolShell::stdErr(const QString& str, bool gui) { text->setTextColor(Qt::red); text->append(str); } void IToolShell::slotFinished(int exitCode, QProcess::ExitStatus status) { if(exitCode || status) { text->setTextColor(Qt::red); text->append(tr("!!! failed !!!\n")); return; } finished(exitCode, status); } qmapshack-1.5.1/src/tool/CMapVrtBuilder.h000644 001750 000144 00000002635 12622435722 021232 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPVRTBUILDER_H #define CMAPVRTBUILDER_H #include "tool/IToolShell.h" #include "ui_IMapVrtBuilder.h" #include class CMapVrtBuilder : public IToolShell, private Ui::IMapVrtBuilder { Q_OBJECT public: CMapVrtBuilder(QWidget * parent); virtual ~CMapVrtBuilder(); private slots: void slotSelectSourceFiles(); void slotSelectTargetFile(); void slotStart(); private: void finished(int exitCode, QProcess::ExitStatus status); void enableStartButton(); }; #endif //CMAPVRTBUILDER_H qmapshack-1.5.1/src/qlgt/CQlgtRoute.h000644 001750 000144 00000003265 12622435722 020432 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLGTROUTE_H #define CQLGTROUTE_H #include "qlgt/IItem.h" #include #include class CQlgtRoute : public QObject, public IItem { public: CQlgtRoute(quint64 id, QObject * parent); virtual ~CQlgtRoute(); enum type_e {eEnd, eBase, eRtePts, eRteSec}; struct pt_t { float lon; float lat; QString action; operator const projXY () { projXY p; p.u = lon; p.v = lat; return p; } }; /// primary route, just the basic points like A to B via C QVector priRoute; quint32 ttime; QString iconString; }; QDataStream& operator >>(QDataStream& s, CQlgtRoute& rte); QDataStream& operator <<(QDataStream& s, CQlgtRoute& rte); #endif //CQLGTROUTE_H qmapshack-1.5.1/src/qlgt/CQlgtFolder.cpp000644 001750 000144 00000004264 12622435717 021106 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/db/macros.h" #include "qlgt/CQlgtDb.h" #include "qlgt/CQlgtDiary.h" #include "qlgt/CQlgtFolder.h" #include CQlgtFolder::CQlgtFolder(quint64 id, QSqlDatabase &db) : type(0) , locked(false) , diary(0) , id(id) { QSqlQuery query(db); query.prepare("SELECT type, name, comment, locked FROM folders WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); if(query.next()) { type = query.value(0).toInt(); name = query.value(1).toString(); comment = query.value(2).toString(); locked = query.value(3).toBool(); } query.prepare("SELECT id, data FROM diarys WHERE parent=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); if(query.next()) { quint64 idDiary = query.value(0).toULongLong(); QByteArray data = query.value(1).toByteArray(); QDataStream stream(&data, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_4_5); diary = new CQlgtDiary(idDiary, this); stream >> *diary; } query.prepare("SELECT child FROM folder2item WHERE parent=:folder"); query.bindValue(":folder", id); QUERY_EXEC(return ); while(query.next()) { items << query.value(0).toULongLong(); } } CQlgtFolder::~CQlgtFolder() { } qmapshack-1.5.1/src/qlgt/CQlgtTrack.h000644 001750 000144 00000020426 12622435722 020376 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLGTTRACK_H #define CQLGTTRACK_H #include "qlgt/IItem.h" #include class CQlgtWpt; class CFlags { public: CFlags(quint32 f=0) { flags = f; changed = true; } virtual ~CFlags() { } const quint32 flag() const { return flags; } void setFlags( quint32 f ) { if ( flags != f ) { changed = true; } flags = f; } quint32 operator & (quint32 f) const { return flags&f; } quint32 operator |= (quint32 f) { if ( flags != (flags|f) ) { changed = true; } flags|=f; return flags; } quint32 operator &= (quint32 f) { if ( flags != (flags&f) ) { changed = true; } flags&=f; return flags; } quint32 operator >> (quint32 & f) { if ( flags != f ) { changed = true; } flags = f; return flags; } const bool isChanged() const { return changed; } void setChanged(bool b) { changed = b; } protected: /// display flags quint32 flags; bool changed; }; QDataStream& operator >>(QDataStream& s, CFlags& flag); QDataStream& operator <<(QDataStream& s, CFlags& flag); class CQlgtTrack : public QObject, public IItem { public: CQlgtTrack(quint64 id, QObject * parent); virtual ~CQlgtTrack(); enum type_e {eEnd,eBase,eTrkPts,eTrain,eTrkExt1,eTrkGpxExt,eTrkShdw, eTrkShdw2, eTrkPts2}; struct pt_t { enum flag_e { eSelected = 1 ///< selected by track info view ,eCursor = 2 ///< selected by cursor ,eDeleted = 4 ///< mark point as deleted ,eFocus = 8 ///< mark current point of user focus }; pt_t() : idx(-1), lon(WPT_NOFLOAT), lat(WPT_NOFLOAT), ele(WPT_NOFLOAT), timestamp(0), timestamp_msec(0), speed(WPT_NOFLOAT), avgspeed(0), delta(WPT_NOFLOAT), azimuth(WPT_NOFLOAT), distance(WPT_NOFLOAT), ascend(0), descend(0), heartReateBpm(-1), cadenceRpm(-1), slope(0), slope2(WPT_NOFLOAT), timeSinceStart(0), fix(""), sat(0), velocity(WPT_NOFLOAT), heading(WPT_NOFLOAT), vdop(WPT_NOFLOAT), hdop(WPT_NOFLOAT), pdop(WPT_NOFLOAT), _lon(WPT_NOFLOAT),_lat(WPT_NOFLOAT),_ele(WPT_NOFLOAT), _timestamp(0), _timestamp_msec(0), flags(0), px_valid(false), dem(WPT_NOFLOAT), editItem(NULL) { } bool operator==(const pt_t& pt) const { return pt.idx == idx; } /// index counter for easy QVector access qint32 idx; /// longitude [deg] float lon; /// latitude [deg] float lat; /// elevation [m] float ele; /// timestamp for track creation quint32 timestamp; quint32 timestamp_msec; /// secondary data: the speed between this and the previous point float speed; /// secondary data: the short term average speed float avgspeed; /// secondary data: the distance between this and the previous point float delta; /// secondary data: the azimuth to the next point double azimuth; /// secondary data: the total distance of all visible points up to this point float distance; /// secondary data: the total ascend of all visible points up to this point float ascend; /// secondary data: the total descend of all visible points up to this point float descend; /// secondary data: the heart rate in bpm int heartReateBpm; /// secondary data: cadence in rpm int cadenceRpm; /// secondary data: slope in % float slope; /// secondary data: slope in % float slope2; quint32 timeSinceStart; // extended data 1 QString fix; qint32 sat; float altitude; ///< [m] Altitude, Meters, above mean sea level float height; ///< [m] Height of geoid (mean sea level) above WGS84 ellipsoid float velocity; ///< [m/s] Ground speed, meters per hour float heading; ///< [] Track angle in degrees True float magnetic; ///< [] Magnetic Variation float vdop; ///< Vertical dilution of precision (VDOP) float hdop; ///< Horizontal dilution of precision (HDOP) float pdop; ///< PDOP (dilution of precision) float x; ///< [m] cartesian gps coordinate float y; ///< [m] cartesian gps coordinate float z; ///< [m] cartesian gps coordinate float vx; ///< [m/s] velocity float vy; ///< [m/s] velocity float vz; ///< [m/s] velocity #ifdef GPX_EXTENSIONS CGpxExtPt gpx_exts; #endif // track shadow data (copy of original data) /// longitude [deg] float _lon; /// latitude [deg] float _lat; /// elevation [m] float _ele; quint32 _timestamp; quint32 _timestamp_msec; /// display flags CFlags flags; /// the current location in pixel QPoint px; bool px_valid; float dem; /// QTreeWidgetItem QPointer editItem; QColor color; }; struct wpt_t { wpt_t() : wpt(0), d(1e25f), x(0), y(0) { } CQlgtWpt * wpt; double d; double x; double y; pt_t trkpt; }; CQlgtTrack& operator<<(const pt_t& pt); /// a track URL QString url; /// the track line color QColor color; QPixmap bullet; /// the track line color by index unsigned colorIdx; /// the track points QList track; /// set true to draw track highlighted bool highlight; /// total time covered by all track points double totalTime; /// total time moving double totalTimeMoving; /// total distance of track [m] double totalDistance; /// total ascend in [m] double totalAscend; /// total descend in [m] double totalDescend; /// the Qt polyline for faster processing QPolygon polyline; /// the color attached to each point in polyline (only used in multicolor mode) QVector polylineColor; float avgspeed0; float avgspeed1; pt_t ptMaxEle; pt_t ptMinEle; pt_t ptMaxSpeed; pt_t ptMinSpeed; bool traineeData; bool ext1Data; bool firstTime; bool m_hide; quint32 doScaleWpt2Track; quint32 visiblePointCount; quint32 cntMedianFilterApplied; QList waypoints; bool replaceOrigData; enum state_select_e {eNoSel, e1stSel, e2ndSel}; state_select_e stateSelect; QString timezone; enum multi_color_item_e { eMultiColorNone , eMultiColorSlope , eMultiColorEle , eMultiColorSpeed , eMultiColorMax }; quint32 useMultiColor; qint32 idMultiColor; bool hasExt1Data() const { return ext1Data; } void setExt1Data() { ext1Data = true; } bool hasShadow1; bool hasShadow2; }; QDataStream& operator >>(QDataStream& s, CQlgtTrack& trk); QDataStream& operator <<(QDataStream& s, CQlgtTrack& trk); #endif //CQLGTTRACK_H qmapshack-1.5.1/src/qlgt/CQlgtWpt.h000644 001750 000144 00000005661 12622435722 020110 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLGTWPT_H #define CQLGTWPT_H #include "qlgt/IItem.h" class CQlgtWpt : public QObject, public IItem { public: CQlgtWpt(quint64 id, QObject * parent); virtual ~CQlgtWpt(); void setIcon(const QString& str); enum geocacheservice_e {eGC, eOC, eTC}; enum type_e {eEnd,eBase,eImage,eGeoCache}; struct geocachelog_t { geocachelog_t() : id(0) { } quint32 id; QString date; QString type; QString finderId; QString finder; QString text; }; struct geocache_t { geocache_t() : service(eOC), hasData(false), id(0), available(true), archived(false), difficulty(0), terrain(0), exportBuddies(false) { } geocacheservice_e service; bool hasData; quint32 id; bool available; bool archived; float difficulty; float terrain; QString status; QString name; QString owner; QString ownerId; QString type; QString container; QString shortDesc; QString longDesc; QString hint; QString country; QString state; QString locale; QList logs; bool exportBuddies; }; geocache_t geocache; quint32 selected; quint32 sticky; float lat; ///< [deg] float lon; ///< [deg] float ele; ///< [m] float prx; ///< [m] float dir; ///< [deg] QString link; QString urlname; QString type; struct image_t { quint32 offset; QString info; QPixmap pixmap; QString filePath; QString fileName; }; QList images; struct buddy_t { QString name; QSet pos; float lon; float lat; }; QList buddies; }; QDataStream& operator >>(QDataStream& s, CQlgtWpt& wpt); QDataStream& operator <<(QDataStream& s, CQlgtWpt& wpt); #endif //CQLGTWPT_H qmapshack-1.5.1/src/qlgt/CQlgtFolder.h000644 001750 000144 00000002444 12622435722 020545 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLGTFOLDER_H #define CQLGTFOLDER_H #include #include class QSqlDatabase; class CQlgtDiary; class CQlgtFolder : public QObject { public: CQlgtFolder(quint64 id, QSqlDatabase& db); virtual ~CQlgtFolder(); qint32 type; QString name; QString comment; bool locked; CQlgtDiary * diary; quint64 id; QSet items; }; #endif //CQLGTFOLDER_H qmapshack-1.5.1/src/qlgt/converter.cpp000644 001750 000144 00000021004 12624063017 020726 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/WptIcons.h" #include "gis/db/CDBProject.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "qlgt/CQlgtDiary.h" #include "qlgt/CQlgtFolder.h" #include "qlgt/CQlgtRoute.h" #include "qlgt/CQlgtTrack.h" #include "qlgt/CQlgtWpt.h" #include "qlgt/IQlgtOverlay.h" #include "units/IUnit.h" inline qreal readFloat(float val) { return val > NOFLOAT ? NOFLOAT : val; } CDBProject::CDBProject(CQlgtFolder& folder) : IGisProject(eTypeDb, "", (CGisListWks*)0) { metadata.name = folder.name; if(folder.diary) { metadata.desc = folder.diary->comment; } } CGisItemWpt::CGisItemWpt(const CQlgtWpt& wpt1) : IGisItem(0, eTypeWpt, NOIDX) { qreal direction; QDateTime time = QDateTime::fromTime_t(wpt1.timestamp,QTimeZone("UTC")); wpt.time = time.toUTC(); wpt.name = wpt1.name; wpt.cmt = wpt1.comment; wpt.desc = wpt1.description; wpt.sym = wpt1.iconString; wpt.lat = readFloat(wpt1.lat); wpt.lon = readFloat(wpt1.lon); wpt.ele = wpt1.ele == WPT_NOFLOAT ? NOINT : qRound(wpt1.ele); proximity = readFloat(wpt1.prx); direction = readFloat(wpt1.dir); if(!wpt1.link.isEmpty()) { IGisItem::link_t link; link.uri = wpt1.link; wpt.links << link; } if(!wpt1.urlname.isEmpty()) { IGisItem::link_t link; link.uri = wpt1.urlname; wpt.links << link; } wpt.type = wpt1.type; if(wpt1.geocache.hasData) { geocache.service = CGisItemWpt::geocacheservice_e(wpt1.geocache.service); geocache.hasData = wpt1.geocache.hasData; geocache.id = wpt1.geocache.id; geocache.available = wpt1.geocache.available; geocache.archived = wpt1.geocache.archived; geocache.difficulty = wpt1.geocache.difficulty; geocache.terrain = wpt1.geocache.terrain; geocache.status = wpt1.geocache.status; geocache.name = wpt1.geocache.name; geocache.owner = wpt1.geocache.owner; geocache.ownerId = wpt1.geocache.ownerId; geocache.type = wpt1.geocache.type; geocache.container = wpt1.geocache.container; geocache.shortDescIsHtml = wpt1.geocache.shortDesc.contains("> stream; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(event.data); event.hash = md5.result().toHex(); history.histIdxCurrent = history.events.size() - 1; } } CGisItemTrk::CGisItemTrk(const IQlgtOverlay& ovl) : IGisItem(0, eTypeTrk, NOIDX) , activities(this) { trk.name = ovl.name; trk.cmt = ovl.comment; trk.desc = ovl.description; trk.color = ovl.color.name(); trkseg_t seg; foreach(const IQlgtOverlay::pt_t& pt1, ovl.points) { trkpt_t pt; pt.lon = pt1.u * RAD_TO_DEG; pt.lat = pt1.v * RAD_TO_DEG; seg.pts << pt; } trk.segs << seg; setColor(str2color(trk.color)); deriveSecondaryData(); filterReplaceElevation(); if(ovl.speed != 0) { filterSpeed(ovl.speed); } genKey(); setupHistory(); } CGisItemOvlArea::CGisItemOvlArea(const IQlgtOverlay& ovl) : IGisItem(0, eTypeOvl, NOIDX) { area.name = ovl.name; area.cmt = ovl.comment; area.desc = ovl.description; area.color = ovl.color.name(); area.width = ovl.width; area.style = ovl.style; area.opacity = ovl.opacity != 255; foreach(const IQlgtOverlay::pt_t& pt1, ovl.points) { pt_t pt; pt.lon = pt1.u * RAD_TO_DEG; pt.lat = pt1.v * RAD_TO_DEG; area.pts << pt; } setColor(str2color(area.color)); deriveSecondaryData(); genKey(); setupHistory(); } CGisItemRte::CGisItemRte(const CQlgtRoute& rte1) : IGisItem(0, eTypeRte, NOIDX) { rte.name = rte1.name; rte.cmt = rte1.comment; rte.desc = rte1.description; QPointF focus; QPixmap icon = getWptIconByName(rte1.iconString, focus); foreach (const CQlgtRoute::pt_t& pt1, rte1.priRoute) { rtept_t pt; pt.lon = pt1.lon; pt.lat = pt1.lat; pt.icon = icon; pt.focus = focus; rte.pts << pt; } deriveSecondaryData(); setSymbol(); genKey(); setupHistory(); } qmapshack-1.5.1/src/qlgt/CQlb.cpp000644 001750 000144 00000006652 12622435717 017564 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2007 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CQlb.h" #include "qlgt/CQlgtDiary.h" #include "qlgt/CQlgtRoute.h" #include "qlgt/CQlgtTrack.h" #include "qlgt/CQlgtWpt.h" #include "qlgt/IQlgtOverlay.h" #include CQlb::CQlb(QObject * parent) : QObject(parent) { } CQlb::~CQlb() { } CQlb& CQlb::operator <<(CQlgtWpt& wpt) { QDataStream stream(&wpts, QIODevice::Append); stream.setVersion(QDataStream::Qt_4_5); stream << wpt; return *this; } CQlb& CQlb::operator <<(CQlgtTrack& trk) { QDataStream stream(&trks, QIODevice::Append); stream.setVersion(QDataStream::Qt_4_5); stream << trk; return *this; } CQlb& CQlb::operator <<(CQlgtRoute& rte) { QDataStream stream(&rtes, QIODevice::Append); stream.setVersion(QDataStream::Qt_4_5); stream << rte; return *this; } CQlb& CQlb::operator <<(CQlgtDiary& dry) { QDataStream stream(&drys, QIODevice::Append); stream.setVersion(QDataStream::Qt_4_5); stream << dry; return *this; } CQlb& CQlb::operator <<(IQlgtOverlay& ovl) { QDataStream stream(&ovls, QIODevice::Append); stream.setVersion(QDataStream::Qt_4_5); stream << ovl; return *this; } void CQlb::load(const QString& filename) { QFile file(filename); load(&file); } void CQlb::load(QIODevice* ioDevice) { qint32 type; ioDevice->open(QIODevice::ReadOnly); QDataStream stream(ioDevice); stream.setVersion(QDataStream::Qt_4_5); stream >> type; while(type != eEnd) { switch(type) { case eWpt: stream >> wpts; break; case eTrack: stream >> trks; break; case eDiary: stream >> drys; break; case eOverlay: stream >> ovls; break; case eRoute: stream >> rtes; break; case eMapSel: stream >> sels; break; default: ioDevice->close(); return; } stream >> type; } ioDevice->close(); } void CQlb::save(const QString& filename) { QFile file(filename); save(&file); } void CQlb::save(QIODevice* ioDevice) { ioDevice->open(QIODevice::WriteOnly); QDataStream stream(ioDevice); stream.setVersion(QDataStream::Qt_4_5); stream << (qint32)eWpt << wpts; stream << (qint32)eTrack << trks; stream << (qint32)eRoute << rtes; stream << (qint32)eDiary << drys; stream << (qint32)eOverlay << ovls; stream << (qint32)eMapSel << sels; stream << (qint32)eEnd; ioDevice->close(); } qmapshack-1.5.1/src/qlgt/CQlgtDiary.h000644 001750 000144 00000002512 12622435722 020376 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLGTDIARY_H #define CQLGTDIARY_H #include "qlgt/IItem.h" #include class CQlgtDiary : public QObject, public IItem { public: CQlgtDiary(quint64 id, QObject * parent); virtual ~CQlgtDiary(); enum type_e {eEnd,eBase, eWpt, eTrk, eRte}; quint64 keyProjectGeoDB; }; QDataStream& operator >>(QDataStream& s, CQlgtDiary& diary); QDataStream& operator <<(QDataStream& s, CQlgtDiary& diary); #endif //CQLGTDIARY_H qmapshack-1.5.1/src/qlgt/IItem.h000644 001750 000144 00000002505 12622435722 017404 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IITEM_H #define IITEM_H #include #include #define WPT_NOFLOAT 1e25f class IItem { public: IItem(quint64 id); virtual ~IItem(); QString getInfo() { return "no info"; } quint32 timestamp; QString name; QString comment; QString description; QPixmap iconPixmap; QString iconString; QString parentWpt; QString key; quint64 id; }; #endif //IITEM_H qmapshack-1.5.1/src/qlgt/CQlgtDb.h000644 001750 000144 00000004424 12622435722 017657 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLGTDB_H #define CQLGTDB_H #include #include #include #include #include class CImportDatabase; class CQmsDb; class CQlgtDb : public QObject { public: enum EntryType_e { eWpt = QTreeWidgetItem::UserType + 3, eTrk = QTreeWidgetItem::UserType + 4, eRte = QTreeWidgetItem::UserType + 5, eOvl = QTreeWidgetItem::UserType + 6, eMap = QTreeWidgetItem::UserType + 7, eDry = QTreeWidgetItem::UserType + 8, eFolder0 = QTreeWidgetItem::UserType + 100, eFolderT = QTreeWidgetItem::UserType + 101, eFolder1 = QTreeWidgetItem::UserType + 102, eFolder2 = QTreeWidgetItem::UserType + 103, eFolderN = QTreeWidgetItem::UserType + 104 }; CQlgtDb(const QString& filename, CImportDatabase * parent); virtual ~CQlgtDb(); void start(const QString& filename); private: void initDB(); void migrateDB(int version); void printStatistic(); void xferFolders(); void xferItems(); void xferItem(quint64 id); QSqlDatabase db; QDir path; QString name; CImportDatabase * gui; QPointer dbQms; quint32 nItems; quint32 nFolders; quint32 nWpt; quint32 nTrk; quint32 nRte; quint32 nOvl; quint32 nDiary; }; #endif //CQLGTDB_H qmapshack-1.5.1/src/qlgt/CQlgtWpt.cpp000644 001750 000144 00000022530 12624063116 020431 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CQlgtWpt.h" #include "gis/WptIcons.h" struct wpt_head_entry_t { wpt_head_entry_t() : type(CQlgtWpt::eEnd), offset(0) { } qint32 type; quint32 offset; QByteArray data; }; QDataStream& operator >>(QDataStream& s, CQlgtWpt& wpt) { QIODevice * dev = s.device(); qint64 pos = dev->pos(); char magic[9]; s.readRawData(magic,9); if(strncmp(magic,"QLWpt ",9)) { dev->seek(pos); // throw(QObject::tr("This is not waypoint data.")); return s; } QList entries; while(1) { wpt_head_entry_t entry; s >> entry.type >> entry.offset; entries << entry; if(entry.type == CQlgtWpt::eEnd) { break; } } QList::iterator entry = entries.begin(); while(entry != entries.end()) { qint64 o = pos + entry->offset; dev->seek(o); s >> entry->data; switch(entry->type) { case CQlgtWpt::eBase: { QString icon; QString key; QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); s1 >> wpt.key; s1 >> wpt.sticky; s1 >> wpt.timestamp; s1 >> icon; s1 >> wpt.name; s1 >> wpt.comment; s1 >> wpt.lat; s1 >> wpt.lon; s1 >> wpt.ele; s1 >> wpt.prx; s1 >> wpt.link; s1 >> wpt.description; s1 >> wpt.urlname; s1 >> wpt.type; s1 >> wpt.parentWpt; s1 >> wpt.selected; if(!s1.atEnd()) { s1 >> wpt.dir; } else { wpt.dir = WPT_NOFLOAT; } wpt.setIcon(icon); break; } case CQlgtWpt::eImage: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); CQlgtWpt::image_t img; wpt.images.clear(); s1 >> img.offset; while(img.offset) { wpt.images << img; s1 >> img.offset; } QList::iterator image = wpt.images.begin(); while(image != wpt.images.end()) { s1.device()->seek(image->offset); s1 >> image->filePath; s1 >> image->info; s1 >> image->pixmap; ++image; } break; } case CQlgtWpt::eGeoCache: { quint32 N, n; QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); wpt.geocache = CQlgtWpt::geocache_t(); CQlgtWpt::geocache_t& cache = wpt.geocache; s1 >> (quint8&)cache.service; s1 >> cache.hasData; s1 >> cache.id; s1 >> cache.available; s1 >> cache.archived; s1 >> cache.difficulty; s1 >> cache.terrain; s1 >> cache.status; s1 >> cache.name; s1 >> cache.owner; s1 >> cache.ownerId; s1 >> cache.type; s1 >> cache.container; s1 >> cache.shortDesc; s1 >> cache.longDesc; s1 >> cache.hint; s1 >> cache.country; s1 >> cache.state; s1 >> cache.locale; s1 >> N; for(n = 0; n < N; n++) { CQlgtWpt::geocachelog_t log; s1 >> log.id; s1 >> log.date; s1 >> log.type; s1 >> log.finderId; s1 >> log.finder; s1 >> log.text; cache.logs << log; } s1 >> cache.exportBuddies; cache.hasData = true; break; } default: ; } ++entry; } return s; } QDataStream& operator <<(QDataStream& s, CQlgtWpt& wpt) { QList entries; //--------------------------------------- // prepare base data //--------------------------------------- wpt_head_entry_t entryBase; entryBase.type = CQlgtWpt::eBase; QDataStream s1(&entryBase.data, QIODevice::WriteOnly); s1.setVersion(QDataStream::Qt_4_5); s1 << wpt.key; s1 << wpt.sticky; s1 << wpt.timestamp; s1 << wpt.iconString; s1 << wpt.name; s1 << wpt.comment; s1 << wpt.lat; s1 << wpt.lon; s1 << wpt.ele; s1 << wpt.prx; s1 << wpt.link; s1 << wpt.description; s1 << wpt.urlname; s1 << wpt.type; s1 << QString(""); s1 << wpt.selected; s1 << wpt.dir; entries << entryBase; //--------------------------------------- // prepare image data //--------------------------------------- wpt_head_entry_t entryImage; entryImage.type = CQlgtWpt::eImage; QDataStream s2(&entryImage.data, QIODevice::WriteOnly); s2.setVersion(QDataStream::Qt_4_5); // write place holder for image offset QList::iterator image = wpt.images.begin(); while(image != wpt.images.end()) { s2 << (quint32)0; ++image; } // offset terminator s2 << (quint32)0; // write image data and store the actual offset image = wpt.images.begin(); while(image != wpt.images.end()) { image->offset = (quint32)s2.device()->pos(); s2 << image->filePath; s2 << image->info; s2 << image->pixmap; ++image; } // finally write image offset table s2.device()->seek(0); image = wpt.images.begin(); while(image != wpt.images.end()) { s2 << image->offset; ++image; } entries << entryImage; //--------------------------------------- // prepare geocache data //--------------------------------------- if(wpt.geocache.hasData) { wpt_head_entry_t entryGeoCache; entryGeoCache.type = CQlgtWpt::eGeoCache; QDataStream s3(&entryGeoCache.data, QIODevice::WriteOnly); s3.setVersion(QDataStream::Qt_4_5); CQlgtWpt::geocache_t& cache = wpt.geocache; s3 << (quint8)cache.service; s3 << cache.hasData; s3 << cache.id; s3 << cache.available; s3 << cache.archived; s3 << cache.difficulty; s3 << cache.terrain; s3 << cache.status; s3 << cache.name; s3 << cache.owner; s3 << cache.ownerId; s3 << cache.type; s3 << cache.container; s3 << cache.shortDesc; s3 << cache.longDesc; s3 << cache.hint; s3 << cache.country; s3 << cache.state; s3 << cache.locale; s3 << cache.logs.count(); foreach(const CQlgtWpt::geocachelog_t& log, cache.logs) { s3 << log.id; s3 << log.date; s3 << log.type; s3 << log.finderId; s3 << log.finder; s3 << log.text; } s3 << cache.exportBuddies; entries << entryGeoCache; } //--------------------------------------- // prepare terminator //--------------------------------------- wpt_head_entry_t entryEnd; entryEnd.type = CQlgtWpt::eEnd; entries << entryEnd; //--------------------------------------- //--------------------------------------- // now start to actually write data; //--------------------------------------- //--------------------------------------- // write magic key s.writeRawData("QLWpt ",9); // calculate offset table quint32 offset = entries.count() * 8 + 9; QList::iterator entry = entries.begin(); while(entry != entries.end()) { entry->offset = offset; offset += entry->data.size() + sizeof(quint32); ++entry; } // write offset table entry = entries.begin(); while(entry != entries.end()) { s << entry->type << entry->offset; ++entry; } // write entry data entry = entries.begin(); while(entry != entries.end()) { s << entry->data; ++entry; } return s; } CQlgtWpt::CQlgtWpt(quint64 id, QObject *parent) : QObject(parent) , IItem(id) { } CQlgtWpt::~CQlgtWpt() { } void CQlgtWpt::setIcon(const QString& str) { QPointF focus; iconString = str; iconPixmap = getWptIconByName(str, focus); } qmapshack-1.5.1/src/qlgt/IQlgtOverlay.h000644 001750 000144 00000003004 12622435722 020752 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IQLGTOVERLAY_H #define IQLGTOVERLAY_H #include "qlgt/IItem.h" #include #include class IQlgtOverlay : public QObject, public IItem { public: IQlgtOverlay(quint64 id, QObject * parent); virtual ~IQlgtOverlay(); enum type_e {eEnd,eBase}; struct pt_t : public projXY { int idx; }; QString type; QColor color; QList points; qint32 style; quint32 width; quint8 opacity; float speed; }; QDataStream& operator >>(QDataStream& s, IQlgtOverlay& ovl); QDataStream& operator <<(QDataStream& s, IQlgtOverlay& ovl); #endif //IQLGTOVERLAY_H qmapshack-1.5.1/src/qlgt/IItem.cpp000644 001750 000144 00000001737 12622435717 017751 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "IItem.h" IItem::IItem(quint64 id) : id(id) { } IItem::~IItem() { } qmapshack-1.5.1/src/qlgt/CQlgtDb.cpp000644 001750 000144 00000053272 12622435717 020223 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Softwareation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "qlgt/CQlb.h" #include "qlgt/CQlgtDb.h" #include "qlgt/CQlgtFolder.h" #include "qlgt/CQlgtRoute.h" #include "qlgt/CQlgtTrack.h" #include "qlgt/CQlgtWpt.h" #include "qlgt/CQmsDb.h" #include "qlgt/IQlgtOverlay.h" #include "tool/CImportDatabase.h" #include "gis/WptIcons.h" #include "gis/db/macros.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CProgressDialog.h" #include "CMainWindow.h" #include #include #define DB_QLGT_VERSION 9 CQlgtDb::CQlgtDb(const QString &filename, CImportDatabase *parent) : gui(parent) , nItems(0) , nFolders(0) , nWpt(0) , nTrk(0) , nRte(0) , nOvl(0) , nDiary(0) { db = QSqlDatabase::addDatabase("QSQLITE","qlandkarte"); db.setDatabaseName(filename); db.open(); QFileInfo fi(filename); path = fi.absoluteDir(); name = fi.fileName(); QSqlQuery query(db); if(!query.exec("PRAGMA locking_mode=EXCLUSIVE")) { return; } if(!query.exec("PRAGMA synchronous=OFF")) { return; } if(!query.exec("PRAGMA temp_store=MEMORY")) { return; } if(!query.exec("PRAGMA default_cache_size=1000")) { return; } if(!query.exec("PRAGMA page_size=8192")) { return; } if(!query.exec("SELECT version FROM versioninfo")) { initDB(); } else if(query.next()) { int version = query.value(0).toInt(); if(version != DB_QLGT_VERSION) { migrateDB(version); } } else { initDB(); } printStatistic(); } CQlgtDb::~CQlgtDb() { } void CQlgtDb::initDB() { qDebug() << "void CGeoDB::initDB()"; QSqlQuery query(db); if(query.exec( "CREATE TABLE versioninfo ( version TEXT )")) { query.prepare( "INSERT INTO versioninfo (version) VALUES(:version)"); query.bindValue(":version", DB_QLGT_VERSION); QUERY_EXEC(); } if(!query.exec( "CREATE TABLE folders (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER," "date DATETIME DEFAULT CURRENT_TIMESTAMP," "icon TEXT NOT NULL," "name TEXT NOT NULL," "comment TEXT," "locked BOOLEAN DEFAULT FALSE" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } if(!query.exec( "CREATE TABLE items (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER," "key TEXT NOT NULL," "date DATETIME DEFAULT CURRENT_TIMESTAMP," "icon TEXT NOT NULL," "name TEXT NOT NULL," "comment TEXT," "data BLOB NOT NULL" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } if(!query.exec( "CREATE TABLE workspace (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER NOT NULL," "changed BOOLEAN DEFAULT FALSE," "data BLOB NOT NULL," "key TEXT NOT NULL" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } if(!query.exec("INSERT INTO folders (icon, name, comment) VALUES ('', 'database', '')")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } if(!query.exec( "CREATE TABLE folder2folder (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "parent INTEGER NOT NULL," "child INTEGER NOT NULL," "FOREIGN KEY(parent) REFERENCES folders(id)," "FOREIGN KEY(child) REFERENCES folders(id)" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } if(!query.exec( "CREATE TABLE folder2item (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "parent INTEGER NOT NULL," "child INTEGER NOT NULL," "FOREIGN KEY(parent) REFERENCES folders(id)," "FOREIGN KEY(child) REFERENCES items(id)" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } if(!query.exec( "CREATE TABLE diarys (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "parent INTEGER NOT NULL," "key TEXT NOT NULL," "date DATETIME DEFAULT CURRENT_TIMESTAMP," "data BLOB NOT NULL," "FOREIGN KEY(parent) REFERENCES folders(id)" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } } void CQlgtDb::migrateDB(int version) { qDebug() << "void CGeoDB::migrateDB(int version)" << version; QSqlQuery query(db); for(version++; version <= DB_QLGT_VERSION; version++) { switch(version) { case 1: break; case 2: { if(!query.exec( "CREATE TABLE workspace (" "id INTEGER PRIMARY KEY NOT NULL," "changed BOOLEAN DEFAULT FALSE," "data BLOB NOT NULL" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } break; } case 3: { if(!query.exec("ALTER TABLE workspace ADD COLUMN key TEXT")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } if(!query.exec("ALTER TABLE workspace ADD COLUMN type INTEGER")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } break; } case 4: { if(!query.exec("ALTER TABLE folders ADD COLUMN type INTEGER")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } query.prepare("UPDATE folders SET type=:type WHERE icon=:icon"); query.bindValue("type", eFolder1); query.bindValue("icon", ":/icons/iconFolderBlue16x16.png"); if(!query.exec()) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } query.prepare("UPDATE folders SET type=:type WHERE icon=:icon"); query.bindValue(":type", eFolder2); query.bindValue(":icon", ":/icons/iconFolderGreen16x16.png"); if(!query.exec()) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } break; } case 5: { if (QFile::exists(path.absoluteFilePath("qlgt_save_v4.db"))) { QFile::remove(path.absoluteFilePath("qlgt_save_v4.db")); } QFile f(path.absoluteFilePath(name)); f.copy(path.absoluteFilePath("qlgt_save_v4.db")); QSqlQuery query2(db); if(!query.exec("SELECT id, type, icon FROM items")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } const int total = query.size(); quint32 progCnt = 0; PROGRESS_SETUP(tr("Migrating database from version 4 to 5."), 0, total, CMainWindow::getBestWidgetForParent()); while(query.next()) { QPixmap pixmap; if(query.value(1).toInt() == eWpt || query.value(1).toInt() == eRte) { QPointF focus; pixmap = getWptIconByName(query.value(2).toString(), focus); } else if(query.value(1).toInt() == eTrk) { pixmap = QPixmap(16,16); pixmap.fill(query.value(2).toString()); } else { pixmap = QPixmap(query.value(2).toString()); } QByteArray bytes; QBuffer buffer(&bytes); buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer, "XPM"); query2.prepare("UPDATE items SET icon=:icon WHERE id=:id"); query2.bindValue(":id", query.value(0).toULongLong()); query2.bindValue(":icon", bytes); if(!query2.exec()) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } PROGRESS(progCnt++, continue); } break; } case 6: { QSqlQuery query2(db); if(!query.exec("SELECT id, data, type FROM items")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } const int total = query.size(); quint32 progCnt = 0; PROGRESS_SETUP(tr("Migrating database from version 5 to 6."), 0, total, CMainWindow::getBestWidgetForParent()); while(query.next()) { QByteArray array = query.value(1).toByteArray(); QBuffer buffer(&array); CQlb qlb(this); qlb.load(&buffer); switch(query.value(2).toInt()) { case eWpt: array = qlb.waypoints(); break; case eTrk: array = qlb.tracks(); break; case eRte: array = qlb.routes(); break; case eOvl: array = qlb.overlays(); break; } query2.prepare("UPDATE items SET data=:data WHERE id=:id"); query2.bindValue(":data", array); query2.bindValue(":id", query.value(0)); if(!query2.exec()) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } PROGRESS(progCnt++, continue); } break; } case 7: { QSqlQuery query2(db); if(!query.exec("SELECT id, data, type FROM items")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } const int total = query.size(); quint32 progCnt = 0; PROGRESS_SETUP(tr("Migrating database from version 6 to 7."), 0, total, CMainWindow::getBestWidgetForParent()); while(query.next()) { QString comment; QByteArray array = query.value(1).toByteArray(); QDataStream stream(&array, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_4_5); quint64 id = query.value(0).toULongLong(); switch(query.value(2).toInt()) { case eWpt: { CQlgtWpt wpt(id, 0); stream >> wpt; comment = wpt.getInfo(); } break; case eTrk: { CQlgtTrack trk(id, 0); stream >> trk; comment = trk.getInfo(); } break; case eRte: { CQlgtRoute rte(id, 0); stream >> rte; comment = rte.getInfo(); } break; case eOvl: { // IOverlay ovl(0); // stream >> ovl; // comment = ovl.getInfo(); continue; } break; } query2.prepare("UPDATE items SET comment=:comment WHERE id=:id"); query2.bindValue(":comment", comment); query2.bindValue(":id", query.value(0)); if(!query2.exec()) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } PROGRESS(progCnt++, continue); } break; } case 8: { PROGRESS_SETUP(tr("Migrating database from version 7 to 8."), 0, 1, CMainWindow::getBestWidgetForParent()); if(!query.exec( "CREATE TABLE diarys (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "parent INTEGER NOT NULL," "key TEXT NOT NULL," "date DATETIME DEFAULT CURRENT_TIMESTAMP," "data BLOB NOT NULL," "FOREIGN KEY(parent) REFERENCES folders(id)" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } PROGRESS(1, return ); break; } case 9: { if (QFile::exists(path.absoluteFilePath(name))) { QFile::remove(path.absoluteFilePath("qlgt_save_v8.db")); } QFile f(path.absoluteFilePath(name)); f.copy(path.absoluteFilePath("qlgt_save_v4.db")); PROGRESS_SETUP(tr("Migrating database from version 8 to 9."), 0, 1, CMainWindow::getBestWidgetForParent()); if(!query.exec("ALTER TABLE folders ADD COLUMN locked BOOLEAN DEFAULT FALSE")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return; } PROGRESS(1, return ); break; } } } query.prepare( "UPDATE versioninfo set version=:version"); query.bindValue(":version", version - 1); QUERY_EXEC(); } void CQlgtDb::printStatistic() { QSqlQuery query(db); gui->stdOut(tr("Open database: %1").arg(db.databaseName())); nItems = 0; query.prepare("SELECT COUNT() FROM folders"); QUERY_EXEC(); if(query.next()) { nFolders = query.value(0).toInt(); gui->stdOut(tr("Folders: %1").arg(nFolders)); } query.prepare("SELECT COUNT() FROM items WHERE type=:type"); query.bindValue(":type", eTrk); QUERY_EXEC(); if(query.next()) { nItems += query.value(0).toInt(); gui->stdOut(tr("Tracks: %1").arg(query.value(0).toInt())); } query.prepare("SELECT COUNT() FROM items WHERE type=:type"); query.bindValue(":type", eRte); QUERY_EXEC(); if(query.next()) { nItems += query.value(0).toInt(); gui->stdErr(tr("Routes: %1 (Only the basic route will be copied)").arg(query.value(0).toInt())); } query.prepare("SELECT COUNT() FROM items WHERE type=:type"); query.bindValue(":type", eWpt); QUERY_EXEC(); if(query.next()) { nItems += query.value(0).toInt(); gui->stdOut(tr("Waypoints: %1").arg(query.value(0).toInt())); } query.prepare("SELECT COUNT() FROM items WHERE type=:type"); query.bindValue(":type", eOvl); QUERY_EXEC(); if(query.next()) { nItems += query.value(0).toInt(); gui->stdErr(tr("Overlays: %1 (areas will be converted as areas, distance lines will be converted to tracks, all other overlay items will be lost)").arg(query.value(0).toInt())); } query.prepare("SELECT COUNT() FROM diarys"); query.bindValue(":type", eDry); QUERY_EXEC(); if(query.next()) { gui->stdOut(tr("Diaries: %1").arg(query.value(0).toInt())); } query.prepare("SELECT COUNT() FROM items WHERE type=:type"); query.bindValue(":type", eMap); QUERY_EXEC(); if(query.next()) { gui->stdErr(tr("Map selections: %1 (can't be converted to QMapShack)").arg(query.value(0).toInt())); } } void CQlgtDb::start(const QString& filename) { gui->stdOut(tr("------ Start to convert database to %1------").arg(filename)); dbQms = new CQmsDb(filename, gui); if(!dbQms->isValid()) { gui->stdErr(tr("Failed to create target database.")); gui->stdOut(tr("------ Abort ------")); return; } xferItems(); xferFolders(); QSqlQuery query(db); query.prepare("Select parent, child FROM folder2folder"); QUERY_EXEC(return ); while(query.next()) { quint64 idParent = query.value(0).toULongLong(); quint64 idChild = query.value(1).toULongLong(); dbQms->addFolder2FolderRelation(idParent, idChild); } query.prepare("Select parent, child FROM folder2item"); QUERY_EXEC(return ); while(query.next()) { quint64 idParent = query.value(0).toULongLong(); quint64 idChild = query.value(1).toULongLong(); dbQms->addFolder2ItemRelation(idParent, idChild); } delete dbQms; gui->stdOut(tr("------ Done ------")); } void CQlgtDb::xferFolders() { nDiary = 0; quint32 cnt = 1; PROGRESS_SETUP(tr("Restore folders..."), 0, nFolders, gui); QSqlQuery query(db); query.prepare("SELECT id FROM folders"); QUERY_EXEC(return ); while(query.next()) { PROGRESS(cnt++, break); quint64 idFolder = query.value(0).toULongLong(); CQlgtFolder folder1(idFolder, db); if(folder1.diary) { nDiary++; } dbQms->addFolder(folder1); } progress.setValue(100); gui->stdOut(tr("Imported %1 folders and %2 diaries").arg(nFolders).arg(nDiary)); } void CQlgtDb::xferItems() { quint32 cnt = 1; PROGRESS_SETUP(tr("Copy items..."), 0, nItems, gui); nWpt = 0; nTrk = 0; nRte = 0; nOvl = 0; QSqlQuery query(db); query.prepare("SELECT id FROM items"); QUERY_EXEC(return ); while(query.next()) { PROGRESS(cnt++, break); xferItem(query.value(0).toULongLong()); } progress.setValue(100); gui->stdOut(tr("Imported %1 tracks, %2 waypoints, %3 routes, %4 areas").arg(nTrk).arg(nWpt).arg(nRte).arg(nOvl)); gui->stdOut(tr("Import folders...")); query.prepare("SELECT id FROM folders"); QUERY_EXEC(return ); } void CQlgtDb::xferItem(quint64 id) { QSqlQuery query(db); query.prepare("SELECT type, data FROM items WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); if(query.next()) { QByteArray data = query.value(1).toByteArray(); QDataStream stream(&data, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_4_5); switch(query.value(0).toInt()) { case eWpt: { CQlgtWpt wpt1(id, 0); stream >> wpt1; dbQms->addWpt(wpt1); nWpt++; break; } case eTrk: { CQlgtTrack trk1(id, 0); stream >> trk1; dbQms->addTrk(trk1); nTrk++; break; } case eRte: { CQlgtRoute rte1(id, 0); stream >> rte1; dbQms->addRte(rte1); nRte++; break; } case eOvl: { IQlgtOverlay ovl1(id, 0); stream >> ovl1; if(ovl1.type == "Area") { dbQms->addArea(ovl1); nOvl++; } else if(ovl1.type == "Distance") { dbQms->addTrk(ovl1); nTrk++; } else { gui->stdErr(tr("Overlay of type '%1' cant be converted").arg(ovl1.type)); nOvl++; break; } break; } } } } qmapshack-1.5.1/src/qlgt/CQlgtTrack.cpp000644 001750 000144 00000023257 12622435717 020742 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "CQlgtTrack.h" #include QDataStream& operator >>(QDataStream& s, CFlags& flag) { quint32 f; s >> f; flag.setFlags(f); flag.setChanged(true); return s; } QDataStream& operator <<(QDataStream& s, CFlags& flag) { s << flag.flag(); return s; } struct trk_head_entry_t { trk_head_entry_t() : type(CQlgtTrack::eEnd), offset(0) { } qint32 type; quint32 offset; QByteArray data; }; QDataStream& operator >>(QDataStream& s, CQlgtTrack& track) { quint32 nTrkPts = 0; QIODevice * dev = s.device(); qint64 pos = dev->pos(); char magic[9]; s.readRawData(magic,9); if(strncmp(magic,"QLTrk ",9)) { dev->seek(pos); return s; } QList entries; while(1) { trk_head_entry_t entry; s >> entry.type >> entry.offset; entries << entry; if(entry.type == CQlgtTrack::eEnd) { break; } } QList::iterator entry = entries.begin(); while(entry != entries.end()) { qint64 o = pos + entry->offset; dev->seek(o); s >> entry->data; switch(entry->type) { case CQlgtTrack::eBase: { QString key; QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); s1 >> track.key; s1 >> track.timestamp; s1 >> track.name; s1 >> track.comment; s1 >> track.colorIdx; s1 >> track.parentWpt; if(!s1.atEnd()) { s1 >> track.doScaleWpt2Track; } if(!s1.atEnd()) { s1 >> track.cntMedianFilterApplied; } if(!s1.atEnd()) { s1 >> track.useMultiColor; } if(!s1.atEnd()) { s1 >> track.idMultiColor; } //track.setColor(track.colorIdx); break; } case CQlgtTrack::eTrkPts: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); quint32 n; track.track.clear(); s1 >> nTrkPts; for(n = 0; n < nTrkPts; ++n) { CQlgtTrack::pt_t trkpt; s1 >> trkpt.lon; s1 >> trkpt.lat; s1 >> trkpt.ele; s1 >> trkpt.timestamp; s1 >> trkpt.flags; trkpt._lon = trkpt.lon; trkpt._lat = trkpt.lat; trkpt._ele = trkpt.ele; trkpt._timestamp = trkpt.timestamp; trkpt._timestamp_msec = trkpt.timestamp_msec; track << trkpt; } break; } case CQlgtTrack::eTrkPts2: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); quint32 nTrkPts1 = 0; s1 >> nTrkPts1; if(nTrkPts1 != nTrkPts) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Corrupt track ..."), QObject::tr("Number of trackpoints is not equal the number of training data trackpoints."), QMessageBox::Ignore,QMessageBox::Ignore); break; } QList::iterator pt1 = track.track.begin(); while (pt1 != track.track.end()) { quint32 dummy; s1 >> pt1->timestamp_msec; s1 >> dummy; s1 >> dummy; s1 >> dummy; s1 >> dummy; s1 >> dummy; pt1++; } break; } // case CQlgtTrack::eTrain: // { // QDataStream s1(&entry->data, QIODevice::ReadOnly); // s1.setVersion(QDataStream::Qt_4_5); // quint32 nTrkPts1 = 0; // s1 >> nTrkPts1; // if(nTrkPts1 != nTrkPts) // { // QMessageBox::warning(0, QObject::tr("Corrupt track ..."), QObject::tr("Number of trackpoints is not equal the number of training data trackpoints."), QMessageBox::Ignore,QMessageBox::Ignore); // break; // } // QList::iterator pt1 = track.track.begin(); // while (pt1 != track.track.end()) // { // s1 >> pt1->heartReateBpm; // s1 >> pt1->cadenceRpm; // pt1++; // } // track.setTraineeData(); // break; // } case CQlgtTrack::eTrkExt1: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); quint32 nTrkPts1 = 0; s1 >> nTrkPts1; if(nTrkPts1 != nTrkPts) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Corrupt track ..."), QObject::tr("Number of trackpoints is not equal the number of extended data trackpoints."), QMessageBox::Ignore,QMessageBox::Ignore); break; } QList::iterator pt1 = track.track.begin(); while (pt1 != track.track.end()) { ///< [m] s1 >> pt1->altitude; ///< [m] s1 >> pt1->height; ///< [m/s] s1 >> pt1->velocity; ///< [deg] s1 >> pt1->heading; ///< [deg] s1 >> pt1->magnetic; s1 >> pt1->vdop; s1 >> pt1->hdop; s1 >> pt1->pdop; s1 >> pt1->x; ///< [m] cartesian gps coordinate s1 >> pt1->y; ///< [m] cartesian gps coordinate s1 >> pt1->z; ///< [m] cartesian gps coordinate ///< [m/s] velocity s1 >> pt1->vx; ///< [m/s] velocity s1 >> pt1->vy; ///< [m/s] velocity s1 >> pt1->vz; pt1++; } track.setExt1Data(); break; } case CQlgtTrack::eTrkShdw: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); quint32 n; quint32 nTrkPts1 = 0; s1 >> nTrkPts1; if(nTrkPts1 != nTrkPts) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Corrupt track ..."), QObject::tr("Number of trackpoints is not equal the number of shadow data trackpoints."), QMessageBox::Ignore,QMessageBox::Ignore); break; } for(n = 0; n < nTrkPts; ++n) { CQlgtTrack::pt_t& trkpt = track.track[n]; s1 >> trkpt._lon; s1 >> trkpt._lat; s1 >> trkpt._ele; } track.hasShadow1 = true; break; } case CQlgtTrack::eTrkShdw2: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); quint32 n; quint32 nTrkPts1 = 0; s1 >> nTrkPts1; if(nTrkPts1 != nTrkPts) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Corrupt track ..."), QObject::tr("Number of trackpoints is not equal the number of shadow data trackpoints."), QMessageBox::Ignore,QMessageBox::Ignore); break; } for(n = 0; n < nTrkPts; ++n) { quint32 dummy; CQlgtTrack::pt_t& trkpt = track.track[n]; s1 >> trkpt._timestamp; s1 >> trkpt._timestamp_msec; s1 >> dummy; s1 >> dummy; s1 >> dummy; s1 >> dummy; s1 >> dummy; } track.hasShadow2 = true; break; } default: ; } ++entry; } return s; } QDataStream& operator <<(QDataStream& s, CQlgtTrack& trk) { return s; } CQlgtTrack::CQlgtTrack(quint64 id, QObject *parent) : QObject(parent) , IItem(id) , ext1Data(false) , hasShadow1(false) , hasShadow2(false) { } CQlgtTrack::~CQlgtTrack() { } CQlgtTrack& CQlgtTrack::operator<<(const pt_t& pt) { track.push_back(pt); track.last().idx = track.size() - 1; track.last().flags &= ~pt_t::eCursor; track.last().flags &= ~pt_t::eFocus; track.last().flags &= ~pt_t::eSelected; return *this; } qmapshack-1.5.1/src/qlgt/CQlgtRoute.cpp000644 001750 000144 00000016026 12622435717 020770 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CQlgtRoute.h" struct rte_head_entry_t { rte_head_entry_t() : type(CQlgtRoute::eEnd), offset(0) { } qint32 type; quint32 offset; QByteArray data; }; QDataStream& operator >>(QDataStream& s, CQlgtRoute& route) { quint32 nRtePts = 0; QIODevice * dev = s.device(); qint64 pos = dev->pos(); char magic[9]; s.readRawData(magic,9); if(strncmp(magic,"QLRte ",9)) { dev->seek(pos); return s; } QList entries; while(1) { rte_head_entry_t entry; s >> entry.type >> entry.offset; entries << entry; if(entry.type == CQlgtRoute::eEnd) { break; } } QList::iterator entry = entries.begin(); while(entry != entries.end()) { qint64 o = pos + entry->offset; dev->seek(o); s >> entry->data; switch(entry->type) { case CQlgtRoute::eBase: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); s1 >> route.key; s1 >> route.timestamp; s1 >> route.name; s1 >> route.iconString; s1 >> route.ttime; s1 >> route.parentWpt; break; } case CQlgtRoute::eRtePts: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); quint32 n; route.priRoute.clear(); s1 >> nRtePts; for(n = 0; n < nRtePts; ++n) { CQlgtRoute::pt_t rtept; float u, v; QString action; s1 >> u; s1 >> v; s1 >> action; rtept.lon = u; rtept.lat = v; rtept.action = action; route.priRoute << rtept; } break; } // case CQlgtRoute::eRteSec: // { // QDataStream s1(&entry->data, QIODevice::ReadOnly); // s1.setVersion(QDataStream::Qt_4_5); // quint32 n; // route.secRoute.clear(); // s1 >> nRtePts; // for(n = 0; n < nRtePts; ++n) // { // CQlgtRoute::pt_t rtept; // float u, v; // QString action; // s1 >> u; // s1 >> v; // s1 >> action; // rtept.lon = u; // rtept.lat = v; // rtept.action = action; // route.secRoute << rtept; // } // break; // } default: ; } ++entry; } return s; } QDataStream& operator <<(QDataStream& s, CQlgtRoute& route) { QList entries; //--------------------------------------- // prepare base data //--------------------------------------- rte_head_entry_t entryBase; entryBase.type = CQlgtRoute::eBase; QDataStream s1(&entryBase.data, QIODevice::WriteOnly); s1.setVersion(QDataStream::Qt_4_5); s1 << route.key; s1 << route.timestamp; s1 << route.name; s1 << route.iconString; s1 << route.ttime; s1 << route.parentWpt; entries << entryBase; //--------------------------------------- // prepare primary routepoint data //--------------------------------------- rte_head_entry_t entryPriRtePts; entryPriRtePts.type = CQlgtRoute::eRtePts; QDataStream s2(&entryPriRtePts.data, QIODevice::WriteOnly); s2.setVersion(QDataStream::Qt_4_5); { QVector& rtepts = route.priRoute; QVector::iterator rtept = rtepts.begin(); s2 << (quint32)rtepts.size(); while(rtept != rtepts.end()) { s2 << (float)rtept->lon; s2 << (float)rtept->lat; s2 << rtept->action; ++rtept; } } entries << entryPriRtePts; // //--------------------------------------- // // prepare secondary routepoint data // //--------------------------------------- // rte_head_entry_t entrySecRtePts; // entrySecRtePts.type = CQlgtRoute::eRteSec; // QDataStream s3(&entrySecRtePts.data, QIODevice::WriteOnly); // s3.setVersion(QDataStream::Qt_4_5); // { // QVector& rtepts = route.getSecRtePoints(); // QVector::iterator rtept = rtepts.begin(); // if(!rtepts.isEmpty()) // { // s3 << (quint32)rtepts.size(); // while(rtept != rtepts.end()) // { // s3 << (float)rtept->lon; // s3 << (float)rtept->lat; // s3 << rtept->action; // ++rtept; // } // entries << entrySecRtePts; // } // } //--------------------------------------- // prepare terminator //--------------------------------------- rte_head_entry_t entryEnd; entryEnd.type = CQlgtRoute::eEnd; entries << entryEnd; //--------------------------------------- //--------------------------------------- // now start to actually write data; //--------------------------------------- //--------------------------------------- // write magic key s.writeRawData("QLRte ",9); // calculate offset table quint32 offset = entries.count() * 8 + 9; QList::iterator entry = entries.begin(); while(entry != entries.end()) { entry->offset = offset; offset += entry->data.size() + sizeof(quint32); ++entry; } // write offset table entry = entries.begin(); while(entry != entries.end()) { s << entry->type << entry->offset; ++entry; } // write entry data entry = entries.begin(); while(entry != entries.end()) { s << entry->data; ++entry; } return s; } CQlgtRoute::CQlgtRoute(quint64 id, QObject *parent) : QObject(parent) , IItem(id) { } CQlgtRoute::~CQlgtRoute() { } qmapshack-1.5.1/src/qlgt/CQmsDb.h000644 001750 000144 00000003562 12622435722 017512 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQMSDB_H #define CQMSDB_H #include "gis/db/IDB.h" #include #include class CImportDatabase; class IGisItem; class CQlgtFolder; class CQlgtWpt; class CQlgtTrack; class CQlgtRoute; class IQlgtOverlay; class CQmsDb : public QObject, private IDB { public: CQmsDb(const QString& filename, CImportDatabase * parent); virtual ~CQmsDb(); void addFolder2FolderRelation(quint64 parent, quint64 child); void addFolder2ItemRelation(quint64 parent, quint64 child); void addFolder(CQlgtFolder &folder); void addWpt(CQlgtWpt &wpt1); void addTrk(CQlgtTrack &trk1); void addTrk(IQlgtOverlay &trk1); void addRte(CQlgtRoute& rte1); void addArea(IQlgtOverlay& ovl1); bool isValid() { return valid; } private: bool valid; quint64 store(IGisItem &item); CImportDatabase * gui; QMap mapFolderTypes; QMap mapFolderIDs; QMap mapItemIDs; }; #endif //CQMSDB_H qmapshack-1.5.1/src/qlgt/CQlgtDiary.cpp000644 001750 000144 00000015436 12622435717 020746 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CQlgtDiary.h" struct diary_head_entry_t { diary_head_entry_t() : type(CQlgtDiary::eEnd), offset(0) { } qint32 type; quint32 offset; QByteArray data; }; QDataStream& operator >>(QDataStream& s, CQlgtDiary& diary) { QIODevice * dev = s.device(); qint64 pos = dev->pos(); char magic[9]; s.readRawData(magic,9); if(strncmp(magic,"QLDry ",9)) { dev->seek(pos); return s; } QList entries; while(1) { diary_head_entry_t entry; s >> entry.type >> entry.offset; entries << entry; if(entry.type == CQlgtDiary::eEnd) { break; } } QList::iterator entry = entries.begin(); while(entry != entries.end()) { qint64 o = pos + entry->offset; dev->seek(o); s >> entry->data; switch(entry->type) { case CQlgtDiary::eBase: { QString comment, name,key; QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); s1 >> diary.timestamp; s1 >> diary.comment; s1 >> diary.name; s1 >> diary.keyProjectGeoDB; s1 >> diary.key; break; } // case CQlgtDiary::eWpt: // { // int cnt; // QDataStream s1(&entry->data, QIODevice::ReadOnly); // s1.setVersion(QDataStream::Qt_4_5); // s1 >> cnt; // for(int i=0; i < cnt; i++) // { // CWpt * wpt = new CWpt(&diary); // s1 >> *wpt; // diary.wpts << wpt; // } // break; // } // case CQlgtDiary::eTrk: // { // int cnt; // QDataStream s1(&entry->data, QIODevice::ReadOnly); // s1.setVersion(QDataStream::Qt_4_5); // s1 >> cnt; // for(int i=0; i < cnt; i++) // { // CTrack * trk = new CTrack(&diary); // s1 >> *trk; // trk->rebuild(true); // diary.trks << trk; // } // break; // } // case CQlgtDiary::eRte: // { // int cnt; // QDataStream s1(&entry->data, QIODevice::ReadOnly); // s1.setVersion(QDataStream::Qt_4_5); // s1 >> cnt; // for(int i=0; i < cnt; i++) // { // CRoute * rte = new CRoute(&diary); // s1 >> *rte; // diary.rtes << rte; // } // break; // } default: ; } ++entry; } return s; } QDataStream& operator <<(QDataStream& s, CQlgtDiary& diary) { QList entries; //--------------------------------------- // prepare base data //--------------------------------------- diary_head_entry_t entryBase; entryBase.type = CQlgtDiary::eBase; QDataStream s1(&entryBase.data, QIODevice::WriteOnly); s1.setVersion(QDataStream::Qt_4_5); s1 << diary.timestamp; s1 << diary.comment; s1 << diary.name; s1 << diary.keyProjectGeoDB; s1 << diary.key; entries << entryBase; // //--------------------------------------- // // prepare waypoint data // //--------------------------------------- // diary_head_entry_t entryWpt; // entryWpt.type = CQlgtDiary::eWpt; // QDataStream s2(&entryWpt.data, QIODevice::WriteOnly); // s2.setVersion(QDataStream::Qt_4_5); // s2 << diary.wpts.count(); // foreach(CWpt * wpt, diary.wpts) // { // s2 << *wpt; // } // entries << entryWpt; // //--------------------------------------- // // prepare track data // //--------------------------------------- // diary_head_entry_t entryTrk; // entryTrk.type = CQlgtDiary::eTrk; // QDataStream s3(&entryTrk.data, QIODevice::WriteOnly); // s3.setVersion(QDataStream::Qt_4_5); // s3 << diary.trks.count(); // foreach(CTrack * trk, diary.trks) // { // s3 << *trk; // } // entries << entryTrk; // //--------------------------------------- // // prepare route data // //--------------------------------------- // diary_head_entry_t entryRte; // entryRte.type = CQlgtDiary::eRte; // QDataStream s4(&entryRte.data, QIODevice::WriteOnly); // s4.setVersion(QDataStream::Qt_4_5); // s4 << diary.rtes.count(); // foreach(CRoute * rte, diary.rtes) // { // s4 << *rte; // } // entries << entryRte; //--------------------------------------- // prepare terminator //--------------------------------------- diary_head_entry_t entryEnd; entryEnd.type = CQlgtDiary::eEnd; entries << entryEnd; //--------------------------------------- //--------------------------------------- // now start to actually write data; //--------------------------------------- //--------------------------------------- // write magic key s.writeRawData("QLDry ",9); // calculate offset table quint32 offset = entries.count() * 8 + 9; QList::iterator entry = entries.begin(); while(entry != entries.end()) { entry->offset = offset; offset += entry->data.size() + sizeof(quint32); ++entry; } // write offset table entry = entries.begin(); while(entry != entries.end()) { s << entry->type << entry->offset; ++entry; } // write entry data entry = entries.begin(); while(entry != entries.end()) { s << entry->data; ++entry; } return s; } CQlgtDiary::CQlgtDiary(quint64 id, QObject *parent) : QObject(parent) , IItem(id) { } CQlgtDiary::~CQlgtDiary() { } qmapshack-1.5.1/src/qlgt/IQlgtOverlay.cpp000644 001750 000144 00000012131 12622435717 021312 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "IQlgtOverlay.h" struct ovl_head_entry_t { ovl_head_entry_t() : type(IQlgtOverlay::eEnd), offset(0) { } qint32 type; quint32 offset; QByteArray data; }; QDataStream& operator >>(QDataStream& s, IQlgtOverlay& ovl) { QIODevice * dev = s.device(); qint64 pos = dev->pos(); char magic[9]; s.readRawData(magic,9); if(strncmp(magic,"QLOvl ",9)) { dev->seek(pos); return s; } QList entries; while(1) { ovl_head_entry_t entry; s >> entry.type >> entry.offset; entries << entry; if(entry.type == IQlgtOverlay::eEnd) { break; } } QList::iterator entry = entries.begin(); while(entry != entries.end()) { qint64 o = pos + entry->offset; dev->seek(o); s >> entry->data; switch(entry->type) { case IQlgtOverlay::eBase: { QDataStream s1(&entry->data, QIODevice::ReadOnly); s1.setVersion(QDataStream::Qt_4_5); s1 >> ovl.type; if(ovl.type == "Text") { QRect rect; QString text; s1 >> rect >> text >> ovl.key; } else if(ovl.type == "TextBox") { QRect rect; QPoint pt; QString text; double lon, lat; s1 >> lon >> lat >> pt >> rect >> text >> ovl.key; } else if(ovl.type == "Distance") { int size, idx = 0; IQlgtOverlay::pt_t pt; s1 >> ovl.name >> ovl.comment >> size; for(int i = 0; i < size; ++i) { s1 >> pt.u >> pt.v; pt.idx = idx++; ovl.points << pt; } s1 >> ovl.speed >> ovl.key >> ovl.parentWpt; } else if(ovl.type == "Area") { int size, idx = 0; IQlgtOverlay::pt_t pt; s1 >> ovl.name >> ovl.comment >> size; for(int i = 0; i < size; ++i) { s1 >> pt.u >> pt.v; pt.idx = idx++; ovl.points << pt; } s1 >> ovl.color >> ovl.key >> ovl.parentWpt >> ovl.style >> ovl.width >> ovl.opacity; } break; } default: ; } ++entry; } return s; } QDataStream& operator <<(QDataStream& s, IQlgtOverlay& ovl) { QList entries; //--------------------------------------- // prepare base data //--------------------------------------- ovl_head_entry_t entryBase; entryBase.type = IQlgtOverlay::eBase; QDataStream s1(&entryBase.data, QIODevice::WriteOnly); s1.setVersion(QDataStream::Qt_4_5); s1 << ovl.type; // ovl.save(s1); entries << entryBase; //--------------------------------------- // prepare terminator //--------------------------------------- ovl_head_entry_t entryEnd; entryEnd.type = IQlgtOverlay::eEnd; entries << entryEnd; //--------------------------------------- //--------------------------------------- // now start to actually write data; //--------------------------------------- //--------------------------------------- // write magic key s.writeRawData("QLOvl ",9); // calculate offset table quint32 offset = entries.count() * 8 + 9; QList::iterator entry = entries.begin(); while(entry != entries.end()) { entry->offset = offset; offset += entry->data.size() + sizeof(quint32); ++entry; } // write offset table entry = entries.begin(); while(entry != entries.end()) { s << entry->type << entry->offset; ++entry; } // write entry data entry = entries.begin(); while(entry != entries.end()) { s << entry->data; ++entry; } return s; } IQlgtOverlay::IQlgtOverlay(quint64 id, QObject *parent) : IItem(id) , speed(0) { } IQlgtOverlay::~IQlgtOverlay() { } qmapshack-1.5.1/src/qlgt/CQmsDb.cpp000644 001750 000144 00000017371 12622435717 020054 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/db/CDBProject.h" #include "gis/db/IDBFolder.h" #include "gis/db/macros.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "qlgt/CQlgtDb.h" #include "qlgt/CQlgtFolder.h" #include "qlgt/CQlgtRoute.h" #include "qlgt/CQlgtTrack.h" #include "qlgt/CQlgtWpt.h" #include "qlgt/CQmsDb.h" #include "qlgt/IQlgtOverlay.h" #include "tool/CImportDatabase.h" #include #include CQmsDb::CQmsDb(const QString &filename, CImportDatabase *parent) : QObject(parent) , valid(false) , gui(parent) { if(QFile::exists(filename)) { int res = QMessageBox::question(CMainWindow::getBestWidgetForParent(), tr("Existing file..."), tr("Remove existing %1?").arg(filename), QMessageBox::Ok|QMessageBox::Abort, QMessageBox::Ok); if(res != QMessageBox::Ok) { return; } gui->stdErr(tr("Remove existing file %1").arg(filename)); QFile::remove(filename); } valid = setupDB(filename, "qlgt2qms"); if(!valid) { return; } mapFolderTypes[CQlgtDb::eFolder1] = IDBFolder::eTypeGroup; mapFolderTypes[CQlgtDb::eFolder2] = IDBFolder::eTypeProject; mapFolderTypes[CQlgtDb::eFolderN] = IDBFolder::eTypeOther; mapFolderIDs[1] = 1; mapFolderIDs[0] = 1; } CQmsDb::~CQmsDb() { db.close(); } void CQmsDb::addFolder2FolderRelation(quint64 parent, quint64 child) { QSqlQuery query(db); query.prepare("INSERT INTO folder2folder (parent, child) VALUES (:parent, :child)"); query.bindValue(":parent", mapFolderIDs[parent]); query.bindValue(":child", mapFolderIDs[child]); QUERY_EXEC(return ); } void CQmsDb::addFolder2ItemRelation(quint64 parent, quint64 child) { QSqlQuery query(db); query.prepare("INSERT INTO folder2item (parent, child) VALUES (:parent, :child)"); query.bindValue(":parent", mapFolderIDs[parent]); query.bindValue(":child", mapItemIDs[child]); QUERY_EXEC(return ); } void CQmsDb::addFolder(CQlgtFolder& folder) { if(folder.id < 2) { return; } QSqlQuery query(db); // folders without child items if(folder.items.isEmpty()) { query.prepare("INSERT INTO folders (type, name, locked) VALUES (:type, :name, :locked)"); query.bindValue(":type", mapFolderTypes[folder.type]); query.bindValue(":name", folder.name); query.bindValue(":locked", folder.locked); QUERY_EXEC(return ); query.prepare("SELECT last_insert_rowid() from folders"); QUERY_EXEC(return ); query.next(); quint64 id = query.value(0).toULongLong(); if(id == 0) { qDebug() << "CGisListDB::slotAddFolder(): childId equals 0. bad."; return; } mapFolderIDs[folder.id] = id; return; } /* Folders with child items will be loaded as complete CDBProject first, to generate key and info text properly */ CDBProject project(folder); foreach(quint64 id, folder.items) { quint64 idChild = mapItemIDs[id]; query.prepare("SELECT type FROM items WHERE id=:id"); query.bindValue(":id", idChild); QUERY_EXEC(continue); if(query.next()) { int type = query.value(0).toInt(); switch(type) { case IGisItem::eTypeWpt: new CGisItemWpt(idChild, db, &project); break; case IGisItem::eTypeTrk: new CGisItemTrk(idChild, db, &project); break; case IGisItem::eTypeRte: new CGisItemRte(idChild, db, &project); break; case IGisItem::eTypeOvl: new CGisItemOvlArea(idChild, db, &project); break; default: ; } } else { gui->stdErr(tr("%1: drop item with QLGT DB ID %2").arg(folder.name).arg(id)); } } // serialize metadata of project QByteArray data; QDataStream in(&data, QIODevice::WriteOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); project >> in; query.prepare("INSERT INTO folders (type, key, name, comment, locked, data) VALUES (:type, :key, :name, :comment, :locked, :data)"); query.bindValue(":type", mapFolderTypes[folder.type]); query.bindValue(":key", project.getKey()); query.bindValue(":name", project.getName()); query.bindValue(":comment", project.getInfo()); query.bindValue(":locked", folder.locked); query.bindValue(":data", data); QUERY_EXEC(return ); query.prepare("SELECT last_insert_rowid() from folders"); QUERY_EXEC(return ); query.next(); quint64 id = query.value(0).toULongLong(); if(id == 0) { qDebug() << "CGisListDB::slotAddFolder(): childId equals 0. bad."; return; } mapFolderIDs[folder.id] = id; } void CQmsDb::addWpt(CQlgtWpt& wpt1) { CGisItemWpt wpt(wpt1); quint64 id = store(wpt); if(id != 0) { mapItemIDs[wpt1.id] = id; } } void CQmsDb::addTrk(CQlgtTrack &trk1) { CGisItemTrk trk(trk1); quint64 id = store(trk); if(id != 0) { mapItemIDs[trk1.id] = id; } } void CQmsDb::addTrk(IQlgtOverlay &trk1) { CGisItemTrk trk(trk1); quint64 id = store(trk); if(id != 0) { mapItemIDs[trk1.id] = id; } } void CQmsDb::addRte(CQlgtRoute& rte1) { CGisItemRte rte(rte1); quint64 id = store(rte); if(id != 0) { mapItemIDs[rte1.id] = id; } } void CQmsDb::addArea(IQlgtOverlay& ovl1) { CGisItemOvlArea ovl(ovl1); quint64 id = store(ovl); if(id != 0) { mapItemIDs[ovl1.id] = id; } } quint64 CQmsDb::store(IGisItem& item) { // serialize complete history of item QByteArray data; QDataStream in(&data, QIODevice::WriteOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in << item.getHistory(); QBuffer buffer; buffer.open(QIODevice::ReadWrite); QPixmap pixmap = item.getIcon(); pixmap.save(&buffer, "PNG"); buffer.seek(0); QSqlQuery query(db); // item is unknown to database -> create item in database query.prepare("INSERT INTO items (type, key, icon, name, comment, data) VALUES (:type, :key, :icon, :name, :comment, :data)"); query.bindValue(":type", item.type()); query.bindValue(":key", item.getKey().item); query.bindValue(":icon", buffer.data()); query.bindValue(":name", item.getName()); query.bindValue(":comment", item.getInfo()); query.bindValue(":data", data); QUERY_EXEC(return 0); query.prepare("SELECT last_insert_rowid() from items"); QUERY_EXEC(return 0); query.next(); quint64 idItem = query.value(0).toULongLong(); return idItem; } qmapshack-1.5.1/src/qlgt/CQlb.h000644 001750 000144 00000006160 12622435722 017217 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2007 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQLB_H #define CQLB_H #include #include #include class CQlgtWpt; class CQlgtTrack; class CQlgtRoute; class CQlgtDiary; class IQlgtOverlay; /// qlandkarte binary to store private geo data /** The file will store data like waypoints, tracks, map selections. These elements will be collected in a dedicated byte array, e.g. all waypoints are serialized in wpts and so on. These byte arrays a stored like: qint32 eWpt, QByteArray wpts ... qint32 eEnd */ class CQlb : public QObject { Q_OBJECT public: CQlb(QObject * parent); virtual ~CQlb(); enum type_e {eEnd, eWpt, eTrack, eDiary, eOverlay, eRoute, eMapSel}; /// collect waypoint data /** This will serialize the waypoint object to wpts */ CQlb& operator <<(CQlgtWpt &wpt); CQlb& operator <<(CQlgtTrack &trk); CQlb& operator <<(CQlgtDiary &dry); CQlb& operator <<(IQlgtOverlay &ovl); CQlb& operator <<(CQlgtRoute &rte); /// get access to stored waypoint data QByteArray& waypoints() { return wpts; } /// get access to stored track data QByteArray& tracks() { return trks; } /// get access to stored diary data QByteArray& diary() { return drys; } /// get access to stored overlay data QByteArray& overlays() { return ovls; } /// get access to stored route data QByteArray& routes() { return rtes; } /// get access to stored map selection data QByteArray& mapsels() { return sels; } /// write collected data to file void save(const QString& filename); void save(QIODevice *ioDevice); /// read file and store elements in their designated byte arrays void load(const QString& filename); void load(QIODevice *ioDevice); private: /// byte array to hold all waypoints QByteArray wpts; /// byte array to hold all tracks QByteArray trks; /// byte array to hold all routes QByteArray rtes; /// byte array to hold diary QByteArray drys; /// byte array to hold overlays QByteArray ovls; /// byte array to hold map selections QByteArray sels; }; #endif //CQLB_H qmapshack-1.5.1/src/canvas/CCanvas.cpp000644 001750 000144 00000053005 12622715164 020554 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "canvas/CCanvasSetup.h" #include "dem/CDemDraw.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/IGisLine.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/trk/CGisItemTrk.h" #include "grid/CGrid.h" #include "grid/CGridSetup.h" #include "helpers/CDraw.h" #include "helpers/CSettings.h" #include "map/CMapDraw.h" #include "mouse/CMouseEditArea.h" #include "mouse/CMouseEditRte.h" #include "mouse/CMouseEditTrk.h" #include "mouse/CMouseMoveWpt.h" #include "mouse/CMouseNormal.h" #include "mouse/CMousePrint.h" #include "mouse/CMouseRangeTrk.h" #include "mouse/CMouseWptBubble.h" #include "plot/CPlotProfile.h" #include "units/IUnit.h" #include "widgets/CColorLegend.h" #include CCanvas::CCanvas(QWidget *parent, const QString &name) : QWidget(parent) { setFocusPolicy(Qt::WheelFocus); if(name.isEmpty()) { int count = 1; while(1) { QString name = tr("View %1").arg(count); if(CMainWindow::self().findChild(name) == 0) { setObjectName(name); break; } count++; } } else { setObjectName(name); } setMouseTracking(true); map = new CMapDraw(this); grid = new CGrid(map); dem = new CDemDraw(this); gis = new CGisDraw(this); mouse = new CMouseNormal(gis, this); connect(map, SIGNAL(sigCanvasUpdate(CCanvas::redraw_e)), this, SLOT(slotTriggerCompleteUpdate(CCanvas::redraw_e))); connect(dem, SIGNAL(sigCanvasUpdate(CCanvas::redraw_e)), this, SLOT(slotTriggerCompleteUpdate(CCanvas::redraw_e))); connect(gis, SIGNAL(sigCanvasUpdate(CCanvas::redraw_e)), this, SLOT(slotTriggerCompleteUpdate(CCanvas::redraw_e))); timerToolTip = new QTimer(this); timerToolTip->setSingleShot(true); connect(timerToolTip, SIGNAL(timeout()), this, SLOT(slotToolTip())); loadIndicator1 = new QMovie(this); loadIndicator1->setFileName("://animation/loader.gif"); mapLoadIndicator = new QLabel(this); mapLoadIndicator->setMovie(loadIndicator1); loadIndicator1->start(); mapLoadIndicator->show(); loadIndicator2 = new QMovie(this); loadIndicator2->setFileName("://animation/loader2.gif"); demLoadIndicator = new QLabel(this); demLoadIndicator->setMovie(loadIndicator2); loadIndicator2->start(); demLoadIndicator->show(); labelStatusMessages = new QLabel(this); labelStatusMessages->setWordWrap(true); labelStatusMessages->setMinimumWidth(300); labelStatusMessages->setAlignment(Qt::AlignJustify); labelStatusMessages->hide(); connect(map, SIGNAL(sigStartThread()), mapLoadIndicator, SLOT(show())); connect(map, SIGNAL(sigStopThread()), mapLoadIndicator, SLOT(hide())); connect(dem, SIGNAL(sigStartThread()), demLoadIndicator, SLOT(show())); connect(dem, SIGNAL(sigStopThread()), demLoadIndicator, SLOT(hide())); timerTrackOnFocus = new QTimer(this); timerTrackOnFocus->setSingleShot(false); timerTrackOnFocus->start(1000); connect(timerTrackOnFocus, SIGNAL(timeout()), this, SLOT(slotCheckTrackOnFocus())); } CCanvas::~CCanvas() { /* Some mouse objects call methods from their canvas on destruction. So they are better deleted now explicitly before any other object in CCanvas is destroyed. */ delete mouse; saveSizeTrackProfile(); } void CCanvas::setOverrideCursor(const QCursor& cursor, const QString& src) { // qDebug() << "setOverrideCursor" << src; QApplication::setOverrideCursor(cursor); } void CCanvas::restoreOverrideCursor(const QString& src) { // qDebug() << "restoreOverrideCursor" << src; QApplication::restoreOverrideCursor(); } void CCanvas::changeOverrideCursor(const QCursor& cursor, const QString &src) { // qDebug() << "changeOverrideCursor" << src; QApplication::changeOverrideCursor(cursor); } void CCanvas::saveConfig(QSettings& cfg) { map->saveConfig(cfg); dem->saveConfig(cfg); grid->saveConfig(cfg); cfg.setValue("posFocus", posFocus); cfg.setValue("proj", map->getProjection()); cfg.setValue("scales", map->getScalesType()); } void CCanvas::loadConfig(QSettings& cfg) { posFocus = cfg.value("posFocus", posFocus).toPointF(); setProjection(cfg.value("proj", map->getProjection()).toString()); setScales((CCanvas::scales_type_e)cfg.value("scales", map->getScalesType()).toInt()); map->loadConfig(cfg); dem->loadConfig(cfg); grid->loadConfig(cfg); dem->zoom(map->zoom()); gis->zoom(map->zoom()); } void CCanvas::resetMouse() { mouse->deleteLater(); mouse = new CMouseNormal(gis, this); if(underMouse()) { while(QApplication::overrideCursor()) { CCanvas::restoreOverrideCursor("resetMouse"); } CCanvas::setOverrideCursor(*mouse, "resetMouse"); } } void CCanvas::setMouseMoveWpt(CGisItemWpt& wpt) { mouse->deleteLater(); mouse = new CMouseMoveWpt(wpt, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseMoveWpt"); CCanvas::setOverrideCursor(*mouse, "setMouseMoveWpt"); } } void CCanvas::setMouseEditTrk(const QPointF &pt) { mouse->deleteLater(); mouse = new CMouseEditTrk(pt, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseEditTrk"); CCanvas::setOverrideCursor(*mouse, "setMouseEditTrk"); } } void CCanvas::setMouseEditRte(const QPointF &pt) { mouse->deleteLater(); mouse = new CMouseEditRte(pt, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseEditRte"); CCanvas::setOverrideCursor(*mouse, "setMouseEditRte"); } } void CCanvas::setMouseEditTrk(CGisItemTrk& trk) { mouse->deleteLater(); mouse = new CMouseEditTrk(trk, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseEditTrk"); CCanvas::setOverrideCursor(*mouse, "setMouseEditTrk"); } } void CCanvas::setMouseRangeTrk(CGisItemTrk& trk) { mouse->deleteLater(); mouse = new CMouseRangeTrk(trk, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseRangeTrk"); CCanvas::setOverrideCursor(*mouse, "setMouseRangeTrk"); } } void CCanvas::setMouseEditArea(const QPointF& pt) { mouse->deleteLater(); mouse = new CMouseEditArea(pt, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseEditArea"); CCanvas::setOverrideCursor(*mouse, "setMouseEditArea"); } } void CCanvas::setMouseEditArea(CGisItemOvlArea& area) { mouse->deleteLater(); mouse = new CMouseEditArea(area, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseEditArea"); CCanvas::setOverrideCursor(*mouse, "setMouseEditArea"); } } void CCanvas::setMouseEditRte(CGisItemRte& rte) { mouse->deleteLater(); mouse = new CMouseEditRte(rte, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseEditRte"); CCanvas::setOverrideCursor(*mouse, "setMouseEditRte"); } } void CCanvas::setMouseWptBubble(const IGisItem::key_t& key) { mouse->deleteLater(); mouse = new CMouseWptBubble(key, gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMouseWptBubble"); CCanvas::setOverrideCursor(*mouse, "setMouseWptBubble"); } } void CCanvas::setMousePrint() { mouse->deleteLater(); mouse = new CMousePrint(gis, this); if(underMouse()) { CCanvas::restoreOverrideCursor("setMousePrint"); CCanvas::setOverrideCursor(*mouse, "setMousePrint"); } } void CCanvas::reportStatus(const QString& key, const QString& msg) { if(msg.isEmpty()) { statusMessages.remove(key); } else { statusMessages[key] = msg; } QString report; QStringList keys = statusMessages.keys(); keys.sort(); foreach(const QString &key, keys) { report += statusMessages[key] + "\n"; } if(report.isEmpty()) { labelStatusMessages->hide(); } else { labelStatusMessages->show(); labelStatusMessages->setText(report); labelStatusMessages->adjustSize(); } update(); } void CCanvas::resizeEvent(QResizeEvent * e) { needsRedraw = eRedrawAll; setDrawContextSize(e->size()); QWidget::resizeEvent(e); // move map loading indicator to new center of canvas QPoint p1(mapLoadIndicator->width()>>1, mapLoadIndicator->height()>>1); mapLoadIndicator->move(rect().center() - p1); QPoint p2(demLoadIndicator->width()>>1, demLoadIndicator->height()>>1); demLoadIndicator->move(rect().center() - p2); labelStatusMessages->move(20,50); setSizeTrackProfile(); } void CCanvas::paintEvent(QPaintEvent * e) { Q_UNUSED(e); if(!isVisible()) { return; } QPainter p; p.begin(this); USE_ANTI_ALIASING(p,true); // fill the background with default pattern p.fillRect(rect(), "#FFFFBF"); // ----- start to draw thread based content ----- // move coordinate system to center of the screen p.translate(width() >> 1, height() >> 1); map->draw(p, needsRedraw, posFocus); dem->draw(p, needsRedraw, posFocus); gis->draw(p, needsRedraw, posFocus); // restore coordinate system to default p.resetTransform(); // ----- start to draw fast content ----- grid->draw(p, rect()); if(map->isFinished() && dem->isFinished() && gis->isFinished()) { gis->draw(p, rect()); } mouse->draw(p, needsRedraw, rect()); drawStatusMessages(p); drawScale(p); p.end(); needsRedraw = eRedrawNone; } void CCanvas::mousePressEvent(QMouseEvent * e) { mouse->mousePressEvent(e); QWidget::mousePressEvent(e); e->accept(); } void CCanvas::mouseMoveEvent(QMouseEvent * e) { qreal ele = NOFLOAT; QPointF pos = e->pos(); map->convertPx2Rad(pos); ele = dem->getElevationAt(pos); emit sigMousePosition(pos * RAD_TO_DEG, ele); mouse->mouseMoveEvent(e); QWidget::mouseMoveEvent(e); e->accept(); } void CCanvas::mouseReleaseEvent(QMouseEvent *e) { mouse->mouseReleaseEvent(e); QWidget::mouseReleaseEvent(e); e->accept(); } void CCanvas::mouseDoubleClickEvent(QMouseEvent * e) { mouse->mouseDoubleClickEvent(e); QWidget::mouseDoubleClickEvent(e); } void CCanvas::wheelEvent(QWheelEvent * e) { mouse->wheelEvent(e); QPointF pos = e->posF(); QPointF pt1 = pos; map->convertPx2Rad(pt1); setZoom(CMainWindow::self().flipMouseWheel() ? (e->delta() < 0) : (e->delta() > 0), needsRedraw); map->convertRad2Px(pt1); map->convertRad2Px(posFocus); posFocus -= (pos - pt1); map->convertPx2Rad(posFocus); update(); } void CCanvas::enterEvent(QEvent * e) { Q_UNUSED(e); CCanvas::setOverrideCursor(*mouse, "enterEvent"); mouse->setMouseTracking(true); } void CCanvas::leaveEvent(QEvent * e) { Q_UNUSED(e); // bad hack to stop bad number of override cursors. while(QApplication::overrideCursor()) { CCanvas::restoreOverrideCursor("leaveEvent"); } mouse->setMouseTracking(false); } void CCanvas::keyPressEvent(QKeyEvent * e) { qDebug() << hex << e->key(); bool doUpdate = true; switch(e->key()) { case Qt::Key_Plus: setZoom(true, needsRedraw); break; case Qt::Key_Minus: setZoom(false, needsRedraw); break; /* move the map with keys up, down, left and right */ case Qt::Key_Up: moveMap(QPointF( 0, height()/4)); break; case Qt::Key_Down: moveMap(QPointF( 0, -height()/4)); break; case Qt::Key_Left: moveMap(QPointF( width()/4, 0)); break; case Qt::Key_Right: moveMap(QPointF(-width()/4, 0)); break; case Qt::Key_Escape: { IMouseEditLine *lineMouse = dynamic_cast(mouse); if(lineMouse != 0) { lineMouse->abortStep(); } else { doUpdate = false; } break; } default: doUpdate = false; } if(doUpdate) { mouse->keyPressEvent(e); e->accept(); update(); } else { QWidget::keyPressEvent(e); } } void CCanvas::drawStatusMessages(QPainter& p) { if(labelStatusMessages->isVisible()) { QRect r = labelStatusMessages->rect(); r.adjust(-5, -5, 5, 5); r.moveTopLeft(QPoint(15,45)); p.setPen(CDraw::penBorderGray); p.setBrush(CDraw::brushBackWhite); p.drawRoundedRect(r, 5, 5); } } void CCanvas::drawScale(QPainter& p) { if(!CMainWindow::self().isScaleVisible()) { return; } // step I: get the approximate distance for 200px in the bottom right corner QPointF brc(rect().bottomRight() - QPoint(50,30)); QPointF pt1 = brc; QPointF pt2 = brc - QPoint(-200,0); map->convertPx2Rad(pt1); map->convertPx2Rad(pt2); qreal d = GPS_Math_Distance(pt1.x(), pt1.y(), pt2.x(), pt2.y()); // step II: derive the actual scale length in [m] qreal a = (int)log10(d); qreal b = log10(d) - a; if(0 <= b && b < log10(3.0f)) { d = 1 * qPow(10,a); } else if(log10(3.0f) < b && b < log10(5.0f)) { d = 3 * qPow(10,a); } else { d = 5 * qPow(10,a); } // step III: convert the scale length from [m] into [px] pt1 = brc; map->convertPx2Rad(pt1); pt2 = GPS_Math_Wpt_Projection(pt1, d, -90 * DEG_TO_RAD); map->convertRad2Px(pt1); map->convertRad2Px(pt2); p.setPen(QPen(Qt::white, 9)); p.drawLine(pt1, pt2 + QPoint(9,0)); p.setPen(QPen(Qt::black, 7)); p.drawLine(pt1, pt2 + QPoint(9,0)); p.setPen(QPen(Qt::white, 5)); p.drawLine(pt1, pt2 + QPoint(9,0)); QVector pattern; pattern << 2 << 4; QPen pen(Qt::black, 5, Qt::CustomDashLine); pen.setDashPattern(pattern); p.setPen(pen); p.drawLine(pt1, pt2 + QPoint(9,0)); QPoint pt3(pt2.x() + (pt1.x() - pt2.x())/2, pt2.y()); QString val, unit; IUnit::self().meter2distance(d,val,unit); CDraw::text(QString("%1 %2").arg(val).arg(unit), p, pt3, Qt::black); } void CCanvas::slotTriggerCompleteUpdate(CCanvas::redraw_e flags) { needsRedraw = (redraw_e)(needsRedraw | flags); update(); } void CCanvas::slotToolTip() { QString str; map->getToolTip(posToolTip, str); if(str.isEmpty()) { return; } QPoint p = mapToGlobal(posToolTip + QPoint(32,0)); QToolTip::showText(p,str); } void CCanvas::slotCheckTrackOnFocus() { const IGisItem::key_t& key = CGisItemTrk::getKeyUserFocus(); // any changes? if(key != keyTrackOnFocus) { saveSizeTrackProfile(); // get access to current track object delete plotTrackProfile; delete colorLegend; keyTrackOnFocus.clear(); // get access to next track object CGisItemTrk * trk2 = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(trk2 == 0) { return; } // create new profile plot, the plot will register itself at the track plotTrackProfile = new CPlotProfile(trk2, CMainWindow::self().profileIsWindow() ? IPlot::eModeWindow : IPlot::eModeIcon, this); setSizeTrackProfile(); if(isVisible()) { plotTrackProfile->show(); } colorLegend = new CColorLegend(this, trk2); colorLegend->setGeometry(20, 20, 40, 300); // finally store the new key as track on focus keyTrackOnFocus = key; } } void CCanvas::moveMap(const QPointF& delta) { map->convertRad2Px(posFocus); posFocus -= delta; map->convertPx2Rad(posFocus); emit sigMove(); slotTriggerCompleteUpdate(eRedrawAll); } void CCanvas::zoomTo(const QRectF& rect) { posFocus = rect.center(); map->zoom(rect); dem->zoom(map->zoom()); gis->zoom(map->zoom()); slotTriggerCompleteUpdate(eRedrawAll); } void CCanvas::setupGrid() { CGridSetup dlg(grid, map); dlg.exec(); update(); } void CCanvas::convertGridPos2Str(const QPointF& pos, QString& str, bool simple) { grid->convertPos2Str(pos, str, simple); } void CCanvas::convertRad2Px(QPointF& pos) { map->convertRad2Px(pos); } void CCanvas::convertPx2Rad(QPointF& pos) { map->convertPx2Rad(pos); } void CCanvas::displayInfo(const QPoint& px) { if(CMainWindow::self().isMapToolTip()) { posToolTip = px; timerToolTip->stop(); timerToolTip->start(500); } QToolTip::hideText(); } void CCanvas::setup() { CCanvasSetup dlg(this); dlg.exec(); } QString CCanvas::getProjection() { return map->getProjection(); } void CCanvas::setProjection(const QString& proj) { map->setProjection(proj); dem->setProjection(proj); gis->setProjection(proj); } void CCanvas::setScales(const scales_type_e type) { map->setScales(type); dem->setScales(type); gis->setScales(type); } CCanvas::scales_type_e CCanvas::getScalesType() { return map->getScalesType(); } qreal CCanvas::getElevationAt(const QPointF& pos) { return dem->getElevationAt(pos); } void CCanvas::getElevationAt(const QPolygonF& pos, QPolygonF& ele) { return dem->getElevationAt(pos, ele); } void CCanvas::getElevationAt(SGisLine& line) { return dem->getElevationAt(line); } void CCanvas::setZoom(bool in, redraw_e& needsRedraw) { map->zoom(in, needsRedraw); dem->zoom(map->zoom()); gis->zoom(map->zoom()); emit sigZoom(); } bool CCanvas::findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline) { return map->findPolylineCloseBy(pt1, pt2, threshold, polyline); } void CCanvas::saveSizeTrackProfile() { if(plotTrackProfile == 0) { return; } if(plotTrackProfile->windowFlags() & Qt::Window) { SETTINGS; cfg.beginGroup("Canvas"); cfg.beginGroup("Profile"); cfg.beginGroup(objectName()); cfg.setValue("geometry", plotTrackProfile->saveGeometry()); cfg.endGroup(); // objectName() cfg.endGroup(); // Profile cfg.endGroup(); // Canvas } } void CCanvas::setSizeTrackProfile() { if(plotTrackProfile == 0) { return; } if(plotTrackProfile->windowFlags() & Qt::Window) { SETTINGS; cfg.beginGroup("Canvas"); cfg.beginGroup("Profile"); cfg.beginGroup(objectName()); if(cfg.contains("geometry")) { plotTrackProfile->restoreGeometry(cfg.value("geometry").toByteArray()); } else { plotTrackProfile->resize(300,200); plotTrackProfile->move(100,100); } cfg.endGroup(); // objectName() cfg.endGroup(); // Profile cfg.endGroup(); // Canvas } else { if(size().height() < 700) { plotTrackProfile->resize(200,80); } else { plotTrackProfile->resize(300,120); } plotTrackProfile->move(20, height() - plotTrackProfile->height() - 20); } } void CCanvas::showProfileAsWindow(bool yes) { if(plotTrackProfile) { const IGisItem::key_t key = CGisItemTrk::getKeyUserFocus(); delete plotTrackProfile; keyTrackOnFocus.clear(); CGisWidget::self().focusTrkByKey(true, key); } } void CCanvas::showProfile(bool yes) { if(plotTrackProfile) { if(yes) { plotTrackProfile->show(); } else { plotTrackProfile->hide(); } } } void CCanvas::setDrawContextSize(const QSize& s) { if(map) { map->resize(s); } if(dem) { dem->resize(s); } if(gis) { gis->resize(s); } } void CCanvas::print(QPainter& p, const QRectF& area, const QPointF& focus) { const QSize oldSize = size(); const QSize newSize(area.size().toSize()); setDrawContextSize(newSize); // ----- start to draw thread based content ----- // move coordinate system to center of the screen p.translate(newSize.width() >> 1, newSize.height() >> 1); redraw_e redraw = eRedrawAll; map->draw(p, redraw, focus); dem->draw(p, redraw, focus); gis->draw(p, redraw, focus); map->wait(); dem->wait(); gis->wait(); map->draw(p, redraw, focus); dem->draw(p, redraw, focus); gis->draw(p, redraw, focus); // restore coordinate system to default p.resetTransform(); // ----- start to draw fast content ----- QRect r(QPoint(0,0), area.size().toSize()); grid->draw(p, r); gis->draw(p, r); setDrawContextSize(oldSize); } qmapshack-1.5.1/src/canvas/CCanvasSetup.h000644 001750 000144 00000002437 12622435722 021244 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCANVASSETUP_H #define CCANVASSETUP_H #include "ui_ICanvasSetup.h" #include class CCanvas; class CCanvasSetup : public QDialog, private Ui::ICanvasSetup { Q_OBJECT public: CCanvasSetup(CCanvas *canvas); virtual ~CCanvasSetup(); public slots: void accept(); protected slots: void slotProjWizard(); protected: CCanvas * canvas; }; #endif //CCANVASSETUP_H qmapshack-1.5.1/src/canvas/IDrawContext.cpp000644 001750 000144 00000027555 12622435717 021627 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/IDrawContext.h" #include #define BUFFER_BORDER 50 #define N_DEFAULT_ZOOM_LEVELS 31 const qreal IDrawContext::scalesDefault[N_DEFAULT_ZOOM_LEVELS] = { 0.10 , 0.15 , 0.20 , 0.30 , 0.50 , 0.70 , 1.0 , 1.5 , 2.0 , 3.0 , 5.0 , 7.0 , 10.0 , 15.0 , 20.0 , 30.0 , 50.0 , 70.0 , 100.0 , 150.0 , 200.0 , 300.0 , 500.0 , 700.0 , 1000.0 , 1500.0 , 2000.0 , 3000.0 , 5000.0 , 7000.0 , 10000.0 // , 15000.0 // , 20000.0 // , 30000.0 // , 50000.0 // , 70000.0 }; #define N_SQUARE_ZOOM_LEVELS 17 const qreal IDrawContext::scalesSquare[N_SQUARE_ZOOM_LEVELS] = { 0.1492910709 , 0.2985821417 , 0.5971642834 , 1.1943285668 , 2.3886571336 , 4.7773142672 , 9.5546285344 , 19.1092570688 , 38.2185141376 , 76.4370282752 , 152.8740565504 , 305.7481131008 , 611.4962262016 , 1222.9924524032 , 2445.9849048064 , 4891.9698096128 , 9783.9396192256 }; QPointF operator*(const QPointF& p1, const QPointF& p2) { return QPointF(p1.x() * p2.x(), p1.y() * p2.y()); } QPointF operator/(const QPointF& p1, const QPointF& p2) { return QPointF(p1.x() / p2.x(), p1.y() / p2.y()); } IDrawContext::IDrawContext(const QString& name, CCanvas::redraw_e maskRedraw, CCanvas *parent) : QThread(parent) , canvas(parent) , maskRedraw(maskRedraw) { setObjectName(name); // setup map parameters and connect to canvas pjsrc = pj_init_plus("+proj=merc +a=6378137.0000 +b=6356752.3142 +towgs84=0,0,0,0,0,0,0,0 +units=m +no_defs"); pjtar = pj_init_plus("+proj=longlat +a=6378137.0000 +b=6356752.3142 +towgs84=0,0,0,0,0,0,0,0 +units=m +no_defs"); setScales(CCanvas::eScalesDefault); zoom(5); resize(canvas->size()); connect(this, SIGNAL(finished()), canvas, SLOT(update())); connect(this, SIGNAL(finished()), SIGNAL(sigStopThread())); } IDrawContext::~IDrawContext() { pj_free(pjtar); pj_free(pjsrc); } void IDrawContext::emitSigCanvasUpdate() { emit sigCanvasUpdate(maskRedraw); } void IDrawContext::resize(const QSize& size) { if(isRunning()) { wait(); } mutex.lock(); // --------- start serialize with thread viewWidth = size.width(); viewHeight = size.height(); center = QPointF(viewWidth/2.0, viewHeight/2.0); bufWidth = viewWidth + 2 * BUFFER_BORDER; bufHeight = viewHeight + 2 * BUFFER_BORDER; buffer[0].image = QImage(bufWidth, bufHeight, QImage::Format_ARGB32); buffer[1].image = QImage(bufWidth, bufHeight, QImage::Format_ARGB32); mutex.unlock(); // --------- stop serialize with thread } QString IDrawContext::getProjection() { if(pjsrc == 0) { return QString::Null(); } char * p = pj_get_def(pjsrc,0); QString str(p); free(p); return str; } void IDrawContext::setProjection(const QString& proj) { if(pjsrc != 0) { pj_free(pjsrc); } pjsrc = pj_init_plus(proj.toLatin1()); } CCanvas::scales_type_e IDrawContext::getScalesType() { return scalesType; } void IDrawContext::setScales(const CCanvas::scales_type_e type) { int i; switch (type) { case CCanvas::eScalesDefault: for (i=0; i < N_DEFAULT_ZOOM_LEVELS; i++) { scales[i] = scalesDefault[i]; } zoomLevels = N_DEFAULT_ZOOM_LEVELS; scalesType = type; break; case CCanvas::eScalesSquare: for (i=0; i < N_SQUARE_ZOOM_LEVELS; i++) { scales[i] = scalesSquare[i]; } zoomLevels = N_SQUARE_ZOOM_LEVELS; scalesType = type; break; default: qDebug() << "Invalid type of scales table" << scalesType; } } bool IDrawContext::needsRedraw() { bool res = false; mutex.lock(); res = intNeedsRedraw; mutex.unlock(); return res; } void IDrawContext::zoom(const QRectF& rect) { if(pjsrc == 0) { return; } // special case for elements with no extent if(rect.width() == 0 || rect.height() == 0) { zoom(8); return; } // zoom out from closest zoom level until a match is found for(int i = 0; i < zoomLevels; i++) { zoom(i); QPointF pt1 = rect.topLeft(); QPointF pt2 = rect.bottomRight(); convertRad2Px(pt1); convertRad2Px(pt2); QPointF pt = pt2 - pt1; if(qAbs(pt.x()) < (bufWidth - 2 * BUFFER_BORDER) && (qAbs(pt.y()) < (bufHeight - 2 * BUFFER_BORDER))) { break; } } } void IDrawContext::zoom(bool in, CCanvas::redraw_e& needsRedraw) { if(pjsrc == 0) { return; } zoom(zoomIndex + (in ? -1 : 1)); needsRedraw = CCanvas::eRedrawAll; } void IDrawContext::zoom(int idx) { if(idx < 0) { idx = 0; } if(idx >= zoomLevels) { idx = zoomLevels -1; } mutex.lock(); // --------- start serialize with thread if((zoomIndex != idx) || (zoomFactor.x() != scales[idx])) { zoomIndex = idx; zoomFactor.rx() = scales[idx]; zoomFactor.ry() = scales[idx]; intNeedsRedraw = true; emit sigScaleChanged(scale*zoomFactor); } mutex.unlock(); // --------- stop serialize with thread } void IDrawContext::convertRad2M(QPointF &p) { if(pjsrc == 0) { return; } qreal y = p.y(); /* Proj4 makes a wrap around for values outside the range of -180..180°. But the draw context has no turnaround. It exceeds the values. We have to apply fixes in that case. */ bool fixWest = p.x() < (-180*DEG_TO_RAD); bool fixEast = p.x() > ( 180*DEG_TO_RAD); pj_transform(pjtar,pjsrc,1,0,&p.rx(),&p.ry(),0); /* The idea of the fix is to calculate a point at the boundary with the same latitude and use it as offset. */ if(fixWest) { QPointF o(-180*DEG_TO_RAD,y); convertRad2M(o); p.rx() = 2*o.x() + p.x(); } if(fixEast) { QPointF o(180*DEG_TO_RAD,y); convertRad2M(o); p.rx() = 2*o.x() + p.x(); } } void IDrawContext::convertM2Rad(QPointF &p) { if(pjsrc == 0) { return; } pj_transform(pjsrc,pjtar,1,0,&p.rx(),&p.ry(),0); } void IDrawContext::convertPx2Rad(QPointF &p) { mutex.lock(); // --------- start serialize with thread QPointF f = focus; convertRad2M(f); p = f + (p - center) * scale * zoomFactor; convertM2Rad(p); mutex.unlock(); // --------- stop serialize with thread } void IDrawContext::convertRad2Px(QPointF &p) { mutex.lock(); // --------- start serialize with thread QPointF f = focus; convertRad2M(f); convertRad2M(p); p = (p - f) / (scale * zoomFactor) + center; mutex.unlock(); // --------- stop serialize with thread } void IDrawContext::convertRad2Px(QPolygonF& poly) { mutex.lock(); // --------- start serialize with thread QPointF f = focus; convertRad2M(f); for(int i = 0; i < poly.size(); i++) { QPointF& p = poly[i]; convertRad2M(p); p = (p - f) / (scale * zoomFactor) + center; } mutex.unlock(); // --------- stop serialize with thread } void IDrawContext::draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QPointF& f) { if(pjsrc == 0) { return; } // convert global coordinate of focus into point of map focus = f; QPointF f1 = focus; convertRad2M(f1); QPointF bufferScale = scale * zoomFactor; mutex.lock(); // --------- start serialize with thread // derive top left reference coordinate of map buffer ref1 = f1 + QPointF(-bufWidth/2, -bufHeight/2) * bufferScale; convertM2Rad(ref1); // derive top right reference coordinate of map buffer ref2 = f1 + QPointF( bufWidth/2, -bufHeight/2) * bufferScale; convertM2Rad(ref2); // derive bottom right reference coordinate of map buffer ref3 = f1 + QPointF( bufWidth/2, bufHeight/2) * bufferScale; convertM2Rad(ref3); // derive bottom left reference coordinate of map buffer ref4 = f1 + QPointF(-bufWidth/2, bufHeight/2) * bufferScale; convertM2Rad(ref4); // adjust west <-> east boundaries if(ref1.x() > ref2.x()) { if(qAbs(ref1.x()) > qAbs(ref2.x())) { ref1.rx() = -2*(180*DEG_TO_RAD) + ref1.rx(); } if(qAbs(ref4.x()) > qAbs(ref3.x())) { ref4.rx() = -2*(180*DEG_TO_RAD) + ref4.rx(); } if(qAbs(ref1.x()) < qAbs(ref2.x())) { ref2.rx() = 2*(180*DEG_TO_RAD) + ref2.rx(); } if(qAbs(ref4.x()) < qAbs(ref3.x())) { ref3.rx() = 2*(180*DEG_TO_RAD) + ref3.rx(); } } // qDebug() << (ref1 * RAD_TO_DEG) << (ref2 * RAD_TO_DEG) << (ref3 * RAD_TO_DEG) << (ref4 * RAD_TO_DEG); // get current active buffer buffer_t& currentBuffer = buffer[bufIndex]; // convert buffers top left reference point to local coordinate system QPointF ref = currentBuffer.ref1; convertRad2M(ref); // derive offset to show coordinate of focus right in the middle of the draw // context. NOTE: the draw context's coordinate system has been moved into the // middle of the view port. QPointF off = (ref - f1) / (currentBuffer.scale * currentBuffer.zoomFactor); p.save(); // scale image if current zoomfactor does not match buffer's zoomfactor p.scale(currentBuffer.zoomFactor.x()/zoomFactor.x(), currentBuffer.zoomFactor.y()/zoomFactor.y()); // add offset p.translate(off); // draw buffer to painter p.drawImage(0,0, currentBuffer.image); p.restore(); // intNeedsRedraw is reset by the thread if(needsRedraw & maskRedraw) { intNeedsRedraw = true; } mutex.unlock(); // --------- stop serialize with thread if((needsRedraw & maskRedraw) && !isRunning()) { emit sigStartThread(); start(); } } void IDrawContext::run() { mutex.lock(); QTime t; t.start(); qDebug() << "start thread"; IDrawContext::buffer_t& currentBuffer = buffer[!bufIndex]; while(intNeedsRedraw) { // copy all projection information need by the // map render objects to buffer structure currentBuffer.pjsrc = pjsrc; currentBuffer.zoomFactor = zoomFactor; currentBuffer.scale = scale; currentBuffer.ref1 = ref1; currentBuffer.ref2 = ref2; currentBuffer.ref3 = ref3; currentBuffer.ref4 = ref4; currentBuffer.focus = focus; intNeedsRedraw = false; mutex.unlock(); qDebug() << "bufferScale" << (currentBuffer.scale * currentBuffer.zoomFactor); // ----- reset buffer ----- currentBuffer.image.fill(Qt::transparent); drawt(currentBuffer); mutex.lock(); } // ----- switch buffer ------ bufIndex = !bufIndex; qDebug() << objectName() << "stop thread" << t.elapsed(); mutex.unlock(); } qmapshack-1.5.1/src/canvas/IDrawObject.h000644 001750 000144 00000010022 12622435722 021027 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDRAWOBJECT_H #define IDRAWOBJECT_H #include "units/IUnit.h" #include class QSettings; class QListWidget; class IDrawObject : public QObject { Q_OBJECT public: IDrawObject(QObject * parent); virtual ~IDrawObject(); virtual void saveConfig(QSettings& cfg); virtual void loadConfig(QSettings& cfg); /** @brief Read opacity value @return Return the opacity in a range of 0..100(full opacity) */ int getOpacity() { return opacity; } /** @brief Read the minimum scale factor the object should be displayed @return A factor or NOFLOAT if no minimum has been set */ qreal getMinScale() { return minScale; } /** @brief Read the maximum scale factor the object should be displayed @return A factor or NOFLOAT if no maximum has been set */ qreal getMaxScale() { return maxScale; } /** @brief Write the minimum scale the object should be displayed at. @param s A factor or NOFLOAT if no minimum should be set */ void setMinScale(qreal s); /** @brief Write the maximum scale the object should be displayed at. @param s A factor or NOFLOAT if no maximum should be set */ void setMaxScale(qreal s); /** @brief Get QListWidgetItems to enable/disable map layers. As this property is a bit more complex the idea is to reimplement the method if the map type has layers. The implementation probably will clear the list and insert a checkable list widget item into the list. Additionally it will connect to the QListWidget's signals to be noticed by a change. Different to other properties, that will get queried when ever the property widget think it needs an update, getLayers() will only be called once upon property widget creation. The default implementation will simply clear the list. @param list */ virtual void getLayers(QListWidget& list); public slots: /** @brief Write opacity value @param value must be in the range of 0..100(full opacity) */ void slotSetOpacity(int value) { opacity = value; } signals: /** @brief Emitted every time a property of the object is changed */ void sigPropertiesChanged(); protected: /** @brief Test if the given scale is out of the min/max scale @param scale A scale factor for x and y axis @return True if x scale is out of the min/max range */ bool isOutOfScale(const QPointF& scale); /** @brief Setup a map cache using cachePath, cacheSizeMB and cacheExpiration The default implementation does noting. Streaming maps will probably override it to reconfigure their cache. The method is called when ever a cache property is changed. */ virtual void configureCache() { } private: /// the opacity level of a map qreal opacity = 100; /// the minimum scale a map is visible qreal minScale = NOFLOAT; /// the maximum scale a map is visible qreal maxScale = NOFLOAT; }; #endif //IDRAWOBJECT_H qmapshack-1.5.1/src/canvas/IDrawObject.cpp000644 001750 000144 00000004342 12622435717 021376 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/IDrawObject.h" #include "units/IUnit.h" #include IDrawObject::IDrawObject(QObject *parent) : QObject(parent) { } IDrawObject::~IDrawObject() { } void IDrawObject::saveConfig(QSettings& cfg) { cfg.setValue("opacity", getOpacity()); cfg.setValue("minScale", getMinScale()); cfg.setValue("maxScale", getMaxScale()); } void IDrawObject::loadConfig(QSettings& cfg) { slotSetOpacity(cfg.value("opacity", getOpacity()).toDouble()); setMinScale(cfg.value("minScale", getMinScale()).toDouble()); setMaxScale(cfg.value("maxScale", getMaxScale()).toDouble()); emit sigPropertiesChanged(); } bool IDrawObject::isOutOfScale(const QPointF& scale) { if((getMinScale() != NOFLOAT) && (scale.x() < getMinScale())) { return true; } if((getMaxScale() != NOFLOAT) && (scale.x() > getMaxScale())) { return true; } return false; } void IDrawObject::getLayers(QListWidget& list) { list.clear(); } void IDrawObject::setMinScale(qreal s) { if((s != NOFLOAT) && (maxScale != NOFLOAT)) { if(s > maxScale) { return; } } minScale = s; } void IDrawObject::setMaxScale(qreal s) { if((s != NOFLOAT) && (minScale != NOFLOAT)) { if(s < minScale) { return; } } maxScale = s; } qmapshack-1.5.1/src/canvas/CCanvasSetup.cpp000644 001750 000144 00000004104 12622435717 021574 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "canvas/CCanvasSetup.h" #include "grid/CProjWizard.h" CCanvasSetup::CCanvasSetup(CCanvas * canvas) : QDialog(canvas) , canvas(canvas) { setupUi(this); lineProjection->setText(canvas->getProjection()); lineProjection->setCursorPosition(0); switch(canvas->getScalesType()) { case CCanvas::eScalesDefault: radioScalesDefault->setChecked(true); break; case CCanvas::eScalesSquare: radioScalesSquare->setChecked(true); break; } connect(toolWizard, SIGNAL(clicked()), this, SLOT(slotProjWizard())); } CCanvasSetup::~CCanvasSetup() { } void CCanvasSetup::slotProjWizard() { CProjWizard dlg(*lineProjection); dlg.exec(); } void CCanvasSetup::accept() { if(!CProjWizard::validProjStr(lineProjection->text())) { return; } canvas->setProjection(lineProjection->text()); if(radioScalesDefault->isChecked()) { canvas->setScales(CCanvas::eScalesDefault); } else if(radioScalesSquare->isChecked()) { canvas->setScales(CCanvas::eScalesSquare); } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawAll); QDialog::accept(); } qmapshack-1.5.1/src/canvas/ICanvasSetup.ui000644 001750 000144 00000006737 12527654570 021457 0ustar00oeichlerusers000000 000000 ICanvasSetup 0 0 446 179 Setup Map View... Projection & Datum ... :/icons/32x32/GridWizzard.png:/icons/32x32/GridWizzard.png 22 22 Scales Logarithmic Square (optimized for TMS and WTMS tiles) Qt::Vertical 20 9 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ICanvasSetup accept() 248 254 157 274 buttonBox rejected() ICanvasSetup reject() 316 260 286 274 qmapshack-1.5.1/src/canvas/IDrawContext.h000644 001750 000144 00000015743 12622435722 021264 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDRAWCONTEXT_H #define IDRAWCONTEXT_H #include #include #include #include #include #include "canvas/CCanvas.h" #define CANVAS_MAX_ZOOM_LEVELS 31 class IDrawContext : public QThread { Q_OBJECT public: IDrawContext(const QString &name, CCanvas::redraw_e maskRedraw, CCanvas *parent); virtual ~IDrawContext(); struct buffer_t { /// @note: all coordinate values are long/lat WGS84 [rad] /// the canvas buffer QImage image; /// the used projection projPJ pjsrc; /// the zoomfactor used to draw the canvas QPointF zoomFactor {1.0,1.0}; /// the number of zoom levels int zoomLevels; /// the scale of the global viewport QPointF scale {1.0,1.0}; /// top left corner QPointF ref1; /// top right corner QPointF ref2; /// bottom right corner QPointF ref3; /// bottom left corner QPointF ref4; /// point of focus QPointF focus; }; /** @brief resize the internal buffer @param size the new size of the canvas */ void resize(const QSize& size); /** @brief Zoom in and out of the map by the scale factors defined in CMapDB::scales. @param in set true to zoom in, and false to zoom out @param needsRedraw if the zoom action makes a redraw necessary needsRedraw is set true */ void zoom(bool in, CCanvas::redraw_e &needsRedraw); void zoom(int idx); void zoom(const QRectF& rect); int zoom() { return zoomIndex; } /** @brief Convert a geo coordinate of format lon/lat WGS84 into the currently used coordinate/projection/datum system. @note The unit is dependent on the currently used projection and must not necessarily be meter @param p the point to convert */ void convertRad2M(QPointF &p); /** @brief Convert a geo coordinate of the currently used projection/datum to lon/lat WGS84 @note The unit is dependent on the currently used projection and must not necessarily be meter @param p the point to convert */ void convertM2Rad(QPointF &p); /** @brief Convert a pixel coordinate from the viewport to a geo coordinate in [rad] @param p the point to convert */ void convertPx2Rad(QPointF& p); /** @brief Convert a geo coordinate in [rad] to a pixel coordinate of the viewport @param p the point to convert */ void convertRad2Px(QPointF& p); void convertRad2Px(QPolygonF& poly); /** @brief Check if the internal needs redraw flag is set @return intNeedsRedraw is returned */ bool needsRedraw(); /** @brief Draw the active map buffer to the painter @param p the painter used to draw the map @param needsRedraw set true to trigger a redraw in the background thread @param f the point of focus in [°] that is drawn in the middle of the viewport. */ void draw(QPainter& p, CCanvas::redraw_e needsRedraw, const QPointF &f); /** @brief Get the projection string of this map object @return A proj4 string. */ QString getProjection(); CCanvas::scales_type_e getScalesType(); /** @brief Set the projection of the draw context This will just create a new source projection object (pjsrc). Most likely you want to override this method to: 1) save what ever has to be saved 2) call this method 3) restore everything with the new projection @param proj a Proj4 projection string */ virtual void setProjection(const QString& proj); virtual void setScales(const CCanvas::scales_type_e type); signals: void sigCanvasUpdate(CCanvas::redraw_e flags); void sigStartThread(); void sigStopThread(); void sigScaleChanged(const QPointF& scale); public slots: void emitSigCanvasUpdate(); protected: void run(); /** @brief The draw method called from the thread. That's where the actual drawing has to be done @param currentBuffer this is the current buffer reserved for the thread to draw on. */ virtual void drawt(buffer_t& currentBuffer) = 0; /** @brief The global list of available scale factors */ static const qreal scalesDefault[]; static const qreal scalesSquare[]; /// the mutex to serialize access QMutex mutex; /// internal needs redraw flag bool intNeedsRedraw; /// the canvas this map object is attached to CCanvas * canvas; const CCanvas::redraw_e maskRedraw; /// map canvas twin buffer buffer_t buffer[2]; /// the main threads currently used map canvas buffer bool bufIndex = false; /// buffer width [px] int bufWidth = 100; /// buffer height [px] int bufHeight = 100; /// the viewports width [px] int viewWidth = 100; /// the viewports height [px] int viewHeight = 100; /// the center of the viewport QPointF center; /// source projection should be the same for all maps projPJ pjsrc; /// target projection is always WGS84 projPJ pjtar; /// the used scales and the type of scale levels qreal scales[CANVAS_MAX_ZOOM_LEVELS]; CCanvas::scales_type_e scalesType; /// the number of zoom levels int zoomLevels = 0; /// the basic scale of the map canvas QPointF scale {1.0,-1.0}; /// index into scales table int zoomIndex = 0; /// the actual used scaleFactor QPointF zoomFactor; /// the next point of focus that will be displayed right in the middle of the viewport QPointF focus; /// top left corner of next buffer QPointF ref1; /// top right corner of next buffer QPointF ref2; /// bottom right corner of next buffer QPointF ref3; /// bottom left corner of next buffer QPointF ref4; }; extern QPointF operator*(const QPointF& p1, const QPointF& p2); extern QPointF operator/(const QPointF& p1, const QPointF& p2); #endif //IDRAWCONTEXT_H qmapshack-1.5.1/src/canvas/CCanvas.h000644 001750 000144 00000014715 12622435722 020225 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCANVAS_H #define CCANVAS_H #include #include #include #include #include #include "gis/IGisItem.h" class CMapDraw; class CGrid; class CDemDraw; class CGisDraw; class CGisItemWpt; class CGisItemTrk; class CGisItemRte; class CGisItemOvlArea; class CColorLegend; class QSettings; class QPointF; class IMouse; class QTimer; class QMovie; class QLabel; class IPlot; struct SGisLine; class CCanvas : public QWidget { Q_OBJECT public: CCanvas(QWidget * parent, const QString& name); virtual ~CCanvas(); static void setOverrideCursor(const QCursor& cursor, const QString &src); static void restoreOverrideCursor(const QString &src); static void changeOverrideCursor(const QCursor& cursor, const QString &src); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); void setupGrid(); void convertGridPos2Str(const QPointF& pos, QString& str, bool simple); void convertRad2Px(QPointF &pos); void convertPx2Rad(QPointF& pos); void setup(); QString getProjection(); void setProjection(const QString& proj); enum scales_type_e { eScalesDefault , eScalesSquare }; void setScales(const scales_type_e type); scales_type_e getScalesType(); qreal getElevationAt(const QPointF &pos); void getElevationAt(const QPolygonF& pos, QPolygonF &ele); void getElevationAt(SGisLine &line); void moveMap(const QPointF &delta); void zoomTo(const QRectF& rect); void displayInfo(const QPoint& px); enum redraw_e { eRedrawNone = 0 , eRedrawMap = 0x01 , eRedrawDem = 0x02 , eRedrawGis = 0x04 , eRedrawMouse = 0x08 , eRedrawAll = 0xFFFFFFFF }; void resetMouse(); void setMouseMoveWpt(CGisItemWpt& wpt); void setMouseEditTrk(CGisItemTrk& trk); void setMouseRangeTrk(CGisItemTrk& trk); void setMouseEditTrk(const QPointF& pt); void setMouseEditRte(const QPointF& pt); void setMouseEditRte(CGisItemRte& rte); void setMouseEditArea(CGisItemOvlArea& area); void setMouseEditArea(const QPointF& pt); void setMouseWptBubble(const IGisItem::key_t& key); void setMousePrint(); void showProfileAsWindow(bool yes); void showProfile(bool yes); /** @brief Add a message by key to be reported on the canvas Messages from various sources will be collected in a list and displayed in the top left corner of the widget. @note The object reporting has to take care to remove the message by reporting an empty string. @param key the key to identify the reporting object @param msg the message to report */ void reportStatus(const QString& key, const QString& msg); /** @brief Find a matching street polyline The polyline must be close enough in terms of pixel to point 1 and 2. "Close enough" is defined by the threshold. The returned polyline uses lon/lat as coordinates. @param pt1 first point in [rad] @param pt2 second point in [rad] @param threshold the "close enough" threshold in [pixel] @param polyline the resulting polyline, if any, in [rad] @return Return true if a line has been found. */ bool findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline); void print(QPainter &p, const QRectF& area, const QPointF &focus); signals: void sigMousePosition(const QPointF& pos, qreal ele); void sigZoom(); void sigMove(); public slots: void slotTriggerCompleteUpdate(CCanvas::redraw_e flags); protected: void resizeEvent(QResizeEvent * e); void paintEvent(QPaintEvent * e); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent * e); void wheelEvent(QWheelEvent * e); void enterEvent(QEvent * e); void leaveEvent(QEvent * e); void keyPressEvent(QKeyEvent * e); private slots: void slotToolTip(); void slotCheckTrackOnFocus(); private: void drawStatusMessages(QPainter& p); void drawScale(QPainter& p); void setZoom(bool in, redraw_e &needsRedraw); void setSizeTrackProfile(); void saveSizeTrackProfile(); void setDrawContextSize(const QSize& s); /// set true to initiate a complete redraw of the screen content redraw_e needsRedraw; /// the map object attached to this canvas CMapDraw * map; /// the elevation data layer attached to this canvas CDemDraw * dem; /// the GIS data layer attached to this canvas CGisDraw * gis; /// the grid attached to this canvas CGrid * grid; /// the current point of focus (usually the canvas center) QPointF posFocus {12.00 * DEG_TO_RAD, 49.00 * DEG_TO_RAD}; /// the current mouse handler IMouse * mouse; /// tool tip timer for vector map tool tips QTimer * timerToolTip; /// the position of the tool tip QPoint posToolTip; /// load indicator for maps QMovie * loadIndicator1; QLabel * mapLoadIndicator; /// load indicator for DEM QMovie * loadIndicator2; QLabel * demLoadIndicator; QPointer colorLegend; /// timer to poll for track gaining/loosing focus QTimer * timerTrackOnFocus; /// the key of the currently focused track IGisItem::key_t keyTrackOnFocus; /// the track profile plot QPointer plotTrackProfile; QLabel * labelStatusMessages; QMap statusMessages; }; #endif //CCANVAS_H qmapshack-1.5.1/src/helpers/IWptIconDialog.ui000644 001750 000144 00000001244 12527654570 022101 0ustar00oeichlerusers000000 000000 IWptIconDialog 0 0 400 300 Icons... 22 22 qmapshack-1.5.1/src/helpers/CElevationDialog.h000644 001750 000144 00000002630 12622435722 022240 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CELEVATIONDIALOG_H #define CELEVATIONDIALOG_H #include "ui_IElevationDialog.h" #include class CElevationDialog : public QDialog, private Ui::IElevationDialog { Q_OBJECT public: CElevationDialog(QWidget * parent, QVariant &val, const QVariant &reset, const QPointF& pos); virtual ~CElevationDialog(); public slots: void accept(); private slots: void slotReset(); void slotGetEle(); private: QVariant& val; QVariant reset; QPointF pos; }; #endif //CELEVATIONDIALOG_H qmapshack-1.5.1/src/helpers/CSelectProjectDialog.cpp000644 001750 000144 00000012501 12622435717 023415 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisListWks.h" #include "gis/prj/IGisProject.h" #include "helpers/CSelectProjectDialog.h" #include "helpers/CSettings.h" #include QString CSelectProjectDialog::lastkey; CSelectProjectDialog::CSelectProjectDialog(QString &key, QString &name, type_e& type, QTreeWidget * parent) : QDialog(CMainWindow::getBestWidgetForParent()) , key(key) , name(name) , type(type) { setupUi(this); QListWidgetItem * lastSelectedItem = 0; if(parent) { for(int i = 0; i < parent->topLevelItemCount(); i++) { IGisProject * project = dynamic_cast(parent->topLevelItem(i)); if(project == 0) { continue; } QListWidgetItem * item = new QListWidgetItem(project->icon(CGisListWks::eColumnIcon), project->text(CGisListWks::eColumnName),listWidget); item->setData(Qt::UserRole+0, project->getKey()); item->setData(Qt::UserRole+1, project->getType()); item->setData(Qt::UserRole+2, project->getName()); if(project->getKey() == lastkey) { lastSelectedItem = item; } } } else { listWidget->hide(); label1->hide(); } frameType->setEnabled(listWidget->count() == 0); if(lastSelectedItem == 0) { SETTINGS; QString filter = cfg.value("Paths/lastGisFilter", "GPS Exchange Format (*.qms)").toString(); if(filter.contains("qms")) { radioQms->setChecked(true); type = eTypeQms; } else if(filter.contains("gpx")) { radioGpx->setChecked(true); type = eTypeGpx; } else { radioQms->setChecked(true); } buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else { slotItemClicked(lastSelectedItem); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotItemClicked(QListWidgetItem*))); connect(listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotItemDoubleClicked(QListWidgetItem*))); connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotProjectChanged(QString))); connect(lineEdit, SIGNAL(textEdited(QString)), this, SLOT(slotProjectEdited(QString))); connect(radioQms, SIGNAL(clicked()), this, SLOT(slotTypeChanged())); connect(radioGpx, SIGNAL(clicked()), this, SLOT(slotTypeChanged())); connect(radioDatabase, SIGNAL(toggled(bool)), this, SLOT(slotTypeChanged())); adjustSize(); CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSelectProjectDialog"); } CSelectProjectDialog::~CSelectProjectDialog() { CCanvas::restoreOverrideCursor("~CSelectProjectDialog"); } void CSelectProjectDialog::accept() { lastkey = key; QDialog::accept(); } void CSelectProjectDialog::reject() { key.clear(); name.clear(); QDialog::reject(); } void CSelectProjectDialog::slotItemClicked(QListWidgetItem * item) { key = item->data(Qt::UserRole).toString(); lineEdit->setText(item->data(Qt::UserRole+2).toString()); frameType->setEnabled(false); switch(item->data(Qt::UserRole+1).toInt()) { case IGisProject::eTypeQms: radioQms->setChecked(true); break; case IGisProject::eTypeGpx: radioGpx->setChecked(true); break; case IGisProject::eTypeDb: radioDatabase->setChecked(true); break; } } void CSelectProjectDialog::slotItemDoubleClicked(QListWidgetItem * item) { key = item->data(Qt::UserRole).toString(); lineEdit->setText(item->data(Qt::UserRole+2).toString()); frameType->setEnabled(false); QDialog::accept(); } void CSelectProjectDialog::slotProjectChanged(const QString& text) { buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); name = text; } void CSelectProjectDialog::slotProjectEdited(const QString& text) { buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); key.clear(); name = text; frameType->setEnabled(true); } void CSelectProjectDialog::slotTypeChanged() { if(radioQms->isChecked()) { type = eTypeQms; } else if(radioGpx->isChecked()) { type = eTypeGpx; } else if(radioDatabase->isChecked()) { type = eTypeDb; } } qmapshack-1.5.1/src/helpers/CPhotoViewer.h000644 001750 000144 00000003145 12623420703 021441 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPHOTOVIEWER_H #define CPHOTOVIEWER_H #include #include "gis/wpt/CGisItemWpt.h" class CPhotoViewer : public QDialog { Q_OBJECT public: CPhotoViewer(QList &images, int idx, QWidget *parent); virtual ~CPhotoViewer(); protected: void paintEvent(QPaintEvent * e); void resizeEvent(QResizeEvent * e); void mousePressEvent(QMouseEvent * e); void keyPressEvent(QKeyEvent *e); private: void tryIdxStep(int delta); void setImageAtIdx(int i); QList images; int idx; QRect rectImage {0,0,100,100}; QRect rectClose {0,0,32,32}; QRect rectPrev {0,0,32,32}; QRect rectNext {0,0,32,32}; }; #endif //CPHOTOVIEWER_H qmapshack-1.5.1/src/helpers/CAppOpts.h000644 001750 000144 00000003241 12622435722 020557 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2009 Joerg Wunsch This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CAPPOPTS_H #define CAPPOPTS_H /* * This class holds the options passed from the command-line, * including the positional arguments. */ #include class CAppOpts { public: const bool debug; // -d, print debug messages const bool logfile; // -f, print debug messages to logfile const bool nosplash; // -n, do not display splash screen const QString configfile; const QStringList arguments; CAppOpts(bool doDebug, bool doLogfile, bool noSplash, const QString& config, const QStringList& args) : debug(doDebug) , logfile(doLogfile) , nosplash(noSplash) , configfile(config) , arguments(args) { } }; extern CAppOpts *qlOpts; #endif //CAPPOPTS_H qmapshack-1.5.1/src/helpers/CSelectCopyAction.h000644 001750 000144 00000003225 12622435722 022403 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSELECTCOPYACTION_H #define CSELECTCOPYACTION_H #include "ui_ISelectCopyAction.h" #include class IGisItem; class IGisProject; class CSelectCopyAction : public QDialog, private Ui::ISelectCopyAction { Q_OBJECT public: CSelectCopyAction(const IGisItem * src, const IGisItem * tar, QWidget * parent); CSelectCopyAction(const IGisProject * src, const IGisProject * tar, QWidget * parent); virtual ~CSelectCopyAction(); enum result_e { eResultNone, eResultCopy, eResultSkip, eResultClone }; result_e getResult() { return result; } bool allOthersToo(); private slots: void slotSelectResult(); private: result_e result = eResultNone; }; #endif //CSELECTCOPYACTION_H qmapshack-1.5.1/src/helpers/CAppSetup.cpp000644 001750 000144 00000021037 12622435717 021274 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "config.h" #include "helpers/CAppOpts.h" #include "helpers/CAppSetup.h" #include #include #include #include #ifndef _MKSTR_1 #define _MKSTR_1(x) #x #define _MKSTR(x) _MKSTR_1(x) #endif CAppSetup* instance; CAppSetup* CAppSetup::getPlattformInstance() { if(instance == 0) { #ifdef Q_OS_MAC instance = new CAppSetupMac(); #endif #ifdef Q_OS_LINUX instance = new CAppSetupLinux(); #endif #ifdef Q_OS_WIN32 instance = new CAppSetupWin(); #endif } return instance; } QString CAppSetup::logName() { QStringList domainSplit = QCoreApplication::organizationDomain().split("."); QString fileName; foreach(QString part, domainSplit) { fileName = fileName.insert(0, part + "."); } fileName.append(QCoreApplication::applicationName() + ".log"); return fileName; } QString CAppSetup::logFilename() { QDir dir = QDir::temp(); return dir.absoluteFilePath(logName()); } void CAppSetup::appendToFile(QtMsgType type, QString formatedMsg) { Q_UNUSED(type); if(qlOpts->logfile) { QFile outFile(logFilename()); outFile.open(QIODevice::WriteOnly | QIODevice::Append); QTextStream ts(&outFile); ts << formatedMsg << endl; } } void CAppSetup::printToConsole(QtMsgType type, QString formatedMsg) { switch (type) { case QtDebugMsg: if (qlOpts->debug) { std::cout << formatedMsg.toUtf8().constData() << std::endl; } break; #if QT_VERSION >= 0x050500 case QtInfoMsg: std::cout << formatedMsg.toUtf8().constData() << std::endl; break; #endif case QtWarningMsg: std::cerr << formatedMsg.toUtf8().constData() << std::endl; break; case QtCriticalMsg: std::cerr << formatedMsg.toUtf8().constData() << std::endl; break; case QtFatalMsg: std::cerr << formatedMsg.toUtf8().constData() << std::endl; abort(); break; } } void CAppSetup::consoleMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { #if QT_VERSION >= 0x050400 QString txt = qFormatLogMessage(type, context, msg); #else QString txt = msg; #endif CAppSetup::getPlattformInstance()->printToConsole(type, txt); CAppSetup::getPlattformInstance()->appendToFile(type, txt); } void CAppSetup::prepareGdal() { QString gdal = qgetenv("GDAL_DATA"); QString proj = qgetenv("PROJ_LIB"); qDebug() << "GDAL_DATA directory set to " + gdal; qDebug() << "PROJ_LIB directory set to " + proj; GDALAllRegister(); } QString CAppSetup::routinoPath(QDir dirXml, QString xmlFile) { QString file = dirXml.absoluteFilePath(xmlFile); qDebug() << "ROUTINO file is " +file; return file; } QDir CAppSetup::path(QString path, QString subdir, bool mkdir) { QDir pathDir(path); if(subdir != 0) { pathDir = QDir(pathDir.absoluteFilePath(subdir)); } if(mkdir && !pathDir.exists()) { pathDir.mkpath(pathDir.absolutePath()); qDebug() << "path created " << pathDir.absolutePath(); } return pathDir; } QDir CAppSetup::configDir(QString subdir) { QString path = QDir::home().absoluteFilePath(CONFIGDIR); QDir configDir = CAppSetup::path(path, subdir, false); qDebug() << "config dir " << configDir.absolutePath(); return configDir; } void CAppSetup::prepareTranslator(QApplication* app, QTranslator *qtTranslator, QString translationPath, QString translationPrefix) { QString locale = QLocale::system().name(); QDir dir(translationPath); if(!QFile::exists(dir.absoluteFilePath(translationPrefix + locale))) { locale = locale.left(2); } if (qtTranslator->load(translationPrefix + locale, translationPath)) { app->installTranslator(qtTranslator); qDebug() << "using file '"+ translationPath + "/" + translationPrefix + locale + ".qm' for translations."; } } void CAppSetup::prepareConfig() { QString path = QDir::home().absoluteFilePath(CONFIGDIR); CAppSetup::path(path, "WaypointIcons", true); } void CAppSetup::installMessageHandler() { qSetMessagePattern("%{time yyyy-MM-dd h:mm:ss.zzz} [%{type}] %{message}"); qInstallMessageHandler(consoleMessageHandler); } CAppSetupMac::CAppSetupMac() { } void CAppSetupMac::prepareGdal() { QString gdalDir = getResourceDir("gdal"); QString projDir = getResourceDir("proj"); qputenv("GDAL_DATA", gdalDir.toUtf8()); qputenv("PROJ_LIB", projDir.toUtf8()); CAppSetup::prepareGdal(); } QString CAppSetupMac::routinoPath(QString xmlFile) { QDir dirXml(getResourceDir("routino")); return CAppSetup::routinoPath(dirXml, xmlFile); } QString CAppSetupMac::getResourceDir(QString subdir) { QString appResourceDir = QCoreApplication::applicationDirPath(); QDir resourcesDir(appResourceDir); resourcesDir.cdUp(); resourcesDir.cd("Resources"); resourcesDir.cd(subdir); appResourceDir = resourcesDir.absolutePath(); return appResourceDir; } void CAppSetupMac::prepareTranslators(QApplication* app) { QString translationPath = getResourceDir("translations"); QTranslator *qtTranslator = new QTranslator(app); prepareTranslator(app, qtTranslator, translationPath, "qt_"); QTranslator *qlandkartegtTranslator = new QTranslator(app); prepareTranslator(app, qlandkartegtTranslator, translationPath, "qmapshack_"); } QString CAppSetupMac::logFilename() { QDir dir = QDir::home(); dir.cd("Library"); dir.cd("Logs"); return dir.absoluteFilePath(logName()); } CAppSetupLinux::CAppSetupLinux() { } QString CAppSetupLinux::routinoPath(QString xmlFile) { QDir dirXml(_MKSTR(ROUTINO_XML_PATH)); return CAppSetup::routinoPath(dirXml, xmlFile); } void CAppSetupLinux::prepareTranslators(QApplication* app) { QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath); QString translationPath = QCoreApplication::applicationDirPath(); translationPath.replace(QRegExp("bin$"), "share/qmapshack/translations"); QTranslator *qtTranslator = new QTranslator(app); prepareTranslator(app, qtTranslator, resourceDir, "qt_"); QTranslator *qlandkartegtTranslator = new QTranslator(app); prepareTranslator(app, qlandkartegtTranslator, translationPath, "qmapshack_"); } CAppSetupWin::CAppSetupWin() { } void CAppSetupWin::prepareGdal() { // setup environment variables for GDAL/Proj4 QString apppath = QCoreApplication::applicationDirPath(); apppath = apppath.replace("/", "\\"); QString gdalDir = QString("%1\\data").arg(apppath); QString projDir = QString("%1\\share").arg(apppath); qputenv("GDAL_DATA", gdalDir.toUtf8()); qputenv("PROJ_LIB", projDir.toUtf8()); CAppSetup::prepareGdal(); } QString CAppSetupWin::routinoPath(QString xmlFile) { QString apppath = QCoreApplication::applicationDirPath(); apppath = apppath.replace("/", "\\"); QDir dirXml(QString("%1\\routino-xml").arg(apppath).toUtf8()); return CAppSetup::routinoPath(dirXml, xmlFile); } void CAppSetupWin::prepareTranslators(QApplication* app) { QString apppath = QCoreApplication::applicationDirPath(); apppath = apppath.replace("/", "\\"); QString appResourceDir = QString("%1\\translations").arg(apppath).toUtf8(); QTranslator *qtTranslator = new QTranslator(app); prepareTranslator(app, qtTranslator, appResourceDir, "qtbase_"); QTranslator *qlandkartegtTranslator = new QTranslator(app); prepareTranslator(app, qlandkartegtTranslator, appResourceDir, "qmapshack_"); } void CAppSetupWin::prepareConfig() { CAppSetup::prepareConfig(); //reset PATH to avoid that wrong .dll's are loaded qputenv("PATH", ""); } qmapshack-1.5.1/src/helpers/CProgressDialog.h000644 001750 000144 00000003226 12622435722 022120 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPROGRESSDIALOG_H #define CPROGRESSDIALOG_H #include "ui_IProgressDialog.h" #include #include #include #define PROGRESS_SETUP(lbl, min, max, parent) \ CProgressDialog progress(lbl, min, max, parent); #define PROGRESS(x, cmd) \ progress.setValue(x); \ if (progress.wasCanceled()) { cmd; } \ class CProgressDialog : public QDialog, private Ui::IProgressDialog { Q_OBJECT public: CProgressDialog(const QString text, int min, int max, QWidget * parent); virtual ~CProgressDialog(); static CProgressDialog * self(); void setValue(int val); bool wasCanceled(); public slots: void reject(); private: QTime time; static QStack stackSelf; }; #endif //CPROGRESSDIALOG_H qmapshack-1.5.1/src/helpers/CCommandProcessor.cpp000644 001750 000144 00000005105 12622435717 023007 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CCommandProcessor.h" #include #include #include CAppOpts* CCommandProcessor::processOptions(const QStringList &arguments) { QCommandLineParser parser; QCommandLineOption helpOption = parser.addHelpOption(); // h help QCommandLineOption debugOption(QStringList() << "d" << "debug", QCoreApplication::translate("CCommandProcessor", "Print debug output to console.")); parser.addOption(debugOption); QCommandLineOption logfileOption(QStringList() << "f" << "logfile", QCoreApplication::translate("CCommandProcessor", "Print debug output to logfile (temp. path).")); parser.addOption(logfileOption); QCommandLineOption nosplashOption(QStringList() << "n" << "no-splash", QCoreApplication::translate("CCommandProcessor", "Do not show splash screen.")); parser.addOption(nosplashOption); QCommandLineOption configOption(QStringList() << "c" << "config", QCoreApplication::translate("CCommandProcessor", "File with QMapShack configuration."), QCoreApplication::translate("CCommandProcessor", "file")); parser.addOption(configOption); parser.addPositionalArgument("files", QCoreApplication::translate("CCommandProcessor", "Files for future use.")); if (!parser.parse(arguments)) { std::cerr << parser.errorText().toUtf8().constData(); std::cerr << parser.helpText().toUtf8().constData(); exit(1); } if (parser.isSet(helpOption)) { std::cout << parser.helpText().toUtf8().constData(); exit(0); } return new CAppOpts(parser.isSet(debugOption), parser.isSet(logfileOption), parser.isSet(nosplashOption), parser.value(configOption), parser.positionalArguments()); } qmapshack-1.5.1/src/helpers/CWptIconDialog.cpp000644 001750 000144 00000006244 12622435717 022241 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/WptIcons.h" #include "helpers/CWptIconDialog.h" #include static bool keyLessThanAlpha(const QString& s1, const QString& s2) { QRegExp re("[0-9]*"); QString _s1 = s1; QString _s2 = s2; _s1.remove(re); _s2.remove(re); if(_s1 == _s2) { QRegExp re(".*([0-9]*).*"); if(re.exactMatch(s1)) { _s1 = re.cap(1); } else { _s1 = "0"; } if(re.exactMatch(s2)) { _s2 = re.cap(1); } else { _s2 = "0"; } return _s1.toInt() < _s2.toInt(); } return s1 < s2; } CWptIconDialog::CWptIconDialog(QAction *parent) : button(0) , action(parent) { setupUi(this); setupList(action); } CWptIconDialog::CWptIconDialog(QToolButton *parent) : button(parent) , action(0) { setupUi(this); setupList(button); } void CWptIconDialog::setupList(QObject * obj) { QString currentIcon = obj->objectName(); QListWidgetItem * currentItem = 0; const QMap& wptIcons = getWptIcons(); QStringList keys = wptIcons.keys(); QString key; qSort(keys.begin(), keys.end(), keyLessThanAlpha); foreach(key, keys) { const QString& icon = wptIcons[key].path; QPixmap pixmap = loadIcon(icon); QListWidgetItem * item = new QListWidgetItem(pixmap, key, listWidget); if(currentIcon == key) { currentItem = item; } } if(currentItem) { listWidget->setCurrentItem(currentItem); listWidget->scrollToItem(currentItem); } connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotItemClicked(QListWidgetItem*))); connect(listWidget, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(slotItemClicked(QListWidgetItem*))); } CWptIconDialog::~CWptIconDialog() { } void CWptIconDialog::slotItemClicked(QListWidgetItem * item) { if(button) { button->setIcon(item->icon()); button->setObjectName(item->text()); button->setToolTip(item->text()); } else if(action) { action->setIcon(item->icon()); action->setObjectName(item->text()); action->setToolTip(item->text()); } accept(); } qmapshack-1.5.1/src/helpers/IInputDialog.ui000644 001750 000144 00000003571 12527654570 021622 0ustar00oeichlerusers000000 000000 IInputDialog 0 0 294 100 Edit... TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IInputDialog accept() 248 254 157 274 buttonBox rejected() IInputDialog reject() 316 260 286 274 qmapshack-1.5.1/src/helpers/CLinksDialog.cpp000644 001750 000144 00000005041 12622435717 021730 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CLinksDialog.h" #include CLinksDialog::CLinksDialog(QList &links, QWidget *parent) : QDialog(parent) , links(links) { setupUi(this); connect(toolAdd, SIGNAL(clicked()), this, SLOT(slotAddLink())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelLink())); connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged())); foreach(const IGisItem::link_t& link, links) { QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget); item->setText(0, link.type); item->setText(1, link.text); item->setText(2, link.uri.toString()); item->setFlags(item->flags()|Qt::ItemIsEditable); } } CLinksDialog::~CLinksDialog() { } void CLinksDialog::slotItemSelectionChanged() { QList items = treeWidget->selectedItems(); toolDelete->setEnabled(!items.isEmpty()); } void CLinksDialog::slotAddLink() { QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget); item->setText(0, ""); item->setText(1, "enter a text"); item->setText(2, "enter a link"); item->setFlags(item->flags()|Qt::ItemIsEditable); } void CLinksDialog::slotDelLink() { QList items = treeWidget->selectedItems(); qDeleteAll(items); } void CLinksDialog::accept() { links.clear(); for(int i = 0; i < treeWidget->topLevelItemCount(); i++) { QTreeWidgetItem * item = treeWidget->topLevelItem(i); IGisItem::link_t link; link.type = item->text(0); link.text = item->text(1); link.uri = item->text(2); links << link; } QDialog::accept(); } qmapshack-1.5.1/src/helpers/IElevationDialog.ui000644 001750 000144 00000005115 12527654570 022445 0ustar00oeichlerusers000000 000000 IElevationDialog 0 0 400 121 Edit elevation... Elevation - Get elevation from active digital elevation model. ... :/icons/32x32/SetEle.png:/icons/32x32/SetEle.png Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IElevationDialog accept() 248 254 157 274 buttonBox rejected() IElevationDialog reject() 316 260 286 274 qmapshack-1.5.1/src/helpers/CFileExt.h000644 001750 000144 00000003423 12622435722 020533 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILEEXT_H #define CFILEEXT_H #include #include class CFileExt : public QFile { public: CFileExt(const QString &filename) : QFile(filename) , mapped(NULL) { cnt++; } ~CFileExt() { cnt--; } #ifndef Q_OS_WIN32 // data access function const char *data(qint64 offset, qint64 s) { mapped = map(offset, s); mappedSections << mapped; return (const char*)mapped; } void free() { foreach(uchar * p, mappedSections) { unmap(p); } mappedSections.clear(); } #else // data access function const char *data(qint64 offset, qint64 s) { uchar * p = map(offset,s); return (const char *)p; } #endif private: static int cnt; uchar *mapped; QSet mappedSections; }; #endif //CFILEEXT_H qmapshack-1.5.1/src/helpers/CDraw.h000644 001750 000144 00000007410 12622435722 020070 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPAINTER_H #define CPAINTER_H #include #include #include #include "CMainWindow.h" inline void USE_ANTI_ALIASING(QPainter& p, bool useAntiAliasing) { p.setRenderHints(QPainter::TextAntialiasing|QPainter::Antialiasing|QPainter::SmoothPixmapTransform|QPainter::HighQualityAntialiasing, useAntiAliasing); } #define PAINT_ROUNDED_RECT(p,r) p.drawRoundedRect(r,5,5) class CDraw { public: static QPen penBorderBlue; static QPen penBorderGray; static QPen penBorderBlack; static QBrush brushBackWhite; static QBrush brushBackYellow; /** @brief Draw arrows along a line An arrow is drawn if all the following requirements are met: * the position the new arrow would have been drawn is within viewport OR `viewport.height() == 0` * the two points have a distance of at least `minPointDist` * the (potential) position of the new arrow has at least a distance of `minArrowDist` from the previous arrow @param line The line to draw the arrows along @param viewport Restrict drawing of arrows to this viewport (no limitation is applied if `viewport.height() == 0`) @param minPointDist The minimum distance of two points (in px) @param minArrowDist The minimum distance of two consecutive arrows (in px) */ static void arrows(const QPolygonF &line, const QRectF &viewport, QPainter &p, int minPointDist, int minArrowDist); static void text(const QString& str, QPainter &p, const QPoint ¢er, const QColor &color, const QFont &font = CMainWindow::self().getMapFont()); static void text(const QString& str, QPainter &p, const QRect &r, const QColor &color); /** @brief Draw a cartoon bubble `pointerBasePos` denotes the position of the pointer's base, where 0 is `at the very left of the content`, and 1 is `at the very right`. Be careful with small values (near 0) or large values (near 1) for pointerBasePos, this might lead to incorrect drawing, especially if pointerBaseWidth is large. If is larger than 1, a value in pixels is assumed. @param p An active QPainter @param contentRect The area the actual content will be in @param pointerPos The position of the pointer's head @param pointerBaseWidth The width of the pointer @param pointerBasePos The (relative) location of the pointer (in percent / pixels) */ static QPoint bubble(QPainter &p, const QRect &contentRect, const QPoint &pointerPos, int pointerBaseWidth = 20, float pointerBasePos = .5f); private: /** @brief Creates a new arrow using the brush specified @return A QImage containing the arrow */ static QImage createBasicArrow(const QBrush &brush); }; #endif // CPAINTER_H qmapshack-1.5.1/src/helpers/CElevationDialog.cpp000644 001750 000144 00000004545 12622435717 022606 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CElevationDialog.h" #include "units/IUnit.h" #include #include CElevationDialog::CElevationDialog(QWidget * parent, QVariant &val, const QVariant &reset, const QPointF &pos) : QDialog(parent) , val(val) , reset(reset) , pos(pos) { setupUi(this); QPushButton * pushReset = buttonBox->addButton(QDialogButtonBox::Reset); connect(pushReset, SIGNAL(clicked()), this, SLOT(slotReset())); connect(toolGetEle, SIGNAL(clicked()), this, SLOT(slotGetEle())); QString str, unit; IUnit::self().meter2elevation(100, str, unit); labelUnit->setText(unit); if(val != reset) { IUnit::self().meter2elevation(val.toDouble(), str, unit); lineValue->setText(str); } } CElevationDialog::~CElevationDialog() { } void CElevationDialog::accept() { if(lineValue->text().isEmpty()) { val = reset; } else { val.setValue(lineValue->text().toDouble() / IUnit::self().basefactor); } QDialog::accept(); } void CElevationDialog::slotReset() { lineValue->clear(); } void CElevationDialog::slotGetEle() { QVariant ele = CMainWindow::self().getEelevationAt(pos * DEG_TO_RAD); if(ele != NOFLOAT) { QString str, unit; IUnit::self().meter2elevation(ele.toDouble(), str, unit); lineValue->setText(str); } else { labelMessage->setText(tr("No DEM data found for that point.")); } } qmapshack-1.5.1/src/helpers/CInputDialog.cpp000644 001750 000144 00000003202 12622435717 021744 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CInputDialog.h" #include CInputDialog::CInputDialog(QWidget *parent, const QString& text, QVariant& val, const QVariant& reset) : QDialog(parent) , val(val) , reset(reset) { setupUi(this); QPushButton * pushReset = buttonBox->addButton(QDialogButtonBox::Reset); connect(pushReset, SIGNAL(clicked()), this, SLOT(slotReset())); label->setText(text); if(val != reset) { lineEdit->setText(val.toString()); } } CInputDialog::~CInputDialog() { } void CInputDialog::accept() { if(lineEdit->text().isEmpty()) { val = reset; } else { val.setValue(lineEdit->text()); } QDialog::accept(); } void CInputDialog::slotReset() { lineEdit->clear(); } qmapshack-1.5.1/src/helpers/CDraw.cpp000644 001750 000144 00000015006 12622435717 020427 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "helpers/CDraw.h" #include #include #include #include QPen CDraw::penBorderBlue(QColor(10,10,150,220),2); QPen CDraw::penBorderGray(Qt::lightGray,2); QPen CDraw::penBorderBlack(QColor(0,0,0,200),2); QBrush CDraw::brushBackWhite(QColor(255,255,255,255)); QBrush CDraw::brushBackYellow(QColor(0xff, 0xff, 0xcc, 0xE0)); QImage CDraw::createBasicArrow(const QBrush &brush) { QImage arrow(21, 16, QImage::Format_ARGB32); arrow.fill(qRgba(0, 0, 0, 0)); QPainter painter(&arrow); USE_ANTI_ALIASING(painter, true); // white background, same foreground as p painter.setPen(QPen(Qt::white, 2)); painter.setBrush(brush); QPointF arrowPoints[4] = { QPointF(20.0, 7.0), // front QPointF( 0.0, 0.0), // upper tail QPointF( 5.0, 7.0), // mid tail QPointF( 0.0, 15.0) // lower tail }; painter.drawPolygon(arrowPoints, 4); painter.end(); return arrow; } /** @brief Calculates the square distance between two points @return (int) ( (x2 - x1)^2 + (y2 - y1)^2 ) */ static inline int pointDistanceSquare(const QPointF &p1, const QPointF &p2) { return (p2.x() - p1.x()) * (p2.x() - p1.x()) + (p2.y() - p1.y()) * (p2.y() - p1.y()); } void CDraw::arrows(const QPolygonF &line, const QRectF &viewport, QPainter &p, int minPointDist, int minArrowDist) { QImage arrow = createBasicArrow(p.brush()); const qreal minArrowDistSquare = minArrowDist * minArrowDist; const qreal minPointDistSquare = minPointDist * minPointDist; QPointF prevArrow; bool firstArrow = true; for(int i = 1; i < line.size(); i++) { const QPointF &pt = line[i ]; const QPointF &prevPt = line[i - 1]; // ensure there is enough space between two linepts if( pointDistanceSquare(pt, prevPt) >= minPointDistSquare ) { QPointF arrowPos = prevPt + (pt - prevPt)/2; if( (viewport.contains(pt) || 0 == viewport.height()) // ensure the point is visible && (firstArrow || pointDistanceSquare(prevArrow, arrowPos) >= minArrowDistSquare) ) { p.save(); // rotate and draw the arrow (between bullets) p.translate(arrowPos); qreal direction = ( qAtan2((pt.y() - prevPt.y()), (pt.x() - prevPt.x())) * 180.) / M_PI; p.rotate(direction); p.drawImage(-11, -7, arrow); p.restore(); prevArrow = arrowPos; firstArrow = false; } } } } void CDraw::text(const QString &str, QPainter &p, const QPoint ¢er, const QColor &color, const QFont &font) { QFontMetrics fm(font); QRect r = fm.boundingRect(str); r.moveCenter(center); p.setFont(font); // draw the white `shadow` p.setPen(Qt::white); p.drawText(r.topLeft() - QPoint(-1, -1), str); p.drawText(r.topLeft() - QPoint( 0, -1), str); p.drawText(r.topLeft() - QPoint(+1, -1), str); p.drawText(r.topLeft() - QPoint(-1, 0), str); p.drawText(r.topLeft() - QPoint(+1, 0), str); p.drawText(r.topLeft() - QPoint(-1, +1), str); p.drawText(r.topLeft() - QPoint( 0, +1), str); p.drawText(r.topLeft() - QPoint(+1, +1), str); p.setPen(color); p.drawText(r.topLeft(), str); } void CDraw::text(const QString &str, QPainter &p, const QRect &r, const QColor &color) { p.setPen(Qt::white); p.setFont(CMainWindow::self().getMapFont()); // draw the white `shadow` p.drawText(r.adjusted(-1, -1, -1, -1), Qt::AlignCenter, str); p.drawText(r.adjusted( 0, -1, 0, -1), Qt::AlignCenter, str); p.drawText(r.adjusted(+1, -1, +1, -1), Qt::AlignCenter, str); p.drawText(r.adjusted(-1, 0, -1, 0), Qt::AlignCenter, str); p.drawText(r.adjusted(+1, 0, +1, 0), Qt::AlignCenter, str); p.drawText(r.adjusted(-1, +1, -1, +1), Qt::AlignCenter, str); p.drawText(r.adjusted( 0, +1, 0, +1), Qt::AlignCenter, str); p.drawText(r.adjusted(+1, +1, +1, +1), Qt::AlignCenter, str); p.setPen(color); p.drawText(r, Qt::AlignCenter, str); } QPoint CDraw::bubble(QPainter &p, const QRect &contentRect, const QPoint &pointerPos, int pointerBaseWidth, float pointerBasePos) { QPainterPath bubblePath; bubblePath.addRoundedRect(contentRect, 5, 5); // draw the arrow int pointerBaseCenterX = (pointerBasePos <= 1) ? contentRect.left() + (pointerBasePos * contentRect.width()) : contentRect.left() + (int) pointerBasePos; int pointerHeight = 0; if(pointerPos.y() < contentRect.top()) { pointerHeight = contentRect.top() - pointerPos.y() + 1; } else if(pointerPos.y() > contentRect.bottom()) { pointerHeight = contentRect.bottom() - pointerPos.y() - 1; } else { qDebug() << "cannot calculate pointerHeight/pointerBaseCenterX due to invalid parameters"; } if(0 != pointerHeight) { QPolygonF pointerPoly; pointerPoly << pointerPos << QPointF(pointerBaseCenterX - pointerBaseWidth / 2, pointerPos.y() + pointerHeight) << QPointF(pointerBaseCenterX + pointerBaseWidth / 2, pointerPos.y() + pointerHeight) << pointerPos; QPainterPath pointerPath; pointerPath.addPolygon(pointerPoly); bubblePath = bubblePath.united(pointerPath); } p.setPen (CDraw::penBorderGray); p.setBrush(CDraw::brushBackWhite); p.drawPolygon(bubblePath.toFillPolygon()); return contentRect.topLeft(); } qmapshack-1.5.1/src/helpers/CCommandProcessor.h000644 001750 000144 00000002122 12622435722 022444 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCOMMANDPROCESSOR_H #define CCOMMANDPROCESSOR_H #include "CAppOpts.h" class CCommandProcessor { public: CAppOpts* processOptions(const QStringList &arguments); }; #endif // CCOMMANDPROCESSOR_H qmapshack-1.5.1/src/helpers/Platform.h000644 001750 000144 00000034051 12622435722 020655 0ustar00oeichlerusers000000 000000 /* -*-mode:c++; c-style:k&r; c-basic-offset:4; -*- */ /********************************************************************************************** Copyright (C) 2008 Albrecht Dre This file contains macros for platform-independant access of data stored in little-endian format. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ============================================================= Why does this file exist, and what should hackers do with it? ------------------------------------------------------------- 1. Background Garmin stores data in map files and in their devices in little-endian format. The data is usually packed, i.e. it is not guaranteed that multi-byte data is aligned at 16, 32 or 64 bit boundaries. This is fine as long as you run QLandkarte (or any derived software) on a little-endian machine which accepts accessing unaligned data. The `configure' script in the top-level folder tries to detect if your machine is a little endian (like Intel or ARM) or a big endian (like PowerPC or Sparc). In the latter case, it defines the macro HAVE_BIGENDIAN. In another test, it checks if your machine supports accessing unaligned memory (like Intel or PowerPC) or if such accesses would fail (as on ARM or Sparc). If unaligned accesses are supported, the macro CAN_UNALIGNED will be defined. Of course, the file config.h from the top-level folder has to be included. 2. How to access data To work around these problems, this file defines a number of access macros. On machines which do not need them, they always expand to nothing, but they are *absolutely* necessary on others. So *never* access multi-byte elements without using these macros. 2.1 Load data from Garmin The following rules apply if you want to access data in a source coming from a Garmin device or file: (a) the source is a constant or an aligned 16, 32 or 64-bit value Always use the macro gar_endian(, ) where type may be int16_t, int32_t, int64_t, uint16_t, uint32_t, uint64_t, float or double. The returned value will explicitly be cast'ed to . (b) the source is an unaligned 16, 32 or 64-bit value Always use the macro gar_load(, ) where type may be int16_t, int32_t, int64_t, uint16_t, uint32_t, uint64_t, float or double. The returned value will explicitly be cast'ed to . (c) the source is a pointer Always use the macro gar_ptr_load(, ) where type may be int16_t, int32_t, int64_t, uint16_t, uint32_t, uint64_t, float or double or the special Garmin types int24_t or uint24_t. The returned value will be of type except for the Garmin types int24_t or uint24_t which will be int32_t or uint32_t, respectively, but have the uppermost 8 bits always set to 0. 2.2 Store data to Garmin (a) the destination is a variable For unaligned variables, use the macro gar_store(, , ) where type may be int16_t, int32_t, int64_t, uint16_t, uint32_t, uint64_t, float or double. if the variable is aligned, use "destination = gar_endian(type, source)" which is faster. (b) the destination is a pointer For unaligned pointer destinations, use the macro gar_ptr_store(, , ) where type may be int16_t, int32_t, int64_t, uint16_t, uint32_t, uint64_t, float or double or the special Garmin types int24_t or uint24_t. For a standard type and an aligned pointer destination, use "*(type *)(ptr) = gar_endian(type, source)" which is faster. **********************************************************************************************/ #ifndef __PLATFORM_H__ #define __PLATFORM_H__ // include platform setup (HAVE_BIGENDIAN, CAN_UNALIGNED) #include "config.h" // need integer type definitions with fixed width #ifdef HAVE_INTTYPES_H # include #elif HAVE_STDINT_H # include #elif WIN32 #include typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; #define qIsNaN(x) _isnan(x) #else # error neither inttypes.h nor stdint.h are available #endif #include // -------------------------------------------------------------------------------------------- // macros to fix the endianess of the constant or properly aligned argument x of type t #if !defined(HAVE_BIGENDIAN) // little endian platform: just return the argument #define gar_endian(t, x) (t)(x) #else // big endian platform #define gar_endian(t, x) (__gar_endian_ ## t(x)) // define swapping static inline uint16_t __gar_endian_uint16_t(uint16_t x) { return ((x >> 8) & 0xffu) | ((x & 0xffu) << 8); } static inline uint32_t __gar_endian_uint32_t(uint32_t x) { return ((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) | ((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24); } static inline uint64_t __gar_endian_uint64_t(uint64_t x) { return ((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) | ((x & 0x0000ff0000000000ull) >> 24) | ((x & 0x000000ff00000000ull) >> 8) | ((x & 0x00000000ff000000ull) << 8) | ((x & 0x0000000000ff0000ull) << 24) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56); } static inline int16_t __gar_endian_int16_t(int16_t x) { return ((x >> 8) & 0xffu) | ((x & 0xffu) << 8); } static inline int32_t __gar_endian_int32_t(int32_t x) { return ((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) | ((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24); } static inline int64_t __gar_endian_int64_t(int64_t x) { return ((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) | ((x & 0x0000ff0000000000ull) >> 24) | ((x & 0x000000ff00000000ull) >> 8) | ((x & 0x00000000ff000000ull) << 8) | ((x & 0x0000000000ff0000ull) << 24) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56); } static inline float __gar_endian_float(float x) { union { uint32_t _u; float _f; } _v; _v._f = x; _v._u = gar_endian(uint32_t, _v._u); return _v._f; } static inline double __gar_endian_double(double x) { union { uint64_t _u; double _d; } _v; _v._d = x; _v._u = gar_endian(uint64_t, _v._u); return _v._d; } #endif // HAVE_BIGENDIAN // -------------------------------------------------------------------------------------------- // macros to deal with pointers or unaligned arguments // load argument of type t from pointer p #define gar_ptr_load(t, p) __gar_ptr_load_ ## t((const uint8_t *)(p)) // store argument src of type t in in the location to which the pointer p points #define gar_ptr_store(t, p, src) __gar_ptr_store_ ## t((uint8_t *)(p), (src)) #if defined(CAN_UNALIGNED) && !defined(HAVE_BIGENDIAN) // load argument x of type t - noop with proper cast #define gar_load(t, x) (t)(x) // store argument src of type t in the variable dst of type t - just assign #define gar_store(t, dst, src) (dst) = (src) // load from pointer - simply map memory #define __gar_ptr_load_int16_t(p) (*((int16_t *)(p))) #define __gar_ptr_load_int32_t(p) (*((int32_t *)(p))) #define __gar_ptr_load_int64_t(p) (*((int64_t *)(p))) #define __gar_ptr_load_uint16_t(p) (*((uint16_t *)(p))) #define __gar_ptr_load_uint32_t(p) (*((uint32_t *)(p))) #define __gar_ptr_load_uint64_t(p) (*((uint64_t *)(p))) #define __gar_ptr_load_float(p) (*((float *)(p))) #define __gar_ptr_load_double(p) (*((double *)(p))) // special Garmin types - map memory and clear extra bits #define __gar_ptr_load_uint24_t(p) (__gar_ptr_load_uint32_t(p) & 0x00FFFFFFu) #define __gar_ptr_load_int24_t(p) (__gar_ptr_load_int32_t(p) & 0x00FFFFFFu) // store data to pointer - just assign after a proper cast #define __gar_ptr_store_int16_t(p, src) (*((int16_t *)(p))) = (src) #define __gar_ptr_store_int32_t(p, src) (*((int32_t *)(p))) = (src) #define __gar_ptr_store_int64_t(p, src) (*((int64_t *)(p))) = (src) #define __gar_ptr_store_uint16_t(p, src) (*((uint16_t *)(p))) = (src) #define __gar_ptr_store_uint32_t(p, src) (*((uint32_t *)(p))) = (src) #define __gar_ptr_store_uint64_t(p, src) (*((uint64_t *)(p))) = (src) #define __gar_ptr_store_float(p, src) (*((float *)(p))) = (src) #define __gar_ptr_store_double(p, src) (*((double *)(p))) = (src) // special Garmin types - use memcpy static inline void __gar_ptr_store_int24_t(uint8_t * p, int32_t src) { __gar_ptr_store_uint16_t(p, src & 0xffffu); p[2] = src >> 16; } static inline void __gar_ptr_store_uint24_t(uint8_t * p, uint32_t src) { __gar_ptr_store_uint16_t(p, src & 0xffffu); p[2] = src >> 16; } #else // machine is either Big Endian or does not support unaligned accesses // load argument x of type t - call pointer load macro #define gar_load(t, x) gar_ptr_load(t, (uint8_t *)&(x)) // store argument src of type t in the variable dst of type t - call pointer store macro #define gar_store(t, dst, src) gar_ptr_store(t, (uint8_t *)&(dst), src) // load from pointer - read'n'shift bytes // use Byte-Reverse operations for PowerPC static inline uint16_t __gar_ptr_load_uint16_t(const uint8_t *p) { #ifdef __powerpc__ register uint16_t temp; asm __volatile__ ("lhbrx %0,0,%1" : "=r" (temp) : "b" (p), "m" (*p)); return temp; #else return (uint16_t)(p[0] | (p[1] << 8)); #endif } static inline uint32_t __gar_ptr_load_uint24_t(const uint8_t *p) { #ifdef __powerpc__ register uint32_t temp; asm __volatile__ ("lwbrx %0,0,%1" : "=r" (temp) : "b" (p), "m" (*p)); asm __volatile__ ("rlwinm %0,%1,0,8,31" : "=r" (temp) : "r" (temp)); return temp; #else return (uint32_t)(p[0] | (p[1] << 8) | (p[2] << 16)); #endif } static inline uint32_t __gar_ptr_load_uint32_t(const uint8_t *p) { #ifdef __powerpc__ register uint32_t temp; asm __volatile__ ("lwbrx %0,0,%1" : "=r" (temp) : "b" (p), "m" (*p)); return temp; #else return (uint32_t)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); #endif } static inline uint64_t __gar_ptr_load_uint64_t(const uint8_t *p) { return (uint64_t)__gar_ptr_load_uint32_t(p) | ((uint64_t)__gar_ptr_load_uint32_t(p + 4) << 32); } static inline int16_t __gar_ptr_load_int16_t(const uint8_t *p) { #ifdef __powerpc__ register int16_t temp; asm __volatile__ ("lhbrx %0,0,%1" : "=r" (temp) : "b" (p), "m" (*p)); return temp; #else return (int16_t)(p[0] | (p[1] << 8)); #endif } static inline int32_t __gar_ptr_load_int24_t(const uint8_t *p) { #ifdef __powerpc__ register int32_t temp; asm __volatile__ ("lwbrx %0,0,%1" : "=r" (temp) : "b" (p), "m" (*p)); asm __volatile__ ("rlwinm %0,%1,0,8,31" : "=r" (temp) : "r" (temp)); return temp; #else return p[0] | (p[1] << 8) | (p[2] << 16); #endif } static inline int32_t __gar_ptr_load_int32_t(const uint8_t *p) { #ifdef __powerpc__ register int32_t temp; asm __volatile__ ("lwbrx %0,0,%1" : "=r" (temp) : "b" (p), "m" (*p)); return temp; #else return (int32_t)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); #endif } static inline int64_t __gar_ptr_load_int64_t(const uint8_t *p) { return (int64_t)__gar_ptr_load_uint32_t(p) | ((int64_t)__gar_ptr_load_int32_t(p + 4) << 32); } static inline float __gar_ptr_load_float(const uint8_t * p) { union { uint32_t _u; float _f; } _v; _v._u = gar_ptr_load(uint32_t, p); return _v._f; } static inline double __gar_ptr_load_double(const uint8_t * p) { union { uint64_t _u; double _d; } _v; _v._u = gar_ptr_load(uint64_t, p); return _v._d; } // macros to store data - use memcpy to store data to pointer static inline void __gar_ptr_store_uint16_t(uint8_t *p, uint16_t src) { p[0] = src & 0xffu; p[1] = (src >> 8) & 0xffu; } static inline void __gar_ptr_store_uint24_t(uint8_t *p, uint32_t src) { p[0] = src & 0xffu; p[1] = (src >> 8) & 0xffu; p[2] = (src >> 16) & 0xffu; } static inline void __gar_ptr_store_uint32_t(uint8_t *p, uint32_t src) { p[0] = src & 0xffu; p[1] = (src >> 8) & 0xffu; p[2] = (src >> 16) & 0xffu; p[3] = (src >> 24) & 0xffu; } static inline void __gar_ptr_store_uint64_t(uint8_t *p, uint64_t src) { __gar_ptr_store_uint32_t(p, src & 0xffffffffu); __gar_ptr_store_uint32_t(p + 4, src >> 32); } #define __gar_ptr_store_int16_t(p, src) __gar_ptr_store_uint16_t(p, (uint16_t)src) #define __gar_ptr_store_int24_t(p, src) __gar_ptr_store_uint24_t(p, (uint32_t)src) #define __gar_ptr_store_int32_t(p, src) __gar_ptr_store_uint32_t(p, (uint32_t)src) #define __gar_ptr_store_int64_t(p, src) __gar_ptr_store_uint64_t(p, (uint64_t)src) static inline void __gar_ptr_store_float(uint8_t *p, float src) { float __fv = gar_endian(float, src); memcpy(p, &__fv, 4); } static inline void __gar_ptr_store_double(uint8_t *p, double src) { double __dv = gar_endian(double, src); memcpy(p, &__dv, 8); } #endif // cannot unaligned or big endian #endif // __PLATFORM_H__ qmapshack-1.5.1/src/helpers/CPositionDialog.cpp000644 001750 000144 00000003756 12622435717 022467 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CPositionDialog.h" #include "units/IUnit.h" #include CPositionDialog::CPositionDialog(QWidget * parent, QPointF &pos) : QDialog(parent) , pos(pos) { setupUi(this); QString str; IUnit::degToStr(pos.x(), pos.y(), str); lineEdit->setText(str); labelWarning->hide(); connect(lineEdit, SIGNAL(textEdited(QString)), this, SLOT(slotEdit(QString))); } CPositionDialog::~CPositionDialog() { } void CPositionDialog::accept() { if(getPosition(pos, lineEdit->text())) { QDialog::accept(); } } bool CPositionDialog::getPosition(QPointF& pt, const QString& str) { qreal lon, lat; bool res = IUnit::strToDeg(str, lon, lat); if(res) { pt.rx() = lon; pt.ry() = lat; } return res; } void CPositionDialog::slotEdit(const QString& str) { if(IUnit::isValidCoordString(str)) { labelWarning->hide(); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } else { labelWarning->show(); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } } qmapshack-1.5.1/src/helpers/CLinksDialog.h000644 001750 000144 00000002615 12622435722 021375 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CLINKSDIALOG_H #define CLINKSDIALOG_H #include "gis/IGisItem.h" #include "ui_ILinksDialog.h" #include class CLinksDialog : public QDialog, private Ui::ILinksDialog { Q_OBJECT public: CLinksDialog(QList& links, QWidget * parent); virtual ~CLinksDialog(); public slots: void accept(); private slots: void slotAddLink(); void slotDelLink(); void slotItemSelectionChanged(); private: QList& links; }; #endif //CLINKSDIALOG_H qmapshack-1.5.1/src/helpers/CSettings.h000644 001750 000144 00000002762 12622435722 021000 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2012 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSETTINGS_H #define CSETTINGS_H #include "helpers/CAppOpts.h" #include class CSettings : public QObject { public: CSettings() { if(!qlOpts->configfile.isEmpty()) { cfg = new QSettings(qlOpts->configfile, QSettings::IniFormat, this); } else { cfg = new QSettings(this); } } ~CSettings() { } QSettings& get() { return *cfg; } private: QSettings * cfg; }; #define SETTINGS \ CSettings ccfg; \ QSettings& cfg = ccfg.get() #endif //CSETTINGS_H qmapshack-1.5.1/src/helpers/CSelectCopyAction.cpp000644 001750 000144 00000005331 12622435717 022742 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/IGisItem.h" #include "gis/prj/IGisProject.h" #include "helpers/CSelectCopyAction.h" #include CSelectCopyAction::CSelectCopyAction(const IGisItem *src, const IGisItem *tar, QWidget *parent) : QDialog(parent) { setupUi(this); labelIcon1->setPixmap(src->getIcon()); labelInfo1->setText(src->getInfo()); labelIcon2->setPixmap(tar->getIcon()); labelInfo2->setText(tar->getInfo()); adjustSize(); connect(pushCopy, SIGNAL(clicked()), this, SLOT(slotSelectResult())); connect(pushSkip, SIGNAL(clicked()), this, SLOT(slotSelectResult())); connect(pushClone, SIGNAL(clicked()), this, SLOT(slotSelectResult())); CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSelectCopyAction"); } CSelectCopyAction::CSelectCopyAction(const IGisProject * src, const IGisProject * tar, QWidget * parent) : QDialog(parent) , result(eResultNone) { setupUi(this); labelIcon1->setPixmap(src->getIcon()); labelInfo1->setText(src->getInfo()); labelIcon2->setPixmap(tar->getIcon()); labelInfo2->setText(tar->getInfo()); pushClone->setEnabled(false); adjustSize(); connect(pushCopy, SIGNAL(clicked()), this, SLOT(slotSelectResult())); connect(pushSkip, SIGNAL(clicked()), this, SLOT(slotSelectResult())); CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSelectCopyAction"); } CSelectCopyAction::~CSelectCopyAction() { CCanvas::restoreOverrideCursor("~CSelectCopyAction"); } bool CSelectCopyAction::allOthersToo() { return checkAllOtherToo->isChecked(); } void CSelectCopyAction::slotSelectResult() { if(sender() == pushCopy) { result = eResultCopy; } else if(sender() == pushSkip) { result = eResultSkip; } else if(sender() == pushClone) { result = eResultClone; } accept(); } qmapshack-1.5.1/src/helpers/IPositionDialog.ui000644 001750 000144 00000004411 12527654570 022321 0ustar00oeichlerusers000000 000000 IPositionDialog 0 0 400 189 Position ... Enter new position Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" true Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IPositionDialog accept() 248 254 157 274 buttonBox rejected() IPositionDialog reject() 316 260 286 274 qmapshack-1.5.1/src/helpers/CPhotoViewer.cpp000644 001750 000144 00000012242 12623420703 021772 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "CPhotoViewer.h" #include CPhotoViewer::CPhotoViewer(QList &images, int idx, QWidget *parent) : QDialog(parent) , images(images) , idx(idx) { setStyleSheet("background-color:black;"); setAttribute(Qt::WA_TranslucentBackground); Qt::WindowFlags flags = windowFlags() & Qt::WindowType_Mask; setWindowFlags(flags | Qt::CustomizeWindowHint); showMaximized(); // check if showMaximized() worked correctly by comparing our size with the MainWindow's size // if showMaximized() failed we change our size (manually) to match the MainWindow's size // this is hack, but does its job on px i3-wm QMainWindow &main = CMainWindow::self(); if(width() < main.width() && height() < main.height()) { qDebug() << "showMaximized() failed, using MainWindow.frameGeometry()"; setGeometry(main.frameGeometry()); } if(!images.isEmpty()) { setImageAtIdx(idx); } } CPhotoViewer::~CPhotoViewer() { } void CPhotoViewer::resizeEvent(QResizeEvent * e) { QDialog::resizeEvent(e); setImageAtIdx(idx); } void CPhotoViewer::setImageAtIdx(int i) { const QRect& rectScreen = rect(); const QPoint& center = rectScreen.center(); QImage& pixmap = images[i].pixmap; if(!images[i].filePath.isEmpty()) { pixmap = QImage(images[i].filePath); } double width = rectScreen.width() - 64; double height = rectScreen.height() - 64; if(pixmap.width() > width || pixmap.height() > height) { rectImage = pixmap.rect(); if(pixmap.width() > width) { double ratio = width / rectImage.width(); rectImage.setWidth(width); rectImage.setHeight(qFloor(rectImage.height() * ratio) - 1); } if(pixmap.height() > height) { double ratio = height / rectImage.height(); rectImage.setHeight(height); rectImage.setWidth(qFloor(rectImage.width() * ratio) - 1); } rectImage.moveCenter(center); } else { rectImage = pixmap.rect(); rectImage.moveCenter(center); } rectClose.moveCenter(rectImage.topRight()); rectPrev.setHeight(rectImage.height()); rectPrev.moveBottomLeft(rectImage.bottomLeft()); rectNext.setHeight(rectImage.height()); rectNext.moveBottomRight(rectImage.bottomRight()); } void CPhotoViewer::paintEvent(QPaintEvent * e) { QDialog::paintEvent(e); QPainter p(this); p.setPen(Qt::NoPen); p.setBrush(QColor(0,0,0,190)); p.drawRect(rect()); p.setPen(QPen(Qt::white, 11, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin)); p.setBrush(Qt::white); p.drawRect(rectImage); p.drawImage(rectImage, images[idx].pixmap.scaled(rectImage.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); if(idx != (images.size() - 1)) { p.setPen(Qt::NoPen); p.setBrush(QColor(255,255,255,128)); p.drawRect(rectNext); p.drawPixmap(rectNext.left(),rectNext.bottom() - 32, 32, 32, QPixmap("://icons/32x32/Right.png")); } if(idx != 0) { p.setPen(Qt::NoPen); p.setBrush(QColor(255,255,255,128)); p.drawRect(rectPrev); p.drawPixmap(rectPrev.left(), rectPrev.bottom() - 32, 32, 32, QPixmap("://icons/32x32/Left.png")); } p.drawPixmap(rectClose, QPixmap("://icons/32x32/Close.png")); } void CPhotoViewer::tryIdxStep(int delta) { int prevIdx = idx; idx += delta; idx = qMin(idx, images.size() - 1); idx = qMax(idx, 0); if(prevIdx != idx) { setImageAtIdx(idx); update(); } } void CPhotoViewer::mousePressEvent(QMouseEvent * e) { QPoint pos = e->pos(); if(rectClose.contains(pos)) { reject(); } else if(rectNext.contains(pos)) { tryIdxStep(1); } else if(rectPrev.contains(pos)) { tryIdxStep(-1); } else if(!rectImage.contains(pos)) { reject(); } e->accept(); } void CPhotoViewer::keyPressEvent(QKeyEvent *e) { switch(e->key()) { case Qt::Key_Left: tryIdxStep(-1); break; case Qt::Key_Right: tryIdxStep(1); break; case Qt::Key_Q: reject(); break; } QDialog::keyPressEvent(e); } qmapshack-1.5.1/src/helpers/ISelectCopyAction.ui000644 001750 000144 00000007432 12623413746 022606 0ustar00oeichlerusers000000 000000 ISelectCopyAction 0 0 471 300 Copy item... QFormLayout::AllNonFixedFieldsGrow Replace existing item TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::Horizontal Do not copy item TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::Horizontal Create a clone Replace with: Keep item: The clone's name will be appended with '_Clone' true Qt::Horizontal And for all other items, too. qmapshack-1.5.1/src/helpers/ISelectProjectDialog.ui000644 001750 000144 00000007731 12570062516 023262 0ustar00oeichlerusers000000 000000 ISelectProjectDialog 0 0 400 359 Select a project... Select project from list or enter new project name. true New project's name New project is created as: 0 0 true QFrame::NoFrame QFrame::Plain 0 0 0 0 0 true *.qms true *.gpx Database Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISelectProjectDialog accept() 248 254 157 274 buttonBox rejected() ISelectProjectDialog reject() 316 260 286 274 qmapshack-1.5.1/src/helpers/CWptIconDialog.h000644 001750 000144 00000002665 12622435722 021705 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CWPTICONDIALOG_H #define CWPTICONDIALOG_H #include "ui_IWptIconDialog.h" #include class QToolButton; class QAction; class QListWidgetItem; class CWptIconDialog : public QDialog, private Ui::IWptIconDialog { Q_OBJECT public: CWptIconDialog(QToolButton * parent); CWptIconDialog(QAction * parent); virtual ~CWptIconDialog(); private slots: void slotItemClicked(QListWidgetItem * item); private: void setupList(QObject *obj); QToolButton * button; QAction * action; }; #endif //CWPTICONDIALOG_H qmapshack-1.5.1/src/helpers/INotifiable.h000644 001750 000144 00000002066 12622435722 021257 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef INOTIFIABLE_H #define INOTIFIABLE_H class INotifiable { public: virtual ~INotifiable() {}; virtual void notify() = 0; }; #endif // INOTIFIABLE_H qmapshack-1.5.1/src/helpers/CInputDialog.h000644 001750 000144 00000002521 12622435722 021410 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CINPUTDIALOG_H #define CINPUTDIALOG_H #include "ui_IInputDialog.h" #include class CInputDialog : public QDialog, private Ui::IInputDialog { Q_OBJECT public: CInputDialog(QWidget * parent, const QString &text, QVariant &val, const QVariant &reset); virtual ~CInputDialog(); public slots: void accept(); private slots: void slotReset(); private: QVariant& val; QVariant reset; }; #endif //CINPUTDIALOG_H qmapshack-1.5.1/src/helpers/CSelectProjectDialog.h000644 001750 000144 00000003420 12622435722 023056 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSELECTPROJECTDIALOG_H #define CSELECTPROJECTDIALOG_H #include "ui_ISelectProjectDialog.h" #include class QTreeWidget; class CSelectProjectDialog : public QDialog, private Ui::ISelectProjectDialog { Q_OBJECT public: enum type_e { eTypeNone ,eTypeQms ,eTypeGpx ,eTypeDb }; CSelectProjectDialog(QString& key, QString& name, type_e& type, QTreeWidget *parent); virtual ~CSelectProjectDialog(); public slots: void accept(); void reject(); private slots: void slotItemClicked(QListWidgetItem * item); void slotItemDoubleClicked(QListWidgetItem * item); void slotProjectChanged(const QString& text); void slotProjectEdited(const QString& text); void slotTypeChanged(); private: static QString lastkey; QString& key; QString& name; type_e& type; }; #endif //CSELECTPROJECTDIALOG_H qmapshack-1.5.1/src/helpers/CPositionDialog.h000644 001750 000144 00000002610 12622435722 022114 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPOSITIONDIALOG_H #define CPOSITIONDIALOG_H #include "ui_IPositionDialog.h" #include class QPointF; class CPositionDialog : public QDialog, private Ui::IPositionDialog { Q_OBJECT public: CPositionDialog(QWidget * parent, QPointF &pos); virtual ~CPositionDialog(); static bool getPosition(QPointF& pt, const QString &str); public slots: void accept(); private slots: void slotEdit(const QString& str); private: QPointF& pos; }; #endif //CPOSITIONDIALOG_H qmapshack-1.5.1/src/helpers/IProgressDialog.ui000644 001750 000144 00000003551 12570062516 022314 0ustar00oeichlerusers000000 000000 IProgressDialog 0 0 400 107 Please wait... TextLabel TextLabel 24 Qt::Horizontal QDialogButtonBox::Cancel buttonBox accepted() IProgressDialog accept() 248 254 157 274 buttonBox rejected() IProgressDialog reject() 316 260 286 274 qmapshack-1.5.1/src/helpers/CProgressDialog.cpp000644 001750 000144 00000004145 12622435717 022460 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CProgressDialog.h" #include "units/IUnit.h" #include QStack CProgressDialog::stackSelf; CProgressDialog::CProgressDialog(const QString text, int min, int max, QWidget *parent) : QDialog(parent) { stackSelf.push(this); setupUi(this); setWindowModality(Qt::WindowModal); label->setText(text); progressBar->setMinimum(min); progressBar->setMaximum(max); progressBar->setValue(0); time.start(); labelTime->setText(tr("Elapsed time: %1").arg(time.elapsed()/1000)); if(max == NOINT) { progressBar->hide(); } hide(); QTimer::singleShot(1000, this, SLOT(show())); } CProgressDialog * CProgressDialog::self() { if(stackSelf.isEmpty()) { return 0; } return stackSelf.top(); } CProgressDialog::~CProgressDialog() { stackSelf.pop(); } void CProgressDialog::reject() { setResult(QMessageBox::Abort); } void CProgressDialog::setValue(int val) { QApplication::processEvents(); progressBar->setValue(val); labelTime->setText(tr("Elapsed time: %1 seconds.").arg(time.elapsed()/1000.0, 0,'f',1)); } bool CProgressDialog::wasCanceled() { return result() == QMessageBox::Abort; } qmapshack-1.5.1/src/helpers/CAppSetup.h000644 001750 000144 00000005266 12622435722 020743 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CAPPSETUP_H #define CAPPSETUP_H #include #include class CAppSetup { public: static CAppSetup* getPlattformInstance(); static void consoleMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); virtual void prepareGdal(); virtual QString routinoPath(QString xmlFile) = 0; virtual void prepareTranslators(QApplication* app) = 0; virtual void prepareConfig(); virtual void installMessageHandler(); virtual QDir configDir(QString subdir = 0); protected: void prepareTranslator(QApplication* app, QTranslator *qtTranslator, QString translationPath, QString translationPrefix); QString logName(); virtual QString logFilename(); void printToConsole(QtMsgType type, QString formatedMsg); void appendToFile(QtMsgType type, QString formatedMsg); QString routinoPath(QDir dirXml, QString xmlFile); QDir path(QString path, QString subdir = 0, bool mkdir = false); }; class CAppSetupMac : public CAppSetup { public: virtual void prepareGdal(); virtual QString routinoPath(QString xmlFile); virtual void prepareTranslators(QApplication* app); protected: virtual QString logFilename(); QString getResourceDir(QString subdir); CAppSetupMac(); friend class CAppSetup; }; class CAppSetupLinux : public CAppSetup { public: virtual QString routinoPath(QString xmlFile); virtual void prepareTranslators(QApplication* app); protected: CAppSetupLinux(); friend class CAppSetup; }; class CAppSetupWin : public CAppSetup { public: virtual QString routinoPath(QString xmlFile); virtual void prepareGdal(); virtual void prepareTranslators(QApplication* app); virtual void prepareConfig(); protected: CAppSetupWin(); friend class CAppSetup; }; #endif // CAPPSETUP_H qmapshack-1.5.1/src/helpers/ILinksDialog.ui000644 001750 000144 00000006272 12527654570 021604 0ustar00oeichlerusers000000 000000 ILinksDialog 0 0 700 150 Links... Type Text Uri ... :/icons/32x32/Add.png:/icons/32x32/Add.png 32 32 false ... :/icons/32x32/DeleteOne.png:/icons/32x32/DeleteOne.png 32 32 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ILinksDialog accept() 248 254 157 274 buttonBox rejected() ILinksDialog reject() 316 260 286 274 qmapshack-1.5.1/src/main.cpp000644 001750 000144 00000004076 12622435717 016716 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CAppSetup.h" #include "helpers/CCommandProcessor.h" #include "version.h" #include #include #include CAppOpts *qlOpts; int main(int argc, char ** argv) { QApplication app(argc, argv); QCoreApplication::setApplicationName("QMapShack"); QCoreApplication::setOrganizationName("QLandkarte"); QCoreApplication::setOrganizationDomain("qlandkarte.org"); CAppSetup* env = CAppSetup::getPlattformInstance(); env->installMessageHandler(); CCommandProcessor cmdParse; qlOpts = cmdParse.processOptions(app.arguments()); env->prepareConfig(); env->prepareTranslators(&app); env->prepareGdal(); QSplashScreen *splash = 0; if (!qlOpts->nosplash) { QPixmap pic(":/pics/splash.png"); QPainter p(&pic); QFont f = p.font(); f.setBold(true); p.setPen(Qt::black); p.setFont(f); p.drawText(400,395,"V " VER_STR); splash = new QSplashScreen(pic); splash->show(); } CMainWindow w; w.show(); if (splash != 0) { splash->finish(&w); delete splash; } return app.exec(); } qmapshack-1.5.1/src/CMainWindow.h000644 001750 000144 00000007351 12622435722 017611 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAINWINDOW_H #define CMAINWINDOW_H #include "ui_IMainWindow.h" #include class CMapList; class CDemList; class QLabel; class CGisWidget; class CCanvas; struct SGisLine; class CMainWindow : public QMainWindow, private Ui::IMainWindow { Q_OBJECT public: static CMainWindow& self() { return *pSelf; } static QWidget * getBestWidgetForParent(); virtual ~CMainWindow(); void addMapList(CMapList *list, const QString& name); void addDemList(CDemList *list, const QString& name); void addWidgetToTab(QWidget * w); bool isScaleVisible(); bool isGridVisible(); bool isNight(); bool isPOIText(); bool isMapToolTip(); bool flipMouseWheel(); bool profileIsWindow(); const QFont& getMapFont() { return mapFont; } void zoomCanvasTo(const QRectF rect); /** @brief Read the elevation from DEM data attached to the currently visible canvas for a given location @param pos a position in units of [rad] @return If no elevation value can be found for the position NOFLOAT is returned. */ qreal getEelevationAt(const QPointF &pos); void getEelevationAt(const QPolygonF& pos, QPolygonF &ele); void getEelevationAt(SGisLine &line); /** @brief Get pointer to the currently visible canvas object. @return If the currently visible tab does not contain a CCanvas object 0 is returned. */ CCanvas * getVisibleCanvas(); #ifdef WIN32 protected: bool CMainWindow::nativeEvent(const QByteArray & eventType, void * message, long * result); #endif // WIN32 void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); private slots: void slotAbout(); void slotHelp(); void slotAddCanvas(); void slotCloneCanvas(); void slotTabCloseRequest(int i); void slotCurrentTabCanvas(int i); void slotCurrentTabMaps(int i); void slotCurrentTabDem(int i); void slotMousePosition(const QPointF& pos, qreal ele); void slotUpdateCurrentWidget(); void slotSetupMapFont(); void slotSetupGrid(); void slotSetupMapPath(); void slotSetupDemPath(); void slotSetupMapView(); void slotSetupTimeZone(); void slotSetupUnits(); void slotSetupWorkspace(); void slotSetupCoordFormat(); void slotImportDatabase(); void slotLoadGISData(); void slotBuildVrt(); void slotStoreView(); void slotLoadView(); void slotSetProfileMode(bool on); void slotCreateRoutinoDatabase(); void slotPrintMap(); private: friend int main(int argc, char ** argv); CMainWindow(); void loadGISData(const QStringList& filenames); static CMainWindow * pSelf; /// status bar label QLabel * lblPosWGS84; QLabel * lblElevation; QLabel * lblPosGrid; QFont mapFont; CGisWidget * gisWidget; }; #endif //CMAINWINDOW_H qmapshack-1.5.1/src/CAbout.cpp000644 001750 000144 00000003203 12623413746 017135 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CAbout.h" #include "version.h" #include #include #include #include CAbout::CAbout(QWidget *parent) : QDialog(parent) { setupUi(this); labelVersion->setText(VER_STR); labelQtVersion->setText(qVersion()); labelGDALVersion->setText(GDALVersionInfo("--version")); labelProj4Version->setText(QString::number(PJ_VERSION)); if(Routino_CheckAPIVersion() != ROUTINO_ERROR_NONE) { labelRoutinoVersion->setText(tr("%1 (API V%2, expected V%3)").arg(Routino_Version).arg(ROUTINO_API_VERSION).arg(Routino_APIVersion)); } else { labelRoutinoVersion->setText(tr("%1 (API V%2)").arg(Routino_Version).arg(Routino_APIVersion)); } } CAbout::~CAbout() { } qmapshack-1.5.1/src/animation/WTFPL-2000644 001750 000144 00000000762 12527654570 020225 0ustar00oeichlerusers000000 000000 DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. qmapshack-1.5.1/src/animation/loader2.gif000644 001750 000144 00000016304 12527654570 021265 0ustar00oeichlerusers000000 000000 GIF89a00LηŲ˹۲f[i_囯:b,,WLVxJyKp?{r#P}İkaCi5/Z vlrhbWνod3\$Jo=_Sȶ&S6_(RvF[}POsBخ?f1yp;c.uFl9Y{N*U! NETSCAPE2.0!Created with ajaxload.info! ,00,".+,##* "$+5 ))& %*Ђ  &"+M h--ZdD 8‹r%XP(A0P2I 0p h! # Av֑L@ϖQAFhHIEUKZ,q C8YR61xǨ 䩈I ؑ,`8WATL+p@%- N3!p!3pmR j ]B, C&zՒvLstI!`\ ̮ ;nĹ7Ek#5 Aq @|yϲ XϞ֖:~q ϟ@=Y TK?)ˣ:IWg&٫ Znز-[`zR0,! ,00, .+,  " 6 +5)ς&36ղ * / "+=(pe\.@dA $``/ 9 |Цh@0覠ީf I=VxXPnzL1*Ȉ`nAVت# @p UZ pq|[ƚ MY/D{EAeQ :```Om!"fnuuY GM!+2i+!Δ]z,jtmrÏBTvb$N>B_~4ǟM|y'Y{""TD,00#:`3-`S"r \H6o T)n )FHm]Y"7qlԡ.RK%pX͈!)r嶢!59Yi3y9Ңd$l2!`:IOK ߋPhaǑq90b-dRytD:4hH)f*6 ©qjf7l`κ ~')fF¤p:׸ @+3->ٸx/tH ! ,00 7  6 36 ʭ+/ Ǩr @Ksg| GA >L[4@YSw(hAPVSU &hpA ~rPVX# @*UZ(XpF`[G L`F/DX{BUeQ :` _PMV`VY`|eiAjb4ٷ@Z-Hrͨ7M zv `̃`ᢺc {<O|B=9[`@[RE$T sTVVp< = 0`j9@jb]PG\C0h&"@ yO$!HMЗb(0",XB!XcDb $Qk ^ڒPSM>#">3O6Y!lBΙ@XI Pc."Jc}$R"0"G 0C IG+&YjI 4d^j7': cX&+& @X 6"$D 8J I &&^P0" X*+^IԳzd̩&!٦a0ノ! ,00 7  6   ʭ  ӾӯǨ͹ *pZh` PDkр'~;mP 0Mz}F@ n1h`@[GDE=1ԉ@(qV[c@!GR@@ ! ~.*1@P15G^p " ]<(7큪,) !"sL/VZϞ'\ZiU)Ac`a͏0dY}A,\..+WnУG@h0%?c;0e,,`+R`@ 0`_e9CdZ b\bH<wAuN$r pEF!И#:(A|g3, `@H)  `)Uh9 10#eRA.@?V#I]˃ ^|ITԧ&eg_g%4bYqB%=$4fbpA&b$ĩJ r Q+YAƃ@*\zh0% b]Z EF)=D{,Dpy[>D{;+&! ,00 7  6   ʭ  ӾӯǨ͹ *pZh` PDkр'~;mP 0Mz}F@ n1h`@[GDE=1ԉ@(qV[c@!GR@@ ! ~.*1@P15G^=P5%4X ӈrP[i֯N-4qV@RD.\ pu\jJ HwN7+WrZ熃!~HkǞ[i߁!B0y),X+R `@ #(grat5#DpNcN$Y26C^@!И# :(AΰP~%N 0H7X{> @d +P@2d>Ì d*dC50^Q@W CB T PzITPѴ@4`™P @@a X֛2 45,P8 b=>ě>{*(,h:'H ! ,00 7  6   ʭ  ӾӯǨ͹ *pZh` PDkр'~;mP 0Mz}F@ n1h`@[GDE=1ԉ@(qV[c@!GR@@ ! ~.*1@P15G^=P5%4X ӈrP[i֯N-4qV@RD.\ pu\jJ HwN7+WrZ熃!~HkǞ[i߁!B0y)3+)uY6dKt@k|#FX3xfogCt嗊;hfI_)|(0^K@! HM)V6D"!ذG`4,Ƞ=08 Ș5D` !x"#5 =0d 05`P6depPAF•.9Vv^pdRpk bo8f" ѧBC1#F š;d( p4"%H2`?PD 51 Dh P ($? >( pA 2s@X`-FR H B>; LE{ 雈(H 70#! ,00 7  6   ʭ  ӾӯǨ͹ *pZh` PDkр'~;mP 0Mz}F@ n1h`@[GDE=1ԉ@(qV[c@!GR@@ ! ~.*1@P15G^=P5%4X ӈrP[i֯N-4qV@RD.\ pu\jJ HwN7+WrZ熃!~HkǞ[i߁!B0y)3+)uY6dKt@k|#FX'B _*@H_)|PÉ:P `Dox  1A#]Z5A"l}FEyP5`ie-(0`<@d @(  ,d R"1C Y .9@~'pЊDe" d@$@cF%/x [@4F)2`B?x #oָɠ5@5!{ l"R2>Zc-b~Z 4 fnH6m L5#  G,ĕ! ,00 7  6   ʭ  ӾӯǨ͹ *pZh` PDkр'~;mP 0Mz}F@ n1h`@[GDE=1ԉ@(qV[c@!GR@@ ! ~.*1@P15G^=P5%4X ӈrP[i֯N-4qV@RD.\ pu\j؆{ i $+WnxУCO ƅسG<|2‹Ү;t "D2Hs0B,T`a6dN"Bv b_muE Aofpa+ ^#}06!蠣!T.!/Av20tp$ A"l0FE @2T RPF\ٕ  ء0A@3`Oƈ5 Bup H!-dRqЊX D4%d]b$$ʐJcvB C@+>uJ) 0&)  ʌppzY kֳݢ+-&krax 0 ,"! ,00 7  6   ʭ  ӾӯǨ͹ *ЃWh 0X @T(q3jUSն^&x^YyDǀ 4Š6o9@OW@qh!< j@*0Z7RD녠l:J,P D4ظ&k%`vP -T ,)(RofQ֨ pAkViY A`v!M)eve0q4Rg'MKzXΝn/^ FO^02˗BS[a"H@M8Ё!< @9Ldf9 ZgE``u`.+i9B"xJ+DV {DE@%@~1 B 0`["d9@]>Ne%p&M84$0e/TWFH.b吜\Iu(up&%#PBFc% 8"-dRvq xI9θ 9>9( b){$=DV$>ʭ)L>)1%]Iʭ>(E0ȭn.d>Tj+ȔҍJ0Ftp_xH ;qmapshack-1.5.1/src/animation/COPYRIGHT000644 001750 000144 00000000511 12527654570 020532 0ustar00oeichlerusers000000 000000 Loading indicators are from: http://ajaxload.info/ Quote from web pages: ------------------------------------------ Generated gifs can be used, modified and distributed under the terms of the ​"Do What The Fuck You Want To Public License" http://www.wtfpl.net/ ------------------------------------------ License: WTFPL-2 qmapshack-1.5.1/src/animation/loader.gif000644 001750 000144 00000015244 12527654570 021205 0ustar00oeichlerusers000000 000000 GIF89aBBLLzz! NETSCAPE2.0!Created with ajaxload.info! ,BBIT̻qֱUalK'eޠar,+0X4^epX!Gw@YfT1xRNJ*;gh%]~[5LIXLs{_}Rrul_^Ld!;a|]_k,qMmn8i-9~oi.)^)M.^?xmip-p%PX_ +O]{ XP A0B9F@!)N\ɲK?b6$89zlr ͜8m JhŒV YcL-"M&m:US7UQ8b~(`)+դԔ"UN'ֶN]q@Rkjkgoq1>uGu|f!xXR֙Ipaoʣ2xC _LPr`Dz/8͘BfIeY5E^%Gߜ}^8 }Nzczi$0Y@MR BT<! ,BBIT̻!qXalKfyV&ޠaTd0 l*VBAp@T |QNJTJ`Y%` slGI[GpkV~zet"G/,cz5awo8S)A!&hTdj7kAgBsj&aˀ#ԭ9/\훷 u}K8x `@['eq]>!]o\dC8sɳJ{JB@躟 Tg(FPJF96cT4Q<&\S*F"pT5eg]bcCo.V`Kx&#խUì@I/5GNtqhi-` UZqHZz'낗.d>N!UwWU59##/p/r7ڙ$JǍ{B_)E#+M:4R`OVW K4J\! ,BBIT̻!qX`lKVA8h|Un*R8 ,' %ep8Ni'bBIXrM^n5&4r`MqIMRl&W{x[{[1.di"S0no&,Ubm8U9"ASf&ȹIɌhơ)׾dD`ϰ-ÍT\sdX@@~* naxD˰=`aE-ԫD 0pE I͛8P)1g@FI͂H!ɴӧֲd(9$jHS*])jNe| *nIw({AD׾!(l .XvE#5o@s,F/L&F>Yz\j3f3ش+ѿ#LGY//DfZh_ 4[E1~Rқ/'z|y㾿GKw?u: |RzQ{_zy)( u_E! ,BBIt̻! pX`lK[%ĔI8hk}PR(X4M8 Pag'QTHRzz*0PiS1 eUK\)t0k)sZ);_`XpNv,od",vY)B~*Bgn.@RqdƟ&M-YCXY&bS^:ftU7NȦ+uk""FdGCs e >UBH!y_ʜI%5ֻ9!ϜO҄s ϣH*52fÌ!(bgȕW,0GQ+X"4dpUW eiNw]i#ykw-^SmT vwb )gߊ|f7FƳi!iͰjn\I֒T﹄"Kڷ A, jJ/Ro]\h@-q q\/wwk-8.|t+|x'9ן 1UimGN! ,BBIt ̻1 X`lKV@8( +E^nX U DAPaG4P6Qo2HVLi 1lMЇrm0 ys0*HvXh^fHYn]29)/C|lO8sHp9TfCeSRŪbécU{qdRNᚢS:ȹZC~A7)l">&DGc1"^G @8Œ.OʜIEF,rsBʐ"FS А}*]ʴ)=-c93Sb1ej6MwإRҺtb3պYcۓ"BrdL!^ikjp Ve O3RfH& iI$Y!J ̊$ՙu`@q_ Y3_ʾ{yᩮ79-oHx~*kƝԖT&|مLY^Ez! ,BBIt ̻1 f!,EU@8(<2Un@*Q'A (6I'N&*%3@F nb./<`J[LYm_sjvxdOu+YVc|6!r+uR(9Nn8ufmD9¦s|!{uRTN޺5dÚtOUXHWd#I JHpmaC/'Eo;hN\ɲ˗+IAき67賨ѣHyRgG:ٱz%Qx3#FN&G 0M#Zp"֚4r@eRDגօ"ԉ:)fhPyX,1@AIwtl V3:#?jwc[rA^\Lܼ|ͣVjbk8 ryd}'8K>x dD|,E'wC܂QL ӡ<! ,BBI̻Q 0X`lKV@8(IUn@*FIɚO"FTb=@)!|RB# ea ^-؎sdM[QNnV~iI/huQfU9c?u8_@:cp8v?.,s~l_!&a^ ~!#.)S޶6#"ئr^&++bG.F'F1tqE1$(S\r0[8 eΠS=: J#Icj$3xTXҟŞ^jS3\e.,h,CJG25[oIe+[y-VvWV9{[q ~֚\MR9Ϧbj%@u]]zْ*|@p|1*Ʊ3*>@ O|c@E ] *|-Ȭ$¦\Nȿ拜B/cHk썭pQ} v]|OPBd㛫egw6fDV\UW Ĝ=! ,BBI!̻VQXDalK[ޠhSE<n *Q ٳhpIL# Ti&&mSfO=pRZnP`{/!J3Ve.{bA!&9_HC8#`]v7wfl&C):D)eU-@{"ڱIT.a7FF3Fϋȭ=rŋ3j%G#' cȇTI˗0cA - 8@h){г\m2uX)x2;U6D (alDu]۔Kʧ1.VxB Yx/ps/Wvq'X0eW  )YCh[[a'U eTYxI=-?3sN5;38>]zSb'i (Xpb+Hfyy/lX_oi>WF]! ,BBI!̻f,%njko=!EX@;!J7Ś@(\<2o TptmNyiM Otysc'}sxQYr"T+D.S`X R#P][+RDd-Rj^G4m.^P?> HפvHP0 J%Ǐ CHRd SxHI3)c~yI͛0j#y`Zyv @KwфmםU]! ,BBI̻Yb,%nzm 4Ujщ2XF ļCC2&@ўtՆ$5! w"2QL)\5yD"_lQ5Ws@zPXe/Zx<{+y5Pz,qc-qn!`!"M([ѪAۈ˼΄+f$U-:rE[xg7z2nA]aȱǏ,\ē" (9!ڀ/_6cI͛]ȉe$Kde@J~XB 1z疨V;jkt<Xh\H;ςE;;')(K@8脽2 \=t@Y*4F m옂љ@نF(v5Е< a +3&|5䤏) eH7hE%ƌWV0z$cCžf}g5B\D`:! ,BBI̻qf,GUHޠϓI60XO =$p(CկPZY{(0DF>A{54:,F=KfY{kw(h8Uz}-vz\7HdCz+hCULlQƤHLy7'Lv_ּԺvA7=  S #( _:L0->4S^2ֈwaEiH\ɲ˗8$˜s8s>' U0o xѣHZU}zZ 3UP\H0N3xRNN1\Th_nޞ*}妉ȓP sK`9{C > 2Y2zq耴PjլIo(j6vDxPdM%=G s}K168v#qGq" hfU="ޱ\H5ßR1D^TQ#3q7]8_S N4O5&BQ;qmapshack-1.5.1/src/pics/about.png000644 001750 000144 00000455543 12623160161 020042 0ustar00oeichlerusers000000 000000 PNG  IHDRwsBITO pHYs& S IDATx\}Q%;E%yK^L[>eUf I$ lKM <`C6a0@2L1 l۠& p A1sdW,ٰIB #ʷ&$A!0@ۤ@#R0Oi3whmل!:~(ڠ< ڼBh`W MÄ@߯+!1W4 x7a1 Mqh A$Eq6eY p@p`0B޲ٽH`<{A4)"F RfVWy%ط/8 i9<(Iʣt]}`NmavKh7{{$ޛ(*n/{w.Oh6d1Q%w83m15%¼1wway$CwW.(1 v$;}ι$i)/x{'F?3x՟x 7FzȁqTB.`fƴ}o .VuǠEn0`<=})s8"0؝ggd{jfȳw$'LiH{U3?"=K>%gaK^xx}ob]аhQIs@> R@+a5Min.lԼ?4[^(-È { zyUh̡!Y 6oS>[i{dMj5bNޛmEdd'B>-6I}I o' 2w$鞀8ͧ#"Br䇅)YT:AWm(3'R9YM-c%GP@y],=I&0I.l%s|l,ach&H 6lhlq4SF@.~;̲ " MO$"7;#{R4=UMMȫe a#@[.l||Q6uaMԗMV9/oLP%PڄLpʁHḧ"3lQ!N^Q6|bKfȚL PзP?i4O?|A03ÂHWp_V+qq~p#3@  9H>,-li#$f6U٤?|ѐ񂅍I2' !f1'ߩUFTv 6oQK2~_Iy(Kl&1hޱQb |灣NԷC{DhBh?%<ݍۣDL{,4($"h_!e$P̠Ś<$IiL;Y5x9&fYp%(y ^$Ǟ AS |V?aOofǘ;i8pJx)b w#</$f)?SB1,iA ĐxOC}ɜ*ZwBC\؍7<%⹬+<Z4} I>k^7R@%<ɳ̉X40O9pa*g0R" xX aް?xcrf>',WV4EH,*4Xg)1u~:Ox.8=6(nDNZ'9 -",dcNM^sx;&|*!) 3a99`e0)B{Ƅ¦m h')*~kH"?  OĚ1=͞׈V1o67N8-dWBÁO¨bݰ#z i0q|&:$qW^Vd̫O3Ys猫Z]2QNѤ_#\[RG'{S398Uw ĀAQ[0yb3rf"`o>asM>'0+\xICV\Hj7,gVEPB3 cWOpldT(nmx,K]1mEG0| ա<n|m?]ۤCM/='ۧ*-,FoFJXP_Dm"aL3=KaB6Zs Hb'c QhJ_ʮƞ9][,Uy>\"T"P{h$,:6f@V9$$pR("D>_ w:}60Vr]$76 (oNt7/oBs%,brP(h AS"hQ0GBe$vV,kRm QC$":' }8η4r¥iԜ9Kn~XMxxyhj߲L_>oL *Ҝ6Jr|u~;EmeO)TGQds3_f c1'|fXUCnߩSB҂f~VjyB*"f٭jɆz^!otB)o0J@Zk:O N֔DŽI$ѫg@co/xi25|@ϲe4NV67 l3 B6R..nzޔ.ZbAjeC% Eje\=;Tc,7݄0̈I) XW856仂 ŝԴfY>'c*jT~(s ۻ8u}i=I,P!uAH*Q M!aE\8X-$DxR4t0I% /}<잪&ZK# ,f3Gfsb@uޜHlMcp%# WBPKnV $N;B./ٖ,Y# syqtaBRLtky' |$s87I|d\+%p;SHaSԼY?d.P NRekP̃6tD $r<4gJ0@cO>&xLk ͷZZȣI;&)iV6!:ΚiKhfd4_σC'HlΎR =-g`E#TDNr![>YK^j}-=~z=~P,p.'US\Ӕ8*Z!F g$C0dp4Qf ƯdbB~xpiڕ49-93x8b'JI?'ر|U:bE'nyiӰNtWt39UlMp}Xr5O_\vf|4[c xXolI9 {XqxX`vqt]SSﳰ|7XJê 7k]AZw34qeEpcӢas'J':CrNf >y}~_ I&/\~@*b.(Ě!Ħ9 \Aʼ u,Z>(}xYEXDT7]X ZRl'K)iW SFD0W0SV6*bƞD-K4m45ԍqT+(|=#b@#>c~| ]x6EeF2vԤ[l]:"הV}/q+pkS z[l V<AϝJ!?$265BY# !xoH6>>IR=&+Fd͢n=uǃùljx O#ecAk3pB&`k,ZrG 4J6VCL/لȈ,B\H3+{Gl5鵴#cHְ"ȗV [ m4qNRS Z^sZJ ;`L wi}~ F߁R!樚\TǗc9B&Kم?-3N|ٷ_B`|@x4Ji.:A姎q$Q =qp]斀u5@zl_RٵnzTa73hS_g;"[bhutV>jo)ScQHiTiI)rd;(dvÏd;a S#e]|S]T++֑Q#ZT|?3 j +9)b0 y%\ X'8`VcšowWy8 %{hdww PٸR)<^,` B igf9S 9%U+oZ]ÍmK0\t33%H-2q'0Ƕho|7@u8E r;BW2ÞLlI+wpȻF % ~N*|*N +Ls̋s׸hq9f]] &0ܴ+ a#vKbIV * HAl<`mpU"1 g_pb嘵x1T{+x!.H(@u'1?sڦ>?*ΓD9t>ymR}e:yΎq4݅$vhQL"9jd^A~cF;(=K5դO+L1?B?-{'%'f8ŘOI?xwzRQvWAW +}(+ؖ.S2 7и&Zy`-W4 pn\`msy&zZiί٠pyr4J~1uX\VemmK8j*fLrIRpn]ADb)熻A4)&Wz7G 'i0ӞhCV`n LwJPC`7hukuSCjKCВ5w~TW4к=g0REs{+[xU^Wa,046~|~KSZ뼝Š~=}ȞwL?5aܣN,m[:]eBkhrSǭB6a@..sc*ܩ|Xp->\Qo ݖ !.*ʉ+%:&Ȑdf#(>+`T:H3lG9oylQ7O/X2m̥=qƔH;Pƺ]8k`čődJ6N=`9L^:l>ǤV2l =89U~mB X ;nkT[A$2I߁M OGއp>98eRHJ qG"3n'Xt]$M>"G [T>xfLsko3|ڶ_9o3 kã+kN4s(U +Rwi:EozYRpIȏHG;*2;saҬ\Ԉ~) ܺס?9=$7.y/ٻӶp@$ݪ. >+\smIeP5ilI)Jj&Wro]h䙌dMC9l$W6"|[ KX`9,`c:փ%<ۥY L#IɈdž|< x›!#€[¤ (˽av@ƌ({sm5 m @؂- bnoEfXuF*sNɾ*Z+d0B~ ݓ(W8+>mz΂㯳-dU2N +ộ=u/3)IH%-T)9>[ D$wQViI+yύ{ >#ʤ#  w2+T YU A*ьU$ClM Ty3W?j;b-B]pJ7FwA~&>ՍaN9_ Ϛpƀq;jd3Wu߿c L)hF;@^bKm5ˁxax~D(tbD /QJ%gՀS,mZMf+mOF , ОC,jC!5EnS7{t%"Z֌Jd4ĉMS >Xv(U*&b|iDcNa~F UpK@f|/n%>g'ܺs3?OKGIBkG%UҳxZH;"Kh2M9 `Ahf @[&h*HrT%S ʱv|2|i4u󬲠J1bQ2w۽pm)7(rD'㤄m3G s)i]0ðnh^GG C;39X+^ԟ 8L-r#rpUq]Hb$LGn=Z9 ӁS}6b,Pd+qRU6@TW0I:˚E# {NF#1TNAji/ZҝӶ:H]OwU(_ŪzY'v{q_?Y{2g+i}r!@n?svS0Uh_ 8ě@001%72MAڐݯkXI+&HǑciC}B*7#ׅpd.zf>Z|rILe"$91s҂@.g!5t ffY2ry~ Pwڙ\ݛQߚc|HtD:WAuB_OY)m@ *CO& "߄?G'8e ݅܏2ڶmOU'AQ^sgWoUǀFCNA>d&Oy%Xy6m`h2q o-?8ӿƽDUrZL[c2'HJ8W`f&yAǾT&<~6E3DCT.Pm2LuħPq&زpCH5Fـj[>'BJ#-rG^zN2ǜ^kL'%q'1D2+g:Խ yˇ8zD_jvP-%~ٲm s%#*P(Fm$ۮ"NVL6ʺb3/.3sH'I%] E> dFbcZǻe(OFtA?ZsPoYOPeAg)F L[+mpU&K}PJ.Y1zynI;w$gw="oO\H~~Or[I% Ԩ耙+dz3(T"+1d+[O0L pnpD*g,`rR||KBv@(NjJBoVbY6XBDFL{'42KA'O#A )I*z)_ޓ/ҝ o7QJ{鵍| Yhp:<2"]dӛ`cZ6mmr}aW4& >}AφR|3$ݍm<Υ cR\O2GiG']tSѡyX!t͙;є+3R4k˯ `lTʜ)伿<lBX9. ȬWP hc#Is$?<WYr>CGm8;qZ֧{Y>f{027sAA7u|73Q)<+#sG2؆N/i2e[/!`KOOlmC{'3πU2Q{cnTwS$ U}3`džמRZ+|gz>a*EqsHl]\]Qѩ ΦoetU5/ u\qv-`SH ți39y~|~R.:^ӂQPQA,hֵ(az5X?B('TȠm#A˩6; 1bkm\-8@k۠k[LlR8p/17TV%s`΅y>i-f`o#r ˢ2ѮNM^1MfɔzHrd 51I%R*vҽAvv2"fS^W:j:xf@\NKud疠vعm E\[v ,J-s &>6~UXLOto Ag9#[o~VoC%H>/ В{GI9Vӯ:?\x+6ԥ,e 7.MC@ (`F]a"خXI-x/1"sP*8r˦az%YwD-aj \V`+0=r7Q +%hq\!t .`ଁOgEZ'6@^PJ0G|ѿ4Jj@L{.G(#1}V=+pjcd Q \u^^L$0TQR> WҼ)wPkў6lj}rI"꯵Al~mDd_\ T %&fVo:}2[fFmt Rn{z tX]o<'2p8 -N !4%FsafI)kZ(L F+] H17(6ƬYٲͫ "nY1'pd BM>,LAQ7E}#$Lcks,z̴ge>s7h1اɥ@@ИK<WViV>%a>{uzОn4}/9z~rOdBR33s}gl |ֻhX^{7e yed#|Û!zIZ|u!P uE//]ܚ:i&Ndw~rpNG+; *mzF>)ے尳Xo/]xBp#zfKYL`#Z8[]0X±sx6жk{J,)F7_i1%Ć= y`VE; OEpyٲ(Ձm9NzfpK[,rNA;88qGă1yÄ1L#Jӣ8QF))(Kg)DžG1Iya<=0>i"} 6O<@tk4Ku@ 2"Ԅgo FNSs ܎d=zpH"};3u%Agh3;:9i~0/X/UzaÖkWI# e&GnNȴ}JCOEN1@u 'fJoi?\89=XH[EN]Yndvc#Yn]BC~wJammbSm4$K:n2WE$j : vB5B$eHݠQ-ͤg;" s36^ƚٯ8mr-JK\Om"Z/>beB~: pJbׇ}9OoXg GX+̔^Y.fiE98iQ =}aCsu#b#V;SLk$WlwoB1fR77x)NqpAS$<131` ; ܍[XxAi] &?7ݹi&rnPeE'Op_b;wJmWs)/( ӒC^@a5>rUFj"OvI{:w{Vɏ];s;9;xrɯn:ٓdݩMR]ewVR#)~*q[4wr)M WFۚtUm'(R3ȡԽɱaZ9in֑DONoR4 +׬sE+垘l lTjd> *sq - ]y`eܩQfI/ R.ksm@SU`f\v,}7uțHm5flSr.{6E巫As2.[b<'wURN?xBҫ#]241}3zs% K=v%$r8I$#߻.Xǹoӡ]٣LI^b{ K/3~;w58kL͘&_1-? ˣua =l=&10&ա۪]!6Wc,'{<}^ asȘ{F8mǃf>^tvWlalk 76JߜN:&wD*nӠ}On~2[nz IDATws [6AĤXoF*N dyAR{$T1ON?. uQmnIP8vI[odl3C69O={! #73d&Rz3O3V jxCDۛUG`?N$2o>C'},ͥ.5T1qΠ̞=c7CN rH1XAΑ"y/kO&لmrAdA3$w e/vp:/ k 6|hsþ_mZ, :)2-W IgPh21q!G7Yzn_WNVKU]d8?.ns;0EM4tB'? $ `.d: 0PJ똈< 1Oit1nxAU8YXl9ONaugж秹v.f]@jCĂ9I_(!@o%Xl<ȣd|%_L16D<W%0g8!&p(Vc&'ƿ9meǺ-G6؃$Kq EόvYdI`Qas^Гwreg$sqyaΈc{Կ3-vwpm HoL79>D9+f&Iдu;K*,ۮwd@m!Tc[so9qvr^G_) VA-tMU3a p238ʱU  ' rze-俘5@Tes4 +2s#6W+[sB*q\4BF/Zg!P@eM`W:'7Z%qzdK2=Rzjn9=35U7O Hx l'}]Q0 ,=2K]:`w}J 2D6+sJf66w m"BFId;^L%V߆V$X1;<ԂvC:9Z pMBvn!sy_+fa}vƐH#{K@b LWHA2dW3c;nq| |n &BG3?7ֈo BIx~%,cnUNweKpG"nP~fm֘J͞1UFU_񘭘MXú0\׾&8U +(WWǘ=k~ZZ嘰HT5ź; |1 3rw ,˴D<$=A(.$~i":}X2ZYy_lKQOV:g7ۃLFRrh*>%-w+c-Sћꈬjډ&Z9 cT݌,l QW"OplJ,:-i=sByCc9ߏɀ %.׹^=6rNP$|- bz@/^zxt0א#Vxf<{ݾD-zrYP#a}oLZ!%+i]P!F3pz[">Wn?J45,5s!l6Dl~k%>>GK{u~qcy4t\iZ) y}T)lLJNJ.APӦ0z Xo7Ac;r U ukjګ4$Ȑ+xS{| i;98X?=lD aնn9$ҿbz`zF$b||LvOhSfkA5 +y[J^f>{EO/Lvz$fӬ$n U8ׇ!लgc JF${kr4Ӑ۶DPnOkqDZ.&'@KxTǺ.Y[ƅ&.)Ƌ*̆#jO"2`&I?,iDZFf-AK$7< ['j>JQK/fTH%'\&D={WԷ >ϼ/Gu倿ˍTi[ xLrжG`J E*<^t;-j#d]w6k" nKB'?=€:y^ph`8t0KZ<&:߁I[}4Ĉ y toR6:XII b1ePdo:&upv=ܝgGa)z=PM!eU! wl]b&6m|w8+?}I\T7'&5kX *PlDyo3+D>"& ]2 p=Y v߭8)YlP#;|():+cdYh`wPٶ"\utʹǓDV1P[Zp&0Y|qVQE|Qg9w7587a܃-̆m] bx)/dMɫ00 [h(}3ѕ1CPEacvvFz4ʃd6%mPj3P04jxLW̛aQµJ# XNfͻt*T7>4d2JCͷO|<a|խc0B\{5v5QIhi>RggH P8Fw3@A-(D/p뇁M#VyA3붉Ǣ.ͤNqX"P.p Ib~1i%z86y6 ${>w\7ş\~s&nOؚ*D}SkVI֚O.)?Bzb-XsP^Foz{s8>j<5Q_O}-q0l@o/̀:Ms#צ| \u{viҍjm'[a+OWTXs9Ji ǻ{Ox)gE?#k2ѐx?7D 1 #u4Ar @MCRMCvg \uϽr2hGeL,'4xV3M*]*zV^Ϝ-'اgLOdt&[~5&ZOX{D-0p3/V4b;s{]*]ovdMvjǺɇ3K Z:~{7 &u/T=!yA n~_[I %J=xs2ʻgv'd~&몗]&>|SzZ^urJP[>dPǔR85D{:\~Kt9`Id!qo|A״錁ʈaL ҉.В썪I^YCa]Y;#2MzR&|kz|L@K6uk$,鰩 XBf!Yȫُ!}:s?IWQ낈͢=Z|M7FcweGiV]ºԚc6HiWUnRl9؇N O<^|>_z_A_&_\bAA^qQ8DxaQv12ͥ]j \ֿa1 )5?J Y@*<ļ`ԅՙ4IeҴr%Tnzs@{'oƜQH._5E@9Wux .HS5+Y:-10Q6!d_>Hx8H/ڥ!*=64'\%)A7i PlbpAF"QJqs{G}VOgc/T>]<[/mч'CDeYǷv `F !>7=/;ES1@M"` )?~]\ǣ7ᣍƹ~6VuB rb]M"BtU52g^E& ҚXScrOh΁yrbyMfB:FR > RJ+/,4ډfюTe0ܛFm9ywpEUrÊ8|MWdL{|IP$Lq־yH2lK9yޗٶsy muZ%9e _zY5tD|˗87Xշ>D+*iwS]o[^J@ ލK{y679r-XA*^6hK<1ak?zIzy0P|o'>q^y}N2 sʦj;jt%k9'4Ui(,De-QG47T;ܔH Ws/nz3`75J|y\ARnLTK2rIϖ('ɱjFntcO`9TZ*AϤq;SoGXMB |p,,$-# AȌg vˑ"ғ4 IۋPYyy- 2C5~p1;8 HvPGJ6|r:\eECgL@ˠ4@L6.fcy.䲅0̺89/R{ kBTw,~^&C1'Pn1Cn7\k OײO.Xy߈I,^t'b~ \Z +?SY/'Ap(ir05*tkg  ]_tr8HiqYl4 N'HهYǾ@*ؗC /lM xHoB:_;\\.JWuZzMeդW9C͉:Y'Ky&1¿/u+PׁZ "ǙPؽt ;R_fؕ-Jv۝_+HȭK\7}vjM1( 07CЦ`_a3~^t-IC[RmK}N\"lckx241.nb7̷l.4G#ij7RZ:7ƜE61AxA,^ 8}q;+0ʠ7 ;8n ׮Ggh}6NqvvKh)a.?ڑpaskEgrlֳ{@]NrTD&./Gkf@NJ/\Tskl]}/^QV8ej` E+tUGRwӑÓ1.G2SD>*n.*t]ۜMp':M!]6_-͢A{0M/ II;%[~`\Cg.ѹT1Wy!",X2k-gM^+f;4O*#6aCTq'} x#>H0dwcy IDATe"KcgyvDt-lOӆ!v f$Gݬ m*mH~D"AݻnZ'?(87?ةhUQGP)m%@&N0EѻV$B3 kUbiUz|-Iv_pܸfr̕o^p(>_'MFC_w&~'gi `eV?lՒjOmbk kNd"ҋ!ܩÁNldm]|LyBݐT-nhlVF kV뫌ϋ7$` ؈xNb@Wiv'{C DZa\1q5pȰfBb0?܁+`txv p.iqkh~2/s*M ڧmA.`b_wT}CӖs{^Wo+F~BWH egJvFTlDWTt8;l Q(mcLkDk|/}y~yқ'0opM:uz8n1%y"N~\ҿ`zC[ _ڽ<LjHGϳRbo `bQ=Y[CLo,&τXnwi qꖂL0%?g"R\8鐵L! eu@>zuI= I#O\v(1:O>xٳ{|}E;;7VQ ,|}fGIO}Щ] e 5hA>X1Q{eaDfBE;cb+\R&FbQxyJ⿄i5UMY 3~{EQ]-ԊG >%]דsK,rXfvRrfL4_x! r)N;>r+*iL ; _O/w/B 0p~0* f.\i M1\7M1m;STQW-Nu{(7 zִmd# u-;I8#Jw~P#MQP$dU%AM3hWaBj %j)[=|yt{pȘOD}}RzwOE/M͛Z'Иo W NC`өm~0b?_`..u +[߉s61n6ۛm}iśפRUh;rs B@fRE'Uע*/Nڀ<9]|,/u3?2 9RtM_]W4?9ޕ*ǔ~H_"ͰpW% l$,V_ GY[Ml4TMA=9)McUC,\NU2HBje=f|DR Jz)O3e/Wx$+ /=VO)0*C…ATP_?CZf\xGr!՞Н]y5:k]Go6^:ܗSHs:Jf7B灿#< 6y qi<@*zb'XE!X!)u18冝G`ޚ}}l>A,w'cbcy,lSf b̋^@3a\Ѩ/摤=VKEmXAf]՗'w:<L.4 80% Pz5 C_݂?GC%ZB%.=CO밽_1%-`XӒ+uz돥rl?;uˁ&gjr:$BX՛0PJ)~$*`:^Hq'G|&&Aٟ%i jIC@M`/;.U+UoU\H]fHxR2AɎa!8O>7@0Nz/ےv: ,b 8+*Jp[}Ǘ3:4! +GlZIlfbvm Mwԯ3GcMozbKꀥA[UцjPͪG77T@V rtx$g"`G8AA7{!1[eo#PEPHո8"lHdy}!Qbo:."݆xx ^[org t5Юh+cNQ/ zJR؛dJ :CG@HV!Җ"fftJ(ZIg3k!2͵nE3Tܥiظ&NIlTT-'jIkCc{a˽T3#Հm 6+ hv8xM*cbabeWL7 ++4 *D$lmCp  yF+%?syEV.,r=|L}c9Cvq+֙9XA[%^q7_TE( HC Ig!kPNo3Wĸ}WvW0NiUd%'̴ ߫H7I3a͒,xf8"@!Z3弴'iN^y@RqB\˅z౗TȌB3 `rj-74WUWT@(V4asG]Q#rq퟼>xkN_V82FNdrN0KID'}CPL /6+} }]DٔyE/~ *nÄ 6LY~]:KӼ"X``YtFzDfP{B (K>pIdhp$8Tt_fI ʌqOr׺Ԍ-6:NIp;Z8og: 8J ]y qzw@ ND˔Q(>G@6-22[6p.4R,:Xh=KR*˔7=êEiWsl,,l$ҎN+h6cG[@msh:R$A:ȥ65$5 jQ /q`si9o<]"?ϤͽDHzTi)‹% Mn(21Z>@7@/)&CF_txpwpgg#7pi:c8rSqli|cv$݅5;J i0KPZ6#("V"빁jyQCyA(Wrߕ|3$9'ϥyZuRF& Sh߮4R 7]{I~'1G8I:@0l&]є +3G+!"_},v`3!kNSx޶6eкM8H0 /KW} `5"l_ 2(K 0Cp<=uכ ozS d {#:m[~+9|,qhIzl L)8yXō}k-!QQիb$^QCQL'1iIǞ>C<:dZEF<$V  $Z3[eX+8KyvuvIx"ƗPDᗀhZ*lZ4S j)qíDH0iD6;i43&!%aёc6:8nr]Ǽʜ~ ( Vj"ű[ߕn:N-Ջ*AׯAX 4 I`B e@r7MaUiLJ8m,ȤH@J:P?ny9:#m$;@p-=7W(:Din V3A0PYFJm῀FrE̎ R4o{[^Sn`- zS bhvXE47.hD^u%{A؏7y`[bQ&0\@i_"i# gxG$7 !`,HKbvR xcpUIH&HN8^F{j [Җ"-aF" C/7t)oE>^*ׂXG=B򠘷a;$z໰r].3._m;;fBi&4nK;ɞ$nymg /d/8tW,IދGk08X#ؒhq1<3% !7Ϋac9'UDjg䦆 =@ e!y&ލP/xZs;!ڳGC+$)@Xiq"K*)@ކl,ȷM>p?e JY"S{Iq~~liq}+eAbnFlrU`,9̭ ֹBQê8Yqe$,HSe=w~1>%8+|}τ a ~x9} 5gj\6 t@jӸB\3c 0E䵽IglJ z&{vTx%9X1ʯ^R7_]4CD(?cvt 0~ar4o$Ph~MS u`xixa-瘢||5|{|^)ym2à; -zhʏHd8;Ryw4 IDAT xnNh% {n}#3:b&943suK2Gu|d&tcA'L"i)ͺ1r NK.t BJ \tis@wp/x`Zal=*B PՃ=5.ƍ "Βu@^~]eպV&s6J޷F[@`H_pIi~w|l~F 21z&(ԕsYճGGY _a|9~OM$e1NyMw) prvwyR?6 %|\$Tejbq 5sAλ{CXXg43>U2-vNAlK^y uo'_J/f=H۫.}}wÖz=^K~)`i_KRrJ )+xgrK`TS z"~Xٖ 饾xX7,)w'8ֲ47K5>ΰ_h !QT'/[_?{[J$ⴼV/ƂpؘB;{П䈅A;+ج џb %w4ڼJJ⦩%NqX:䄉EJEX2!m.-Ј mnU+w6ɼ(u! tmMrӤ1+ m{$SXrwD.˷ӧ! 0)ؙp"aDiU); ̛=*g D'H&SHӰsAP!&; `ry&#%rGpYm2^Ҟ!q,s}elgLhDu3D G & E6U4DpdU']Ns!'Ϗ;ٽ*vIn~ޡXJp>mri2V`4t_+Uԓu^=/ypf؝Lb3Զ~|kl ĥ6տ=9lf'JoGC@Q }vbIP?G:ѝirf£g5lY^ujG8fD\}JiS/"bUA^~v%gXR.Zp6ۦ#;fQX-Hg 8Cd$ժ Y,bO󃷏-Yiˊ|4QKPe@di=Eedߪ p5R  we*b ӛꡳlh<:FEpK[4O%̊ꛤ;b+mP@Ǒ}q@k:ゎ8zW?dx #iPlӪr]e!MJ>wE)'d? O\!F !!8pی M;4pB/sfQ/x<7q>iec$'W誛3F/YyzpbTy,@Ϩ&[P k^qZj.q: fv`a-QqaGd6LRȐWm\1LB4Ra _uZ\Ts_o=ŎS,0{Z/ `*^_#kH\XK0ϝ'I?q p^x8Ei^"sAfSoWlsi# ֨}8%%x>K?孩^ȈhO--.Y,᪐wI$_iZᩑ$Lf0]O9r0`6˾7m*hnfG,[+7FG-)&OG5xW)M¬Hs2#6(*n_G鋑%#>K\) ~xDq^&$&ضZ ŅRI D%z[pO%rS3JF?.ys: T{`Gj-p2nPRْ  `r񌡆 Ic ׯ/ ˶rcw`u{ɻ&XܫtқL < s'YԸԞ{}$Qb!}V1a7(o?p2- sRIQН͟sbsgM-qhNn+hD/n@W $oe_"kyl n$&IB)2;1m-*MTm p`5a­Os"PM(0P+Y4Q hf=Ð=K >  }}9RL٧4?B.|CLHq3Q`V j= X)A}Op_#)2v-d \DbjF!pSN{5;RX!fT'6_Vl% `MDN< mod8#w)afF[28pUaZVs8W+_ Ή8-μ\&7"L@7yޔh5''1TZ"HZ$+\Mz=ABiuI*za#~EmNmSi8R \!u13L +5y[nE]Kf9. 56%ךDzS}&'k\lDW}L֐uQrt|5n5M WIdbQ+B7(l /]";ϸP#)mxS9X'3x˽brp;#]{%w #K4^'1d6 {yMX>;=2Fwxb}7N^h *Bo*Ē0hZUSR r^|J`NbOp7_~>xҏR. 8๿ˑGP u+O d@9}^]u#\_( йF"|@KA mѾ&JhJ壊Szy[MS+㗋7R6)YUp> qv2\cIekPO0`n)*jp*reSꬊ!w:Bl/ ,ߞmzҎi/VYc}m3=6f apOgaoզ)d)hI,ywd!y^]4ofܠbp҂MQ')~-ͯ}:2r@0)' Z,a"Q2]gn93/u?Xkϖ4ST(e-2M'{z5m5-u&Aڬ7o) El3" ZJv6 9Cn7xatĩdݏY| R}ZQ(9 kXM}Z_ǞL}8| i W54ͮ^JSZS_[J{&nɑ|/|[lp/Wlcl6^ILi7˙^6noO^JZhG51YZKݕ8Z&!%QJ8Զ\k ,죿jzg%inK혩* lFZ/9D`oYb5Plh3g_ F7_Nc\t1 \2 (G"C{y춱GXj-[@u&^n,nY$/ie\L h6&$q?=WaRzMc"r>P&3`?JD8b kX(i/lOU` X /4W#~:Fl\ "B;vwŠ ο5zT옡Nb|hv9Z_t\vU𣿡Y=|\ԝ(ur"btpTk轟nGG ӳvdDzԳ1[ا6h!QaY?rf |8EA}N:vQHt˽ߨE^MJ-Af%}`:GϴTrY=|/Ҋ=Zr׫ۈ:^xK8\\ D1S/l832Mt[ kR%Av55/XM@̰#I͎(\  Ќf cn>Nj4UJ7DZgz&#u2 4w  9c{=I·':6_;ZU3:#uE&Q|cu=.{K4BLo  I{@y67uBخoYڢnU_1Zqp ( S,4e 2#c_ֽ/av$֨&K&YDK.P hgWlQCר.zP# : yBuy<>!LU ag-&ѦAHYGjJ7z@D\Z[ HC¶j#Ga=j 34ej*K0yJ~ Ӎ;*+eV 4VWbI_:_ Dx8%qVͥ6CzyB!{Hu8c:/E+XD:uH#LQjF8Jt_Eš_juV{`cCe{h:׮( qT'6\!R,) wxDŷBcgդ }}Ԩ2CZhEu+ߤwOy SXPwz~M&F[;QT,cMHwv!%=><.eW ]K*Ca:0TC8q=^c\DԠ° ,mjȱץ{D p4w =X:q8*E3F%)>0*ڊgyR*-epgrw)PgGUJ!+pFs.&m BXiTAGˇWKFJNˮmωo}?vem@+㻷׏~ǧyKN26Fw4 *H:II ' {~YSGDc~>*fWlBQQeN,(N? k@vdy5G$!H9tUuL%&MD|h`h'.j;dNtqo5^$ǰq^p{W_>$Ͽx{~ѳ\-Ͼovy]>h~gnVX-8ߜO|A=ï^ޏq"OA}#r)gڇϞ(d]m#.> 0]묂u7jCyzp0](IdM5ӭZ*so{!- 6m&j/g=O"̕ 塡>.p#:!9fj?kpo.#e 1"[J+W1G}5S%W|.-ik0sb|۳w>ۻsEpr&-CUg jL 0Я5?_=eP-dj!RaW;plp3UGe*GȌ EfJ ?0بԂ׀j vfXIC-U02ю=?Pؓf>m̉Xi+%ݵw]p*0/sÀR%+R˕H2Z(  +)CTYұ I*5=j E0˾%< IDATw gog([gD8W<5Q^0mW eKPT.Ηy4,gp)=ɉ8RP#v5\yԡj3ebq~G+2 G (:T&zk(-FFn{dQZb3N ߌ CĶ2Ks`B֘:G:mAb rFr*# `ۖsjC_~W?>~p;Qr*rgiJ V ݗNus]Ov}O̵EPVk_'ɔso}?tp$ч/wCUs3^\6Aa+nӞJ%UCaR6U.Gp4;sD; S%=9RݲDJuUPh>v#KĆq!YPjl@'#j1c)$1Ā!3jF2֚52+tElőZ\J) 4X,)bOd dbu" d Eo{Fy8 ZXp], 7ZrYBQȊDn>~‹ꦊuD 7.J9%1LYBAPo&T1DCͺ8jXGYѿ4៙Z [ C^jRXX?^e*ܐH2!+2J|+qBg_\_o|z5f햿GL2F+Ɠ'U(Lh^oYݩbKiT-4H.'|HռUb \ʫmf ^M q##9v$,b/HQhi`myٹ6Zn,gb Fpt0LBr@1q ΐ6Gd$VFy&Vz\ka[/LJ1ʨ&|7ʻzr?b[v7a23 rS )٭-yG X} |9b72'$*>/n0xĐI,e@!OeL!UZ>ZV/]`vS@zlG@r%'1C"0($cTPJ Z,֡f9س:+`P2W8V`2s=nIe.̕^_P쨎E%v=[!]RuVQTaxzxMW@Seڍ&Y+x`҃]9}]v,ZT/ 7Ċ]MZu$-஻vE4I,(͡)CjgІT.ք#8/`tL$ ,l'ёQ;6)Jݝ"vQf2`,Cf]]ut_kЯX.qD;l"c<]s_iNDU?,/|mx /go$ M@ei%)rXdDhL==Yc Z>eQ^$.++ 7#2u)\ߗox~O)I`zHV]_:\aNmyJ<.ZcYzru%=`V(Cī>(j(ÊY 5+^Ջ п`CQ_GKA& L4/\\K1V clD(k ( dbH{OFj5QAb}߀mks {8qs^g/>~ɷ?yR>e͏תE5tŹ*Ӽe'94BJZS_U_F\w@: 8[]t3i UWb%n0-@9yfU0dfPeUaH\ꠤW!8:ӣa.85BZ+wuZ8 PDzDP#ݵ/NfoM-λxHexA.3OC{.u^]{<z?dIu; LFUXTDة(VG!ױ^9{;:f^cFh'!V!`yQe ]ФO=^lM`$|vzL4oJq~23yT*&Ҝ$Z^{9T+9V͑(L S]Jkm'Jf$MQؒLD.jPM0%=.ywyop>i*YÆYʴ@7DK!6D͚R#Oi! 9PPu?cnz1X170pحv()աJ%g?EhT ප?;5%Q"Z˧Mnt4.H3DHieyPrC#y(yڬZ^hH+n/ٲn+ G#cY-h0cƐ^zp+Li?"*Qsz©MCY/FWf u{8O?D:=؇.IR;TŤ9?F 82seba%$D_DBhk]skCtN#@LGYF~r[) DfXlUDYG cٟZzظ; # / ]\9ebi"iMH@ 61\vRž1<ꚺjlƎT@2R3WZ*A 醟F2vĠZ5D(Y 3b14C0w(q+tf=J#ieϾfTCxqd9P Nm`%*r,P#<˼!躯~)"Buu HD ,_]HZgB4aUVEpJXd eo9Q2ƳbZAg# Nr-Xn81,BfNYk&cDzmBb-.{Ist̔F"a j4XERY}3ǠVD_x~Ͼw> ӓOUtjE P{$@`xX?C,Q3e.0L0G r.Ԗk=b 2ŞB YA9o#r͟ݻe":X/#-#Ou~=ߣ] [73KmG.iaKr_ʅTEWTX#HŊ#u]jNwDCɁOdfbyss>͑e//yn\ڀRwFDyjbɖtu>{ȝ" 6f8ڍ=ШH=7o\{s47y}*,OG39؁́?3#1r.(HP<ߜ/Y(;dF%CE#1  K3H]k`_!d_Ғg&2'xB;1ի۩b*A@bQ;U(JVT]~w}Ɋ,~J]wHeyUIvKNpɏE^9h|V=G\S[ux w`v:sIQqg-3/%'8eXA82u n {d"=P\ONh([pHXIh+*sذ"-j8ڶ\]7'OT[tw==`(;`o61";b`Dq>M'<psM""Aq &bRbSBǑ/`E ;FgdK碦"O2嫵%&CLlIsޫ) AZތtо=.B$cp 1EO ĵ$ja\w&Ԃ4tnRm[Ea'qOƔ0zВRaص+!#\+4UW_V 5ʣ;% \I"/6:RQ` {En]:6Y{9Ԫ@`DM6Xub6OqyG5zhw6SD'&+#53c,,4 ΁LdjX\^(1iӶf!%ր">3yZgr'7c~X1}FYBJ2&jxٯLJ/?uM 8,h^m\WW7^PU ,o5NIBSe*] >jVwӜ'P6u\O8Ξ#e<>+˽}Tmi-?4TiH @4$k*=5Y#>P`{Vsg>^n"%=Cd\rT e 3#Oο3{<["$-4%¾ ]ʸ@c*m ɂm aHi=sdҪrEOه+aK<msTlPw4_  P F9bßPKi )#Ǐ+M32d]YwJ73TKc01 -$ ̕TFɊ8j1:V_ +)ډy9өM4un(nh8;ftS3\:G ׵ľy WյFfav><dbEAD?? Dԇi{4r9?4QTDn,s2c@ '`'"9oEBȵs@DZ`h Im yT aLBC[K\;7Wc6YC2EWƮ?Erϛso>8}kn'nەt*:°:;vv-ĜBuwMV2>.JK &H&L5:DLJȂ얳ʳɨQoKP&Tɧy]뢛!ț4. H)Eb #K!oNX 3ٜ>`:E)?JzdjA&K YȔ8\-7({mN'ԫWoo~?Q*e OpKe^ȩcRZAREe)*3y+Z}XhzVewu;*ZeoUf|Vo3DZcN5`0F䂽EϧTP\݂ʡ:#C>7'QxWb1fK6y1sRMT#\0AU ,JO.@k嶔ÎϿzsy|Q{^5\D8Raލy.JI eK5\})IWs q39nΓ}1Cm[Z۲=>K2EzI}G]j( #&<Z Kܒ+`z.FAӐ! %\4an' (2D9$r 27?Lu2U@s~|D[w9\r:qsL 8{Ic Ԩ\F$+QI 5PeFώ@JPnQ~O=3s>kOBEc"w\v,2F Ԏ,h0P99s}ڵƘ3aRk)zP©mk8_y"&%ݿ{Ƿr!ӧ7D3.ә2Pv($jbWcr Yz jzԐ94'"1f+uLJn@rȃf"(.%8 3qq)M=%fB?w$ 'F%vǍJ$ŠA,25(s0!V]d04xR),8`hf oU9s珏a=\.۸lS<ǿӕО+ MӴXΉqCG()ؑcT5L3ʨZIRÞckO#F<>^Rt<Sf N 0QwP}ۺuXs` ˛@BESgw?v/(L"pvtE'Cr ˨_u`4 /t\_EE~M~ 9PnmP1l54#C5-Faj"0_e^?>8keEz{w#(|z8qAv "T~jð+!3 JچcA쐤}ItQ)zR '(0M∫lCG\d#] 짥}P387MT`fAoge,])C{$$D O+u險6e6~wo=\/{JmTz䞸d^.+뷏޽+欠9@/vj"&ݡ0 DN.a(%$Mγ ACVg|:/q ]~a1^$RkeZLHUU2-*B_}?ݝ#u)kb1] Yʈ< dNN I "Z%,+;rU~R\}a_!1'&1Qߟ5}}!ŝ[>lkx SCz{St- cix#\z xBpƉP,ݡg\.Ge1(1i>ۥ/ϹQ= RnCHFu*_|ͷŷ>|LghtRQ%kNŢ1(Ԝd @dbo5՞2YQBۓW&;QF5zxՇ%rGsw,:9Xi&@$ '[0}x X4b@=Ѯ/q8A;YHɴ D$ %G͛ yCJJ5>*@Z8e_Q/\iC*Jhc8QT/tobƗ_=;}rd [7L,#Q@ fP ^D ńR15[2P0U Qi7vN5@I?ݟ#y:qH$G@Z`ܶ"N76j0䘌 #nnd%DB>WJ+d8&SB{nLLYWUThI" J5 @Fڵ(Dd PX#TZ;YwMk/ݏ>xv_~ͳgs Q/yS;cS%#eEQ`4!V }S 5wrya͙}1& 1F,NϲuHFy 1%Av>[xFw*?*TxE2Dyg$'%-be).󕗗I ߰$ݫ?c4- K 3EPy4cLw ޗ*E}XK6C0ůӓW/^ޞ'Op<m Uc?Jե짿_sơ&Z0T0,,=%#7pyҘT5;눞Kv24=%2!2' 2y俪U?="a݈y8Xo.P'OcHh Kj2!h>NBqr31#lJI|#a_,Jx_D"Wu==={ve1gwġ@py>L}t W; QwBǎ^%&%h۪>~P,}6 Po d0~~r: -ND<1r " +[Vb0K>⋄A[qDчD=Gpl"yLaFSkrjdw#%b HaKr#Csna0W2K[zOOǠŨ?o| J\3Wܣ P?i9z9j^ ,`κ o O'cho}$L"_Rf2!)Wyc<{;ɛ' m̓嫯>{"Ur$@1%IZNaJ̾yx{ޚAVxڋ69:]f#b@)2= Rb*ח'7s Xum?Yz6  B\wçUQoZq۩^Yr*d5j[j_1%d%\kg_-$گ|x΅UsΘ#޽MU~ً_}__r>t1Xe3=9Ua rف,"i9v}CEp5=S::8Y  `;0y3kT D|L//f*afa9Du [1 )_x՛#ng5ei}Fs+FZxHRkϮ?-Ga[mAd-73yL&%!׮_{a]ƾ#5pחm߶mw߼~A)ƻ7c^|} or/|/z|9Fx3N3M3j5X\i8SmEDZ?f.IӈQR@~NWf, c5) SL{!7/Ͼc[^[ow7H 5Hŋi/_͇E nWo:b6 yl"8۫Y9m%Rܤ=y{27,^>z~ЌIre[$ag8.݀0\=-HyțF}i$,Re} }W⬵k[9ۇx9޾ݻׯ}g͚mI+{8ܩKHd`$FZ/M#UO&6ТL$Mb"APӝ=;3#ܗ<^ַ= 8r JQS4`5smXlS8Ypl:’F(k 꾖DV[PǺ2&ח{ųv3qͩQdȧſz=!k=phw7>qljK, )뫷Ӌ_n,a*N|y}z=G!wGMw6@$NjDd+}g ='Uf+XY^"cZN :Q@㼴vwZw;sxm W~XOyTB_]+Pq/f ggp-r~>yWDO JZ;h=$MvaV~>7ٛc?|t5pѩмɿzۏTZVJ |$]wy$sjff*%ߛa U;`w(^E5":<x[s{g=b7/8ohTJ|ˍQ]nEM&`t0uy'0w+3QiKU>?e"HwErLŒƀTY,x@DM%%+y]>~pYkN | 1QIsG@#YQSfG4ijvK#@C~^o3ľ+Hׯhcs矺@0բb,)sňDΗBHUѲ:"2Ltȫ)wϗbGGl1a;-tixsiipvۓ#ZBJ0kx(]AefL`4X:1Q'#)*NA#[KnZe[:x B F2.uws "oEPULΦR=9ufP0"C.vΠC$F :N嘛 <-8+ O`v"}Ut$w\0 &RT̾D57Xd֜*Y@%}u#5(i HFIxScPؕfμgJU_BWw=a"̂(HB -DפZ=^ؕlkoq7-m`нMn瘜q<~I{rYoNbBDpx?PuWdOAd &TTwcHR#U"PHuuE1T,2d $}Dyss{honNo|ێ#X 7\m{ivc'd @tn}0zt.קO3'r:P$: P#8uܜ@ ȵ1/)r8cRaLaw}ƚ "Q I 5*ڗCDB{&W"'Պ×9 ƢnnNG 1 rGUCd.-!kC52`@&N. !k%֒WfH@Ĝ=q)R NBynq:\lN #@)eiOWT*%CAuI, V ^%Gxٜo=ŗ̂X5ú<zއ>>.~\mǿxP5.3o?1/6|0ۖ|jGf13@n0֢#N]b Tm2K/?o"g2x;2[?Q=iRR̙!su1P.a@tY A<(}^ J,.Z IܗX}dxh(Z @Sɶp _}T.iu@@ubMξ\ f`!*}"/w7lO>~r?ޝ!V fsB5]y^RgཝmL|ϯg ~!jZ*l.9- p{PcU/.qPX 6A5gxp{z¥! JVK5)~v'_/$eUo+FȨB¡YO#4z~ }d eenPKHAXS`er+0{2lL I 9X,תD)Keɖ2Jzd5Xى"TC<˫8}-ǻqItٰqEѼO :lg;T-%DK]G/NO>|do߼:;&NʀGoi@Z8w6eHƀVܞ ;;TO~N]l [LIJt1t `pBPbU*c#Jz*tgò}+?GĔjT!eaE <͡N@u|vshxahGL$(guS-'dXvXk&Ţ*wn{?}ӷSLwRl:RXRFOTV/kq]-jp'XX٣8OAZ-k*г+V?*<%mE@uqV~gfl2\zzo~[_<޻=}?}>*,0#|GstjMKФTUQ ;Dw@tIBA% Gf"MC4SR#~Wٟ_O1Lp:Q-q 13@1G8ٛ3Z z%J%Q"|AD)+ʙP%TeJf/.uڨH-b@BI'><,]Tp}|`""Ee1RLWS5`W P ~~>xvWonSwQajoZ(R˦T5.*4ۅ2~t駯xZL2߽l܌I,6]TEZTUکw'3 p|ӫ/K歹6/Bj>ZwKDHoc/?LMnN81M/ ;PJ.N 󶝩p)9_]($X#0pn!BPsn" qEV+@_$ օ5aW|^dͲI3'(. !E}xEů^]wRTq2-JCoDžۻփaME}@IYݎ*ӟ6CɓO_=ͧyeG+M\a}qe|8&\Z/§>M苈,?`6-$R eAmevQc־U$Ӏ (H$ HI6L„VRT2ؒn`v, Tk&SL=]71% ̔WL{xRu܏L9uSuMG0: Ur:.uHm>1f`ԍ)Aу[>|՗_^[Qsn b֡u(Z@lw1lA쎥Zgxk*uea=ˬ]֝RAĴ0؝*<M!}cL ފA5 _HŊ(qj絬PSWSXN!b&?\v,fӤ 22Ta`P3MFa&v;;AbLQl.vl*Z1Z]PU ǰje@]Uۻ2b1Ze(X?z10)bhT5+Vk6㾎[XqB?oNK,fF-nj0a;h 0UWŴUMҊS|0[}YzdCUsfk̽hӽ{Lm@",˼DwOB%}bo{c})@=nsDCнl]Q/+X|W殙H+\T[5"@  -EMtZ]JsZuP]~Ujt 춧S&VBEűm RFy㋷sQ">X7Jg.o>~ynm,V6T,v1bO#"1z&FxbbL}ͺ@SVj`0Pfx3 "f[} jU TսO2Xz 4lTBARxp/˺mW WS{&Xj.]ח*W s. gtV [Sjec&2w 2J8>5!ҝw@l^h)2"0^&U1jyPh)u^fB2$u"ePkj2>z|q;{a`uID@)v(2h)A";zoscVx=cY1ϱ6f;# VlyTHH|MEfMV; v;t]iN0$LX6Bxtwedh.pҧ}i, RP)}7%Q[fp'_T E1#S*PxCE()@DAp^uvqs{J]lKw.SCEr]lM<3pƩ{xW2ZpaXĖc.ͩDz6m WWnҰ, yi1{p!sWJ&;HǏEՑJҧv?+ Gxwo4} h)=@< A TRJV+b XzGw(k<(E77'ƈKwRH82VuY4n)*t祻#`iJ&{d8 +uA+C^֖fZt;ytj=xtᓻnZJO2ue,jezoZYJDn.rb5oӼ iYJTn{ӱj-9녈#e"jw?=yXE;T]^x;9[@P/ [[SO~$r(cND8h 2fLb!U%<`"9`q;8kUZ*e-Վq7`CJf+[/ *)o~ﻇm'(}uWAono *jlf6njU|o~k:~|Q-:륁SDf`v01UO/:{%cu}(ows?S˯;͌6-}>qxiYVv'ǿ>;\QFHbbqcJh,֮pjdCes[6{o]m3śI o (uz뽵$ ڦ9ϟ/͊D!AO_gɯ{ii.o_FT\B d];\ ӲuH>ejE\@—Vb<(O]/w?͗ Թfn !Kj9tGkM)N 6;TpiI6-MuKB &O/yZ<|_;(̓"?5@PRu(:&! "(D0LT uhq,)iAivF)%TDiX𵇩1ΒT R7L>ğ.4}lE~wU)`o_>_ӏn!*??}sGwoI|60^/o,KjVaspowo lw1kKtj1#ЧAңC]{@wv1 0 q:4Z ]>_o-JR>[Ϸ'!;Os;UHhy ,k$7@L"fCӓm*SH3*80&"I<i^=8[s Fq#2 fm (s~媟 1qkqO~hlj}mwoJ)"f}?yrQBUO࣡[iwj^/:A6纻uEI!BRXfѲ̳+5w\z!"uSn.~j3)P4<3ya\[սe%*0jtO~?޼=EH&h-a]w沂L@2Բ=:P) H(XD(ZȢ:RYx$j`dU $Vi<Q"BC<^i⳷͠$Wqn}z4i99ԘVUnؠg`@7_!e.al\#4k8>Wd3D4>ڍf31\m>6=@&,3s&L- FFxJ2,^\ ծs(&"( g*Ql1]{$P !%K v I] 8lwf8aj@4y/æ!T)c- hnV$Ufl.jE"lR,39I>$:VkJ*R)BN>o)wIϞ~uɿz}-}ZNwP,NX_R, T˫ ?ő$k"ݨ{ !V"U&oE:CMWkߜkֳkIx.sK 6"CTcia&Qpk1STP@ڇQC{e@s0HG3R`k$ T=/(dm{J[ aXsԴVmTHM |>.Zfԛ7qfPTNT!H7jr{ZZ;'b9Mw}2O 8M˅b^z̠mskRCqnp Q 3z ȋj=/ʖ1 䛬Vq؜aED(YQ$?v7-K[@JYԒ+:! b.Ժh#{EGvN˴Ě2TjBQ h]f:l@dA=^F=/#Z~=i'Ϫj$zk3}︀en{s}C9mvSIKn/a ūi!TrOS_ܙA;3q;t$ 醅4I9cD;st+Zz,'W"$ ٕejl (Tԛ%I$Kb"=>7_ @  `f+3LE^p7E2ᬡk:U+)&n`պ:z3o$hhTbA (׌.:# ##L]TD+h<}VabA"gF/@D_~5& ۼ ĸ VW=Lr\*0q΋76?Z/ n1lT$37F4o8ɜq))`} {N X@FF0 ZiN9#dtqvBuH~|3_^Us|?_׳j19 5欅:.!av&-,8&UW!@= | yuEq1%kUT3{eyc 3t iL`¿SXJ5GE##n8əcȑ1R$󏏟7"e8mKP_b)P(#o1o#T5Ϝ ;#Zh{ n n}]^Uuuj-s^c_A:G+JHw^;s?nsm|#1Pߗ2":eJOĈѫU*!퉉-4Nvn cFv?!I ќ z8oΌ%PU1x]/3U|UeIpouϧXMz:BzߋPIξ]]h#BT A@R]1nCqǘ:f6M-ЄڼCJ]jMu*0sB_f1zU s1,% 3x[q[v/|UmֵH]+>>f]"rts1uUv)~}]13:#l|&2U& W?5Sk^yURFud׳ $sjI$T30dI:%ϑ׉1"u_zz~~1FUaT;U =跌!"&תB6-(Ev.GJp $ȫy˜y}_,P;sdoG*imBeibXC^u5n#UCΫzUN th.!q1r8u׫xܪ6883-o9tCHF b+im'z^FK:PU*g7ƑH~=m 3Uzy:UL֒ncUAm{҂tV]5Hٕ7d[aޫHV6M1F̑36.Һ뵘9:O:{ɒm\I#<ȃ;k篗[*3cW8uqFhViR7s#9 ʄIOW%~y%vXV-;U6so…ML t[ob5"w>@ߧꤪx>Ks@ j 20ȵyj鰭 ő<EB<]o^cz~]RuB3Z17 N#38:Wʞc ukL^_-)2wp pB#=꾾^9n__w,O* ck vXzz ۟wɂl@S Bw$2cU]I\5$+Md1jԵS] rRBnvv@( %Z߲L)q\cyBvԪqt]F/xcxtqX6ՐzIfteǶZKBd *WWJ5bvU.oԊZ>.zE#g`qy#c d x-PC@459R˪+RǑX?Wʅ@rңAB#.{HXg_զ"91Kh{o@c#EuI;pQvā$6׿Ы}F^Ίi*;gH5fFXFZ:_keP,sJ3Zד<[^/ #bJoSI.[:pM?}#!I}6qj[F*BEQ X%U ##-:cbTcox^UAȴS3/xu2U}i0(ֺb;tʵTƸ P'@D WW}>H6|}-ux zCSSȪrV`4Bi{fPdғޓ-i?jI8O'm򖉯_ڣ@}D@dHdQq]Dyjm]֎Q2R0>@WmdE\f\@^v:1rkNҕ-%n9Reu5Zjl Ps#<_5#e㴵bZ^!oz9޷)ՌpK~13_G_Wzc-OṈuz5",%jxD٭k9>81bp?yk]@13ccD W#u)!ʂFG%ЮI*y%3?'H{[W(qj#M+2K)Ί*5 li~+2]ҵVFwfTjZZ98bsԷb2 2ݙպ߯'eU]cmC"֩K1Ƹݎ@/>~l`vF =_{y{hFG!|]~9sis [63H[r [vR2<t QQ{(NϙA[̈ :, #VAPo{#Ӂj3+-\sEly~ 2^SE~3g7͒ǾK%>Ȳ=l7c^l.YU i_=)^=- .#70X&V`D*2})@[4㷙=;hkHuc^ի6Yj;Xs&u'h Ġte_s#s֫@, e' r >n:ϑsa IDAT~\݁VPq@i [hHklmzҜ>=Ԧ> kJ/rUFF$U[`wab@`a{&m9v1:D6 ^]'\4{j`Zx}ED #??/+s<0}<I&zyD~^S7?19~!%/64HjᏏ?~}ui\OkXlAiVHiE߳3q?n9BhcsW]/h5I'yBdchҮ"jq빭AFf >\g1U兢 P8`]9#᭻91$"9"!} ->-o2xՅx0if턙Ey:@^K $;%t;1NYrn v7B\>A""c Ƙ7!`tG묥jjF)k [/z6g9VJ`ВFq(aŽwE ֳr^*\l!=Ӟuasn>f=D5N X8< dȺ3^KT[@F U ܟ2fŞGeX(@Z;^+NzV=jZ?>"om[E&P-@`cyO nDt%0qG?Kmi K2v7~˜59viBA$72Atuo,;nl6Lvka2LP񽷘ێ h16[fz=Wu(^*zf2 #10Dk0Z}h 9 w$PXk^uDYFp}Ǒ~.!ߓv1 n=[?$Hzn6 P4bu2H z\sL9&-&{3;MFHlVh &S[a ԚB ~ Cȍ"kV)>wht5'Zb( aXhH@>B0־Nn7 ;?b0j$p4Hc7]=Oŷ[XVB8PPt;M`A0bWPmt=>lm)*[.fZ8rI:9A=nstVQ`dȆʥ_Fa05 ^LZŐ-Hq4$D!uRTͲv(혶B ?%c$ͭ8E 0P ȿ偲(K` ~VJ19'P ]Sf.0nuώ8]BܡsTmb @ج; ˧)н_+""8H*b/VĒ$"lsʐPilѸj<{=m-oRﱊopOI2LU B-ao=K z\Q8F=DZo˚bvl/%kݦZ< طyeYrjƄ2sx@nyⅆ46P #!7Sj4w8`3V8fú@pHuv7jC6Jc/АHH Ւu,\O.LBWe(JB3Bm]!l a3:v ŀpUʄ??>v ( +T$4-R9)=ZY x{HЭrnv Y JZ}!>Mه3D] Ab#_N)ɪ )'<~:A1ӟY̾16'S Y$ޫ_`J9d6ű6_{kB@㚰}L6 h/)\&>۬)Pƫ_o@qK!^d Û^.qÕԊ0W/n8PyoJВ`O#9'˲}pm cPخ|{,.uu46!"4.nXz[z,D=vB)tVWU$b8 {M_&'Iti)zc1MT 0EnI+Qn<3nG 2np"]9ŦMv"L˔ ѫ O#`RќiS3mHo[;(>hloGpnw=s"oo{#\ Cȳ(GG1qUvieHi-q`i Fo{y[7 C#z`CW4zº @`'@dSC rnF9JEZ&"*拾DDtCڤy*.iǻK3cYZVAZ[?jb6jy@a<h0>?.z{ /!m 0 ]^5:H]pl&*TQ>btn(Mnn7A*)X'(tR@3#ԋD1V1}Reв6F~QBZ؏ȇrw@ÜL& GY$Bݛ78X۱:Ķ-ɪn4kωdNtcBM<.5SLДύ.IhB2 45 %I]a ,?M9kvdFh2m4~ze,rHh隫F,{+7no_*F#VmCZܓ3 Nr61Q5lx#S)6_b/ (%T-#]r]=B6'a/mJ1.41lWl0n~j`MM!>Dh0r7hm2 ./m 4({^Ђ;Be8՝K ͊K.ϿϺ^#MݕH4nGVcY6p+˥ v)o,0˂fD)HA߸s'wm9~ڬ:;#:vQP%s6B^Fj.OP c(nL5uCJ}niFw(@폷Y ~۟}k8-+WKظ+Kػ{uOޏ^{+`6p 1q$[)-A?_u07ҋ~_ u ]{ۿG21kJ"@1Rrv eJ)pl%[j6fIa"H[=hSIa/:lGn, =&R3hދl;O  6&i7 oJ)۴X6Om6ҎF%>n"ǽwjc_24$qP|kߌKe7']gc ow¤5z` ~զQP]}E͝ СF- ,gZBcC]Cˀr(?d`AI4=o]w>U 9DoXwݾޜL!=` Qfe~zfeP{B&F،TP^^p`2#[>4όMvQ~$ٰ!g*s?g>'UnwMz{Mv)VMNs{.xC{\JE>IUuc;c>4@/-t#;T#lq=xV=ys0IcܶЮ;[`r_݆;ګ:1=skFyIh/Nچ>8H#,؄?85xt !(i zXo} v Z }%)riieEKKFstI+-"1ӮoԶ2x+FĄM+B0`'f1GE?c*F0ƹTB&zkv^̚!ri(P~ DX== 2_obQ /]QnN;v`+  50t`bX7j1egԮL Ž !rEd߾6/h[B<^T嫡tN C b{h`\#B+\Rf0&ɖr?w37|e&%BfcI7o r*^_g8nH ݚ807Bw- 9M{+:`8>![k{];n/}{,ln87Dt>>ƯE~ D# W N!+j1B]vuֻ滾[@e~~|:뀱ͅmKp!1+um YM&=U)u`a7܄s\_uZҏ?~m9ίt ֍2ç]9Qm6݈ߌ[$;r`.261cO$,V^~&Ø~^LN)g)WVW\p !bz V7BhSOwuf]*B֖Zv@DT#(U3L''&&r}pc*Fb`zw6cnC"r{?{h[{X̭L6*1o~}R>7Ɔ,JOINH`qp_lKށl760!>1_a@\=mpA(3*k`z(TFy cxKB#7;*1ܢ}{s @ <^Me 0*-7SDɼiSvw,U[fvd~&;[WU8Y".رt?F{t|fܐc CBVܗ$Hhm~Q. Q&5jX$y=X #Y$ZRn |>nM\$b`lL*>@h:~nfLYr9v@w+g8 9$gFv0dAS(ٟ'"Aɒ5$gntc{VUd?h}A".:KoKWRP2*"o!+յU9g~&(Gˎ lppɀ}a循1:l mi#f#68)d:WTF}0ǃ \#ILVFf:G!(02KHD=Z`B+S!犼ZEPAppՂuR[ʂKbl |`:o1cT+;^նآ/!Bv>U,PMQ=(P]7`"!Po^1fӀ cT,& \`DsƶҁnT(<~qu`* GB4jR*hۉ5[#z '_E7/G-(rDC*(,55Uv<ևnL s}7ny-mP(F` C?? {+9*$ߺkC3(8yTε"Y_̛q; 2do7/.V_>= &dTD!+PEabS"b^=+5Vn]@  P&T!a@}!ⷂzXBmو`b= 8W{ Ⴣ^Kbҽ[1g_J.WnWЮ1b? D|"3Y-j5'vF[$aջm&Ùz3"f L(Q#¢Õ1iF'R`'qй}oԐ:p]3LzQ e*F q guݨ1#T̳^9+03"?ѐHD#kG<`C'!V Ҷe2l!<#gvo!-Vu,P{p j1^;/κijɔ0-̦ͦvgBv__e33$̶֕&w/1Y$OXT _^6G_`Z [}YTjb"m88AL("1YV~3&2 ª+>&>EǢMjB MfD16*DRd%ǜE6v99rcaݐYF cVWB:ߨ@Hj%(G&4UF5 u/j7z*!G# 1]`vؐ _dѩU\"@wmHW3U8TD7@&$ru'VlK["6k9 ,$u\? 2c-Î G-WUl @ xݣCC%ѨD5 '_M[ ĊZ vI휼`?CW ]ۙk5Wm1o$DmjCb;MXD0Eg,v ߼9}q''7\>&-Pe'q( j(!N'nV5 2[wH7Du!uI!V6D2G BvwZ֬f\䡌Q46[N֠hqv"sU6Q15x;$۷v/O>E&K(1*gEE&YXXeRe{~T Uz{ [cN/{!^q~{IU $2`0V P2d_cPuT%dD4 n_g'E~URiiL/6z BX'^c0~9Hu]u=_\M`SrOA͌mF#?|+3l`16M6$bh քL̍RHU&|2@ _0ު+ǁ6Qe OR440vњTxw*fFP2qe9Q%AML* }-nu LĞU)(nU0Eq+'j-t D@z(vCb=Q6T=%'v4OTzya/?~7#M !um@B6 =VsZ1e״  +Q hJA+yJ[*9Q@l 2"%3TL땋47땚d4W )$B?IK@] )u깁Pj4bMObHmr|uyfy>혥?fEt6ڵ"wcF`JLMZA9ME5%S F$qȠ*6X`h7[rNm۪U~,x`'b]V<uDu"75`9`P dըBN-^TަlGq8(] j(ʤ,%}k>;b7d``O]u6&i>?g/qo_6]w |Q0MxRy8@DS2cjmYwFT+z5nBƸ)cٌZ4 #.1h VPA$1H*x4ik8dHL;8m@)6޴kӶTOLb~!ZmbdZc 8 G#Y9Xp'ץe1Ej́|"JmW%B M"12S"[6E *h"Bތ&9R6ҽ ȦċNjtDdn~TlnLf|` HwMBQ]%V*f~"Wث׭iՂuK=MTa˝buVj N#cR"VM !1aSTv!@l޸=bm[kjMr`6EcKQ!Kcbf /d±m EKd+eMq&8eJM5"AdD"ȪlbR-X}*VDG`(f| a1SȌS7ou;7 qWfwvKFxp}?ojZaL9^b%kD)^Cs;﷞ >JȸUzjg[?`%6URS0jV)H`! W[`&RP@MJc0&6brVQ&]%ޙ) pcf[&A PrF/MڮÊY#y 2ٜyS2r9Dj^i)V͐#i" Q"$4UAd+BQ@+2#d &;0lF4k 4 jXR4*]K)*7"yl}|=yɳk7;QG70BJئ4B3'1d&vlYN+rԉD&4b/{ %R R_˫ 1ad p/V,*CȺU mN;mѲ ^%(0 "g Ör$Lt8F`s!6Q#X"aYUM&I *{Ve }pwFm"Y ,Ahvx,"@0FSC@ b!8 y.WU~]f(cU9"w#B"<9a\MܴQ UĊ&="Ø˨ӝp;_^ 0 14)ӺBEc1˗vlu9ϻ|k{)Џf jmKݿNi!wv.We)..\>TARC$f:{7i+`Oғ^:A_mfH*`Ek%*ͻp}7W!#BE5 }?Z)-؄O"DC6(嵖|xc.NwM1bJDbM R2"Z3!1;PdQ^ XhmȇaaΥ+B6uo0-ys!|t!jeX۳vl!cl˜GhRCB L^>=??ѳ?"_?9Lm"Sb\O?}3mgjX~'/9䓯N^, rY.mJԦx |aț5&!/";;w&):.;nZe=GoF3W|[LD٣EwݸKжq2iLlڥJY-/A1t !*3vxd>4TZ Ԋ@) K5k=Œ[?t޸\m WCbS pe^qDF̜a"2&a)bE-HCo`/yҦn›1_uo7I-}IphnjHĄ>Ƨ Dܪ* iAfRRA ] EsΨL=AbJR'(! bEJf079PըAa ׮ay?\l8D~{o`֜.|x_OY`b(g&4|y~դ4Opp򏬓YxT |lϿ woY(ĒQ{ztfHE! Ղ4юv׏&{c6JQ3S0Pwg bJ0"2 qwnwj1S@Ƒ]NLl(cs-H1` [-EV_?G;G`!_\,V;vM;faR~D\v c_ռYeɆbDkv€ňc RclZrDD%K)Ȋ]].GgWe~Njgf4@0Avgε-|p/ŭ^F >x;TMJ)j(+_=>;[{wwvw|V/B!(Db6U?hPfx=}8i yi Ri7 6hgm#EsMM6Xf@G:P>0#1&!"R2*%&UK4r>_^y]9;xڵӽb:D,"Qб [hx;Kr|5]TDBPFS8j`FH󦨈o|R"TbL+LsuXr  2"S eN!#,npVYuμMN JF}8e L']{f ތ(㚕mO~r~>l+f"_VÇn|qyymX5fyUaH\,z#/. ?63`˥ʤ(x`A Kt26^e"ݩ۸"@REVèm[ 9X^Et1ix<uR4_bSDAbR^FR>w-_›❃)*PKpEqr6;! 6^k2%®\~~C @d@`D(9s!2J)t~>bXA,޺sѣӳH 0yO@/hfhvzz55O'W:y/}_ch`dj9jf Ngϟq8zٝ}!/h#mr\]ke(i4PB&83!16)fAQ5S%&b"Lm;{m:a D1FOC9= FC4SOlb "#HE- UXL!"1ܿ67"02ۍ7//^{^ācK*CM1UYŊi͏97 nZT%T#!#fE J?~3}^7(JnCAQDF` Ҥ 2 _z3`&4g/O?~rܤ0oiJRTn]yy~~|1 ܟ.k1D( YO]!p#f48b{$0AL 0w'-٩&q 61Eu4svﰛ71L"L΍[ EY!`Q59tM4ws6o0ΐA607w"[7@SG+@JeEl(68.zDVeF+9.2\CV4 ԬG~Rj1,D+ʁLZFZ>|z,n(&@R)FD 0Bl[nUE 1v7G%,*A؆3ֱ)8 訉^ONV罨MOrWCo}W~_ߟw/^\ݿ=w\\1nYƾcHBQ^k/^^^\1Pk"?΍}(g{݉d+1)`&"ZM"vMZdtӾ~w6&ФH"PQJ)9K˽il#2Lh*&e,r.9`C1qY" IDAT5j9 xk`jYTQ(rȋH,}t񻏞M.2RA@+)4d=u֞,`av4Pk c bF%gf$g(R4DrHm( v#Y,5G7;ylӣJt>{Z=>Ys QuP _qC^>>y~o~;NϛI49єblR{k܌h>}|Γg;qS?~vH/rh^a/80JBh!sLju `ɢnRJp!\W÷4 hou`,UWbPw[kD5b5XB 0{엟Y-.cjoߘ~KQuЈo|l=d3@gi7Lo 8o܏_}gOyu)%RO_.OWX3~lLm2&DgL @F-v-18N~8Y%Kd(F>ɳs1Zln\?DY{JcIGbRTYCFq|tb Cv \:BZN%;?מ$8G@_JQ& Y P,wSбY߽vkJ* yU=FD0@S6o矼=kVƗ{aX/ nκ>xb_{|ҋ(p vz>2@)E!۝YoXy6o~օJAA>|Ub.%D qjWCH}dϮ6Uvrx;wڽCJl޾x^^"G˽&@ˡ"GS A6a 1a`Wjl4UGwxZ;RB}Qi,M^eaV})9ZN .Ei q4Dzy~yu`fLmA%JL&v};x̘27rȈ8횝iXn'B@)5"B <=Ӽ~ ?dœ3`V⯞/o%8YŤZjTDɖ!b)2o't;=Xٛo|ųzN?j3g!@VB͆! QuG<%,S>;Yp7C'7~~.ϯz-fu|>ϛ߹um5E h! ,¤S=CI6)HF*C1"dwf*6jMIaE(^Mjt˕TD^^Wy\y5 bdEk645D2EB12GqK@@)2<1S3iC #"l<zlؔ˕\^,E413E#"Ė"͛N.WbhX"x"@`Ej[2bkypkI4H.dE?4̚/t9*Y@H>@L<dØsK)Y״h*e1Z8m>|7&~b4ls$M#'SѻG eYr)9|=z(Hp78ƔW`Џxyo8rɗ>Owogvsm~8!4:U/{U@SSj1Ʀi1PH>)(QTݪ 1!q f<FEce!П(׏ajjTs0 J4`$&<5qtq~9Kiܔb >e/}qrhwu;M?Ν_Mr@!O &5o6p11M) Ѐ|ۋAhF)^vcaȊ{gM7<|vݳ')y׽8z :"\]O_n ʃvې^^ϮA5)x;D*"m0cj"34ml(}!"Z!jn&g MiiC)(ؐkǏ$?4ͺ۷o.;O6.<-P@޼\,+@;HRȈu2f0-@YM̐"7)A32})H8q30OԛhZrη[gefU]n{xc]7넦;231so%&jb%`_ Wqol8<]NjqY$ȼ Aڙ;uAA"C/_<([բk B${6ec SAseTWˣqZVŴC@`S۩ ;@v ^]M\m3s8d61׷Fżٳ߻r\ЄDjm2;[#&.˼QD2>9ߟLfH$hڣڳ'' jU;asGNjgG|9=>>XTv6/O^=+| y%`F0jy9>]GS#_)E@@d+ʨFhєy+3KxJDƬ7 ;fT%,sZ{EEEњ rhQ-8&tg/oٿOow}vp~zmp<떳n%0c4bP6*bW""dpNkvY]~vѨ1gr@fhH5**rjcm e16z۷X4#yb .: 1/Ndz٢Ey%l:_%?Dl fKy4~os&{gKmmm011+hʦ;;pmF(uD_dQ M87=O뭫n\YfeT\nNo~'"$2bܽ2 b`U=sfw?Ȍ 2daAiFUSQakDTmY!)*!)99ׯ_̪4LT}"GϞs{v1?:M|q9_;ۣg!/Użs]Tey[YI#{ewtٝVMr]`wx4^M}AE%ؕX} Qu!P4 <*/߽=~v 푵.C$"cfEn3( v>&VC blVN%NjyjYzŬ8>{v*Eu}?pz6ڎi6 B()t?9X,Kd%~.env6O?\45x<*bmY`qְ@L\+E@i`Eo 2Qj DЮ14TY 1 ^ p CLD{_Ժv棧I6\>O/y;G6/iE#3c>v~/ړw_8]h1U8\veM(_}珏"3J4BnOj1 +AR9|vtZ  VK?w><딣13Qog۷oO?:ؽ'8*H7O LF!5t b6g`;9M͓G{ڇ>Vt?Aa&>~4L 67Lu{ÅE=ֈ9*v<^\:2gSCxw{_Lj̴=Nbd`QPݹxVwMRk x`1׭2!Ld;7 $$ ʍh;mRGrftom 'ABv?FˉA|89$FSB`(`G=1\D$h&`KiF %u:9O h NUgbd߼"*Bh*:DtbҴ 6*N6?z1z5y>qeaEшc"QYîhH쳳ß~~x2[^L'"J+xDx;@#JEƤYGfLa4# $uR ) ݻ׮7/&< A_Vn"ۆ|7{0ltEՖ\(֢!1zQ/o`2S C~9m\-Z3S,L6FS2(hɊݿ}/Qho:v}7_9:ͽ~ϟOd6,OT0lֶV./&O^B|vyyEhCwg'67{;aT}ck6KD"a6'K<:U0UŻ7Ǝܠ 0Q$SFq@` "+"j lh/sB#$B4Q5 EO_|jcP;o/z=p{w{_{ݿ 4,em=Қ)3ECDb +F 3|}GcӄY-Ѽ'@2 0v#lo2qGz1[W}taAZb# m8R TA//ڢN'NpOώqd|>^UfãSCL'狦vtޑ!YAlPk;e3`Z5G@"+܋Ĉ?쀇n+]{j_zp{9:MJ 2`#_Lb HD$GŰNGG;_77_O/Mb0fKԡ^?xsT^ΗQΐ_{RͶd_qc'm}lRp8o{7wQwv T`biT>:Fc`6fngvZ[@A`޶©Ѕw 3;֐C#fV aWM%ȃk[k>7b@@ֵrm+__~L4t- 5Otli1آ-{lNԠd<,UuǏgae闿xɣgle|tP#!2"2ʓg/vFShF/IѪmH(Zg"޽7/e'jWConWUM(IMȄDέ|GC̝[g8e/_տY{ܽ{jϛ9GgϞ~>zvƠ|,qApڬng/7=?EE"Cd.vDLB9+1ϳnGb^F/C^M&63`0OhKjQe1Ut-w_~xv;[Eg6oώk+W<ǟ<id\.P d$2o|G/;oNZL (tZ1m]5e<;zq>qA4/f/+3_.:'Gg;׷mfu7FA@&,j$rɣӠhkt>mCܱcfPSZvEN%{PjY"K+<-kisJ?O^pQNyS˓O͆Coy?_|{sgp3&E7O/ӝa2֝"w~h8W"L;bFx4SW]cL Hjh jLXY L6͢杹iemiz*E?۽ݵ.1"*aN*A_~rxV#̾g,/TLA{lQ""b\s_|xpV\żkW;#!LA@M |aLIaX/^1Fye}1,{`^r^wn=*NgOyB䍫 IDATv"67-ۮ:::zmo].Vhk"TEQ U5!1ٕE4a2(E%I̔R'3j-1Z^p]袞΍?UX(`*<qvp4<{G@y]q2:EUw`$#sѳ\ƳʕYnN%Ph! 5 FF% B<9Jl޸2TB Hx:51EDV{Fb6 3C< :0$e p~9 J.}5xx_zM+WbQb<ca2bؖɥ Fjι[;rɱb—#5SѠV29C[#}mnݼwn/orѨ(&cʍkleG~/b@cLxdU"{4PCӕ(QVj`1E\Y@ M?~|Y6˶n$z_yϞ_uŷ{g<u@p`5/y[Qie˺Pq5r;Vw?`mR&ƨ?Y4F\]@X`$Fb]P |!P|d&@ʼ̳6`B9S C ʎP6V.!("r¡i F0/l:2hXܽ64`Tto/7p~_~2F^_9v}Fy9ŻLJ Yp$Vi[KYEGfHQZI@51Y0s]XLg wGev*ީ|[nկ.QF$;cgT͗2(n?Gռ--Y,b3_/{x1kE#r oުg͠4t“(fgYJg\7h>&Xw\7D#{O?;U$Sa.4+Uu Q8 be&&(%y$N{J  2^eѐ ڮ3 \yo׊|ϋ~eum|?gr0?){ET gx:;{_k;9j$bɊC2I'D%r~H4A{xR-u"EBȐ=XEkI Y˾o'ӌh \*vt*H"z9^¨^_=g ѺSAݻ+_'O [x^_o3vvCgw+_>r &JϾF,/@捭?yNȔِP{6G!;Re`rR+"(&9|I!4zZ].hEN^6om 7rkü/?ha/?uy`,.F4# ,׈.+bL/:*QgӋٻvJ~{9,$j1ld "&QM 9pk}֢jBn<"r;ss^2gc'vwm-.QFa:ĨѢX I{R:;QVӮ ']gO.[ (2(E>ٴ u֯m3@Ԣ ihV+Hn}^i^:b @"am?[t5 0ouVA6@$FT5NI<^7`Yc&'?:>g/z~rbNb/2dGDCNZQ3й)!Wu|vmm4b2fR2Q۶3Aj|Ld7Vt5F PI%h:e]?xx8M8:@|ݝiX(>{^XHï1g^<9Tiܿ.VKlEW6tk}pYO ]LfnM 37/6>mO'Ħu F]jjIh*(bPеߚ;$ՠ֩;x`Y2?:vAgDILU<3tF& 9nj8[tǧÓY |VPޣYvҼN1JI4Tbе(땮|b>[ ] YE&;=9FHYleTB54e04K/XҔKʀ~=&<*a[͖@T=qQ)]/7:?~GϿ'Owpogo,Oև|Q{Q%ƗsT%$PbnыJAM"bh%Y3`o]?x{~>m[UbdqbAb4nà l5]#+ Dy+ouvzѶR5fF#c+];d욺EGQ޹9l[Y.rj'A4DYѬ ڈDM>X37mXTzi:XL1r:%c $9&8%"qVrzyp1o2Cm1i_yً۷oNgM̀N "":Ue0Jo!)arz #`ms?^KDE[4 N =<;?8/շl^7u[w]5[|v|^Ř\5XY`—Efb.FI!|啝?~4,I hBi&% Vhz5JWʧ>C ưG'Z(`cl2 ?~2WwbKvDd?1#!0Dw+1at^uP0S ]؄& > TuE|)RrYƎ[A0 |ǃ+{~vAY4Vb*Ι"#%+-K] Pd'3 s"hCLGϹ<|zE;]vh?9,~䏾3޿ÙON1%W i&S3uQm Qhhc* "JlZLt(2`PfsbҒYeރPO`/gVͪdg>΅(Lȿ"ro1z>,g#lUaBBN* 9T`Ҩ]* D@ԢXlitRIC&L>0&B!FU:$HlXxgLB椭C!\]euJdb$ Qp5Ie%UEvHȄ1糶^:@ФEFf7v=Grtg,"BQOY!ZQ U(ZE4Bk:Dڸq0a*@P.X2""0 36B4ԍ6d,3_"!w7v!LjqVn/Z&EZ!8\H),9窶z)9Y6u/i# c lDneM"6 ڵ(4TW|V|Q6d߼?F1*x2:g^QUCgiĀg}αJAKw gg:CQQ&@˶`>oo}h 0)20!$c"eb*bbș*b Vϧm7GVnL!)F`$C" 2XixDH0 ~IWw߾ч;2DdS3wwJ&;JU$CwKRMc"i )N0U3f&$eձ0Z]bc'ލM 8QBߦbp'M]_L(ɑH@awïȐl5^Fu MXvPHbhSt5,'NQ0Rh {Nb. /?xh5]14"b"3!0$ wfUL<(oڨdFFLu;݉ꪞ4dwzلG{ ^c&Tbo5힝/G][]Jϐ@SUrv2+7z(83J-u0pHD=CӒ.qQw!*$ɐ83:՛+rx@8]QF[k~+Co; QEpQg/&'/| k}+r#fuVHE AUT-՜MU MbP Ћ:,*)X0,teK aV$QTl1B@R 9m"g@rPaP8L#L ` !F&Rg"F4c7/]cX2ASU/&vk{T0 gsuU\隮K7^/9#kB/d:׊"cPBcBhJ)05!.j Sڐi f% !Pɀ/9,Gqꝭ1 #thtww7noorfdP/ ..PAELM54Mc')dFMUEM L@͐ 3BjAZ$wC]-4BJR5"M+-LĄ`QU#:Ec/<`llQ/jVCR6l\(+w"U;g{Q7o727~ o֨ػBЮQAB脚J%M"&z$02jTĠ6U L΀)aP([]HyI)&F qYw[Cdi"<|ኲ7(J/fۯxۋi=,UgTMH^A!, URcd66KDD4Cuu$4+|;83398y[7kD LE "@PgrH#(&ŹP牾'S(JFHt6фeFbkŲd̀ e)=ƵQ?Ϯ_{ _яݽ+ΘDi|Rv@%"Kzd &rwU$$Z1% %aR2IYc%b` hPӋtYٲ~m"x䬞-,(2X7lEARlljHh1=R@@>s"da:#SYU-,~W\$9hwn?|vBfTum"( J}޳2b+)'aމU{b4sHU GmFR LjYD0UV٫&9 `qab>ϼsK:4 | bqCʰau4,0_JNdyګhD"E0f̲41LD@AɈYԐ MtTLFr/O #DTHT#2 3ICPb0bB"hH.7ֲ;.Ԍw*3<@|s[;G Rd;#Q^  N)C@Dl';,rM4VWES3b;[Zlg*!}l_gd"P ƨVN)*ݡU  ]JPSHS%E2*˜ Uιkx\ Psq{r>/j:Ͽۿme;:_zne@ A1mᕽѢn\qvyrq~\|o$0d,uyŀD8([]]TjJvzW j6]%*h D+r⼥ےWwD$@j&Ȉ[0c FE *ʌB׌/ݸXt]D6Q:>YLNON.ZzB 2eA3bN #Z]P XhZD{RAFb6[  NOИ^UX̚.jR͂ Ѫ^sVQ@eeɫO&ʬQ Pb & >i ȀVT`AM 3DrH3":Б1r̗"J"4mpt:y>nDpts}w~7!NfKG8kBW/AǀMDdОkbgAiWTR `u4PpiU7 M%vcRXzc\B-^xZFyN.飣꓏!~[o ''.* |1tF`*b i&G@1CC0VBA&Az1F~ZZW0 Q M "JjYbk *PJ#,ȌQ[ lfB1)ٙ]wTA h mU3SyEmaJ//F?^ŲBSvzwmQ- fl;P, J l%pZE'C!]Ht262Aj+F@*JX`Us&D%#6iS]]۴-xt, 1 0䙋w9SWe6ټQp] G$R( jH@5L?Q#rPM"b^f͢K=tm iĔyhPE/$N'X^`uF()C f.U)94DCU'?˲ 1Yh>g]"Y`fJB.h.' [^={VOguDٓfuݷs9geUj4fhiRB$EJ HQ4A r/_/|Hq hjTWUVw8gs~@t22g~kmɰNGX!Dt!zƠ,8Gw|v (~ѯ}&Է9`$=X 3~b>q_ n8Na*06BEQGWs*%*"N\X0zN 1fb}^v<ةܺ7˨n7Q {;?s`Wg/Φ iQT 4"}y‰Mn gUU -1v"K@e f҅^LC, IDAT_"0h…րM:P7N"  Ӄ{LE&D7.DJxt-UKGr;['[yab&*LE]}u|1*oZQchCl8a_FQ0@D.#*Qn囊`Df*'WD{vK &,YW&i"Cd2VU9i[duuܘd_̓~txdYm\f{ u%v9=Br`ѐ3X->MA٤ʗ똾/!bC$S3ś %ŔQcxd3) of]4IwrP43Cbc#h JI,e!h`Qe.= D0wgΪ;ӳ97~lyzv*)هN6v6Wmb\yeH.D b:(?<2W//U-B 4c@0`FG&v>hT 0("1yν{tup;.2 omoΗ^ ug.tmbԻU @?&:}d mL+j t8HR%ur'g"A@WP0)]xsK c |ӵ4DT$#`.晢 AQ M;IpM/g8:DEH^'O քNU(0_vWVLҚe뺉N+?z5 Eݝ)C,g2TlR3Ppj751DL+ dLZ ,ES0%%@M@5SDG |1y>3OVTjIU }~}]DU/.=*'|vV*&>bPPs@Bb/[@`uEIee6haL40&|7 .sC#Y_-k DcL!"$UE5r`@G TYMlV%DHc;XE "``jj4)7Ŭ^]|m}aP-XI|ѝ-Jz_S"%1  (qrPj Hp%VR.! (S}"YHmCtwAv &rc:3*|O??=>~ 'NgJgmvUzAzOX#q?p[/O}sSKuhZ]k ֭?9#QfÞؤ^!h46QUA!JسFr]@L)5F/Kef%"DDaՄպS#$ bW3C ƿ/lu*ap{;yj~Z ,)jޓ!EՓf6k>杭:!Ĕ&SM Q*b4/3H4r EDf7Z" iB$9  ǟgǯ.ׯOfO|P10a鵒5=Nn=xqt:ۜ(]ݘ@?K'])M1: jVunĠL@!aR=%iԢH=Œ =W@>3GHԒK7rR&it!FŨ1j+g͂w@ӓƝtzulmvbk3 `Ýq> ;t JtWO7 +$G8B`7E=>w(Y4"`O?EسVd,'CP^D cb QЌ K3#QH@ʘ00U 'LVm#(d<:U`1L( 2Ϝ1'bM"44=fӴ'9eEVI_$MD ]{1}8C-#8{ǷܿjgQ *߿/ݹ{w{<Бwcbn>R|" K9r3#JRRr8DA7)QY\z͐1=i `&390I""hHiEtOVY+39" Tc}$0`ts\&`h}0 ؕ[@AE6OY 9]Jp:.tUs9J Qb\,w[VVQ5[4ʥWWpX /_%) qOI#)-CFE0SJ[b X",+fb~:@BjH9J2ș 2p VI0ʌa 8)7PQ%2y VDTE$]2[7O:9_Jϰ=*j@%G&P8΋|*I6)Ҏ}bi @O{O J# c8wd{p4Gװ\8ꖋjuX99<>XkT<fjɢazI$iRya zSwҀD+iY`쁦Jyƪb*Β}S܄ lZU;[Y1{Й$&͘@SbḦٷ~W $)٠(mn׋zcsSE_:0eAYf "LVb{h$ a&@8H6u:@Ӏ")ʼn=q0Ȑ w7loͱK?_p?x<|MknϞyV<Ɲ'ףo  Mb #R$x 6x}!XL]? 8h$Ą[HQzU$F6 u$){UIu#$ʨreW:=U~F$UTFjD_ƌD5)vqށZ<繙Eta6_K?wi"H fʧlEɕFSu6O*uXnflHBm6DrG2&1wÃ7oߚMFng2kvo?~zfk[E\#Z;lvEse7yiTLW$-QHf!0QYJz视D>R27i2!Bd =ACvӴ {S sr'&so.b٪Q" ݐӸGhݘ9;#3]Fa1Uvwl8,]5RQ$"6WndlM$DpXFCt)\uMBysLW5}HS e ALP8ڗ~n'ϼᳫ:*EӾ@:6˵̛/A׻;j0S$yӄ弸Xs/~e'fq4F~'G @{j,Q4RRXWH/"t|&!gS,Z A>E$fTC%܈b]B]W}ΓA~gg/O?zX' g;Mu-d`H 7AVn:)@TDD$0pPĝqb541+:-z30AKP"z(I`ȨF[te{?s*2ѓ-gg}ټm "@4 F6jQÝ^;sk瓧y1(_muų?`'rld1tn0ĝ'?>l d^~rU1u$AI?~tolg/_'fUB7FUnubQETKt=y1!"6ZprXNtư;yw}ٓot$i(QQxtk_|{ʇw~;UYytALz,˨2Tj%"A3sTgO I"#p^ݿLUY0R… QTAA4 MwnLFO0*koWǟ_GG2H@N'A\ҥ(s:dSe 19 #Lu)).ay-*v0=~~pk A=F[~ge^t<\.8]QC4vphA4#'`IJKs$q FdHu2"2cF]l&RҒ$xu@.)&#:Xg񽟜,p=xB2g݃A{;-g+Ijhh6bvvm ]&"88wg#Tvdڼb8,ϘCPYZ2ia1$ʗ羰Q_}*u)G5T骊v6*2lFDUJ44AC^aLENѼ~ϏdPU1q׆m {Ue$R% 顧G@$Ң`ps28?oATTR $EPLR Pi:~ EeD&"1C""ZxKtOϚy AooOFƺ "\.CZNQGE5.x>rQ8Inzu݁bbpD{ʋ}&!j2ݻSUAׅ29LPξ/߬^| :TUAUDAOu+0jQTCl(}LbiUNMfs#2eBxbk: xe__|/֮=ݙoOC KL4k LlL@wFd]%C@&k8_ **qRJ{;C36IJUF ɒfhL8W&+?9_1Xz+؀pX{㯾ypk 6 ʻ Ք,h5ޕY!B\fĢ*ԴȊ683˺=Z挣AT% A0ZF()s1/X/_]_ϚAiI_ A #@Y"8#4öدf(]^3 -W뫋j_i'Ҵruv|[}|^/vnޢjώƽۃ,όۺQwLDH.U2 ltiRD2gU^JA݄^0 HɃ'!B؈eCW8JM9mt`jy&@ a^(<|3+;ohlOKҢj;ߡTh2y%DSw̰A4\FrPY f*P@fB% 3aBOrŔ]jJBl:E&u@"*@HESЈw7HhN#b\/Ymӭhum-Qm6K|Q;y9FŊ]f?yqqyquk_l 2 ذixTUPIRD&X4"Bb@i"w*!JH/ AL!P߷hˇ": %KoՋlQSM8#GyYTE9ݚϽswsDuwx;{l|&Qa8̬)&&=B9,{b"T5Lλ Qz)ELCn< D=zqzqR붶&1Ʋ([/gUr1 zC]THUh<wMGeӳwךU|k1l0;[]<=y͖h<*3ߵ-E'Z!!sdYETS}SꨪFIyB24@LZD)2 1ꭗ$ g"!9?pg6@ƑYY +v7qo~* %_hC٬Ʊ8;ǎ٥<41fg;ɰ"Fm IDATG}B׶)+jk\fwTCƠ\']F Fq~Q`i  1@fX*}2##ic<X #Z.g(Eb0ޜTQ޾s|sZ>~vdk:g1b},\4=X^\~[?Q$(|/ /$H޶z6m#*8,9ƔQPE3b%L}J ]gb ĈQZg3E*ϳ*oV?d3ÏD2Tiuyy37<;o$DI !"C8gzH Q !>5$9ngw3#DUU)g˲%t xrBȊ3nͯf{d6ukrvElMVUz5}0I .^QTo&B@n$P2H]C@`u @&ovpr937D$N=j Jd 40j heF u w~t||%Kr( ݭ(4>}q~|Yrwu״]g!3"I !h&ؙyy1("#iĈLbf D@O;[ӝX .l:$Q }{E.h0rφv$f1j$>KLjT4 vAn[T#dж!#ddUloT[ڄSc@ !/j<&@B"Lޝp=on]IԀ{umZ?=aq(PӗA9onH !@OӄTQbGu$sԾ 9Ò`Tw<_x>^Z!dfUmx<ًu{ܳѪiOW?+?e8sD` fXjH=1SVѠ2R;<3yhXh(27M3J9 )6f(#S){ ̘8;0b!Uޫ  5t(pck"1kTUH"RUmֳjV`w1F%լiՂgx\,f+S6pڌ)USd3C&6B36WDF>n @>[R*$ gr[a¢<ʃǗAAtPdY.3pT ~e=+o<;,|_iP7 @;ώ;Q(&( y *F/rDJ m\e^DwӳyU]ȹTըdf!.H]mT1#VI*vJ35-T"Mj06cjյ^6:Ե!m4>uve^i|to{{sdA"cEv~㣋˹E޶mu\^dAݪnmHRM9!"a)u& XSc .G p7"*fYVE^v6۶ "Z1b(6]1W3$*_/ge}r6;zv fs}v1gMh[[Ct:p.uh׳*3a% "FdKlzlb]޻()*ȩ"C| A!gdݍ2?ENGcwno'WOuQ>?׫|q>~2;YFٜ?% 뺋!8VȱwGu۵m QNfzsB*y5ۦ Պцef!QF9*e[(BȘ*MFEN H*0' #&+a")iMTMVU\ PRjNFxLDʈ zg9syQG\,հLi6\\-fp Ͼƽ?zYqDegȌ(sWa{{ыyhoMWD}4UM RmyM+QUb& ޓcJYüeΡ1З5Aj>VGwݺ,^(O>}ZT/3w\[XPmB$&pU F|Zu?dZ?w..uH8ln<9徬 cjlpǓLJe[DK<[ʗ_߹\t/O *0-G\%h0J>.he@hLňRL(F%bF.FW*0@w}}> e~0&|Άf*Kΐ`j1~//㽿ܿϿ_CQ {gM?yrVo[?8[@EOY$nQzR7`a{ǼyJA9!0Zɐ~4߿?2'1(дqcsUyj~`s|~}}?3ʼ:Ow'W9+K6?7MhtM\F~ίVz%`!t ] 8snXmiU$mwl.g嫳[[[Ӧ.̤*J(B 4t6Q4DVM}vvmnmqȀ|~v:_,Uw7ǣj?}oVd)pXTeLB%3f\*elfGyF& @h䫂3)ȵA(|L1HʂFĺNZ`'ĊܕUpg{;;OO.fg˳umg1n4(;ݝa]GiZe-YMyyFb^r-eEA9_~~:ˇoXFVbt]ztk9g" ϒaޛO}}ܙ;9l]X`IMY i]rRIO.\eYEIdeAE܈l};pKwy=^=ӟfI Z h(V0L POC:O@* 0 &5%'@ 1BO]@!2@%:IQVr1J`8j[7è5[^"!$`b96f "FԏlNȔA@`DA aLI~-9i7Jg.~K0J R+<I*@F17Fܿw?RX#5g|!$ڶYѳ:(.VRAb^k^DF! 2B\*z MC&8D@ u`ڌQ0 G 00Xؖe92k ;'O,%(ʔ ۡ)9f$ XF(LT+  ʏ0o!@)|`҉C&Pr XsFʈ23 fMRAc  K%s2@lJ,;9eHNzrQR($.h`"sϬ,V}?1zET,ؘU+FM`@=~3G_8:8[yoWxV!hses3) @Bg\Jm ;{ѧ+@mTF͍QJAkLE[,2[/kzcD<_"Oq*6@׿e2L)rTLR1*@3j`TX'faQykRюb3ʴa˖VI5B i"4H+BBjPp$~`lI0WrL~@:ѓnV25(v %0Bdh :oT &Rh%aL)(Fv1Iȥil6K%j%J%b8tPLIIU+i(c%=֮+}׾x;_e{rڞqkVO8̢ M,˲42 LH-Rim2J)Í{]!LZJ-Z+eqa&DiqIS@F{ 0]:ӚmSD!+ope@_2YR`4s` Y5S@ F+),s,#6o+8N\ +ՊmQ1 B,R!ъ<4Rb` \2 VJ !x( ʋ\1!63P,ݴG{XM JԎW|nw{4&FT:K"D1` 2cLONʤM"+J=Y~*2Ã!Dd*͔J4%PH D*[j^y/ܼшkt2*%hmB aT,˲!]4UR810I*4,k۞++ME)1-z#E(4$:QPeM K(q DdHtWW(3ҹ J'QFs@C gʂЌ`<<<<<ވF?sӟl?Ieffs3% Za!2fK)A6!\ٞ8ũYc4 3SjAdeD*SxId՗CQodB\I"Hu,t p.WJL4IʓLEqY'qqsxf)FliKL,1BhΥ2X ywǖ\B&8M44 fQ!* Shr '_{~߬,8Όz2ʣ q9"ZBN2 Lt8+OJ 'i%sf; cnz@!LV0ON# g|kpL\)R5$S80B 8A@׿rje8\^H!&4#1I 1&׆OMo(^jmF0@1B6Ka3|N0 HʪE϶0ay)9Ovw&B hz h >>q=Q2$t?8Xټ$O"PH x:_AA'k #sKLΫtJ=@%1qRE̱Ea?8J*Ƕ*9SVJkӅFjTv:?@j7sMa`9M~å aDȀcLuJѣKO>1ԷHh r3ӻvkVp#re @hc@)QJ"1J_zM¨E 8SOȊc\0BZv PZJB$x.%iZp]`ƈ#\%Bāo!2WՇc?h0lzOS ?Ttm!1Ţ )DFr16R֧WI7 Zq@Y9rڙ /N !(6Hd+49 F~_ [zOB `MDZSr HY7Jd$QS_{R1?LFYF eN|[ " ň֚ PRk2YQ$q 8M0e7׷+Kfs`Yi+1f8( }?8?cA&(V{g۟'[w `:1ZQrCҗ.=SoϮYo+>}$j#evx3[_nxcekgg<:Jx2n>^w=?Ier)$a0#42K ٕVwp0"Pkb{иEo4 ΞZRnolc`ȅt}d RBBrcB 1R/ X`iٙYʧAd 6˅ YC  ³p@ CBŐbvmZ.y40|[7/|W%uav{P 24pq#ZkE΁~sB8KAs(FA%4N^X# 0TJ(΄c` WZB0Nwe9WiT*I;sKR~M/?ï}W_y~5{0a cHM?Zzܻuu8*3=7/rzu!L0(a}F=Wt<޾}+ G$oyLXb}~eeB{!TCD?u=\#;RUCJhv1{{QLcF/s }2f6c̖O eeνh{tN48~鵯!!c۔mf48ł(RJ0+b2 %Ѱ4Ә7*P*BV\GJ̀X(ئ՚ۭY˶1Ǣ0(e,ס/OVPg"D]5aUG*`Љ )Rrye007n=^>"aY&0FTkS*?<ҐQB FX/ۧf7P q Ҍ3B4* pĬ4 f0ɤmYe%u&"4 0DPʴkpf0^ O7T/ߪ-4ʯ?d?CbBӬR"Pa4JKcěpnvlIAK)x:Oi0Fk'WӌSlł(5˃X>̹N^7m$wz ?*Bs>c~{[{DpQk6B;;y|{fKՖDkQ-zq& ( fJ'z37 Nz7㷿'?=;ۘ8ʢ LP+P'Y M83RƧfD1aԟ]uڋ ĩ2%GI*x5O΋gNt: i^a x:=y]w^$><g,6: m[\]tgoxF:h#"H)={|hCF T( p;YH!bb3*xN] "3D4H#MPD oPD~5c1\hQJ6U0NDO' LKRYƟ,z*+D AS)!% B3T`J(J!K%5_!@,|kW>x{s'FޢUG¸!*F۞,oHA&͙zRX[[r#ǓYϳݽ1>xNR#.Si[LI $?@8򹋮k+-ZMcd4&ƓIb W5H.7 7o>l;ju(BYc"ei"r6vŢWK̲Tp: `ko\'i^#nPX]o%: |Zl4{thWȲ 2p FR)Ly4L*c3ϵ1Bxo:V^gI(T '8-77nNN#8n}kyasFOn]~FT.&~d'OjF^_I vJh!B (c`T={mhF2TLv _+w~bZa@Id G'jv8ByB#ʤPHbLj7;`Fo(TYw]Bbe7ݍdaqqmeXlx{wYEkeƮb{@ @&!yM^XT)14RTVZśLO9fX)%˽?v mvӫ9 ӳ_~b ʬ2gJ$"8 ZVk4{L9 bBpR!~{y,^@LS5NToVtwa`O/<; ]~~? ͺ;St Ҳ?=tG(m͵Ox"0O?|_k}aV/YA&Ra)nT6?G˗+8V\2 ^o|GンCHW>{ᅋŎ/"`;֜P_)PFc!EE%<`h0J%DQآpldHCB)h%C * rpHΞ]a P`•J~1rkF#qq/#-= H |9MͻdN%V0rT(LY%Q;&?O~pkWZhB٤X,$3JAs][)PZQB: XD+IY)QiTzx|HsFAGh<ϯ{ BP`Y'ZV*?_7?jcS5(>_/{W.\;S̈́}w; (I=ucGn},dbۢ5_H%h6 pyL"qm њkӟ$BAe:vT\"J׽X&٧WO{#]r L4JkVz8ML֡bYsal|Jic (ԡP72SOj@2D~?t?G,_aTj/:\-xgWVX`͙cW-Rij &q=Cqڎa6 -@TU%x`!K2:}0nQL>:q8.Yzѽ$ǩGdQ{s8'fj;k!F7玵S܇}0 &P˶r~HW_:^\g_ܼ}6Z);(ٽB{fv6 0|V)]^7.^xZ⑕7&"_hi7.jW)eڵZ= 'GRuν~,T8Wal@/RًksΕgj`F>) ,L*uHc҄qU[h]pȵwK&B#Ũa`KAϴy2zrL4Wn*_ԉ|vk(Fo|嗞{Pu-hkb`ld1hjEk2$OGa4 xU:}v#<`R1foml~޾iP8z+_l..כMida~iMg>#DQpgc˵?RMp{ÝTK"aXnqo#r&Ӈ;>|K_7{ڃ;J>Gtk_mp|6؞Sڝ61Arq' GVa:?Q05E|啣2F/k S{>N?ȢLw84Zp1( 2:D,FQdL ť( ?HG1j1H 腙5SJD .01JdR֊8J*ߴFG7ή\e;V`6kszSϿ`(87jQ A3S%I˭M5hzճ7P*hMJ*cM|` IHLzR(% x7;@fmPJ}08[t- å, " ;ca !LOz@d342/,/#ͷ=TpA-KK+9Bl"VloLF]FivǙ|G]x$h7߽Ok FU-s/(,A&S< n|˟s)GQI F D 51% H- 6 +x3!;>w>~N;o-Ϝ9_=O{"pjT.xB3%dfLҕJ9"rK?hgJk'ZNvj {4m2׀L 0&Pd!OJgc3rf`{h AȍV' Ё}?L$YF!͖C?EZMljiYڠ$lioqV)BrN$G]J̱D"}J!,9iwrM^v럼4E @ $!B]_D+q΍Lc$5 &_]T-~`8^\^<~z(bݸͽP yW>'4Iyfb4IV oֽ[QGz<Ccğ,= Li1ĵFNMF+?x DF*eQA( -\ # lYAZ(I%B~ G@7se~_}ig;wfM4|dh0J}j*zE'%b Pl;_?nKY2sܹu1͹eL{sp)fY ˜iNܰfn#_hDrLEJ`4R@s"`80Ԃ+ TPH?4JiAZt4J2t 4kT/I1q x*7?ztwR-(vv; ۲y>*F#4F'Ϭ=un1R?#:氻4J2F0&rj͙A*NmzN&zdhkcBqpa'/YmcBe{EZqGZR-;sS+?ڝۏ7ַ;řG/sL+Tomn '2OrcB Ę8nzrs\¹p○^^}G#Mt,C';wnܿ3[-Ɲ`53G{Qt8 ե6g+E.ZqqW,ٮ;KWgN1`,E4_F LbRL0)%s, +XFiA R,`bQ&KӢ(HV*&7m? JH~s]o_l52꽃d«/2}[?2JDzpkKƽbv}t- S)>?~+]h *˘EfRH|=ckc7Su=}6X t1F\ٿ|Q=3n QΛB2B)Čbd{maBO ХE EdB'YR{`ˮxy?{jһq^Ql/p3gff_<{CIgq֍7umoĉ5`L/5/=xg˳BZjM-fO裧Y~䀘A(0"0[SEŦ o5Z➇?ޚFiL9aIJ$tL(N-!x+I1YaKYnuuƕ_/~͍$}N"Ra~ҍٹ$VB3VU$ ߹rN^Nͭ^Ϋ/=sw\[^];{ Ψ(}H(!PIDiEom&S৒j5 B}Q\q冓ҳWZ֊}?!:37{csp0vqVXyG7\~k3սw$?-⹦'?(]ɫ;sLZ#ogi3'?T*|8q"BK@!`Iu=ym;QȤlhV0>'|EB#=?c?3+?\;M"jw[Ͻ3 'p3{'s?_L4t2tuݹ? G,Þ$m/,OFSǛG{̛?B󮻏<7_cV>O4hws=X"*EJ t#cK$6-a9vhzۭV"r Go60YJB ))=;BRR )(uH @罩nwg[g^{‹>>Dc JS!m0B$yk^)'ʹKvt?ac⫗jvqɣ:@ }>ģ:zp2D `Y  J`+ulpczPoOR3ߤn[3HVbE<J;8Nrk\>t?3X ixc}‹όIe\]}3o?-틻Z9ҽ8w!65~&I47vO|;7SE3U@+HR,-Fqe^},h=; y1 sov+&ÉtHCiXG̯ꯑ;O?~ϿTGWOyVYzýAoܥ[{ᨪ:pIL!٪4u8G!' =Lyx F2לI-`0aJ..!@EtfZ:$1.*DH( ;w<8Z`Y=%J&xa""?o/K*A#Y-}ӟ_oXjyɥcG]sWk_U bMIF|V;3xA6.Fƻ{??O+(LmT/0YoqhodUYql4ZCGcLk~a+Fo]YHQ7w6 EaC$$KC0!7+gO8z[êm`O,PTsC(*b$$)_rNȞ )*~=jǓ`Aolp`^S{S՛'iڍLB<]HӨ"{?_?rs+`Po8,Lw}skOͳWYT3WjTv8,*7/)%5cQ6 Z-#lWЃݳqřvh5UĴʅ6PfLXFQw~n~qKyjd|37/4U5uZƓ6sp(@ǃSyJǤ"pﲵG9u 8\uon sc<$T %TTI|ޛs-]kA)TWsʰ JpS@$6\@H8v+} ,$S~ߦ y ǹB) ;spEI)u )RH-꼯 lxw^x^r+>ÏO}vmwwj$s w>GCܷ8էm/-_t݉L3tgNwR`eb2Zq|sowua~W~{t" q֜=!aZ\]IU^zͭ~g buy;)|E*G?O]|sgsww{J*$URPG/xHRp u$J!Iyn1ֆx߬ <%(wf=Ǧ?)$i6c$ T2BO5߁t L?L3_ч/ᗿ~_şΝw_°7,&F`jai1͒ob,20"$Y~IEUx\|\"lWY#4fŰsf! )8" $5]IylM>XA*Tۇ`~H "B!js*A H,V!T*03޻zrQh괳fC &Qd4IH,2BZ{"z~2˺L^{W;+/Gċcw|>[hr6O[I9/>_Xq RL򡣇[NW^Iq!iI1|7J˺'h_;w`x;u䛻o9u/.v<G;>Ͽ4A(Nn#K?GS7_PRY('$K%)XJ\]{$" 6T=@Jm]kV$~=9ֺNHJ;")HZ"!3HiOUa=3!0@eMm]l҄rw7n|%PJ9xc֍,Q,@j`:;$fb{H+Rb‚[qcۯM,'G wf*#)_ij6b\L@P'tjɇ>EIRHv\2F.՚f53R6׮\qƵ0{S':ѝmE$!F4 ;NV3%F^^'^lwq'}A FqB!HD $t"S4e{+#DR PM]tZ~J'FD0*RrB+bID D)!%1UU{,4V#˲4p{ԁNRDBOc A @*6@‹J`;k@F08gʪ+ 3 THJ! &ZKyhePNl:cy} [Bc?_P:񵵛7n"V9/_ݸC*_{ZoXH94Z)(Z"-{\=̶[F1ˢ0'OiEn$I+`{w;@x7_d@$B`3 BJ"DJp@S嵩ӈ`DA :B2虧'^JAB 9`l !I0 }0(pǷ!p~:)a RIL&R0C&Q:Q@I!Dbp*Z˫*k/t~Ʒ/.[/JWg> B>x~\%d4iy!ʡyL@ȦEYX.Q륬޶vŝN{]\\R`8qnO5byE;mg+7, lw;7o.MC7A A2$dRHuUc{zr* qSo4F=h Gsw ߆Yq=qD:R$YLZ IEU*c:o bnmޡՕV)I/OYj[嫷.]bMeJ7S@ \Ͼ>O}slud#U; @)Id6X{DiѠٞNxXGѵΥ[^߸a[Lit[4i6DGɤ'v|xuvh<{;ho\ڙ;{@22R@^E APs>bSaWi4tDd%fS @!0*e0i|Z֡kXomiFu gY(*F (*?s܅+˛|Y8N"pŅFDi'F52@71e@9Ӥ8ؕS"`)NrYAi`Pل{kk/!ܺykKvG5{8$:zrӪk0jUU޺gwGL@Z Ib ( @6$ĆR∄:) H%@` Ĉ=i\ޕ1MM@(S(jYK4k-Eg4pԁ IDATpCej@ DK%M68 S >L*Z)`Rʼ/؝I5DHZKkd8RlUJ[ J.-]E wǑwt!`2lmm>i*Ouwa2gsf"$it2x8043XьV'rʥ W68yxfnplr_t~lőw.>w$/9DJ(D;!єw)Rl4Gq^5@Y'DfTL;R)5>u$<$\6H4KUYr`BB)akOF(T0)qi8RUeB$H%IYS!K,ɔ{F뼱-#$1 5R7*a Bl0a<.TSyPR8ql 4Z*U9XTMrβ勯w!>@B<~ 珟ҍk4a^ĩ& 1"N#I<R$gUb'I3+JЇ?lv͍S9rfRRjKkو17?h{apC9ضwnx;;ۦ?NZ:1<3$BJ, "1\-B©5"#"2p{ oG3Ω?\B%I()#ϯw/;| B4 ű(0ÔQL{a|O~3g/g?=@RS$Leho-.*dGY @`>Pd!3Àa*yM EhcE􍧾2ݴf>21 8E]֖#0 p0c DD5T߄HLWK(`Lffa6 >$P"NqS&x @lΌR(!u xOIHсҷ$09!9YTDZ:Ra`TE"tmhX:>x"3ȩ"Na~U;B 1,o?3OAY`a" *0/8&^Jo;y1I 9?uf plVdJ`$)[=c F8.b$fr'SHjMQ0 )[c&08Z~;D(40UE$@b ʒ"0l:VR(x8T?2)#B)rI$|6*ih+V$`  3! I` 19u$P9[K%qY͔$)dmq!( $Ɍ*QEQL* BH i Nm{NZ76zÍv_:1"r5(u# 8ORL7V(9w ʪMmlg[h=qG'N&Ͻ| @R r 0A(@l>'3((P`طu!0{[X֙bf5ּ?FFFd PhB<+%I!O}Ĵ&X1  vބ I"YS$,I >HycuR9É.ɔ7X?XED B1Z';Ktum5Z8I@AlZȦZJޙ"+$ D!$"Jdvڗ\H9:ˢleGL8rk]!ʨᝃP !%*^^;ʙ׎9904t*oܼ&*zovѩ#| 7nl3({}W=@! oh"UhmBe`d dakaJ>~tN;"$d=TUZSP”uXSvz69DxgA":[5yǂB$ @8PSv8Hb$: P:uh7h\8YSIĝtV3N&{yY6x~17߹oy:ߑeo|;o2b xwp?鹙 k's7Y#œl5 ݏ;Vko|sj"KKD!X$Gn^Ӈi 4x2CBn7;`nB'•o{=>xo߯@GoKk˾}Gf/+w0b\)oaAU=p0ފ#8/q (O??7^, Щ$hۚm5Vַn^p3N\]X*}J =SӴAH\~_\^>z!qt\?SJ˧ysk6vFQ { tILe> Ƭ Q六 k*cJןnGx.j4΋:ֱc@H[B~?E?M'HEiY9q_oD2uz8dD^ybl¬;ٙV4;0י[Zrc=N=ʹn#K$ jg Hhx{+Ew¸AQTV3nCPr4KM1"$q0F0P{-@6SHtX[W~8܆.G[k:ηrܻ^wI"|"/,t{umϮ=|w,9P2/j$ɸÕk;Ǐ>} V^IHr~~Z7d$[+m)uYPqWq#ɒ3yko޸ `l&eT{)"#"]@l _zڍpl\T;f"`ys7,gC(|Njwxo ow`3뭣߿3۞)-TyjqqnN ,-F㢨lN/6)j phiӦ$NʢW|+OЧ$A=  ^@ObX``fF4V+^yZ>.vF噫u6UƂ]@aoP>r< hbp=Xaݞs74aؾK+8Hօܺo}RiEɁ'|So-#<8U]}Fe#ķJ$؋WođP tyqu8͛E1YXbt43J3Hfsgt !rb54sP*$0 NT dd%H`{l ['w+t7 H?4=Xr;@~^[3)j|@aT0@k Tz Ĵ H(>SR?1on-w4N4Q@KU׵)`Q`]^Y^s3ܺ5֎X3sۘɵ9n/5R҅v"{l G{ftA<'h|$iVg3N$$/*hC\]^iU( P0mf"?~xve -]tk޷+n_6x+Gwy80CҁGQ "jl@!RZ~OF7_Abu[;0aoI2ҴN@@|`$ =0 PJ ؉(HB,|Z\ %:"%'oUuN̼Cn;,dwЙsWY139|]cƉN7;yb!K~7YUإ̋JiS32$d*QcVg{}i B*BJ%,-M]*!4IKTB !"jn+{_4I}Z:vxU?(Bfi R7edMY*j6ݵM;GFNYNll cï~!˚Q:ŕ~/_9EL6om~lqyׯ}kOcz.i[{N3k4c33VkJ X10FًY#m43IT2VO,kW.o`sseDZƥP! ,-6VY#U6{{ F^fF0LLm7XmZ~l,۟ $( s4.˲/iJG!%+Sԡ;e|RF}O DPR h4BXLi֯mYԚEH~yƍ.^9,@bMg{kv1FNk/xZhO}Wǥ >#gۮt46 =rtu@ژX'qaL) EJ'鴾9,BifI\N"T.$s8BßyDavWV LO;S?|̟|[qƧ>v]ճWSJÝvFݯ={՛UQk>}g߸0 O,nnvkR4QwWR Ǒ@d%R6Vk޹.I5s(I90rI)ec qm ȚJo6[IABk-!pe:@);gQf"Hk$t;$=oVY#gi#GQmYXյu;fg:Ȧ۝&Yk4-"D[ )RZ~6ЙiuljJ !`t$D*.04E1K8CR` ̵PE(%Y)0fs@BDQٻ${;[pl;q+"@\Vj}; vo'_Zʜ GYc@w~GK~Ksl#I$QJEB2rp$\`HI1eJrA%k}3T0`!n$$x#% -*^7lyi%H,k"0cWB$M4ͅCsD!B]UQ  k8HǣqR@(B+-@DB@|VH |UJkkňӭEYslqsa Lw"%RC!K w2PHH^N4{OU6.QCʵGz?<ݍ?p\8H68DZTY1tqFu4`]e<ĉzm%d67O) #)ЙrN`Z.>C}y]m]>"Y.{|w^WUWw nu+ Sw!qZ-iaFD_nCa]Zek1P]p=|?ͽʈ1FU}615_U??1cx8YqS:3e8|^nӔrιGu|⍛j%";1Nwkjx$bP:{e]kScuȴm5?'2Mko0BqW(q[3J8/R`m‘cb@gDUmRDd9z;JD޶y]b&r*kij9Ewae+mZ~̉E2M7[frSf.Ub Z&>şXD8Dm|ZVS;oNò.1uwS-岠0\t1sl܍CFmeáS8x:2_/)u6! =0sݘf̄KH8$Jwzx۷u5Ma?NFscN@U9=.w]Mc˴(j*/?Gx:a!DRp9>TR x|9@0}H*h7m)0b[̱21"z[ʶ a|Y7`Sj^&DH)ezSmڐ\ !0y-u<̝nMR"s(l-wY]Eͬ?_1r 1cr8m 9A"1-[o?|wLT(t?aJ2@ϫ<}z ~pZ7k AjUŵRvݮJCtĶeB䘻á_|;t %U4a8T~V\s`[Gޡ L e!˜"Q7~[2mcl+U YMm3ܽk֞ε"ޟBc@7FRE3|]ݭ1Ɣʺ;!r7].IDATz[1u]`]e >Nc=0^^?>/bL$۶F x)!0 6um-́v@-@ R*bE +#MEm}:"!#=z4Svyf84BD҄9H"fT|<Lv{.v9]g}|Oc]?).Vj )"TZ)́om^3vݠM޼\cLmm^x)8V|C!ϗo|Iupj)1ˋ%0Rd{=x.apGPN# <1Қ"lb|Է0Dǻe9wf^8Z2~^ ILr*>j.1DbϹDr(K1%}0<9w9BBphMBmT"0ZQUUdl1[:ߖ'p3VB01$04 ̵RkΑT t-u"`́z;|Ƅ1wSU"&f& 9!81L5w$b7R!TеVwue^ơ_y<o)g.zu->]4Z[!o"njV1!Zjm_3tw8uLđE<ByO@{ ??=SKﳨ˲f*:p^}?0l)0"qH]*[ 13w{~B~.}.2D\4$f&}n&L̄mCp/~4%t)k CP"8Pk-!Hr`f&i&M/~'\U9Am[|+˲ ]^EM9uGQwG$Ak*;1XeD*Z%ܪp Qj]`$(Ҷ]j{x- Bdrmn*Mb8!3[aAUD=cn#g]͉9ήM6"P&3[H=rd&ZJUwf!V*!939u]Dk)q)lW_.ǘup8Mfukѵus(L[ (ݞ>9":1#!#t : !:3=(B0uBJAT/"!x? D-fM-1YJ]|' !ZTm9nuUO*TC1)1Gr[n7 cLI}[VBvj[7 xMbBS$,HfRj""ϩƱqf6&͈[1jXbU ̦Ъ@d}EBGUb9e\篾|gS 4lB@ߓsFmn׹S)zpdi.8˺)FwG"զˮf)ηHZ2R+E v[۶B~ܤ)'mvuxrd0hU83gbLbJ@:/b7V ##`Jazy^ðC@Hm]IAiB1j&!8K'UO#!ܮ4]4.]3syRΦȢZ-RDr8(K 9@b=MTJYe;W{H9Df1m)SJ~0^{YaKmmw9)r|Z}7fD rxwDwm%u=":??]ci׭4R8[-4͇AմU5cb R+S?ݽ5mZT+V>~lz;QZ:f}!2m]Bu;9b&)wy[7uYYZrPm]y2oޚ*$MPJT3ߊۇcQjDLbb4~"a QXQdUcbw+ƁRNR#mYϟ?|%[69! Pͥ TUMJ*ֶ-HCƎ|~:mwפ1q !Rr*ȁLEL*b] ]ߩ;uF`Tmp |"Q@ZZiXoאxVdbӟ~ȵ*rĭRtcbDDĝvfH.F|qiϗ~6Çb˶V7m!$ơBD8dۺqѹ*9ϏG"wDT8(u\W@`BwS؟{8]kMC׶.K: &K9BDU ۼ;2K61@aUZ+%t!bD Ӻ=>Rce;*kk]ʺ~ߧ BtK ͠m[PWpZ1䜖ǘ>nPeODn{ EMsLiՐyozwbTU 7055'G H4p DkYw?-Dˍ^>=\&u>M[JMMD*BJnZJi8i)go_~94y\ŠR Ā> }1M_~-c7FĄ?ļW;DETS``"V+1w?2fZsܙy[WpxL)uݩJYqeYB`Zk.=}|WÛ7ЕrH[iRH "S\Dm[kJrS:; pTwԪK`nuʶC0p jkM)63Eݶu;6 4/F@n753̀4sS! h\ C dZk,1]o0#N.m`..!>vDڱMT HFUC`h;r޾hßɛ CwmCnHT._ n#mlHAj꒻.:mJ)+p 4_g eOw>qD SJ? :q1ZV3)+ޟ/_hGG b0SD4V"xS]9t'Ҥ*hƁEDZE[ᰕ:9cJ"F&nR*r[3!"su"FM݉)ZVkȩ5pC̑w ֐AZS3Q !,RaR]Ŕ0ӈ,jsLdꢵjN5 ].k Bv2*""X7s1Wn88P)>yH53Q75t*(D@D(l[1w%sGD6mρz[s?=5C!%0}֤61"95Q1E./~h}RJ]2u "슿b t֎cʶֵeb1m9z2Aw(Ɯ8"4f{goaUZJ!w.%yGI ڥԊ"Zsq$ofrt`"i#R"R8Ǻ՜"Hk:?/Ӻ eZ[{GߟRqF93t9{+Ƶi:jJ}c_=.bΌem!E0"ӣNNkSkm۬k5|~ݮNr:B"m 5JC 't5LĵTU%v׶*;nշ3ǧ粮no"]HZe Lĭ)EM1R9*@~p82MЯks7]u+8 ppg2??_ t8&U1wjW)gLwl8"kOO?/)a5}6/4LHxets r;Ůsd@UG$0ܥ{К-k]ia|xx`nVU$DTnm+ @~ܳA^bČFHsVKBL6e<@]!A۶#<}H.g&Tc$D6 ft~\/p}*NxSGX-crw;L!N P_n3 b<~t}euf*jVc<`bulUvWZ6@ߡ_9em9>s`$vsU*s$&pPw03@47BvS{6>~Þ&Hj@!EB|=qݐ;\o}1W?Rb]TQ`WR fbiOFpAxZ3%f8mB!$MƎGDN q<@Td_ e)xB,>ҷ,F̉Usw)ΪDtS"F@vŎV|"R̉)l< !=۶-)QE :մs hjjh*#;J j29hԥmGDĠn+ Ĕbʲ0uPU/?ebXe-߿] j]Db@tu<,[Hॴj]s",[!R%`RHDvcD$Ûmջ.zښη2y\1RR{ޚ@NM|.Kr7&4 =#][qͻ&⪈epΟ/O)Zǟmk__>H[uY@ i>= (r3 S6UĽ$EbQEc fiYʿ/rocﳻڶV饖tReM/0!Đr2SN[-c@vyǮ]_}~Ķȯa7w-!:V-@{i L\bDwmbn#l Q@'m]8žku)Zqp$$"w7w4$W-[ -sUCiBLCHC `RH--phZi8_~O|{pGD҄e8|~݁w`ǡ_WyTfMp<0uz!>Mo8iZ8x!#P$IENDB`qmapshack-1.5.1/src/pics/timezones.png000644 001750 000144 00000243106 12527654570 020752 0ustar00oeichlerusers000000 000000 PNG  IHDRe.sRGB pHYs   IDATx-t95m/3Ka4젼,4gQl(gQh4 Ai'a3h;lzYeu}*I%룫H}#B@Y<(@ P A@(@ P A@(@ P A@(@ P A@(;l8|u%_a3@|#yy}%Wy~8I0h~l8>^]]-$^ 4@: `|}1[ۇ_?j ItB@RWo+J Ëj4I` /7;Я 6xgR<{#]E)~m˂d! =մ٢ڈ@a)Ɗ $k~+c~mbqe=r@_3kRsm_@4 1bh\g5lWWcvuB~*Yשr~h1W4 V6br2'N^yM K 6| HbHmh Ii%!ևT"w__dw7mn0z8;oRϚwG *IISH tPP AC07Gϳ^$ApO)do kw ƙ23!  (A5@@JM_~hNuiҨzr 30}gdkү$1s{0AxwvGo )Pi^ɗ,}( 4:=~NN$_ PËUg4 ,!/s\i.r03ķ@'G0,` P*8A$" tH'K}HQd?w vsֿW)l `Z|,ij 2Y ?@hi֪sޱ!}@0wB ?O ~O0Pnl2W;l:0:yиEH0-~}xπLSʉ ޫ0W6Qeѕa*9&R׬&R}A~t 1(*u;?@TO%/Ww>i%vJ x\Щ ; F[3> 'wp/_Ỿ5 /d_ "> “Q Is!#4 IcO?p r:~5t@BS!ͻ``)w24|A Ԩol/aXQ _1a#rsp` 4WRA@'sOOm< }*Ձ~!UKA60-I V ?f00!8yq(x}o{1#==Z 0TVuE$H">$!e@gjtՉSȏG ~oރXwlc q{7C'%|x/7 #t@8W@_{+@(@˳tS/zaˏ[= ;Ȩ&@%݁%0~23ۀ n]j,~BIC)q ߆R5E dmm{˘"D&66w`,HcJMnl \m ʦ-_[r`0hO^-Wwp6`?pnu),*`\}1 >ue} Vn/=f.BALa_~?т~3)hzA~z9c*?>^Uމ?@: }l^e Irq<)ơtÕ!2$ը(BDf4`~e%x#O* `J߶9q_=Z D$u?Y?L=Jg.}lfL*A` xd \5gYar!:eƂ?6S@LHa RH'S8-w# @ƠWH- 1@O {gm{lߵ]2a"=exAF"dW @Cps8h@@v젃(Ow^&зYboϼM(z,RX|Ϟtz4^S"_BJʯN4(֎Sx_Yi~*jLˋAQ;D-Ud_A \PW+U!*@x=keD>fB[πP<p A*If4թguu|%(/|[/,Jx\z#Y؏`9@4!1H,dx:o~6H @) gU!>hh߆?Q( ͷe kN4vA+cT*R޽ˀz~ndmn 8i&JrѣK̑zP!v+q1` 9֏ @.;5.ŻZQGV=Tb²@@F >ׇ L%W;m~"'`H"AI g@!@G6ч!/adi|+5 AP pQ@@RT2}L@?e;D2O佬$`R ~ ({'p-H2@Rc}yD!H0HGnxokU`Au }@57*-WDA\]-̜,8(?:k--dv$/nR!/ _mcN\p⯣' \ +HEԳ{ O ?v3vO+'^7ʮ- v3u ؏@dLe 仮t'Xek); O  8$PIwiK"gt[0M0,H0KAs +4@h>.ZgwQ@_WB. /vW#lЕ]KoW H~~鳡;lHL5?jNZBoapwٚ6}Sj8g $B"m Ax|~Lی?,[~o>6Mǂ6P+LzLA Hɻ/,@ˆy:m|@(g[C@tտJ]~`ٯP=7u? u =&@#e"" 6*b7R86x*W QAwp0hƋaw6u\4v AxQ@DN{:x¥wOVM~ZT"~NOsf hRfz6L~`?J 7e ' 0 ]FIEn@/:F0җX#;$[` #/` EyE@CSקm<\vQ 8WC  J[Ж/?b^in0a'r&*x{l  BD_LUHܗtW(0`8(_@ ٪_x@4p8]@=* yD?0SO[ G@xI~@ ֜A 8(@<&S^dxVngŋONЖ!j w6cAX)$t:e+3,Ny}=8@%17,wG 0kb (  wg&ļB Zqؤw0 Ff[ 1|gcR{YJ)o[!qЄL0"rt/ i8x|zh5P0'tؤ}`A7깽{ c-g _;nk.xb$8գ 89qiRgi)i.[{!ӁIݢ^|ZM_jdG{[5ϡs&x1@ ]^X@ 0ep| f%QO]oyqS,Ego-PS:<Jȯ~iq)8~/>YM>dž{C]YA3H !Gp0W#1GԱIUv?|)eZ@/ # #T #i.bؐ>oӂ42I7!.~%+hx J~O+T4W> rD0@m넱~ށ24< @?M` 5x[d\]u/fxQ<- !ו@8|rAyLp9x.k8YM'+0P_Wz ,]:r؇1ķ96H!w.azL@6Gݕ%2^Cv}!E 0S$1%@b} lB?N7PyPsm=ԮGmen3q/D@#йzN @t<*o^|Zuڒ}yCv'ю>cA?IuS&@?wV#_OJ߈}X?N P@}zwS1Bzi˶ިP2"LBծ:B1M@m'  QIgHDVJMƻ7OWLF2ͷ2: e "yϔ}Kƈ?P%\װh2sE-2F L rȑŁg hϾrf{к 'nGuaDo>'L%fo_ )~%ʷq(&H͋|s|O3O+Q+s{Ry-; X."FyRBcUw Pqj1 `# emB|9}eL(0!;S a|a0(Rܜ:K!Ϟ&m_wL[6緟8 `0mtu~Qj}]K 95g^eyf5P( }Wέgg~Xs%*DP*6T@XI۶mK}{!?yJD{d$6iMzo?dVHGLȄHU;zӫY=~J?g=O~wL48 ,PYE{DĀMhԦWbwYeL"K4aK!vMHΜ@!u] IDAT6&\XXOT ={LrJ;N-@\]py].H|f? AY寝`nju Pdo# rAϾ,.=.bHi~,[HH1z21OT~G\HL@grتq)Zo?CܟOݟTjZo.0%}9^?6T@ao|@[xXk[x2,֐C4~rMI5J(d.#oM@!B<EK/ݯAEQ4J?qP⅀ QW̧%Gk7BxR>tVn7WޕQQsz/b _Zh@Qؿ2;Fd ?dA>M]ۓҾvjv 0=9v\_UޫI:דz{ _DiȘ/|85۠f( 2@8ŎR~:bT[r@㊁@*@*2 /8iSmmł}W zLsB =\ÍUp{\Z &  &Ϫ C&`tzW18^iu5 n+^oƹՋ?np?L_ʼn v[+{I5ζГnvK1O˅J#bр ,E@prLM?L=[ A# ׿Z+a}}Ă#g +ܿ%.O<=?/ bV ې>M d;0 |,c􋻫 UiNhk n`6k1{&QLv@ pD3[Vձ1Gxr!R @oˀ k59>^9@U)CaHVCK'~i@^q;@E4OJeO2># ȱ@2 eHdxQ^ AQб)Zth[@?pS2Q هig_43 R p{? \yT"B);!/@F?.w%AWnVDV] ̸ܹ%cDgȨ=P! Brwݕ(z`ިd/MEnE.Xm`?@s, $B#cҁICHRks?=tk%CzTwM&&Nqӯ+ԨTiKH.(1μ.h #;MhlѠHzdx6~-F"~E@X ,m@V~9tM:@~ghh UlpS_>GAHW70w-p0es Ï{\`-} )߁T@ ʈ% ?q @ CK^  ϛ1nλgB%,(@= cn#7D?yA?P:)hlK _ͬ(KaB_c(%1Q^H6"Ñ$L@X^/6)4AP2-)p #00m榪RtH r~Llu8lF@x"wN~@'S6hh4* l κ@pxl_qjGե5 Fْq ܀QH&_[~|q8d&`urjczE':}Xgey\Ž _أPkT-q W꧒m.H@'IV+˱c"ubu&ztL‡۟J5Jf󌱎h_ $ﹼũe0"ѿ$]1pH=ĿuxMgƅ?s#<Dö! EzzdJ&Ј ٧P1c]ۀ_BW04mnmB!Gl@w)tPX·͟XS0"OWCזDFza{cb)5k@#|XEo_>,SO})D都CLOBwQN'Or S%U{CMsk}ȝd|d3 2oӗ єĨ%IpH@81_b;hqÝIvga.=D2`,Y'I Hj Z?^QH<),>-T3s91  8.h>,m+HvU~`'Ǐ C"!p-(W;M*+=M kT0-[u?7Kg`/P:vǪ=gS.mIǂ?#ܢt$RB7)&+{Sw~6ȗ|.$7ٷ}Xlؖ$י (/Bny|Ec{'_@F@DY' GN-Gp@@ @@> cݾm3()[+XȂX"@{o 1!Owy-.g͎7zHSHe֯ yA b( ,HBEL<{ __*oS;; bh&ٟ$.gčߒ>ZFo95@@v&HG$)0q=O@W/fZlY O D^_]|Yg >m9` '(J(x-fτByOPR dWaz0TLYJC4@=ۿP & #fe%zJ~D@d =9mQGۛ,ڲJVHl4@`mjUA0^81(i J/ D0YqDs10_Gq+1%ljAY|ON\[29n5߿2Qٻ!]!!B_/ }ƜiLE b y[쑲4@Ij/66zPB@d$dW'u6p:G!{t+$,e@%ėɾz_i&*փKA@oX?qB˴NR nbd%jX`@"~Y F1*p){F;eh W-ˇE9h=LacO`O|@*?w_C?V{:^J| K̎8̖H Ic-#r%ubJmx"^j sgƊĭEY o_fh_ȣ HY@b +C"Wg:?DZWl8& OKA< o}$:89e_Rww\<\?*vaO+8Õz9q3o+paQh`ʫWԉMng8SĀiI+qLmd(zh +֯h7@h#S@_۔1mz 0)h3ȒmK@O "ӫT:d0@g>\-~Q j4 =w[@K` :Bu_ ϶1HclL@ KAp\R=[:ܢ4NW AC?^KaD3X%Q Uꁁ~z,7CzdHXgze @ % 8rϟ`3FTV>K4A@/V!OWl_ݰ R6D/: 5?,A} 1 N88h*?x$/z|Vb߇>@]b?e@%Bo!1~u h\ 82WfY-}F `_eP]xVX9 E~W27`cZO\Y1A KcIS@`Ci9F=h?sǒm xN7W !c}=0sf%Hp@5ggwB/kS* Yy1 `j r7%clVK? @?)%Ѩ"@?MhȀOlIإpaoBpWx*f'>@; \kS@4" 0P w "!֟3Ȓm5*8u QMջŔջ$?p"6Kܾ6bHRg&h$U)@?5hzx?S@bJ9rNw! _q|<'\B!CyF/+!ēp'poyǣ idPcB\V'Cp}%Dor+IChI&BWCpNBuc%<  di}:;7 1s?=L+mB/{/.s&}@ERz%Dzzi,fk!s@O,1s/8 XHi?V2ѿ㯽;gtvz>|*l$( KynweA+ r8UO%{tjM^zG=8I @]nPz 725uP/-JY#'o7Bqu!-pr8 H$|dyC1`^!W8!o[B 3֗[+/Ă_g{/~2sH;w/{`N١W@w˨K;#_/ !{+BGpNwַ"?4>a>OK'"_})b\' Fur6*]p)w4NV+lC]0,?d_V*9`92: Zē/ n3 C }LխL{Hr wjG#@Gc.=3Zns$f/ѿ3xdC]m&k#w‡\[&yyp2 Ip&OOZ=uw@x P 8`9n@*'m_A7l&o7m` K#nјK a<xպ_*_/tmI'Y5Qkg1d̥O-?޿w @R#X-vޒw%ϾY4Y=^ !<yC[l@0#!>4I4f69$܏_&ӿ>wH OF^8=ϫbxxw%> ,_2B9BJ,n>>$p8@@ ~#oq}4+O- W6+IWgva_sdu_--+)# N}h~/wW 0m}Yv'ItoܠG}(&7gmƹ>Y&#/v~7/>7={GQdo߮uzq"{+zJ/:g_".z)?_m}_9}Шx/o^`}6/nEH !{ }2˵K|9<ʵo~{WP/0f ZOVP JE=ۣs?~mTFR@M[wy3-/c87^Lcml$xcBkN76, [ӔAQ F0`/)D4KH &oeH ')=Ur .f{x`i~o @d 'سM[FG5!Ҏ5J+T@}Wf@ +"mM0Τd{<C=&r]}/CZW@aԛySp`.(yqHa;9cwkAi52|kLg !?BD'yt pz, ;ڪۤ"= 9IP' w۹Xljy)۝NorP7:@!WfE`Lzos=&B=`6Qo4J{6_ 2j8|_HRH\5H9 Lp{0sN7[؆noaw;U:p9qN\&K0&Ci_L[ SpJuaL`Vbl,}c7߶e얶 IDATtv?7EgM[ˀ9'V{6.'Z]l&N^Cf\-Wc#&o/oaOew, $6OqQ~cR_I gA6eWW =WJb; Gh?VwOe%*q]6Cߝ7BGBt7pc`LL%DH4>/wWRo&2+YYc l_ !vZԜS-6[53!^kfφ-w s? -N|oL[f4wKqpf!Zzz2?Z@tpMyaB@飺lMV $pt]=\ at_Hj FAʀpO^/  7@d^JS/PR~W@X>|7tpJ+ѿz4\$&:6= G$28f̓[Z8>W^W7~7\VZ>``,jګQzۧY[9gg*\GdPth=פ^,N Cs0Fv'eWa`)Gw/c>=X(j%N=&eC8JBgBu3|﯇~O(&4' 2鬧/v7]/\݀ 0~^?^Kó `dP6M| l+T f*u|#p{q͛ϟwPITx|9exaZ__ڤYO19&眦yf5t;:3`X_/+_/~YuÛtп.'{eM{9\5W2 @Do Q~4~@c_l'п!n9'`uv_o0o sV4@}5 @} ĂFw64P:IGrJ^(tCڨϿYo|*{yU](SP`].s/)^Z63mfۑOPA~ו?<H}<%zꝌģ|9 =EzK7pMa#\?Uwm.!sð!kn`kKztyYߙϺvV&1Oq`4d0yQ{dІ!}!~ddV#,Ӿ HJR\GvqJs*b77*(rErκzq)or}y沷Ol{eA; zef1ͦקwD&w8A:ѿ&@wNHSY/; /D>`z?&2l~9:!?.rw%NDqz#,vֵ79sNh IIn? qױ:eo_G}}=EWr!w; 5唝Ӕ]H!(@]m~%7O>Y8@le@RR!Jym]-~:^\/`d'Dtj<خ15M6ߧx_w &`gs;ʂ ]s3~Wy&!&_7B,2c}[_豾ԧz4dt(/Z g2*~?{}!i`x,@S 03YN{ @G#Kw@=zl$@ PT$#q_5x\c2Aۛ/z~"îSƠR h+kd[΅HClU\ron3[B%ŲKg!^ !~RnvS|x3fdaJ P ꟍF 6>qv_0@P,] p0~ͺP4kyjJJ`h+low P+eonH@j0/Bᑿ HΏM! ^'TOI.aTQQ|؀Gjgyf+T5@ M/EؤӻP& s^wnI9n s*t()d$f.1%K}?{DrI/\t0*@é4ls_6AeN+;Rg=V;l",>AІB\LZGsO[p 5y-6qKd-=Q8e022ٿ>ޛyT_t9#Ӕ@R&+H7 AЃL%XfJ6d>2#ľ 0 x%{uZr]. x,kiz~Bݛ%t~];{fĔ ɀi:M -֟=rfp (4 h5F?@ule 4|৖vAy*tIcr׉nϒ=Bcߌ/rA(v'f^7Z0L;= ϸMR A]d2F-Pe8bd g`JCgP%8L&⸝%rz鈁 _s ? gB120 儮 İ*р/Z+=},>~b0kkI@|pl9ziN? zV}Sk5qI|:a]t-Zg"迢: dJHEcal/[ _O)Jdө4n%dJ ?:+-AES/!8|TN'SFp+@_Ni[~H5@Wbţm/SYrfbs!` !s_3r6oi֧C@PFD4@,;sq}Rd/_lԉYx{s䍊E-~ `9.E\)^?Ma`ŃW>3ok_ H=ol$nfB݌VȕzTbBˑG l4$hbtn>py ^ 0>9] qַ<whz4D "_VΚξ3!j  |;7lKBhhcA7{oX^z:hyk_?~~[J"+ ^\` ۅ(.Em&FAx{wo`=ߗ"7GI6 ma{d[`K~-R2`o4@бF *@B89qo&(5s췥zu0 oTk`t?l V[]9wH-ɑG-?"iyw 9IwOaöl8oNnE=ԗF #} RG0qۑK(@$N7ϡ)Lm@xnȏ_m8 i|_m5.z"@} {jw$%Ǯ! _ ? +ZznWN#DI"K<-gPr|< i+K&5hM"x !8Bq)6MqzGQLp-ӤWc ʻ{?/^8eS؄9͛>e^_  uMhi]~͋O@{sS81d4`%+.] oVr 1/-wM~/B3o]E4lwޏOmz-Y/EDU ?߿Fi%ߑ/9rcq!}o'hwCk1&@)f@C/IE/lM049=mad.Y !> ;/ޑ/F zѿUN>; .8{ˑ|dz.QH^hz-Mf>5׻U: ].ͷ+d!p P\qQ H 'E TA[9WQ@! N3ֳtd=X@O\{ljڮ4m$x8]NnoH潘=y w#04qŀȆQa[|BG[weg/&+>>_`p*O_\ ѿ$w (_lHS@|ZL!|Y<Т|_ W8jzsܿGH< h'S{&:M%sR☀ټkw;&6`~mx^~) MLY`/6>rjlae/;88/pq[ ^_^U8hhWJrz8=ïse}ut^Ŀ¼{Q5bukK?ok`) M*@·،3<WzJ88r-s' S_v d !͋n.wO'{!aea7|uu}>e 8о`) &!Dz/;\l(5b/r @Bw'Bq} F g[TM"zK0 "}`?/̍?LPBlߋGV@ x];EvZ둾P@?zQău"&ȋi:Yۄn#N[ѿ ~fG8Bq"-@sC@pB%MBN7kg0dL`{T}) 6c~7;s,K)=tw `C |2+6%.S {Vwt4NOWl7N[Tܘ d;OR{#l^uS1|qFI4؂G={z(߷ڳ+?'>`H!6o_d{47vVb@g` 'ɗ/{Vz󠫩~a|ZMQyG~Y_~GwnϯTB =y 5}BXB4*p. d.+#g0קr)_& ~:7ubzwA@(fSuFwc>*F; #HP fu 6*ʬg|339ѢoIZ{;xrq>̏}/Iv{[7<]M)P! S牃zOJd %弜Rt9,>t?8p$FucnYRv8@$4,BF0 l=ˎ'%k.,gm 6h'ݰB r>w& ?գr*>AG ]`d}:OC~cVjJd s9[Z>H+Oq*p009fpYXz47pa}9q)q]ooSz?PZwy9|~I'=$׳ۼz&6BݽE}J޽k!4 CzoF> 467\ڭ$N9Ex' `i}*|vRЩB}]@7@}Xy.X Q+#F3A0g  H!t%xf^0p850 @.]!E7:/_W.AЃH>3q0A#jv d̍;ޛ@3Ag_xmܤ?1kyVci@X _? g߾8vV'mJwAkH/D?=rZu2W O`dN^;Y)HwoO|rծN>~`iTnWB(UfbP/hAlN.=ޯZE*=d׽qwxuA4rT^Vs18'N @j;:u<<]YȆe%@W{:)Og&`L@:͊wFYJu {-\;?~MxM{S@P%,ѿe/ÔSs೯}/xr#<6nP@k"?"6䯯~Wrֿ/FB4Ox@}[ EckJgue-`'q湌I>@_<[ GfHO@P-sj_D` ?FxmQFL8&udjH} 91?2l~be4"P6zr$/u pw*UOdW8?>7E^o;N @2U??8M\4U !H2l)J[/Rd T'g׃L[Y=%#GU @sπe-<LD.6!{+/tC@ ?iq^!%GSK.+חN(F[jB!֦J:Jo>v[by"nvz]e@ $a9p2L'8+"q7kf~&'>: /-~kUMc5n'<&jVy"0\lwv[\:7&$_jtZ{$g˭(Q_1v Bs&Go c `7s=nߖS;>>';C0\OngW_ª+z_# 22pfG;cVyquT?Iҝ lw?#0:GNjb?gAo#?W r'S0[$@:!!]CDuh4@Q`AǻCdgVE%E \0  .`4,^` ̈)Fw1d/'N_^x_MS_b@jzt˛8&wao\]w|{.O3sKB@gFTL(`UGۯPt*A nK)a4@=įABZYР.5,\ R~ U*ȏz@YM1tB/8{$6sտf,~a(ȉ`yG][ \&s%=&Pջp޳o\?{vŰmWkE@1z)> P,/d<>Q!T*Gs;ڻ7֛q"AaOt(o4Fd?E@: (mpq|^w0Ȁa/-1Zxyt@j2+~ƙ5^w@*T~x7.(-2 >(`2`* `c#] v,-=C"(왭K[a|Kz.]WMOo+OD˹0i8jBq_ DŽB@CFVfrcಿ͒g (Z܃Ж}֍+Ba:v=R(/PyA-ݞo倲kh`߁ 1PSGvҿŻe5pkU?yt6LQl?o~x/k(P ~JIXU-t8cotb}nb@_^oy<6|G@:eB1^ѯoX'leK !!\0#w1+utŹIlV%*_N$y~KsAhKA|W\Nbɩ`Ij)Jv)cwnɈ`!0n)^2gEe$4%5~@{ :`'m$RW+*quT~gg{qXҿ i<_ v>Wn_꾞FANQ%?M?$NG)&m^5¥]cRo⍋mn.9%@}ߞd ҿA d7/Nի)?׫I߂})dRY؉A*Uؒȿ6KV'yp(ۨ8؏I?eu ޶vMP\LswFMfxK6r?x]2f.U[W/N^U:{7}ǟ5QMo>@@|I usO*8@_e/DE_"ul݁  B_=Md! #6Ԣ%bCHͷ0C%mw)>C0^2М>߼z=VoU.'I._S' @1`rs73} Qs'NUB^\PN@l E?V1I2_'*@4 97gK7Iߓzjl{CB\ !Ӫ39UkͬGGIҗc7}t:9X` zJ@%4 !Rzۼf+ن`P/ [hƩ֌?eDp2 <.]b*`嫯*F2d q$Ƞ~6⦝/;@TTNJū?`1apYxFTfaWOC1 0~|=DI_QjTh~>  `BI3p(xfQPY*P*uptl95_6Tzr>d(ZdE<qYJv\UV>T$2nOx8Bu[J @' áf:@dnb_|jBz H5^v5@.thܜFGYX6D04['ousDmCX|ˇ$N%#y`óP'O?'"&UB/{U}}C~{`OkAf3?U\>9N~Z+ IDAT o= y l>$"*1"xD W~@*}?rOC 9s'hӨIZIӥ?S/ vhqPǘf]g'=:S2*OnnObFb&`"hy&༟*@q /ꥪ~>Ir)L ̃6et@puZ~n(l f%!nZV~Ӳ?ZEJm'=ZpfOh3L#rSg3tIؗi/Vͷz=A~/ d- ,ˠ8?I}ѺV7Ul~d1rXR37Z6K~@-NˍW4 ZHKn&2r~rGD0`@H[̦J_z2`퉸O ^ڷN-5r+!p#&5y=uiMXzo0Ƅ<[Ol{,vy2x_Ox@l`CE_v-st_GM@)5A% >Vu(~g6p'DPkרj `qEY_3'}](k ge_Tc`]9ĖgnҸpt7DPaUN@i' bߏn<ȪYɾbYw349]9֙-$NU9 r.D !3GNWH@zwS)ٯTsmj*Ήylnډm` @R_PofE1Cb"N0~!ƍnK؇a^Dom{fR/Ka'۪A*:~/枔kP׵uJAJo% :݀CY=YtJ G򟊜"\j\Hm`ɛ޺,2;2N- a/ؿ` bUa/D  0nr(5ݏba9zsDR a/.[Z (N:~?ɩ-mLy"?MmwOdh@h C@_U @ !xRWO'vTf!(яo4 }oiCfu~ʈ J+EKN(+ꢚJ}9uxQ3WZ@ s!7:d~ZdDKpY`8PjUZPࠤO?? %[4XPiW,&K묑* Q׷r哹؈m*^d}Sv-`bF@-tv%?>f `1GA>do4#V"%9__yta+Sf+}v[ҿe@96M F L Aq?c1@jb'˷邽^os`xL]wgalk`? %AXTe RxFG?nC(5su~Ԝ4QZX"++3?3k<;267nG`.=w C]<( b&/bo,|d$ϛ+B[KV$!泠6wb-r}'$y?|1sƴAT??X!IaLejs?&%KXB`TQBb !Tjz 84ZZwҚ?$0w͕oZQA42~4RvS{1_=stC a %G31 m̈́<4 @%0dL__hSrwa/$HE t%~ͥ!d@+4U %r=^ɓPC"ZV=M׌Ru' j5U  ua'&d @p2~pRrq`LlUpo8h:^$,IyE_zǨFy+Lo!`pH_>P1F?! XL'0|ZF\qu"uWחϪJ^Ǡf0W ( M`m<H?@xmK/H8S7IؿJ &)g+ nF~רK|08zokv-U @)qHFׯU3EK'ot) ,%t7lg7g@dtS6t^%øD` )4UuE!<@QJh?`rb( V. ϦCeO<޾Y5'_m}*֣>x3SDz5.ؿZ<&k< gPCU BC=S2:.B~`Cc]=5 PUQ$s+Ԩ5[Pe0 %@c oAP MqiB*f|+__mW[Xx LaO0zC.M&uYF#CXPL. it$ѹTX=8WJWǔ g1e) 뼰fI*@_dcy<J^n,%P@O<bvNd F8ܛa Jqeh6wU&l0zTuGY%Ek_X hc@ z ҿ*BQ }b\3áņޢO@]c_>@CosFYf#ۘm=1Zx7] #M@^[;_ܨ 2!|4TR:{J_\5-ΗfNV}~%LKk}v @`@sh=0zѯAE z@8FWG~fiűM 53?D@dR |%ЀvI=/?*/|R)]jf)btxȺ#Vf0}/ fCŌ="ٛxQN*LȲ2!@q?b: g# PJ 4rXM'F/Ytf[xyj& @cohƤ O4R!&?Вzෝ /ZIwݪ9ZSRPR-0&D#OPUz9ny,d2`ͧ-74YS)q2#d;‚ XQ~`)i!x^QT:}W<_'?@)Yerԃ-iQ (pO*--#M> >_bB) D,K߃__ N% Lb1`:]$[U*Mx~bo8'7HNs^z~*(C 'A_ пKFFswݝmgw@|tpxQ^\jos`^6lN&Lp԰b-D??M+UjI1H5y5uyA.[dLBSș1`|#ow'?RAGx"`9)w!+@Bd=)(u{TQT%P/ol턠~7bKj^b{[wM@gҿd~L*_>1?HηH~+{ 0'yRZ lu >ޤт PIB_KR/ :ǿJsxn1QSUܰb>X"4lt*ɀ'7K̍Zۖk ēBLʦ7_m~ƹƿ #g#I>@]GwRoo}@_w-6_Am )]I"6{ݾuI7NV][(X#34ߦAKSPB+mI]3ߙN׷kX2Z@<F?Ѹ29xMd3$`xBHӺbsp@~RjASAo S/۽>FbijJ-&"-|d]\O.Ll XHd]txt}ZkgXyd=]+Ȧ@[(_*jʽ%{w;*O?U0v%j^zwxў/#f) M<M@wT?m) do>O;+ =MonizA)~I-lhYOQ ilVKw%߮(Pеa@. 'O- K^s2im `#?HȘl~NoԷ?!IO](9Mu14`7U?6qyeIN? K@ -*gu-qyi CfQ1R JNM!%\wf#9"꿸!(şNJVⷝ}k_/, ?PUG/;-Gc.N\"/*I-h3z ,&'xU8w3cQP}שU!PZ 4]h|39bc^ q&_tm]wI2q`7>Vx9SXpy ^Ңۤ Eft\YeGQ7ziRR88K0.õE}oN&t_}`,6s`374XT{cl_,4= 1 ^?ȩwW$<#='(&b<]z& @6$~t`"'@S?a&{@kqOn3US ".S7B%?').%h .z"p[TsX-#.9N;g!hKI>8Cbp_nA"}8Xt )k ?{eNNdʽ9-#vSPIMrR#*" pIj?8 66ҏ{A 6B+%9cBWs_( )!17)#݃X^pMM*|^Ps]zk @Z >j@p' 'cۛgxht#ݟ,fGow1$fy A2Õ |Wp#tLa>~X0tDv2o p^m'P|eBGGC3Ӷ0@R_x*ݎ4+GE<G%=HV?m0@PUzФ?9HȂ +U~I"$6H?kR;O^xxd 1bAI^n1_%aAc )$o_$<#W*N@CJm@~~G]wZ~]tB'23]'!s۹`Pg<4@"$!l$IKvY6R_.PSWƿmQ  IDAT@?sc/[ДFC=R%KBH)' >B@P0N:<;HKE[k#h.prN&ғ}Į}7l7 {m.npT/@P͒$M.B!{:3p6J?j&/ŹoBp͓l >k8TQOl(i\Z퇽}N @7?BWgPi jzS!"i [hfXU lw97J!Хwd6]9( 8@wB=+zF [@XKqirpiG+ ڐQi S[\aX:bq\0:ObhZ@b`NZ%+e4=M9r^pftT (j {S@C~?j9$?E!f DZ0[6s-S`oSX'/j8_Uzz!W_"K`eI !p+ƭFWk?am! !R[C-2N8޾~~8NxS9̀I|AC'R"=S*@xu 4Qkf~dE+a&B=!CS;kՁN!ǀ_I>BŬA7 y!Ğ)'R߿J<0dYZ|Z;k42 qץݽ2E?h^o,:.j7S+fuy xY__4lp {BY0\ō/+t,B@c=HISCNיٙ 7!U͛?uc.1BECx;=vҭae(i7}_A}K  7*fe4h(q-т=1MBLbƍTO91k_E!گd jh>W_ U %`(wOVR_Oi 2 YqEyk,8QhTJ]Fl#kFnÛ+ov0֧BqnP"$  u `k\%{bSBMֱ3 VՂ4;jJ31ٓ_ѦXMB _ӠС-zDbYd/r ``q>H;q"KѿJWK_=y##W_D 0̓Uj. .8$N ?<pKEoS0T`õͭ|\ 8jk^ʉ} c]"?7lӛ5Mo/|OBτlzp|'ٿ=݄0 1=H2 4d L}Ӿ{Ts ha<|:Jc}'3"l\̣փ?7\FrbpK`;$UUJ@cN$ Y0@QTdV܂lfmܟ=|n |O_R~j @5 UQIT feύmjk31P )6?MmNS/ ='Fs!|!c@y g7Z pF |5J" InJ K YC=^6dU@%Ϣ/K{@#}_gORwui &V6 K_*S_@H_ o.2Y2n7tnĵEitX]ڜFq?Z߽JOC)bT `4$UsT 팄vHOo N3-!`i(nشpO9uTׇx@T9 n,yC;; Ђ$j|1q&㽨)6@?w_| ' 7lɨy;v̕@?,p-*qo@=_;"%NX޷bO_tP=$t*_# %cu92!v,}Kp | !2^@`xmK@(J FОz0@@d^ лxټ Y/œΞۯ `JyO33/nߟ&rj.ONx;&(S)Et?KiК܏폥^a^ZH tW@|xiy!6 7geVbVzmo Ce5!S!D@C@ߡ x@-!\A! *@G?'2 6җ;Hq)wR<:zDgBTQ|~,Qg*"F_H?; !? NW wV9$gS!N?=<%C_2Tm/ y!z^Xf9ks97 BhP7Xuٟ-J5P?"_ (?t@C"'߆Ŕ3p#%] '@ vE.嘀X[oM3[hݫ4QBx4ܧXӢx m̨8<%}H"Q BQs>rӰ+ޖ?I_;#!7RUz$h` 20}@VlgC B!Ğ }pg]"J]rVũ=J#+7+) B`TA{`ЦIyj`X% Jo(0\ :04dypc)h: 6@s-_,#_UK_?ҸS}>5`8a] PD`ߞ ̶bb @yT}NLSER6GJ [m̹vR֯ߥ[!q##ׅvqZpr$I=(_>gy/'= -d-j`)!pFZj~3| 5J޼I3P 8XB/ڿgvK{UjߞnrR~V/eYpi3&f|\qdZȴ=KB4@I 0AoA(ɩ>Jbx;:zWZ՛Ixv_ @#T?>w|x8PsxI"W?% H2_qQ cjAy9_K$_D%T;Tm->cDwPhIpIW?qg `J9k6g8%K_V"@y!:YQ M~{| !n/TjW򰱚c dZ@?CLB'\`kQ9'@z+SR0!Xq) !oӚ_8ܿM~}q_E1`g| @q 6Q|\`,U=ԃ~U*U{~-m ~%/b[|OM,r'pXLC` !>%>]9U p E ِ IH$M?o!ٖ>["+"&.Rqa2_?>Os2 A$PUFv$9JA%r?I<3_+bB=Ii kek[WI}?Y"^2n7R3^o]%R̿vem7xR(nݿ&^/]v;0p0J׉_i6&±Mr@O>1Qeэ \їprȷHQ__\ı/ ˲(}T6SKzd,B|fۡĭ'꿅1q8%נ<2Tp{`~Z x>WGqC`1^xb<9KvԷTc%hT>JsiJI79r݀%@@=7x!%9J /mp}k5A*'<펈#.$mRy!d+[t )g<xA(?$Q-wdW@Kݿ@zj`%Pp HzM3[B{~t\jW`)?G1޿\̆qP' tp0 ͯzoz4.+n-} A0Ay^R`%@xg0ZSc U<$*@S»4@<+^vgR |HUN"-! h[f͢6=?'GKJ6 a&?BCV#CVp"_ ޿`zLPՁSӥ5\n񞘿越`Z{ 3+)r|:txY~ѲP!{T d ֌T*@UB@F +IS?IFŤ|0Ϭ\9 z=? ݃ԸwrtW={YxɝDJNW9}FnCu"ǯk{O;\zǃT`H=HEQk_H!`Rr"!Dl_43V~3 ܲ2#C;vԇsfj(W@g;IF@{` iHz7r?I!p,y`O/&t(+ @wDxGBdLz2f 0&eVX!櫗FzbA!= dj! hjLn ܴN}1?b> 1+ V'R9U e?3)х IDAT*4{7ε$2'~UG{ߦBOAPTE%O98$`7?LIC=W*`ߚ6БW%t0p"+AV/.h 9'B6?RRPsmWnA*؟~A:;Ƞ#JMx"p ,X?B!(QOr$,@T:en4llEe}<Ϲ#(hP.B 3nTrTw&Q}f?H#"Pմɪ2FB̝Y猐5<:alw3EybɃ"XOwё/o,<&/C>'m`h{s==@ڽZ0P_{co9?7=Jg拽kM0޻|a ovovK~_}H̾ĸM:}OzR+ݛE)@PA h^QXL Y?8T]$ͨ~b6LJ5|T %m / GdNR~! R9_3g{.oZ!c8՚?qI Мyӛ/14GJ 4}1q!0t& g2Q )W+UH5`w=O=O,d@3|vԿ(xKe_՟V"j;^0< !;N7=HB-U 1jOθߝvV=_#嫥JSZ,?e) ڠ_ ^wtXߴzOwY'-Ryf[d~w }lkM~dFBc|='G jAmeǯ}.?lmWi $>+D.B.E:1 =!^^qmNR%P {B< _~a?o^d1~a'ULY7# ډE!eݴF'B?jWJw<.egD (F753ڳQk<h#S_SG?? wq.5)ӴO#`tg+`m0-mX=/ L' ~#q9 bR;_Тoxv h 7RW9K:l~q; i..OBL,_N\{}troxͼ·(W} `PJ;r@OWO1^~QoJ>YP QaP{T `6)Ԋy,Rƿx9<oVR@ _= JWggw{_ CKcdCB6z>[d@`C;7@(% Ђ86S`Nʅq>@wFi%ST23./UG Q76nu +ԫq~MU$E^s)|P\ hWI$ }IȵNo]ODTDԦ/Nd7+foD|݀3;}~\/.BMDO:3;Ȩ޿E`d0g5vB|ʟ{^|+R9Jd ùXIPK{6Vd~@%n3F! hWHz .;]ҿ+I^0RH ~_ ϾEKl{xUW'\_I%T'ox^Zn᧝&?ҏ;{W4^י[?s&?!"]tgҬU^+,:TSȏf?=R۳=O0b>JԣWͦ{ʩg ZIGjD3`&Ĭ嶹gK_Q=2?g :*o '+l.uIcy埓;ϗY|<P»A*h.V= XYH*o?'U/f - t"#7|Kq*@t.;[Z׆orO쓯stE&7ZFr׬~55|~8J5) UmѬoQEQJ5礜@|:MCRAgyy`DM"ޟ$z,Qh 2?>_?<߈@/}@q!cvj?RG`mS_\SZ P.zf-*2 xE/!2=+gj`;NQ& fq/~Q⏠^(me\lk,95{%~`9 OI16@F2LmBGڞsC󻒜X~R(=bY*͎}tEOS3 e~1? DZ텬|~|~ 3$~~Bw0g6mDI}x[?E8.!ݷMƇ !drohObI.G6L|tcm$r6>7`H <!#A'-į9&|ful\j> p{nuq_%@ {5xb'"607sKTq*@?~N.NZ@=Hw-\%.𐢑Pe96㵜ʉ(%$ 荜|_,S_`KÎ"Uf?.6OV!i7aES z?UJTM&W[/o'#;F6֩l؋;EQBɽ6_ x~U, 4hb+Mt2JH'^o;껤JgJ&RJZvE͖ks`*m~Q-03-{MC*@9TQ tnhڎ\\Y`fx&H_%?3U*o$ ޿?~?amg~W˞ ]Vzf G\1m&lBiiKM?VT?[89Jv-fB1/;+:=Y[K?^-֣1`_S~MQs#K8:LPx˧~_RTAд yyđT'OdZtHitpwL(b=A?&rb(qt~l!|ar}Kr}O9"tQ<,4Fy]2>r==Kf! S-wR9g 5zubh'ߨ.+'[5j,.8 =Ȫ>'zeB)aQJE~"]B0=OU9 ^M dI?aK;}y3f3?s($!ZG1eH( K oy|L*4XTv@dݻ,+<@ݜOC= 7N ͏]a4 Z^Z*X*Tl `5Bd<t}iL]lU@4x-JK:?xϮR) lYL)vION^BT n=Ix@|I|Q]BE @"CߦA:zѹ`%@)*QuTO{8lק3('^Il?yS! fmȢmn!"d@<Уps;i87nqR8j󾏻Zf6c+uf;ىAj .ڔ@{{Ͽ{2+1i'KS M43dHwbfƋUr@N֯t6뛊}UZWͿ)+o p 5X)> 3b,.pjl/MĨ{) o6o69Y[VОXt^GߌRzQQEx>C +(\'Ӗ35[;`|уdP^H#{o|$dKY,\˵B:o@~ xARu*yIf>_h d@<{=S+5.STw0:#{ * 2f7?8r7DRAT/$r2$젟"!$qy ߃z|0O:y(IV'+p6Ob  K:EwTէ6A6lջ`P1S_0:o 0x{,DLzQaEtٰשt 8!Ҡ@061abDx TfV 1ϮҗwRVeԦP>+cvgNj Wj{7V͘Z1L'+<|V:e`}(vޞU~KݵY *ACPzfŹ&@-{v70lWXe0?2z,C@kٙzk 0Nik<쮥wL!߈pr96%z-!V5X0pH+K̿1]8\1=Z@+G#ЙUǁz# b@4=q7PjȠKg[=:q tT1@`׬o+Xfy?' `blj^U_O@3Ȑ? {deQ MԲUԄ\Ǯõ}_U/?@/PO=_y#ˋ[}&.'3+2Fِyi3ԃ; Dc_#-=$џUG[c@xx^g}S\^Tg+g+\S߇BC;f`=:Gc !'_j]HڿR͖9IOar?|Fn*@B?XU̡O>|B {ˊC}xtoIѐ$ИFWFkR>IPfc"'"z]?8yT2ng.MG7zjJ٥݄̒wv\E M 0Nb㶵53JVvX_El@ת:bU6owT?SgE苪 -Jdи+)j)ACځf,-K6 2$'oٯ֜[W>,5S;Im^xںpIrFZv+wIs ( 8;Hտ@܉F5LGUy9;7tWC)ٶy&`-߼:MEY5R@OM߸m |˿*FPZJN/ Յ*${(@ km3^I  M>?MΟ;B։pQo 7*83r_*t6@ 4![GķHFJ`ό IDAT 9{;&ﺨ1:4-Ci HGMs;tX~K=ujL,z 2ڟK]*@a3Qw|L^oB oB2+T p^YxѰpMωN>] 0PعJ#n,/cpM7 T'柞O.sabOT .Rg ;BP {04)*^ Pb7&9/ h@i兏z}_F!_o%o *}jά1 O>'O_$U0 ޝE~lP<4F_$Bv4P%X- {#3)_>݀K(k Y~ nb1"c^%NQJ=rUŋ > {[- D`o/U'Q|3[Rj@R[6E0e3M%:VT 0?@|t,cUw[R{emU_3sVoJsA8{cL(+K*y*|:I4@9&t,Hr1}~,o6?@D*o hѬc$@E45Rv`)(3ɁTQԀ={[UAvnmhbp 4Nn*@ͿrYA$gW"@JuRWl*5:꿞 F~AaMJ x}q{~J+faVYbluBrd.)FvQdE^n~Jȣhf4so7=Vb%ه>o61Z#l>|dl}PV>(mK%OFKKMg{v'=]LqIV] r٣$- dcD`U nb^@]bC '΃F |||'] fh5G/x!W0bкy`?^x{{z Z5QDZ=?{x"Yhgl@KwAql`@J uE_J?pEai?Ͻx٦? ]qXҁ\T?zҬpbzJm}^B:qp-mzlVp~hpط0up02~ m2LN N? {װ2c{@]Jw@;,o7W;^~ t8Qw*| j(Yrrۣ &;\~`.]zImMJЯ5[m0r}^kS:xIW](9Zp֋Kn`$yzV  ?'*gBjT8#vWha^G}ŏCIL'|?P>˘5ʧ^ :۠5^zw, (\-"L+ dյӳB4 u}.3¦w cj`]i\fyM?Op'ܿloxH3ۯ6?^n u@j-_ `v@/Ķ Ω?FtUVkF@~.@Jw]z)P}֕Ⱦ˺閂A"Yn_ P]v uwq:8DKYXm@/pp{UD4sw߮mvG mwTͅ}p8+S;vtiSY>.V]&c@'vZ?&lJPSaW~@Te+>BKoT'mo `J; itKC<26?Oä3X4c@@?f I"qw+َv(WN,Y~~|l@7@氺t>tBʿ>]hqBv;$&6OX(? ,] Z i.@t(wz3QmB-[zQŃR߾=7-n+1 c;l)\ !ßB!Zf"O.vhǭ?<]^O #eTg-`[,=X^οc5{FRg@]8+G{ڙ1\B(1.>/|p|F%Y1Z0`ϛɛMހmۢV ǖ空9eLPOoOp;E԰Q~׿jSҔ?(9<9^/AYKK%`@/v_]{i/o&_v麻H,#_̮O#zAI?K]}Z䇟{u;B^'/tKOӳx9J 1~MX:Wvpq/\[T.k՟6jkwIQ9.O7Y~./}(%2w]l62@*P>dS--"/I˿>h! s ??My5o#M+9ul*=[u[w.KG{.??]WO27C{Ö8[IYFQIRhlK'O?'_ɽN5^ ُ{iD-T 8؊Q7B w,V5 a+p^hg?׌~OB'b a5Uu\Rq{~jMHC4Y_?W}i= KwE}T3.:ܟPP!!0B / ~Z_HSo`P D$@ٿ.0W&vi?5a&0,as@:Yu?,5~NO 0D8>Oe_Ӹqc]Oo&7 `l_F 5`OJ&k"3{](,~W(&s;,.bŽi6‰ou΀7@_ic,0>vvh_]7꩹v?ZXjҔt*@$96h< ]HwT8rs/^~>Ǿ?wKο^GkPˍ~8 j]L},Q Dk@- 쯺>;>-ˌ<s @*@2p`5\w'/O{ptمs.{~__N/O;XN˸ kuF"k,oMfjQx'9cKg_{_ӿ|5-~}x_;B ^G>9 >+rMxMp~^??rQ@mڑ}- 4Z_8s'Kk d?=gj ߓ;@䍀*糒{,\< ! Uf:kx]i'.@mWa?0;;=pJ Ԭ.ԟ+^_'2>opvßBJoQhNj_Xmi;7ŀ~)03R5| ^XLRFVxvsFTJ@E Řߚߞ)}$Z2j!ӯPszp{Us@]hZN`%JϿ}: jpsr''y@{zc Ïpp$(@# ^xHEJki:AB ݜl_Tܵ? ru*2@!=^Izq5B h ID7dη/իWH֮pQS9֯:UJk)N?近0T/5ҋPN_-: Pٝ$p{ŇiTՉz;sy7 T>h$?LUhCc(k'iM<_Ch^hX[U>6E` &̢PyvMɳ~~nj{y'?~z2ǫ^80I8a%@`@Qj?u.'rHhןG;6%y7.@M2)OM[hB Wp5羟9nփC fδ} 79to{!_gѷ S kCn% - +/V"* : WَS{!jCa@>b:|ČZ0 aB/?]j.C:|>5WV `|:-&d|:ujWȂKN?dCkj!_f @Vue ,<>ɲH^@JAW(Cg85O} x#5mW  C_C ! RxBߌt F.lG9>0SR6uJ4-.SH Qb#ٝd|_%s+i7\K= !? >Â5 ,|EWE^E!})-9m S@XWk/Bx5SO%}^Q1z̟,_E?ݸܸ1q-f2O7Cɇkכ.[g_$\/lҏ/o&GK.d{Tv `;.~}=\9r{)0MC/l7d;"9yG!mb[{LPu' !(L}b+0m u"=a\puo @gk#C/ïI}~ŗc.q%K2j3  h^ W/0to?/2@{j@~5QH$^D XC9.ǯ.`OyfF@5b `U pBs -. UGU]-ӣy} εDZx._!(8g)竹ʢ ,˿$_ݱ2dZk.Gq.._|m1š_ W?-9kq#;p?yٯٯ-hR'?'G?BY($.z_ٌW#Yp= ֝z [ =WG_S+Oy>ҝSÇ_M3U;U e~O7Qx4@lOT>g7v\0 H@)/{z+"n]}/pOO|?ۯ6cNHWy%ZX }:Pg@ך-[z_{X~OB_}ꏅNJ#{^2%x㧡psϛkFc9;EҴ?] p NCYDOt27YZ$Te~ {ӅD@#}^kMc7W@Ə{ϏvkrҰ2g ׆—T~.~nKs^wW6YNXt+f׺;\V 0GW>8)__sn[(`0xQuHvg ,S,Xt- ]�&{F`?ў*t#F^{ yk1%|n>-Hah8=S'ٵ @a/&2d/]w%g<WV٨/y7?6;G&W9A+?%釻`#'Ӥu+N_7ONBM;lz6WbjC[_bF0Y 6@DQZ7so^SҽB8|^?`%vܿ23580G_+o0s `Q@Xt`W=q/y("OKILgfy]rnUR/.y.I?<~rT}!k)_uB''X&=lUv5qwIo П7\%4TiG9CW  Z] |uuV翥54YR%` ۓ2*} 6|᠊hYkޒg],h됥 χ_K>'?Ojim{~[^V="N?_/ ;r`ղ*i?ਨ&]濝/p-&ȇk @MlOZ^\ȮPOק@jgW=kcC\G`>?4YLH=̧y?P_: @/8 w Bz/8W麻 Htuu&aw:tooluLk:7Z|KBnP()_l3b^&`/!X0`b?SLm?xf}e`dC+7Xxs}vǮ |~ln?p3;c PU+4^JZ9~]ƻ.˻! (,>\XPG/f7>ܤ_I,F~(|T$lML?5E ^`a^aB^x᛽C=k4?wWGͲy׿6 VHp<[q0@_}9g&eM^@&WKg<3\/n pB px VC0˭y-J?bN ˲wd<>#Y/o*}@?ɩ=3QNl5\^i c]T"`rO?Oҏ퀞|' 6߅i_H ~\^8g֩N(@2@o ir.ϧ{eFŀj3_Ŕ_F*V_;55g?\m?B_C tթ/<,i;Fט߃)ƛ(1_$' *>O?O WlkO~@C'yܚ 5 Hϵ_U%`! hQl+ykPTVSZ8|.C_q ]]`0 Ar=(D~- "V( _:5¦Cq`֖ҍwɴ̻oڎX0tgX _mkU_o6?M4K%}0e. Ϛ]@nޜP!js, ?F`1k@ Y/RIK;'f_͟JwS$hq^}[8.Kk i7yP%5V|jpb bs>Tۿ(uP0ߦ Sg&aU6TMZ?> yz]讓՗,Ugj6 VWj =bj?޿M3j]6&;[=Mn?G>D̬P@^} ~B>tk ?y' _J/)eT3(캈l195Ád( e߄[ H5|/ydpGWsp0.̋1{}`P$GO~p J;&ٷCn&_@oHfQ|jUg;BZ?h n{byw눮Z*";5m@tv?oS?>g afMAt:=Mґ]ukr} !vF -{o:N4d?"]M-KQ\T5B_R&=˟—1ٝ$p[&Zdb}60XWZ"Q|pw p럖ςH`3~zM/OjHl }_K ?mf޸{uw*@$^\zy:fee.h5D5Wtk&#k!8vafw 0;\妹El>@.{܊_|kإ?@/JWɻɻ_FH2/IwPMB&]GT'هn՞puO uGH?UJ~3ꏳ|]k8Y~Y_O{"5\Pru[:N },GOdl}Oh9 hL?g)ܜk<>YHB_w(=OS@@ܿ{m&8X?CTl /I kS4z}J(Ơ660rHM??vw[RG\'1;O+X `l`{:J Phg3:xvz.~P;n~ᮈ^/X>/lHuF&OOV UZhewM!_Pܗk1[&j?| =;&po% P1n@jY۾~vJ Ћ;x~_{+t+}|/g8j l4LÀ_`(&N~a iXL[vyzB@5mT>? 燫8LνPK?y $adyS >g-qˢ+5B8*s{3n!H xfso.|f;:pgo3''>M!{K;=eN !!ʯ6,, SQsw'D?LDdp/`=|,fT `>F4pt[p|1\K8rP0o6X϶].Va[@_MY1рeژ` 0`F?5P `*xXQ-G`8''2b(l=6{?NTvz&εB| f~ ,"}`ofp%[ /4_dL<C,҇zqms#-&l~Gtu{Y[<"r` 6ot?o8֕@|h 0cB]ޛ MG<]oS^F/0''_oN07Kg4g@M]p'!y=֫Yx&lG=ܛ7ʇg-;h̗{WBWfD HVbgg_b[Pa~Vh`y!Oycx{? /0/~QzI驧+AzoKB{x.;T?UМGz3 7?y%_@@06g,ԧbKs-qK{H0?@lz]-/O'ױPHH,2('qp;f7hvsQw 0 ~<|6/@?v$a] ] uyW~u@p>li %l IDAT`vRש'O7ٌ{^9lX;G kWy<ϧ-Žf5?Ds4=+TZ!A 01xqm 4=\HAIh6GmػiX0ZKF͵g~_Wp;ÉBc2[\̴ `6]_S$1@< LU ڗrG>9s7b('-` rCw3"`|ԺO&m 5PkE2X :@lBI?|ˠ~R'ѼOYRhf@]u2x,m,L]wzLgH ŵFǷۍ`a]dZ|B,?o6K$i? jWX a s]R>Sg1$zݤ PC8NJ9J8Iߋ =;+Lיh5ݧ /N u?#{ז D'9գr:*$vq[f?y;TtiY^ hx^(M^@> Ћ>jΜs 9$#:X?nBn#u_o(`*|ޅE"KO?CNRP4{8Vܼ? k+,&lp-ǦGmTuyWχ5{REl@!ONAٚ>xkG-v4w8>o@ G nw{ l7@(VDuX?+ -5LR0IT? dWoHo#iLO `TYXT"רH`@rZ*;YKU:"xwᓣQor'nJkP˳+φ9s'Q`rjݿPjl9g,Į?p(nԂPQ ,&`yK[ێv;pQٛ?A *);P\@ s.dT5+t-tqT\ m @PAld hHmG'n{lE5g-B 0}˷k I>.i4XɓKu^6({=DK`0@UQ Ы׹e*>\`f0urv+>> 'G~]3v(9v7J 'Uk!xo!4oYLGuX[?H7fҳo!>ދkKI8%}if"_Sn@vGp>O _+Z!p]Z#m xDt]滍5e|t~fZG-fLbg@>ʯ@<R+4@> >y_!⯈w|VH,2JfR3ϗD}ؗCŵ,"T?f3`aFK_!1JY@=o7GkEsp^~K5z{Bx7}3z@?Wԙy@'gIoJ@P jfG3Kr({uY*O~OlF- EyH(~ 7xx{$pw׮Bt~n~?r>]^&B8 |MϘ*K3.k!x|l 7`^v/MKϬ~L8ڛ_o>R$[ϒ[ZPHe|%/F2o7- OB/Xk"ӯ ЏW`;8L4ؒ HX?u` +4tm/.p1(y; a5o,at(hWG׏>7ˎ! NO`wP \ kSm7nEV$0-屜vbNCB`ն[ӧ,[I j`+h㉯`eOQ2?Z?!?lSi,bɂZ$5mj0lX-Ɣ?Y\B;XFkǏ:ueФ@SۛS?@[)w%`Q S Vu6hbV#417ڤm`=?(@>qPe+wx, GPP.)@MkcٻH.%f=:}>*=&c?zmŋk:] лk!u Z@pq5?0>(jOi]@4|_Kb+5&twɋkO%kN<2+^Ҭ 3u?)<7hyrzvd;`ص̿?C6,o W@U(k_} X `^\th@̓Q.?o$3/~q̉py2PXa@7SXJ2Rw%/m-V@~MB?D'?ux98-`l2Wt Z+O'?=z{N_@v0se5`,43愀4K>KG)Pq;34eλŵMziK Yog[c-[ D躻mO<.U3f,T`;`RR.f X ;`` 1X j`^u 3``'mX ;````>q vsKq`BX8eVHH t`*v]mgF~ῠ"ВbfP,B,ߔ2̔@@y/YSMoW֟")2UAK &۱~zJ 5P%^_w ?E!bv-o`!h~;7Kf 3~Nv!gQ!ߛ~,J)r YQĿ#?̋?0; 0?LM/ -gX}d,\!>%[ o_J 8TvX Pwį\wAS(C5jי(}zs+ai^9:"IbՏrp#8U/~m#{/s^IϽEԡ].rCǖz_FQ4Cn.K~DU/Wj/!ZpOj 07`④+G|xN貨hUsׂػ*v.;3VTΩD^Ǒ0K]`<Ӕ /pX=~u #F`<ӂFA=m9ޘ"x;>Ӻ 7ux^<O=`wzM (3?~,Z(@o o1⥖8# I`]t$CɌ|.r_d vQU2\ڢٚt CiͧC1@@ӧFObgV /-Vw(zڻ.d6Q6MYv<5<ͶxtFV@oܱi;IPz #nwcm"9P&t~M(:`1/rs5!ޯȦ"; ;5;Ljlw _~%2摌JWKU5zhv~E+8/LuŃ `ͯ2[ŎCu|m*qo#xD [G>q:pUxpWY\)- ~90EXv8Cr70#.:`/-Fon{:コK/Ϝ wyxRh{e?,u=@XF3qޠ;$?qmՏz5Cנj Nśý (:r<?5D!V(o=7 c&mj*⟅z3#S`G1S`]j.TjmqR߸4 p2Ќ(woǭ@@J:H巃ܖEC,e]ǦpQb,.x 0'{ w.ōJG=lg|sF+%m v1C8(`ZM>ȳ=p Sm _eJl3u@l%[03s3C]~8xQ`f 2!5}+gh6 *X7(Ӛ6vo<6mgV+`O՞*m{}v+hs`'KB_NO/3/l$U4x[?;5zV|o/;\>wy?-˱QxͿ}%k旴kSU1C=1'׫UO,*t+YDX]z/egWRNk{ 6h J5*yˬMtg7eRb?Vz)9gbw ;GUh O`_8NUQZ +O-kY1>p4`9%Ns]֐,o9H_꯼`ZxΣl<RiG2ss7a:$ H32k W%M#W|acrW~b  ȯ4|A:}Ժ8޶7ps gv!y;ML9uۜ_ G }8O{:x<2p߾=LSX@ #nN N-x'!p}f7p^|X#VcKYk ^3X`%`%H@`9; yWٞ 1R4ZgQ!,2SO#:m'0*?ҡvsIDATO>H+ ع?eRџv}w(HNs4`{\跺ĥ,;g04vOs9ma Z`]7 ^43R'+2bVn7 <`T3/6z~-zޙ`z@J0cn>[>0&7Q4<=R'n1@eor17Rax(r?:hL>TmT<(|Yn^X+{D2 Ɔͪ@z؃3rr8o/#{2I'NzO _h8>z <bK :h{7X6ɭb7>n5˅ W$˅6 uQ( IO7oX^}񆙵\*G q*Dg5 Xq\ ;CVJ`q Q5 @*V5 $w$V@]>*R}`+IoC奄bDc/B-OEzgt& %S  F.? SʛWW7_֎")BIܢ4hC rPI`;XE 88%b8”bP|,E\84`́ 1'2T7,56XB LQC@&VH#H6~$ZRy"I`L6?Eݶ$zdhJ-~Q04"Ph+@cOw@ OQBr=I91 åorw <`-cjxv ?~z @e HI 4큆 { &E+Jಟ:ۢs+6t  H4 H4 H4 H4 H4 H4 ٳAIENDB`qmapshack-1.5.1/src/pics/splash.png000644 001750 000144 00001337601 12527654570 020234 0ustar00oeichlerusers000000 000000 PNG  IHDRXr5sBIT|d pHYs tEXtSoftwarewww.inkscape.org< IDATx|K-I-<^y 1"DA!¥QU}T~ͬy \\࿁pQ[@qp\>p i6P-<[*\"z\zlVT]T f;}ZCܭ…hj&(-nQjUeΛ|D~)@pи FU4o|;{fBr់^L6(t-3> n~[FJyƃΙ^ˎ~s-v܌]ocۆG ujx-#=mvF\3.h{kZ[Ykz^?qzpִ[d+_X+FY[52[ˈzY!L_jTI}y8K9e, *3+zG>F>W͸GB\'YnEuz57MZ7qeWÞ)E"u˿y`{yt~IA:R&.QaM;gdV1iy_8,~o]kju:]׃π^R~~MM쬺P8ug-4 Ia(1 ?(9$k/Œx Fw )`jp(Ys}B v@b* .^ ~*Sϒ-+,X"FzA@ *U>.H(}}mw+=LQ &7%pdIdE2|MYРr+ci:fĊ"DPb_!.Bs^O)32A%B`40>K5iѣ4~~nã |l{Rh*B]guҌAwq8>=n5bCIM':_4R p}i|mLǖŋcCco 7h,hqq7 qAxe zh!#V^Db9 ec1K^(@sXRuȉYjl_Z5]Fw. OuI@uB'dB/!}l+i31Uc>`-$<8FG6RD@?#w "2Pr*!(t Pj;ub rWόe4%*%g8Qx=i]:Byb1bjO קC#jS  (bcF9^DYO;O^탮Q4]oYBiYba  loEѽzb%BI.C]F4CK##_t͔KיQݏ! l#;<ˬOfi&GA(téqm/ .o"֠0 cTKе~(/6V{2HSecd=ɥTk}`V2Ru3 .^:wd[LTR@)NC L pNԣ(( fEo )GUtPHڊ))b"=. *b#AoFwl ͍/0>^6P{s9y0C~i50S Om5)N^/q ;I*TF)Ew$e?~85$ԡtwlI+@QZgalӷ+:QxIϥl*F20f`ǓB.;䥒v_r.oX QEietƳcc$l941:IN/}a)Rŕ$3 8Kv$vD#K6p. JXHԈI6\d< ˃h( ̚uqÉ'BRhcׅ ]51rti\d$%g̝,^'4xWhe:D36CTI!I²r&ww'N\xP}0q /.vLf@k)am"ܧ[& F '|JuO ܇0*~jef?֚GoO5Uํ1z<#~w1Uy<FQo,( /Է0SZ;9(ծmϤ.Yv'x‹YgCqq `vʭuEqH?^3[>.qV2+Ic*/p-#oxatj{JբyLGJ@ H\˃Gn+8@(2+`%:ȡӺSWGI;}u`ykUiVuZ%'bc |*nxѺQm'0ЉGϗAQH?  `䠎hU;EINLY7Jv9P6XZXi ɉe1jbT`#]45V-ܬM5yjD>HGGLYZPq:rDvAi( SgM(bM\Ȕiޔ[$;!%# ]#&N<'Dh(9Z,+O#ۗs_-+B£KtL.S7~Fo:`kP5D~T)A>Yg(MܚB1(: xya$\0 Lep?$ ;KYDc/SFJ.;Yʫjқat#3B@+ p(NZA 8=0-(6BA͔C!:ȁŤMY:["romsKwнh0ᨀ VhՂi,21PV;υ5"SNǿ5UߩeBצA;ZG4ʳi#Iߝ?Pnc)]?f_WZlEu:6Rh8t Xѽ]y,=qd]΍Y&7~&f$(hQJ,ue)r?8d|+ŐXQ!f/=kt{CE,2( Τ8ٓד4f| i/'?VZ¼;yf\\ eU"6c IJBVa ídې p) 姮-F|j,RL +2VRzV: 7G!!Lg~ݱ)AM;hb[~?+Q̨4]ol'bG3ﻔs(9o*P22&{82OJLQ=fx$YX] uF(N[\HI`Zd}цhL[ijؑIy(˙8gCLV|+,옶#5)Ж47(1t) hKwM;IKgS<҅CD35_ck{>ky|_H~R[2\'—`uM9l(h2DG9 %;mt@C)oPB花 Y `@Z޲.nlaťDw{F;+k#O qbp s~З|X[[hT mhkL 6wrԳ w:zn J-#6ϳf9% SQt 0(rlE3I *RUVL$%$3d?-0RU!؀tS{jKU]3~g 3|4I i2g(-Ftۤi׼l0Sz\oj=h&1jmg'١Vn=Lѫ o?q/Ow( J3:<HKlO,pv@yvYqe]GiȉLҎH-~{2*AGYJS`Z>v̤x敩C8Q5 f]MhXw7*X‡-Ԧ{K3y}QvM]Zmlr+zRP2xż g|fU68at,h qޔY b$8;L]~lxTUއϱ0apo^ĭ?50y0SzykQ0&~Z缣0ᨴKS`O<X Z9Ł]#d6wҐe%Q{O,%}<,U Exdīr JPF}Rg% )*C S*isA|^yv37R,kֳ1Yh++8fۑ}R|ܢ=@LHi=ҏ;RVQAw__"C5d'^ԙ槧14Ș~g)TB-Bv0o_Ŭgy-{4O7\kDzimasi1?y^vq׊6)0c$-Em,HM}̩3X"&j9&CΟ o<# :?3ӱaE*`l4ߦa% -K!3x|0R4ɹ'exgQp?#,p&vl- J=;kcDe؅('{M2_yëtъd59λE娗-1 6~0-: 3mƒWϷ2:j#Vd ȐD,l*rV^Ƈ =JoM1'2JI3Ғ6[sq)c<.JC.C|讀;V@*E!vp2OuosQ O髙+?VC᫋47.gt\P~yټ@Ay{'(~7G6C F8ULs|4#P'بl !b҂-#I؉vqUsjk*S8O0Lh- ׵EY@1ecdH#tNF4M/BﯡI[aXyS]Ձ|yk.S1uKkSBƢ کH/Ȁp5R$Px-`VfE'zdbr  a2r*uR 4vrgSRrG`[u|\7Pn-F t/ dwTVIG٥Xw;>닶G \̺ <5{ -FL}(ȻAݪ)pf(*2G33E;^6 GQbُ|ٲmVT<J60%ra2 |#ɵ[\jL=jБ-Na g%5Nь:o5/d+_YaD">F"ڃO$f[B]?;LI\9vvKQל䮘4/ p7ùc0`E8whEXݏ IEq>Ph{G{82|R>veVPgVZ#(/ĉGЖ=Y)&5nEHtMYj`l-Eס,OTHXw*)nu=t w)䁘&08S?zJʭf~h}j ]2,3EI9.]UF#> ъH [+i&0GO k"*:roF'̋ V֏5wȮE?OKQZTv+J9QY%2S)'Ѱ$"?8Xc\?.{ڼ!G?;ܧQ{n#eȱ6_I5|k\g4I*-ZXaP|a18 8o:,%^fl!߿O"Z BE!rqwԂZI4 S+U/TւdT Ӈ)QE补%7Ţ{K|x_uANI9`J?R6.o9ْ҆*J/5_w)s@E;"j=yiVۖ)$ʡ `c0%]6PaOO;(8 L)#<&1Z)8 2֥gQFz2smPC .s_:0DR(rޑ*)r n#f._J_ $\8NkRd^6RdM,1@g ]_$k|ZmcL? Ф>!rNq?аq%yN;;h]IhQ':]+8"R\;uF /f> ,981E؇}iwtl@OgZ#*QLDm{=ϑ2(^iSYIfy(yVVZ3q/sIXc9X0cS|zƃ:K:;]';ҥ#TP3MtGeRT:PS*nnfwJ#q'HӤ\QWsiZls,uqghqP0@aSJycvsyAW2p^WTcoEiPZb ~i! ,/QkŁ׭ …7i\'g\-u?}6Xbn'Z0f">i)ge\<|+S;=eF,y=dxxEBLFNx{@ 3OOXU ^x*тwX,zJ+t{?Eb?C:ecx|uVڍhNd;'.+*' `[Ig,CV_ ~L a< k{Qnk2t IW`^\6+Ԙ_}&zp T>a1vCbQw8MZ6WxߍV{;]D'2cKG+Ϩ em@6%..P2۱@ZGi:ƎC\Ok`ԩh$v~rשU=[Evn9EtƎLl,^qvTfO}@.mFkLɄjZt}_oWBOU[ye}KNu D#-mrkgiq9Q[CNVEpZ7GPi7lN2D Z c⋔ |4f2iG lfjhXm#nG7,Q(CWϝă3Ib`\<5@eI\4;oE;5&腣i3}􁉒{xTtڌ\t=`qU5FpQ?ghjKW %rQ k;YMhűN z}cC`T=~.Bq9+f$.=\;O,^xʹ׻3+1N>~s+Jc!q(sCCKnpWNhqk w3H'&.|gV(Xi)#pa^24K"Ec.PVVچcS@6sQHjǪiQauUr"@+`!%P-Q+ϭ 6 m F%6e>o=Ez ;]*1TA!93brE6pt&+,!FMiZ* pAG lXD:?Gyэ`yL?O(hbQD0J?6@XʹPʅk|0Mjm LuRM}d(}ib@g v7^E|]PRaE~'[}8(΀_]ڴ$Z&a|Em5azE5ˣ5ړ7Ϫ 6KK4U:ҷor*hPph>4zVA׊ pt>@$Ņb<+,ņzu9:?yԤ7 zQ\JEi^f{xXzBiPT͒[Σ}:A]-B+ #k`Sއ^dʵv$Cr(yfnvv m.ͨG<@] 9ue|)J#FF?Z+"ZVYXOy,d[VZ L]YN0h6'*`VQ#{ ZJ8cg~z%.=OC(.EQ0՚NHJ\XdCLOu[}:F]㒄+.;ǿIsTtDABYDeϸm EUʱ[;'0Q[xӳ V! b '*66QT{a*|P}FaDs{ڿ0(ԵŨj'|&w?H) X.v鑟1]~{4i(2WP\fِcY-e c}Нvn(P"Xa'@(fDi6%" lt)2z5p{uy2S#>sw^ z q.Gy<p u}>Ixj qMƟ~ &׊=D@):bR#9/>i^ZQL hajUR3s|N%#XKR2ШAFl5#[Hym6{AOtG1B>ɞ'S96sq5A1TӢ>üOU`E;F~.?CE@)I+`āg1:SҦމ n ,!vywZRAo@;)H8o`^kW*x]~?qePpWF0ަI gЬu ۠8^CT"h'a+)sG22N4Y<Ŵ S*4b%+TetZ\ҾzХu5bp'\4(U" XQ{,h.ɭו86auCl薶DJoD2AawҁRUAOFwOgd* ^qs4=—A\ JJ֪CѩMe.r̭%  ~,ovMKF>v1Og6 <բ)\Y3WL&#`gb zb;Q^}B\dN FeX?R 9L(# BԷ؃|C3sĨ;H5o- -7+9P aM[2?ѵ/ӗ?w!zcuA=682g{Zo,"ȩAkqjC rݿSr8}^] תhUo~KCX!R}Oc? Bm̶c=K\ BlaaQ(GH(pʍ-Q 7s_QSy?5oqCFͥCuwn,4 ۸gϳzV( !I.EyDfkY*o[G R5ET㣂I1 o|(kCULBÄ &vSLɶ2Jl :tgZ@) o7%T' K٠aҖ2bWl^i 2lQV1χjL|* y'H^yka3OVheb y9P@wD1Y` ʥ]!|SF6AI[fE!rwx JIBqMk{kf;.tFx~$E^'q*&%}g|99EnN,_Fa:S5{ϜUT}m2z=g[g a'%0pm]O׻PKPN2sr8LXA-MT9PT<}r񵇾iQp( TL1۵q9k@P,ퟎ\L#4y{kD/qT눛I̔HVnd1mZ1XzwRn4NZH^8SOsA?K J< u#K5DdvT̈́tEJ@ks䒑x*UviF|иuLԲR@R,-Q.MF4.t%[918ӳm1\+d0JsܫW+) [;Nrj'RF8t&md1]kBaٍ]<49eM ?rV%ۢRkռRKןyl7}#n66z^ĵ_v'ZctR:W󳔔@֋Qnh ]g74.JΧy>qx;2.^{xo_XPgs~Pt&_=@W*Μgy4Sx`#\45LJM:*y!hOsYdws<#Ck;AjlM5RqW{比H StT$ fn>dܩ6@a H ej GP9Ő%2$H>'!I'(ei!2BE;aX>Y2g.X ZV;qKE~w\-5ƔZS(fW#/SgywK:&z2HFp5㤰.OƘn3ymC#O0r)bz pj~Pͥ.=MJ(dNOa;)oď#P1,\TsV2Fm}"f={IvE~]DUV:^G4I~NqjY<ء}咇*CFhhU/|wY龎 {FL4j}1T&lG(MƘ2ӭ|HSdg׎尪xC}4~CW`Q$.ow;YzӮFG*pmZ t|u?ɷKB6ݵSsg>`fZUhjR+FErY`O4>t=gt(Ec.g![؋d#gw3!gtV2A(Q Qh¬hrM:a=kѤUG;Zx֫~36,i(#6}u3%Dp%gRhQJ ea^<6 IDATFtR?:VEFUp *%є}3' kQDbGCfR3o!9mßԥv)*NA˔VNdZ4җƝUĝ"?xAM(dvM~yte!A?6ަo|(]s;D$//9unlWO6~4Egb zյs \x sV*rć] ;'C0̏E*theN{ź/ ˳Æȳ5.IQe63B"[!Zd$JY_g%ȊQ  qz-3OхEO-Du׹ٲ*MbZ"U'm$R1ٰiG&}]rR/¡M!^[4UUPm UtG84Ͱk7" fPۀ@k\Z%ϵ"`sEԢv&䛿jqP'%r=/ڦZ*uٽc.Z-`I\ UAV#Q2 A9!cJ0F'p_ˆpjRIk^vy8I&[kdZ78B]mMZ__/VQjYF]1I:kTBA#r>B/Y;aUS'2}|m,tsklYLƕ QG;9?bs|zj' %o!&Ԓۃ1 |ɡ=*9 yh->\'>Vb>?s&AQep0Aڂ D o<^r9XGߗ>5ԻÀ 3w(LȚN7~zqe1G~|oƁhYPzc+"WH5'|AqƬBBE=c2UJ?R ҮVvJ1˫?jՊ((rUѤmq 5Gܕ򚿇<B`D Nڳ\,7!#,Ch#lQPz+jޅTƿN z.޻&ͭV=/9Fa~͸%? UWqC1;ReJJ|hS6WAi*H?^6kO[6e%"ңʩ`v3: dMP{m;x4!ztE7cm̩eigeu"-v#`p1֫=L'wx_:j Irܸ~v-nTMLPaпs36h)wB1'wWNrfQd*R Yy{*4^n|+GgÜ?h6\8 euB|zxNRYتrq 2miE<,O%x?ic8A`L6C?{Ѡ0*x>+ oenF]D f8,hy}:nPԹ1u4 Õ`%d=GƓq6`'3&EfCC'AnfmŸ2qXo3_jsՋlQm꧀7JfUߪgbE&b8gmfljoU1_Fϛݼ>Hgh(vXΗJϔS pL,Ct;hj&{w_/'5Fړ3~b \Su\ymq'eE1tIFOI\[%᧴'ѨڞS7\:QqTs&_ZGĶѤ\_:oR779;U)ݜypC[ &qxBa^2>c_tCLz#Sgi/0D<g邱v 푧S';h _|o JAMsPgp,Wό $0[x294X Gi 񡺧9QJ_ҢU2X~94$ď }#=/w$g k@'I s:?`(o~kh5\sPZ# XG"y58)rۃ`xDm+YM00}jk%a JzNɁJueS{4=PMHGF ݋]@;ehiu% M$L|?$L"p^^hްaSR !=tGN"e.yp ee Ȓ2hPdbT t-||:h0ksvǐG.VTu(a5x)D_qͳvp\!&aTJ8[UGZ,*Gq46IzPj}b?+]mRh5l,XbT4 FK ?#^{-fQR m#̛rh ǁlE!LEɄȍ8ckv j`N.9 Ox :g9R:sBMP^=EKPa,ඔ_]/IwN 4XQByojh(vl͏[ZM$XIv*ĭr_!b(5`H#)׻ʗizU[VNeubH S `n*.q]*n? |S/jJ?6Bf1^)c5o0;*}0]u.=xeuɳfV+KeyG9SKQ kC#Y kiM-^tZQ:@\8dp &xI~6QRYKp=4P3ܘ`>Rko?ŰRFa w"<*g]3 uv0P纪%a8,_}UϽeDtWJcUeh,ݤ.WV}*^k$I-^S{ysngHD8s2ex+M'3sdJ|h5QI"7?|(#0v~xQmQ x&ovN4䄓wiѪQ:!9%}jtϋ+OЛ|1wmIP={vy SHP<˭) T*Z@ w5NHF>7P&j_ГD$io֮d/ƒRג'Pq/>_C=#XK;G/|822?Xp'}O.ӊ]u Б<HS.4!jW;TWYR03#%a<[H(q_`,0⥠Xa;RW+Mamȱ8MaJE.F5fB]hBҙƥ$,m%h\*\?͸~ENcI}=n`ygqHlV> 0Sj:h-,T/oYx쥷NyO-3;UM_^ j`sHme3׌ĸ[|vi|JiY]bAv+g.r=R~B!䧌;jS2PܝDtG!@^j{-c<爀#uk|/SZOb"}_l~ kaC;0|?ZW~jlQ|l! EYtֵPQy܋d;Qe7Ľ(gh~X୕ Kh6򖋠5Nrז 5mn7b1̪ ]-#Wmnx #+ 0Mw;u(WvX}sgN^բn=5Wigpxӈ%c?"3,܉40}WU <"q,KxNS`i3OBj7Lg `Ԭ J|~ꛊk!uF;n:}$%vHOH'B<(RxQ1laPRP{U=$2 {;u%*^^VWkͥrҭ2 kwQ~_XHid*iUҢawA* WjC3}Pc)2~nƪ;Krd 0#6嚾QyTRU7DM':]G3>qb`6usI(my 1u0楈!Jkb㊀Ppszظ0Mgeyz54:nFӾsN5Sx[֫:S/ߔtMjG_2l#t2z*#߿ O)8ƠVYHAdj,1G~WUQ jp>?^UN`Cp ͪt{dPcgrYW;B9躳2tF?8&96w]l`Qlgw+H,S4F! oCԤ08PS1t]40$07u{αچB@!ᄋ7S+PTf|7ii4TӰ%%ݑ0O}`'^04~ ">?QftGV|$P2->½P)HA6.{RYK Bުr?Dj`)ŝRCm:.zmths@_2Q>3i1<\ZKlV+`a# |Sᥤ<]f=#]1 ""3uõ2ȕ}~a— K-g(]xxdҳ%ُ}$x8Bܱf qlG AZߙ64rMePqNFkF/vϕs=X= »pHkMBZ-0w53ܳ-iY{qÖ,  uH3w:o\Xr*Q-O'toә?g 8[qS W @M=*Z׹?wii$yRv8g-Ly]v\#1lt{_]gCAq+=(Fgz=6w eXdx/4t&%f%rB צfhn mc֞?@ƿ؟:R|CnG"l13Rwؗ}@cJi>'s_ዦ#iSTT,.wpu>k1qEe) >QLqb?okr`dhHiAעzjП*G|o#2 kLx 5dt8\/53Y$Ebn_se4=9L! .;D /Qk-{,狯J}mi _-iqmے* V.!DvX^h{3Ni`Q,3c?<61& :w^P9Pwk JL$$ن.ެE:5{ZH6mV:@ק@)go}~;4/*S:yW_gI+\oC_l/e%E며~.^oO;-PCTDg¦?ծ$;4c5.&~b͌lg?鼗`;=t m9SܛI|&^% I~pM J(5$kxKw󾢘j73*MsVd[C]=7)_ .gH8iS6r(*63>Ϻ?n| ֤N5%R[1mOPtJi U:@"yZ/xXD'ͬq: hM1z2M.G;~-."f>3i"A4Nk9;(NߪJtbs'K5K)"rgCsQl ;/n$ˆ$iiC p:ԿZOg9'T8ޭG[g+P_fk_:MoܹDYV0y t5s1`gżȓi7%76k#W#\x#,TF0,N-:=н Egr[R@[e~l|+T6FzV5c䅗k>Nڿ4]o9q9^i#UZ'Ϲi6ڸsKFHI1_Sе_*–rQi C4ORHokQ2 F'nB&'q)o]Qlz >ZdlAJ|}^J:qr{|y}Iu.ՓKb;nwjz] nihYz|YR69b= R @/hEd d>R@DLX/oq ~'"@II)T ,-A9{{w|ݵnt뽳-:ٖdh%R%QL"  0!9~yCIn~ymh#"53)Q7;AO]To 90bs*1gJp2CuBfި9j,<.لvV`Py)&c&bTm!Ğ#h IDAT вط:%% ZQ $~zJ~E^mYgCihɧ}N_`@j&T# TApʘ#P1Xz!h!)2~(!T ~`ͼi-]!Qצ-0marֆfX Xpzf^2XZKmmF䪓Wy=iary@proR$AvӘ w:MG(9q<'zMv8blUrk $KFihsnQր$ѨN酏+F}9^;!lGHLGGOd_q|P۵:tx@$bJ|6sE9KWm0W6Q޻)ψqΡ[3J1$B?-_^?tf&F#LNǻ5mn_pE$6u.9Zq2%!hHA/ghE/T#]b9a@2I?yˑch$\eRھ$O }@ja'XS%Bn9ڟ5R'A =[ c};I:mBABjMvLtI3JZ eEa7G=PG6Q.,n{S]2= NU$]tiS٘tөJC*HK6wt{=O:xz0q=B*Lٲҿ M Xyymvj{bTZAv5U q2~ (zr0^=k3NzYV6mm 2oߣz9e::OtYa{ TVQҖpI8 .>Jxo[V?Ae4ۨ{ž*wz ^> Ggi޺I:.6?Yofn۰u@tz6SŲPi#dl۷f.(ImAYɼ (*/rQ'f 25%dMm47-F} 3Վt2P čE@,P25-E$֌gi-/ႮPɨJS] N;Ā5$@"}b#F1R, 3=*@N ݃Jt9y\D}hzELH(4kAL+ /h -[[5lb,ɼ:~;XD`J&Y庹᫴j*z(t$QJ˷u[G:PQ\T,} ;DQ%2k4;}Dgk1Nz AQYtX J8 d[_tyIm$FnYFBuo5B=[`J<2C t1Z[v 6$q{".]$zDZ = YR1_ZɈްexl7-Rx kM:Gphm<}2h)WZx27JYي)SN՚.b=z1gToXK6 /-ͪ~j4ۘ0`U ǝ<9c!u<73i1$(R%2O(s%::~ciBO`<_n6e>R+=8z.0r((wʘxy{ n(afJZT<}eR'mX﯎rK54v'ylCV.B1^( 2%=J?\='^Tы%}V;0?3FøHq/|S^=1PP^ʢtJ'47#5"=XĒ2e`@흆~9E($i8{ĆqL ,5[D*7^yJLԄDr4% F Аnw]6=Ra.vv9& JtY+9e.abk2j{A߉RLٿ-yⶎ(˚nE g*ŷ'JL߮Zphz뉊ry;cg#'aPrb`FJ&0$kGj@L?){کr{^ڍZ7o ScnE0}_* 8K]KAH1]AO{%#IclƨK}ە:rkQx'î/VoKM~ٱ@6-^Np*80%3>fڤF2Rsp $P*;=3NvP- fX eK XRc]  )6'Uv"ey" ]zx 8 V- w})ʊ+.|2T  LJ8+e"Oll7K9A]{{„oSns$(a/WDl}~;]NycP1U6᎗U*|a sM+$!$e7t&J.\EzM;aIyyćlu^0Fr5 vҳ;1[- Y5)S7& q4*a2q-$>cd 6alԗ mO1 ~Gcx"PO,̢)Q;"@4rJih55A f&ODDu#MfΖVQefiH!u`n,#/}2׎ʈ*xYX<2w8%"?KH{% {>o6y}$ec}BU5A[@Va?d]5OMCVD 6W\}!'^aίK~UA>S )bɘD冬ڡWaAlSsS?NGIùǢak/i lZP_?c>RlY0} lg4r<9E>+a$$?öĖ"7wy7($\t}^N}[ϭۼ6g#&U׺fѓx˻7كe&D%,SB@) {ޗzvb5pkIC=wD@Ӡ$W4xd}{LtxQ*<'7E"UheQnsZMQe#K:WL@ TL^Ǿ$WCx ̐Zх_qF[vZ#A0>!6V țt{{'CG$.TƦ&165;x^ >\"u5mVS~&EBDfG&w}*xazlKY^q=_sʷuLT)~FA =~d.xdmfd3g-ÛbyYe@ˡ˞9u߽_z :}lz c#ptp?aâvz\r]u6,Zj#D $>ciܸ X!'.?<Os+A%cUdzN2v`m1'=iL2}(}*3G0s !ռs=Q,(vѿ.4=Y h Z⩒9ue )蕷 kዾE;SDR;A 敞+ԃM9/Z{|w3A6{kX+ϞAڮd<3GyG'8 |vJ1ETpkv1}P[ѫpzMQÄ+ ~$QOTF`&5$d2>|Gfޏ_?} x15c/]dg+f=L >Cϫ}xč 9OȲ>M6Qrz@і dFhO-'?tDUI}%Fu;+Pef0O6y&J39gNO=ԇ;~׹ tc L5J㭴'^=p%@=^P /$H * TNr**%v$L)M9%RVzeZo^z2!'uT-_JL\?'dج* j dA FJ#ĠD-s$|8ZS@q8 o."F/@ _yAF'B\o2"<`s1"ë]y$.O@CvO4N~kNf gϼ˱8Q7ZW'4IcQ R"Ic6(ҕX[3*P[mh7a:Rv\Q=d=31wpAgO >ă/ 6,\;X)DOy-K3>{;yVAmGSǞWbjjuORPxʜ\:U i2>}^\:C1y-tR&tp#"8~c׉#=z.iZXx~x%)zh7^xlփ=ГlI"h#7TVfDΧ0٨cO$|+R!06݅V )SX)G5\R![~HMہ1em$ !:(K Luf7.2)b;昅)VmӶ̈ϔ$fAn!jip.0`=jӲ(TUto+؋fM):BqA$M5/KI9 # \$7O{,rj N1FSw͚;Vb=ǎm >A5"T挫zԪ(zHD:}/O:_?C{[MO=^'dcy3O=>%[u3I$#O<Ao΁ Z la9~ދZ6PQ +˔탲dT_3o[q!&`%SZ$ѩ)gW\TumVc/V=}RƧ`VЋZ >i댟!*0jA[t RGz~pS)[uz]H`PSTwW95Tވۘ737 IDAT=q jJdOR o읁a#,m;:ڒ} m ʾtr5|2#?(܀bDh=?w*qM=P@ aR?62~o$qY#yTE6-rN䂛J+ "x9̙256T&EN<y_0;rvbOw-. 5j(x3؁8ãgO=k@Ƒ-?Gj #ҚQ9$a6N9A+v uܝ䱏pU~3 VNe <}Zkby ȇ\' Rr9GJ<_ mDZ6^6`&*$jAdn`3!zTJ.)0@Ti"[hL3 *:9CȜ :0,CM˳>6icS@_0g̩N8֐-*zĮ;:(%ף_ynnD5ẇв;lw-o>tH(U XJlS.SjMtR8'zrbӕy该TEmi"&T%J\h⿒ [zOs զ~|oGA\"7dJIǧ'W- @V's91 $vȏA wUY_%FْIg םݿ>v`_.] Ka0 8Ny<}8&/XGl\iܡx ;%՝AX4 N{+z*,F&$6AMž~f@`MLV" 3`i9Q(xK>N'-4$!-YhP6nxˀmƘ9 ȀVom$B]wH:* !0J=fNݰ@AՉ5_"r!β wDyZ3և瀤ɀ썿ހ'I9B״ Ic\c7@Z?1h<+^C !1Ď;Jd{(Pwhi#E.RΗv(ٴ v=ZV%_ɓ'lN]rrz rA>4uI?mkhC~|Y= lElR&gܳ=^-KWWހ[7\}_c,w=s?<Ź!bK^RLu"aeZn.R溑Qn*bc.bߙVm+Vuچl,T/9_iډWmkt*Mj%AW0Q.5&P?MxAx- }QN7by(C4O1&FnuSp+`4J 0yHS\ɝV`@1~|PmO8g9t 25" ·Uq=5XZ/c?u e\黅 :!Y~5P^ASڶ3 !'aDޠs=ʶBV" @1O1>ȑg rQG-{n}#n\QGLgӷ.]zx+o~GFuc}O&ذYXo7+-` >sT+k֣Q [O6/ 1W9W{F!Qou X\ׄ^SK9YqʲkS{3 ғ ?3˖=P(%qɒxÖ+14qNܱw\ d&Il6Mݬ5 _Q]_>`N炆k6Qy;BA1+f-Tÿ C 3("QZX*C/I /P|fT5/|h4WYP 8VBb5wX!+hguP Y4#J-̪'E*S3YےaۍHВYVYgE ?<{Ϣ* X7y*UT' 9(hOJ`2PPH&mx4?uioᑎ3)xX^s6f[W\VDZ)`Vf$3薕MrhlB 2e`(1vf"#{& u}2vۏʕk$MyWy.sʵ?oQG;G/{澓L5 >{~ẗk}s}ݢsl^j6ny=}gN܍y$Ƨ&14Nbx>,[˖ƥVa^_ Ee/ϟ#Gq|sU7x[c)$اv?EC+^w^qGs\6?qٴ5U݊h-[.c?}Hv?8zppx`ҕ/7X!w)".,g~ ;cqty2ׇ[_o7^}dj-gGZ||' =z_~q=?g ǭඍᆵ/p_B X>a4 {x~Oc<|wY:4_`%|FְWRpYNg9.[K" h'y[l6Cj,&n MDυTQ 6 mhLE1p"PFvOW.S@i%HbQ@CጵdpA-0`H^Jt PMi\-!FYJp4^Pr Fqy+ݏӱ(?k{gqͪux_N񪍗k{'wmɵՋdh3:16ݍWoV$Rzk9|[ /ꏱԋW;ө.edr{1|5\`@}WA߹w7{8 _<|7ܳcV\!޹87~H2BGFt J@!.E8?9wO=='W݈_jT@z'9$b߱ (l©,L| *7_r%t被J@_p|w莈D仹P@'>\c~O:Geq:%wJt 'Ʀdd9ʉ?<'p>z/rvlm ^X?B;'C3;!Oԑqѯ3wʞ'o@,iWƔ$w, E_~>NGp~R+6b i$'c3 RNHI/Dɑ;mP㣸 ၞg<ņRG0 mf]am#ʅk_M C-7䢗%ݓɖjNh{ ێ6ry%K(24ǞR ^g7ޤx\q8u-[r`eW(rz| Տcٓe .tkcNw;XmฃR̡re$x$LB(W?>uc>jXE(=PG|ޯr׃Jnק;f[1`m2E=nÝbTZSXL;['۶zȰ$>3+{6aâi){r|~Tcڻ̣֫$?w垯~?HĩQ_D^YVr.Kh%B7O5;p%SF%KYw^e6] D̅G|Ϝ>>7z&g| eް-SMh P)!y蒅K6fRLY{x~#rZa|?ĆU2ly>#dXPIe-!iw0e%N1(C壠BN 3Y\FIeW!\; 8`Qjg<2oqT=oq5tSkueVvڍ[iO;Q-,dɓ%f+ЏV`큞A ޠJ#3?ܓ3L 0׽P ~㏽,I/RI5j5$qgD-_<ʯ%}dt %31V~R, NLρsgv-[Ro|^<~+okgPv5X00?96O=zrEKq5|jl[W,_A{vݯ*w-g'œu);ƮGpfk֊<~,, Y`drGqf| Gv{wvby!"Lj`Δ߿iwy͵ݎꅫ7O_$]g)j4F.@DZmԐ, /[@ٔ/*DT)s6]Sc"Ξ.?km EY],UV ȣjʪΙdG񁵥Œ;Cl%SYyh"*fBfD*=.Y$]B74&fVEz8 '$Vdkhw]NFc'&JlGײuʪv &SSNu-'Fl|Jzή%X88oܽ34>^ۏ fظxnZwGm9^6</[ 7ۄmWc%~DB? Ξ—y۵ Emۮ`w;L ,ޫm.lͩdOQ=rx/79P}퍞`EK׾޴ sNcشdEA@9p+6\w |%O?gƦw_ 1Ys~/Ƨ|{ʌ `y0aX><ݴ oB>RtȢ5>ԣbXdM2pCo:$]^M=Ҙ?S&΋$AVa,[j}hA@޿Xaɚ;e !@Rc0Iޱ4Ůvc* O>T]b}$_[4*Z&QhUފZ Txi`@㠤 fz2_TN@ky%WO^f,WYAXO`ż$Wh"֍͑n> {85>ʺZ<m*pN_] ९02~sWة'O]<'>mzE7^im;DAAm+nzu'9OjWai==!UoǒyJAbz3K᧯[/*C(e"\c~ҫ_DbgՅL$2L; /0*g$փ+Kl|e[bʉ,dG^M.RDXܑUn.d{!,lgU; f[rB ǡ]~8=4p}I^Hm*x:Dxšή%O*Lq_{( dɂWId9ϊ*62{NMy8ڧ+Jxs N z2ҷvZ? )"Z;G)۶-9`I3҇Xy`ۮr׎ %W` uU>N H=Ǟz0]Uqe,_];,sj4[O [w%Zz zA&>or՜҂̵L5 >{;~GAĬILyٺ;woNuNMuLZe4E'OێZ9uŊ5 G}uG{Wm]W4V2*7 篹cxԗMUdZ{;_{/8JDˊHKj cЉ  KpEJ(%5h+K1@Z{4r<\%@E)Ѡt-(ToTˤ$S cu=5=w浳m5dhD⭲sjXp9 @U/d'{PƊK<-H59^#7`BcyDzVF\4prµt& ڦ1[x'@HSc* Dhz:A]LFUJ4*w(B.f|YR3mu=V| ?_ j@NɻSqA?u2tGG-rnP^sV@+>z_\v m>z#E! ڛ[^)GF_ةo+'%{^e"b/Zl cg)&[Dt I9m)B8?59G8V_8+M'i]:Cp=6׏_RPnbBxPʺvNKތekiɔqiW+!,GVX!/:~(zW|<ϲ^ew3ZL%CÝF&?㡯ݸw®'Zs1T  5%e7>w]MU91{P9o 8')=..o|B%n9Х\b-_wEV_ZɱUPEVSq=x ;sϞ™Kx|?fC]^i ǻM:?_;Rgg9~"ů۽]Ҹk*X|J"8״Rj 6e2xhM:r+mD`${2a,>, 9z!.Q+SdȀsX{|T;n;85ōw R}d4 44 (^ O|Ife|ic-V ^:@uSQx7G_`\)(DlN@Zm,4EtI 2]?G)>Jf"<,@LI`p|!"@B̮oxW/gYN@}umP5|zacl896}ҳw Rؽɉ "?'mX~W9tWAenx~庛wsOĮGg+GGO<3/zd\Y4i>׏.i^]1>SuoϷgf= ;y*rFQ6^[i2pkW^Eٸ _DZrH&`ի I(e)1/2qC(Ffnmv UFa4JП9:gr, *oեTO{Gfy| kcAr4B6x>AZ3!G8IՏ8iYqe19K_*X8]Ao@7tʆʔ[n_-N"qK1ze7r۟PEbU@c#XpW@,LN`k0R6/\G͗SOμ 8Sٰh)^aek3dӦaG_nAR%ϩ{HDX`A<\!k̾ǃ/X40w\~=q816ÃcԩsJЫ|3WO{ƶ{]˚ uJ;&) |o|-]Ƨ'Tf;_moVj߂C ])b=N,)=/{nx¦A s/d$ŃjHd@"Y!'1".n5}2Ίpz qOӐRDX|>ׯ)qwr\1oO1\At3 (KwdVZSw"K"j υOIr1@ QI;.4(/:Nf>wH ~K{{!@м6666EE]I§,^+N1dIe2^oڒn,x1_`8$$S_w+O>PoY@S0(OBJvXovf7'x 뱌?|q`?RmbF=T,dj@jIvQ vV>dϜW z ,6S1`ZgP#;O=W|Wbgs` ePd' _#,AEu8i\ a]rLmXފ._?]ʮS[ܸAiCE2W'fNY˖D7&m.=PNʸt pz|fEFӑLVImL?<0.E8ݪ+& (aܰz~qfbzǽxNtCGÕWkre.Vzp!;Pu=RWY׊([8tgd3m gD5koIY@];]/፛/w c7iGo r9MBp22OH~yO*HDCb31Iݒ6,1!'L҈zVQ]%#hJ%89 rЕI[|6:3e--i^*^6che;[L95, Cg)&nҖC$e!rWM-Ahgu?6&}Hp>G䴨(i[ausTqڙsA垃W} GZ4CA rӻ?ӹ]HTX"O*/nNM[! [] 'Ht KUJa<}}3M@ɵbx>V[zJNXz~.GFΙnp`qc͢AtֈBbYo}nhLﮛ:X447]z%^r;ɎcuZmT,{~pENDShbzz2ߏ ||,o䇎>7㧩jiv!mBNbg{zуVpϰm#]8>y:g)M2R~he͂DhŦtlR΃T tmn$YV3=.Kf5*[BqSsn#Fbd5;JBX"l' kjne@OϩrB xVɉ=51qxUJG2ber!քx$'&ˏ{t ߝȬTc5(!zo|6)@J a^.)]-\#)?1~_zve:$q? slJ{ۢc#qoApuzJa xrl q|,ovzQ5\uХ=Kv7%ݝOZ޲~~qڍVC ^t6W?NHsNM8 mB¡cpАBnVs={LEc_%@_J7 ɓuv|mNªk15v/ : F=s@u'x^a3))M^=bS֗l$4{üN1ΟoZtn,8%)Z=s._hrƻE.i_>M4z3"tioXL 30 "bSS"he$]M)$+:})qu / 4Ԙ#gH28`[Z3F2xoe9[{i2A9$\B3?$Pc@<,wب1( A&-= R֔Kݔjx{19NI_BdJ"\0y]>ĎMq ˙xr"#C?9 ї[b+)%ѭ/7^t3ڃYi(5J|m}yݦ0nh#/l\ʟ휞@C]԰K9rt)j䎌tXˆAPr FOO\+[y׮^׉> Q,?_6'%`_&nr5Gq ( 4Cpl|:-i}©q&qߋa/N2_4-ˢMkbV7^$dfgB |]ƍVɲj.1rgaH\  &ϒ]M3mlcVڀ"1@Iw21>*Ԓd/3$9KhI^8{ U r4&O* ?YRJ\Sb2j8.9FlBNoRROn9=fVB%Y/v@R*3J^f.kGjl[!Imor9<0=ԛqkk4qluSvn)p8)XC [/Sw\~?t `3)3w7_J65+%G'4f ݒh?O2 tNn[:xIzr&~랻 bŽaKU73Qݑ$Ɏ.R٫QIsݞUX<{D텷WP{ȁ 8{W%[+v{w%՗A a?tD]W0g?|04o~S@$/Q%"9oS 4ʺlyu+hY>PcSEF5#Cd ]A'PKuw|OR~u'3hVc ;. ߱Zz,27/a".F&;mNMÐ Th>s̸tϝ{T#TǺy7M?h s`/~KůG'QL; dR~NP\ b嫇z5A_+ˊI!lLBb{B&Sxpc&riD#p憐4v\Lr[?VPZiUVI4JCn[o| pq ώūO>8mގ*"Upp m@M+8= W-]'Nc Ovִ2Q=:oy ݖT _x#gnE!`C?Gxep`31*aG{}Loëo9ulm.v'S,{v'w|.;N7/E .MqږʘߦnuԱ]c$b*/1̥cv<+RxM'=u˿x_UxͷY- >|G X㝞q78 fc[^*쯖?}h_O[TjH&Ыrhd3rR lް.?8{~ &B >¸Wu.ۘ"I,f tٖAzRNK,WՈ;AXX'`HUkZXKpe-0/@ \1 Ą[؝=LvҰZU8ϳzC>.h הlZM8ոQe:.]9¹ W#F2U-Ѳbc`&`h)w(00TF3'q.d}.x-ˠO@ݰZ8yJѺ2+?*4Q`қ { ׽ WW~tTo~'n;f Mx@M `?%KLX+$iFKV}Խƻ;K_x^|;n\Ÿ]<>ȱ浒x7  '~cb=<}p;0_Yqǯo=x2)K)]Bsܥu3~5ww,e?o}+[;N≃ go'Tn\fɆ3=/|~es?'q.wNN1z/x*cB[,O$φ_&Bѝ`9?>_OxͷH/%r6jL;KZ&~Z S%&TOʕZ-6J4#QN >ӺnmoyR䐦߆fJeGbMyᡯr/\,+ J^*tJ؞vy\fɇ;֦BeOD$̭rRq*f?Ǫ1 ippr CPXwb{*z]D p`r-lMUЍ/-KZUSaDZ(ú[2=!<Ɗc[`7n5t7=>|olxQwyArtF5(mK67mor[0eΊ_z%~S7cu"L8r㩃8xڷy2ywg7 ce/9pnM+^O~XQC'|}?7`2ˊ_ Kٱ@ MFdWdd׾ W ßnKU~[`٭/ zN>xX)@&!el.| Cʱ(UDLf5FC5ɭI%yxԊֲhŹ9U҈Q+c>[Ǟ>Cg/`6êu* \ {a@?T ?$j;~#~O`Ԥ<9ƸVWPWp}-p4_aW\x! K 0$ClsS3Ep,ʆͲS)elRk1$M~9U? Ѐ~4͊4 XFy:"Y}ȣOpvzXR0PuնbY+f'<|}2@O*iZN-̾L#H@U}_\"P+a(WRT( {fh@FφX @~_} ~b 6+3m &ݼHsRChQ'55U!%nv˪505^M>_Ox:|iIa#HU65c4]SFYoBBLuV/7~]Bͯx#/S|X7rjGi@|{^z[a\yOpϩ3-￑cs)f]deU}k›n۞uBΐ\Қ4W'oO;;|{H)O^^Xʤ"g0~?&هO_nf9򎑷Q-I0fCDY*YҸeOj@%ر&)'0U @0@,o&Tv#dW~WR c2TQݢXz<<,Zu=9UL(;o/CVO .rjP@͉õi̇<{Oau`˗qUVrd#*$h (Y&]DF&ғOtx/`>[4 2*1۴ T:ɁOۯ.퍊TsT$m4P4VZ"YKI}}[׿]NmmN{LhU6]mD@1=?hMPS)oVRL G z~)5k}lzI/E{_H/qДnׁRyrr;~k~K)F+y㥯­_~>7}/MXEqsDZ3n1iYnGW/G|'ٌbIr} A`'_I9X832}"&f-6i:[طi{? x˳fn K`R$v+qJ0k4e-H۵kEќTA@LA`Xa0z*ЃXD--Y\Z4XfO]cOjtynV&Üyh-G)|,֤ټ#:3: =.]oxrh~wSHMby@_(` ϱL@kOU K؊$dk* Cw.Щ*Sr,'H], Ϟ);`ZtG+<}<˅(t2$Wk'a)NLSAB zfK,KDdrtUEyٲǩ-8tWfCN*~(f 7Bb}PJ @aam"ڇLjn%eгz$/+jHEp(PÊ24oc/Η j|Dz 6l{=gZc&}|OƳGǾSW}|G#Ωc$d7HUˉVĤW]ǃ*97 B{tĆS7KI83nÏԹg?xJuw܍wR|5_H W q]>@(jѫ ׿0~푇k' w݋/n7 hFi1,.y8Xg=/\'+8U.S7e܆Wz'qKq[Bb 8W3V*wisb|ý/{'mELqۚpݛ߅U_{O=pxŭw]co/Y?` *.uQ$y"#o߱w^=>SxT[xљ[kn o>jx6Ac}Ÿ{3[;),4O]/xor wS'Hj,vJ3c`=wߊ8xk_%Jݘ{Bn| 2Cё\r"t ;8qrgrH"T R„T4&`5tݖE+WAc NġX-{ ` ̯BCD"vb©;>f}%4lDV_O29^ӎ@N֑Ma]|,e3Àˋ./_.Kiz?5/}ڎpcU kQ!@B bۏsV(9Ƭr9<,!⫴SLn̝+>.g85ƭ'p:w Rx,F^&g~⺖r;~=wU ƪ8wtsG "ܾw+l);ߑ,Z9̈́y>Zri1jG:`d7CxZ_FܜpZXV})hmf3CS+J5mɭva>r n=[w0)cL^Ii.8s@B)k1`@?0渺tZau馸c$nt&`ϱȿ`0 7h % ټR#'t-5MbnB DŽ_|Y<{p OUlwêd{'="3Uڳ0݈Kdc籿(BAesWV2R ICUuw[z h1,ו S\W渰?*@P㔺JKKQ@ m N&W ˞M@&Kfs "BWBa0*3 tKFj2m5N&&H1 JXlZiXQXVt$`CQ DMw{2wZq~'[ j)J!ڤHf'ZqZcj ٱhs{-rvn{P0 ,JE!@{ Yg>y,f3Xgå( ,/>$^]7BW[; jf"#Yj傣g(R=P*z@W`3M[',n/}&9%*s8|\Ո s\p]G8ybyãVHP$8tPCKXI"U9"t]܀*(JvW{5 i,)]G(Ez ՀaPDepI $z3Wd 8yb |;[>ߍgX*<,MhNru J4+Tɶ2Dn+֫Z WFI}qDw?_ħ_;2}xλ1gc*lzd'>\I4oЂPsbWtO)`3}n1{,i#'x!iv&s0@w)g$9WCtnz&x]K֑[vcu&P&J⸒:BM]w.W$ME `6ɖ%|q4T;,`BV*R=1NjLJ#}07 IDAT:>he(U6 G4znhnX[`f80N"*p;1YQVCųO_ĕKWpiUR &:g.c@ Xg.΃8sSк* + úD 櫄C_Kٹʪ01&(]bq1-2 ;]rXp hCX%k.\rBg p ^9*Q n7xQS-WƀJWP&v. uCq3_l5X-Ksu#\=ZUv)lmQVtj3nX⑯ׇdG(3 Wh`8sM$&{b`N&o|<zyc*~]߁tKRBXSY!󞧱Zh$9o UJ zXcV 8 fZv.O#&w خ _}d +=6,:yrj9J slGM02\v  _M]CN8Wp0F@lcae4P XvKAmȉ#Rԥu8l&sD21mk }5S꽰3 c 2‚i~)O>lC ˭*Rf`Hi)Wf it.\&w?Z4\/&ØmˋC5#Dʅʙ7sp:b̄~(x3_K5ƚv\? %ZAh]p8#PTXy65d bC+A,[ۻSI5eZ+%V*PM)6}u"dL,-pef_xŢōpR'͛Eu2rGDA+ЫNվbX1Qj_QWu,JP%N ,I):`2!L#ͬ0Pʧ1 X:etKgnQ 19eh8 eꜱj1<]Ь;y[yCc2bXvl48}'/31K|FJa0ji i-y]O Ud4.s+XyqR⌇5Vzq v٦}L.9 5+0ΟXɆ'FxjѮhL3_0Ijb,IMyŌ1 $0Sgxx:/2c.d C\cഈ PPWc"eWxU<'1pEG^p):Y2]IAfMg&LêXUw#n"̺R)+~ bƊAG$jXR ,ZI#utM20F? W[2(AeP/p4Pwb,*OLO; XT3K1-cPUv` d01ne T&³lD*H`ȽU3On֛Ocwg;\;G9V 1g1L!a% 9dž 4c!Z=n?Gi+}x>#of|79O=r"U߇I}f7`ݭivcG(-y8-U0*v*ϿI6Nx"(șDƕ>$cߎURN OMN&K |o5S|`D@N `_)ςVk+Y#]v6zZ@rMB h%/J%LnŌjޢ&V>~`ÕOVlo3pzvm0HA"jn'ŇVtQq)=t̨;PŤgjAЬ8wnx3c>G0h'OrVc1"VBAI` =a'2uNwb!#,;ZFB]$dNSsۙ W$-``{sgsFPs Kt͚:L!V?<<آ u 0ĊU1.[q'xyJqG83gv (p݂vCo+V(' 2@UŹŽ}2׺)k5 p($ JeM8X @{oy#Et\¯aůH‰BQ4vcPcTe%b1Bm ]_ER/6J=Y h'KFzG3CϜWٷBy̸s(Z,tLDSh<ҵaX)u1[ANer=ZT?jֶ74ۖ?Ar>d%yM:yFgETB纲I`R:tZ9  NV~3?x >mϒ#\ۗXAr]Q 4F/UF 2fh(ã9jMn1f *MB@-諲Lǘ N'r.J[0yb1Ge`M1NQR5%hmfa%U)@ʑ`D̒rPT*Bz&\98+"tjJ~app|I*L*+ vmwL|*M1b`]B,aH,.ju7_$T"-u? -(e"LB#}PcV WXX(085C~ M}(M*bAM'la!0(R-4VRUQKҬir'iu,Op|t{ w DRSC6!.c̮MlZ,ĹĆe,hK^F2ov<X 軭05 G%bZw(s֘I;\ ºUZ9f8ߡ<567lƒG`lkrɂ&bRwUyb"'_'Mh=f_g5iV uju ؘSK}blIYɴbԳp̖#耢a.b;7H{+֕z28U)pGO(! CC8hā,$u S82 45^ cL 䂷]_㰇HNr/-گjOgHzػ)!2cKsDå[5Vt686A\AHS# kJv^3@$ߋǼb\7o1HYƊQ) wcJ OecR~(6i5hK sZ|7Л4O|< N[ex{$qk5ˀq"#Sɦ~'E %`%cSyH Y0.Br.x樕Q& ^|EB`u8aƷmh8XT  ޮѹ-5nx,?@X >/etӉ'a".8֫t*Ҥdgif[PjRY]nF Fߥl0z,i|]7WB]Z+\lW 8y5ف6HKjY"Žڥ΀$6*ozG_:T/sjVL *֪idRn@WjՎ J 7+Ή}HS8L(5RwCڪwvԗNPڷj5к!a~2{q.<\``BFaNgUL1'!]%:Ip뎙?Ao=6ꌹd]\ {GFcF͆$ܾyN1Lmn^Ž RE,OL03ӓyR?Z$IMJjų-TA˜4UFͺ9f)ØlkЃJtUN {p/>4"KЋn*\"mT"ZlmuB`r]ų.Lvd3JU E |gqP*BG5r@f[E.A1dmXH-ciGms)V b+\} 2kF'OcF"'ҀQ%3 ǵOޑJYBE~)ԻԴ1iAZ G8-MXXJ1T;FGEɴ^3ނĸ!cq=^YB9RbZ=oQBZZ|iNiV|ZQL7֜|xH,sZs$&,Uh/ al)_-؍F)chmK]k9vO?sG>8.5V~'`!dF0NX-P'z5Ū+0#spwL" Bl+̌Q ~[QLwzL: @~])Pg'8GQf# ;`nF8+("KpY$Hܕ H|_=jœf-r(:m% 5CJ'"YSZ| դ0;e=(z:JS`qH $=N`Ǚ1E,Ѻa0_i6 l/ie&-rEdMȓ 2pNz_mךK 0TZYXrLm-#0_wRq$ B(]Qċz-Z7B)rkZ+W T3Yi rRҬeyӖve&+•dqIDKW!A8\Шl8jcuبn5:-FCkS4"ve,k2k]Oi$X+z@l!n Ϛ, FqM[1g4j*氦afgVw`[HWW{E75Ѳ#ABTPT-7DH,Ξ> \W0Яc2}:t :^4id$P'pɴ:q챵U3{ȓ]x=$i'y PRU&[@W0z Àa7Ƶ\eѹuH]kg4QFhy3Jٵˆxk6eĬcvEC>_Q@8\~d28.m-*..}iEw{i&5gB>  P(Y!2Aamn!xSCvIώ$EMmͥs S+21ɊNқ B'S',gFOy F:v6!y fZ7t4ɀil')wpS \R IDAT]{.Yl!efjI[)ĂULJ.B&fѬc_<+ " qE-\ {]0R \){*^y`HvaW)}/ ϢzW+ c3ydwU=EC=K ftӂ2)g(,9]JL.Ra\$IPAjՓO2^fh*5=A%r]I#BEx#C,SLr5`ǀsTn:@2, PX-YYl=Ysoդ(mdT;=5<XjEӵnL~(j=󅽦\_y_ˡMpo߃ ,36=Xw bV\6|]D-Q4pÅroOmeoߥ[=fa '}%߉( d1MJ>ݹqMҌm |]*`G._IGOwma6"u&MqA@$,Ju׺lGmHxp>щdJhMȋ$5F k `/r{&MHȺM'T"p—W(BCŕ4WUơ{%)x;y :ڟ E*@~W2qTp(H*VMb>7;VDMu*n/U`0>8~a=~p0 һ` K6a=ƒOj:X,I&"%"@t`M1,MaGbB D__ v4yU}eU$B V=UtRd>0̏ eqX*c0LIIRpלHQX9<&x@ cVb%?zɓ)[˱I3ptS;̍F;7Y# '~iT[T7LG}$bf=.P^x'05"wgF$zN1d wEl&D2m]D5Z!֏NrMB^yހV(vDbv=aqm!3)7@0b-^H9M ޶N P=IEs.읾ZZFk!)`6 o 5Ma^h5EuF4BXM [N qm퐣}TJu2_//XGc2 fcQaJ*%U+Xӏ$˃>Y<")xȀe*i$#IsXv\KӁ5v=sK8}`t?9xP@Wc`Q"PVro"W"X *Wl1JYCbtLjVf0oRB2Rkf$i \*)UӚȈNÔ5yaWSsट/0LvDtZ|Uf C^F*8=qf!Lѱт,r|' IC [[lmBp. Mgpp8DžKG`V #9¾ rr&].Cp3'>9,:eصbX3*mt%ACaǡZLahŦ1i%YKnE` p0[ !8*;<͵$7.dIU:Uߣ1&vvOYWUeQ58 VT#Tɚ+EiHe>9Eѧ0 zʷ$1޲i*ey{qU6LVJ _#,(Wtd~u? A BAкbhXO:Ȯ\ZDX( 5bnjIXjg@Te ]ۺSؚn lCP)jcI?c ;+Xneȟ9RmQMƘzBGqiqtf-ז_Ny7xr=4&Ν:np+$ڗ,l7iX DiM۵F#`V@ Q7LJ:@fza%+$G&{PQOM8&Sit)ʤð\E r(eg`lm,hkPt4P}Mj6$ *-p+%`W:c5 `V%Q}hNY݈ړV:9nĘ)M$VU9 O,PI'Uar>G)DQ .Lّ)Z+V⤔\+HLȼhA؂g=v-ť.t+"l?8,dvfKɈKjʉ5/XJL'dmC *ÉYlalE d7î"ۺa䛌Xdp|¬eGbZR3Ie LgQM[nqC+#V|MₔMOhc2Q!$( b~=z<~CCԼv h+fBhwKJXq$ݖv}FrFx4e\1>YGk:87[SlgKSkR% e"Zbc;i@xx/J7^-ͪ_`+gbuq_rֱ+`b+K,+ni:~m}X]ᗮ:05 bLȂ-Lj Cr13"w)vVH-Z]p2 k$B-TP4(|:Unր|DvǠt,EብFz1ht Ʃ$14F~P&0ݚcm P&HVْ>&FG? , `p!`[v'^ԉL&ֺizgA2Xcv/ 個Wf8< {,mM Y۝Lmcf̌Ųb6[3A)e#i)&KR:8+8c!kGy, 8wCAZ,![I9=gNBnj|άҜ҈ova+n=5MX/BRz7>{h3B };dkܒZ/6RlY|FrHFcsС+xo; VXk74/ؚˆ]x}d N=aYԱu"/R[_CR )H:K4*:0eg%2V%&$ HJt+ TĺSZS%M]Zuݭ-VZ*=r!1=-+ʤsR7af(臡8| UHj)&iU 6B計Y92M(Wݴ}WT[3t~7+-cGrL'J؈P &ܡ֪BkT*NqOV(?`P*+S\r%9 q% ^͓%Bpѣ^<²Лi!*Jtb|޶(#'Prd7G bew/l3-ZUbˍ[D8snc{g"B@N5@7fh܅C̗ʧ% `h>9NS;L?Y?ܴ&:$ޤ,݌wLs4],3#7Ô;B@zh}RE'hQ=,}BB.Aڴ]-shs~ݞz3I__ȡ ͶDR [`L hۻ;ZQ*A\PjS6*Y;.,AcR| 1r)`*ʤ`RD8HZY.B:c&uE/iAapE[H !HVvJ@n,']a:j0􃺼lz)[ldU+ [mLSqw=*]>MŎ*Q)t"[f%ζv g2+F۪,L/lf; Ȯt1GLVٮJYν ASY`d=1:-@NO(`kge>*ܬ3g%\R斡Aepz%Ir\MH93G=Z[C&id:[UeF~BxdΦuWeEG* ~y/'=91]< +ۇng%߾=qq.5//;^J>{ruW/w*l+`jc`00Ͽ=+>~rTU0&o9|XV5:zٳ^ (E.5'%bha]5j?uDM7=szCy.s}SF<*Pmozc: #cJ\ L~x:pc//Gb"FRc%)k\8PP;D+;˞$0g}q._qÏo\[\<ܐy/BX$l-(OWQ,ZpgmU9p:C~Dg:e]2-h4WhI72V SphZ <J%Uc9kDOߟE{|b ~9Wz{ _It||uuL ~b])˚?kW.)$S a? `H*iD ƋI%|ʠ p3W2e 7f`HM8-(qAJz " x Ye7}?02:%@mdMae uOfvPCRc۬'1'vLs2=JO!G"TTxw݊>Yp0H@@c!|OAU!Tmb[Z!y(NSϪ+ܑgp5@ϯLȰ#΁Jie5.9̽]˳n^r jx58nC?Hyrwh_uAدk̜W{ݛD ӭF9&hDLV v1XElWbL,1~8+co.xK lsRb`52XBo-WS;/@@f[q_nRHv:luEROw-332RZ5D9b8f`8L3ՙm 4~<[ vÜ,sZZMYƴ84V 3nnn+N LkEu 7p_!D]FRQk `oS7UfK=' &d3! %?+~#nBiSB^dT";_*>Gu ~|O _ 8"-,8gm96 [:brrE:ܥH8Sط5 =;&MmI.u5"Nԟ|#UڅrVd 2)DYZ^XM%Ktҿz, &W+fY>DgÛPilo] zӛwUڿcD-s[ك'q$;K !,FXtX*-]{gY~#`6+5{mmu9&i'[ %.FX #KuZcSpB'Pas?b6$/Agq(PӉfnr',wN|km|3Nhݸm4j°\Wf5L^ZdX2].h W-ai-DEqء"cb\A#T-3/@-9_3wJ5| 1n[/;&Bqن:mJ1gy/ehqmxޞrJ[.En+?^\b']^sJ8vb`ۚY1;~chU3݀zR&H*@# `[  S1[icMN6ڍ 3"Fl- \H-&R#t&OYQ;(<5No,A  +lO~g*I+SVg՞|=p7+xr"ל+޴Fm:V̢ȅh۱tב s(}rz'މ~f0ЩƂ\t8j|Yϭ я򯧍gkҙϳRy1r''z ȷ<Mvi:O] cLtK{pp;,v",cGdrD1w afuq`6bl;$"`jā{==or*i%=/fy*<.BxPZ0JbqNx)rw-9c5n dNs'1 21e}Mnl9' Yq@: ?oZh[3 J=+.2tφ 'ٳ YR4އ@R֑OV"rgSρߝFEX]^o Q/?pçOW m~]X?Ff*VvzMYy}ʚHM6x~v 0j-iHd?&(S-P& Qc G|rZgy}vXe4 vb)?|~|Ϫ o}VZ6[~0A(X,SÜBۗ&ɟ΄L Yn +!nl%5ReafwUc_hf!i>):@a,oi5[x^h}JG< l)sBov/m42J{jL ;Df"o8IvXℸgؔxa:H\i"RI_!ȫ/m QV xBjk^Lk[ȞB?+ĢXZOOHʬo 7swV߾Iy;X*e*Z8bՒ2Q#aN̮Y?4!Jk+,eXJ.u}(t4pSvz}iyZW2Հvkɘ85:6].&),X[WLPEk4r4abH58YD|ДZV8QeȘ1&#nW*q82R2I1v늻_oHӊ@qK">zs0@Bx]0` E8Xt7F`s/*Ǣaa G7dL nۅp.ɘgg`Lsj(R`e5F AK4nT9YiD(w j% %rQIHj1woUU{G4/?^8&/,ה!}?p?X\֓́ku:(%}z\]VUPY]UAGcHyWN6RfKSs9 `Tw ̲F}"%ytzzl%t{nx8eRXTnLS)b鉮ϼ"=Yk޳\ Kq/ߝf;/ux,Uźur.ݬ:ڝerD4xAOWQ4DQ"7SĊ>p]jK(~!q%#ȕfEufqURafm~92ăƭ9Q>3csj蛥=y,L qsfql`mGl2n}^vža1L08ØѺ Nќ`&tnmCߚPs߱w(YPz2=[|92M DoxrkqQ՞8LE5-CW*^2+NۘbpceS)%V-K'Ͽ1ỏ/$OK+|UC+ec~6-,Jnkf qmr÷q`&4M}l !!8)yu:3=Q"=sZ֢e^'DV#s R푌c< K><~O?}JG.A7tӧSy*O:VQ5+~XgXt9<0 iȼ+{txd,Vn|<\]a9,߶];YB5ewg b O@ tyytjH A׿qC#r9 J>d/4g<]jOG<@`QKXD ؘ]<7dLd9ڄza"jyj5(#5SFYcdNl\n <)Zmu8]A&\>`n Z7{zoZsbqI>0S}3ApP!2sh(Z77%jōHW_ xi ]Aрh8U80'f#Lȹ.pj]X`e. &r(Aur&|Ŋ7*._gl&2 PE͙9!f]1ly:vA ˎ b=&>=>~ۘ _wcei"!D{ґӚeL*uIpXUZmVS 48 j Q<Sc7- huY֚\|"Ns]U).ٸ]Rx<6 *׿'$ps?wt><J=~v U}9\&-<;fDΝ+Uޫ%Ί8ޛ>b7/CNY{iMJBZƷ; q]s[ψ❦Nğ}ωmxyZg`6|x0I>tihJ}z-3:kY{ttX\S@8¶66'5f?7@-K X(cDbniʔ3O,4:bun|&Yi֏YbLT[Yl1 Zd(8DoLZC 9nbAA#mۼFJHZqHGYTM,gK߰m1q\qː5\ݒ2[VOnAQ@`eU,f_2(1|SJXRXtH#"؏!SqHaQSSdl,;UFGH\\lPKJ:xwGzx +|$`EYJFЩD@#VLe!A1݋H@#.g,h )6lHV5]G93q1 Zɂ%Tc-_|O?ܰ]=I*w䀞ve}^VOWWsE#UbKZ%E o;q鑒%$P{c ~h7 @dl%<}Ms %7| \ !` Qqwq!Y|4TZU`L tj(@sNr2+^G|x͆zG!rۤax/k=m\$䩑C,R*=}_Uw@ؓ'׀\ "D[`!_9lԡO^-eq?;f|Hy⍓Ki}^pDTF]d@ߙT>B(DT*xWla+e ]rhȑS'H=  o }u I:yy4d ,Q&JylQj23!^H4h,{,'?ԜOlHvw^(_Vb꠰˭Y'orηN}zT^ x4'-{6;.X 5&,>x~MB=@Hcj[w`x&[={`iLRǡ[PΠ1m\@,1FF`j<vˑsF$&% ocbeefY@UV֥jn q0E&_PX@`V![YnDp. ڈ+pĮ$l-Sfaqbv!jT-*)2"yî}h1T'Ґ@RX@t !H~K%LQ<8"wY*VHBEl 5羘*y$<TJqd`.}V,!8Y?`jDǘPӪgs?~3kK6nCqI8ǍౚW?"IATZUi-K60Pҕ3AP0@y?++0fOk*A1VA~cS ]vNFek;a H֊'V ?gOq+k fX-ז׃'w1#~S=պ(ɹ}soK-!@O;}_= ;k 潑\ ^[+Uded/>RGQ* :vbirƲ{I>R@ 2ex\90%Hm։@|NdeP.h"#sAUJX6S(f֥3\Bb,)K>*mit#PsyK@ B`AXcEi) 0(MeO(b5p1N*+REWYTZn``|ĺT)}f~[ē#!nF3>m@#Օs,wK\k#-T_;(jx勘7"t`M)P=6Œ\3:+_^:?;|e.@xV:qrZKS)9e=!W=i@F>Qi.&AJB˖rX|WZ͕BՕrSAVJ<5EbHysiDU*,p@`b(=#uZt;'ZsWCf&sۓug`_;s_ei,;Mp(eoq^늈Nc:I.  jɃ\γ ؘlJhnz fTchT:cٛʰJS$31ֿLRX IDAToX5hr8.)'EMf!dJd$6;bKޞPZP?ښ@$_(x]!S:X"p4mjM~%9eN{., ͒D|^ 8@/a I15wJ:xH'Q$DO> % !F"XTQ/(D<ezL\@*Q||-']VJ1V&]e㖲 I)}7'rE$a(Eԣjໆ4c'A2 btņ!{uY?5(ZA8A~ǟ?s" eR4R"dcUÅ. :媝4w쵘#;]pBd^6%u^<ȥo@7hƛZ6' $,*.%K5)P0՚* Yf4[,<X~MJcEksCĹ,U*%,/Ao5dE.Nt'5G>Xs S@$b1eW枔B;C@Kl?x~lP EIԓu/fWMݐ;em*9.P,!=5',77Fdi%~Dy3( eUPaä?3/%s!d`i|/h=c1-R'Ruי{r("30PLw}2$'a])8NPQrs] SS"FXaE,jT-P~o]6uBE>QMCrTSr> J#b9ᗒzv]@H\^pQXJ TWRv@L1 q^_K}8:$/À+EUjVZs[f$^fqc %rJS!>/JXO@ z嗢>TN`~)zu߾3g!QUFQax fJ#'?@UTX!KvB&ӥWxUqN0E(\e 'wohݞq`gmfB@ф92r7.ƞ1}T!h8 qy'V)m 'I[* yLL2 ==VbZ$'8$2+E$@V3sOâ֟tl 15Y{T R{)sh0 ϿY?o ۥ'+tZta۰%*@4XV FcLM.23(L@`L@:zTqoE9B/,=(Xj:RBĠ-m7ǁp "c /,f4!#%F52@J0ۜ-/swvvks&S0zyi`p bcLΘA<-;J2E Ȑf*tymn7BZ1trL^C@ tx RȻSgcEVr3k\?[ash?I}ax< VS $Zpo* d إ(՟ YH|~>ֻj[sgE񭤾qkK8)Sn.:mB72V&^YmW@%A@/?ӻo[x3;9KCA2{"q_L9jk% Σ셐SLV/^P)-q:;M1  Pv/a. ̩lv9pmfc `J[ڮFD5X)i  tL7\s`&lCN͒9? 0>6KI;H m:T,$ j^1@oqXF(bB Ȯ3N0Bm.7.L= GG* %n+h[;s avr1lrst 7*Йpup7KʱkyLRR\-n`*>u)< ;=5R9uŨȍ'" Ö dPvm 8ꖢu^JI۩@Y FPa|.9JfRr~ͩWIkF`h31,fR1Ye e[S"lؔ,jB΂Ti%T7 A}]5EUNAȪDVQxEEKiw_iR1H%d8 LTpr^|+2*MX8H4bLۊ0xcLe~̂U D-Du~3'R&@tX%tvB偦Svv-LM0w~LW%|К)vbv.`)cBOǴk?Лnpp0krS(zx; êjrZ%R܊345= ԻՑM* "1㘘1u<47jo\63]0pub%Tƨupove4)xS/Rdrmj2K"&6b2\ x`c]qh)[RtML5N-jt60Jq W\@Y0bbmh̠f(iMfj](4pKɑZtM(P"ETo8hX#BJOmzy֒ZtܮueBG338e8ogBMb.Xc!xNAJ2dS/!9IK0⇹aXQO>:@Enᐁ}DYFFu)[+.#<*62 7;UiFzj''d$f]-Ayq*1ct1 t(h({씂ni,b| dbz r`>n /;hsm|vv$VMN@/ĀDy-bztF~/~ԥlqiD'CqL5|4L b6洄0!"n5ĢV; L ӪgMq4KDzKgg*i"-5QS]C.|?`.qu/ C0$ 2CnI%Dy \*Dw}L#.:OEdKx:j(ɧZd,lPpk.RwP:PLxA3,WUmhz"[.7^ MEѤ8qT S 5!NV@˔NSOz7)CҎRxnb_꥝dXdvU{QH]Dp<ܳ PLXY dյB1\:e(tі;(S)jj.Hs(-T uC(^? q̾_Ȯ3W)<\~va݆A2@dոx fɊyq  ZApjV utfntgIln eWBӬ|Seŵ%Al,2f~ULK&jgq8,kJ̏;J5o+o;~a րfK%pS=Nԓmdu!;͠/ ?bBCm"r MY4^:;Y{O:t:5ޘZJlFx'KyMfu=7`!Z@sbI !J=,q ߥjQZC[9iUHFkd ֋9ss:S/iIwm<%kecՏ-z? Ѵ7\(_NWhģ|8ƪc] vTXrANZV8^<4j6µ>t_;>EHvc&Cy64eSjQ),Urd5CFCkX}n5aHe$@C02'֮q|P}+DaZAְuWstg`nܮP0Xf NpFv?p@EMcv7Mh#s"C7j P4^:o_Pֱ 2&_x‹чGzo`UҩPbbJs!u!ׁKgw}EB11恩V؏ 4+7I9oB {P8l@F2m )Q1Ro@ 浳5n܌5|h] ̭>`4k1P,g`KB`F,~vSL #Yn8eV+s3 ,ntɍ3P4drcAJ5D X{ya溆kfԬ_@}c?6m$hI#0 Vw+n`} N] ì^ rRKn$$b> SgSͶ\ig|.EPnryYKW;}Y1c-/x-Vk;- @aiŠ6DAˍ ݿU=?8"`a\߅3tAе0?gRK\ԛAṠʍ'сTKX\Oyv2eR d(~Į~ya=`*6({F^|ucq2uಉA1QNF} n44Xud fgCJf R2AhF%c[Q@&OA@ôq77 {m(0-;a2ˆnh#n,V" `Yv|

@&<C1zl/?~`@EHSÐL { 1؇]̴6D^IQˮEaڽhDݒ25 -.uR:'{ ,̌rag:0:i\VЅqĭM@#Hϐt#qKb I>۪I8t".SJ[0@ ^wq7ФYG¿CX}m|5Ag~ӧIsnU0ݵkͱ RFr%xJa#Ym[>#܍Dơ+zä13]==)RȬ`bLFķJV 9JA~ ;BXBW ڵDƪ!,~3.tM)*ȓ #@&0|ş~+b ҥKf͢m-f*^{uzX $h*g`*Lh>Uoa ]mpUŠS:Iw} :LZiRUyCm3$3 Q4 p(EUo{tdZ`'ح=@OkǓA!ۻT9h^%bjt.C 7D@>SV)Sz |]UϿ~Øﮌ%ێƘcǿ,&[e#|;[|O7_Ge*FwXaWɾi7{[ sB*Abؓ;cY/\5udp?A 䁿4=c@f8./FfG.H112.f9`.sfZ."3[t8/W䲘a4̩x,H|9 UWD@k7po 4$X{\'fꤦZ#g__]7kDZ,5\zCKwW,߾z#xuZy6R,>P`<0Օ8K[OیR6`ۀM!  IDAT0k, c-0xߍ*pnV|LB:oЏ񓽈T{wzͿQ 59⚿cKAS-Vaugo:{8ٚ6,pdM(C2%eIiBB D!kZ Rkv 7Fp ZHkq\}#τv(ۿ}Ͽ??_ &+bCp E ' r~4 ,-!ePO :3ΣK[sr2P2 rE1Hbq?n0{ήb7X奰uW݊cGOuQ|ަi%+}"ji[6x5`}`L~rÇ 6 xLŧ_udZ~];d ^^W|;֭!Z,B(MaNm %[vÈ"bY >\@8]gxZa #)ulSK \o`r+3C\hְ]fOS<_aЕaHt(az&I!F{gZI[;tmC~cYПWY(c cn*ْ͓R7f~Cc8K)c &(q> lf ~hL!dqiȅCvm@`x7lm]YcjJ/YuNK@Λ{gUVH=(Uد LX6b@XM {J@,jP0 ygT-'bƴDĊegn/JԶ;<~k9) )B[sx~>0J("K<'d艥lN"Bv`2;~+_ͣ떦^hj]̓fTdžif1lY,Kn#2Lwb($QO֒zR55L9DG^2ת w776 Fu7׸M 5[/8ʐwT!n+nj >s)oIFϩ+{ tqoE ػj^=}(k~Ol Mqk'Yb[r4sD28du5[&t. ƚdކWi-<^+ 9p'pʟ?_\ KY1up4qT4c>Qr^T=sܱ k ӄ&LWJڱ%][̮5!8/'jkN&oƒ|);ujsPJZ\M<ӉHm -Cu6@SduwAKoqϔ$q`;_)q\<BD:Bchs.$)v "I)ٵt엫 bF˯k $iS2²fkct)J iz-T|]3e{~!%/.5tꈙ?zFX%H!;H<^ _{bcA0y͔AOVBY3rK#t%nN FyGL+J^=SFC Δj1K4@vԍѮreu77#~Xb6'%dvnٸ!iNEѨCLHl1lz̩쳤{vw]~vUK~3݀n2I] pk }u_Wf2Gb!;<_Ȃ$:>s{!w[9dts_-!s-S>ʺfy|g$'Ϲ-o@)wQ2(N+ m74+H\,~xvUH󙄽*]w7*Taƻ8Vw󕟿6 RgI>]6PTO[ "u2 рso8el<=Wx|p&ׇIȚ#g8-w\D q'WEqSgq%9"^MTo7'{ S2eye]OBkmjiy]X) ^(4p>/4vu , Grn&烞&3)S2`aF7uUlDc99!kcNc*%%K5 NH.nR!ӟ1:H"Y>VS -# VZmS祙 M:<'oBqj6m7$z̛wo$* ;o>(\C$4szOuw0őwdܡy4M%oh "77gj3u`kp8 0j}\ =LR\ 9-w@vyb);Xկ~Bɾ[ &?Eܘ8 MH9p<~Za]wp'n)1JkX7TF؎,8V!DUv萋pd ,X_c s?(Щw7/}tPz~ux@+qӸWd^l8p`D_ / ͺ (bfc>þ'.s_+swnXvdBMu@/r4 w ׽edP7`p)}{TG>9:/$틋'[zl%SN;|ym|*ܟƧ;Gf- ̒{w}ĻBZ;<|zTu>qW*t\w6Heڔ^wY|B_7ʗƧǨa4dV14XvY!gnAϰpP!VF'" tm:pP haJqDdǽɣLmAVrAL=c/&x'\kԽnt"MS(6 5fW7)s^[ǒ]238w8ϻc;&?\#"^Bʶ5^bQ^ Lo͵Hsճʲ^e?jzZ21BDCpލ+E\JJ.KWVf%#/7rNᙚgov^ [$˚]^؆wCĀđ *_$,^n]XJ*_}i,d2ge.Rorl LAhLAڲHZ2yzܸTSJB4ȝSLjh3>hU }DPJ rNvMia`@,_LOBI G8:aBO$;rR)rQ^3$O, T80ܔk͉Fi-HJEܱTw+ sΧEH)A8߿~ ᄧOoKoӚ2꿿>pL%2/s Faذk"#6Tl8 _(!9Am81:(KJm! V27whﴽQkr>@wzj%Χ7oΜN*P뷡u-N5a;R9km#=Rw ƾU)%Bx- U+:V >W2ay8ÄK274pLP4wY('%J 1F7\~(kj9b$c9$f\XDZ2rgDnumfԳ9rg1`_#/[?<ܯ\nF9&%0L+ 8:)2A) x nӿ@q|ܪ٩ƫyc1d8nRuYj>~?ĦqFr|Z`7{r||d Pނ㔸sx&Q(uh8TO; F^͚/eog[nBw'fH..oRPmnBQ_0S^!L^| rN2utmF{B"[!C' 1O;2YcH5q ,fϾ噲]=R$Q.v5$$rreb(>Jܦ7\@yHGֈ϶ڐXrϭ^0 k1ȌTH,9q>HI wg0\7N\]j\C M(6UT`F_xЋrƞRsH=k1&L@ĐلLݣ\(q=ۮ1߲7Zݱ^Yrᴬܕ] ͳDϚK!xʚg:ݼ7eYjlS^$3xÇk,Xv8S575!7f{fw#Wj'u;ktWΤC8MyWfig ~w{S|I;'OBE Jd-nl\oL Gc3IrA.yKcBH;+^NR+L$x`vV|jH*~N;:)LN4鴠n._O@Y:IRq)9Ʊr(eI170~rks4dFS("4֒ڱYOBN̞ļK7$_$P]%ޟ9Z'2 Cxޝ-A&GN9glZ8ٜ\pż,+YY弐SF$p8Q.ՠּ[bZw}'LZ bݳNs{ciG+%3NꊰKCx5zB;=6Z9+3oU{8Lucfީ 6$L&^㒖Ltp}~ȡMhf|&U钎$%i-|o> !zH+ R3<Q,$2 l8ᰳnȧw']Rh7՛%qZ\:cw@cN!jZ`aPS!k[9zJqΛUKafS3I@#A $?Y)QBKc< &87pztь2.`E㬌k  ħ#쇇na@G q23[#l 9ZNpcB I)o>Q'0fMcG|a/&[= nDm.5?o\lʜ7$.єoC ^m!_aûܖ ͲRܿ|Jxm/{_^k _(JR%#4$3?2}F^ @$r:ol)@UgL@N@=}ҁT(RεȾ's" 2C!~sw~a fQ EVK$Ily9[QD7;'RlZ5wN9JHR;^SpMYZ3`f޿KmRruRpU &>W3<ea9)S7rҩcc)-8&)ӄ'=x;2l uek D(kA{3l(v\>\S Ldvu.Iҝw#sV7Ak޽rJGP;V5iD렱R4P:tZHڱ{͂@{}|陇w߾f̭KG|M—\<}3,T赹InrDx:Oϟ9-䥰 qG^N [^w%pBgt?ѣ46uovqӒ3>!]v+/t8}z1;yzujDD^ߛbkb{ji@@C;y5I4@Ju͈SoRہ}Mppx&*0s_k%o0ݩt3z~oN=X?IV1JZ |E@(?.DZl @)~刏r(o;[far1u,Xy=RYJy05SA8hr@J2MčĘ/2˜Q6>267)7'fԮ<_{S}O§+?}|'.BЂ~չS; Xz}]iIxv>=W~asqd"IeNUoOKaoʗkO|w.GSŎM,55{/-#3+ Ēh{gS1WkDՐ+Bkͫ sUeI\7GjЛKD$;[gZ{Q7ldW~!.0YE{"@0AXdt>+j,tI=!>wrdiԐ" k9EZ#{P̩6OD|v [=bvԠ$5JNG /1!OYCh17 s=з| ,a7P*晎c]I0k8R%#mozjnζWgzT~RLn(4b jvAK uWjG{a7h(kb.Rr5lvR+yKҍ@%%w%GJەZw6 r6j ŭ(y6Gk)_Wt ?}~-|{}xGZC\XKb)9+(d IDATm)R}]&Oe/50~uW|O{#R//<ܝ_'❭f&&q},h,@=_9prDGwI#RN/ Nbĵ}??ztpI";mi04[?7݇{ΏЌ2h:@vU7b DF,3b\f.L9&I?11TtYQ:+vmn~~>g;ߏZͰ7?}"qL,] ܖzM)@rݕ~2S:0}z>7>?kډvd=,SF3j$v3.f7nAqTޜs-}ڢ9aM|<]] DDz9߂XJSpA3~5ݛf-^ !7>9]rrm<-.V<1WbNJ"KDFÜAWYANO51B%Sl|H橷:琔a]fIiq啤ԍ˥h6EꇾWPm_1kR鞁')@ӻ+;((q E"CDf}Zoe/3z^}m|QoM=EIEHKLc3^+{iE2992zlD∬ٝSLfJvz٦@u `Эg3G6}w<)퍾7eV1BA}ƗϏdIܽ}OXڢ`aЇ<&$ [W'~+~,+9iSoI;Tatxytr;uiQ3ǻ̸>n||ڐI[dB ww"GF2#OOpD[ `'zw<w kɴ'[!vYpƑ=9(\?+}wwf{"4<3r4JjY(QF?d-/6eGdeF;2fFoȜB &Q ;4gZ-beع_I#0wBm[-st}d0cs%(DʫES쉁veSNܝ|}i\we*,trJJ3{^եUޝKK|DݟX aא0sbE]KUݡF§/;mo|oޜX l{rۻj-ys`sN Z?TNDv:@ctdt٫ y5o/S#uC$#洅d>O'\vJѥkkeN t1kG4.S+'Ht'ڝ Kl: ډ:G9kՈ CaƑ;-=0)j:"b7uN761rWɑ B›Jv]sc0HzDw@G27G=u۸\Q:eq>)eNw+`Ӷ}.!@3eޔ#2x9v1k-3dfxz3ubs:D:daC3 Eo>Kmނ$ݍڒ.X.Ӷalm7Iнj,\V$4s18mtyʮ呧[`F(!#º'86bڜ)'Dm47 H1j'#kX.`"eW~`#w͇88f7ϒ[ "ɱfFTfIKedK>M[=>ߢ?3dXv"08@-u`;r ng~A$VIs42cߚl--Eeoϟ7<̻?|}NKKg<~21p:)Wekzs חS kw9A*aGnta, 1dZO|~6vz_^: wݹ|]*_vjuN!XDW_b>2Gqe "Wt"\~Ryx{Ҳb%!f4-m!X*`Ԝt1vT$qc%w!M|_p\WakdT^k1''/[ġP.r`Ha/?Oosx&Y3k=R׸V\-u-^%sg*gSft@Ź^\"/Î?D6~T@DPf7{F$fB,nspFh̿@`zdɮ ^+$"s% %q*wwa}mkX9 %sZS߼++N 96b̞+&o>]Tթ:f9땧|. ww )5x/%vguک}2ҕ1{⼴?S ƏP01zB}j6J%5M3^1R苰Fv_ M..z $, v&E3x]5%aє- 9%./W+{^S06DXO%KJ0Zwkʞ_s#EXl[dC'GYƁXrn)I-#0}'4@eEM煡䠪dzH)ЅMSlˣ x)zQebR,rVd~n6v\V^#m~DnGT?ܖed+~4"Paø43Z]dqf #%#r* .o : 52φeݹ䍻sPwyg_Kw6k-ʲݙtk~D?D4/Tm#ke/S)JϮGzks9K)y|PWA6eXRaܳ`GH1zeΓtM5#ƻoᴼu'lFѪ ?30& u$9s6K/"i.IHGϲ9zN: N ^wLҮaxOx 'oޟI'p>&J`< d6FfE{UJ& 2J e"pSZuo'8 И?\yRx7=풾|篜27;hnunykcWynl&oJrjW݇qyy'.C{ɧR&f>+χ\LX9Ӷ+ZGiY:#/ aAĞ|.G3֜8ӕ|awп1VNPTݔ0-\@a|x%\J_Bt0LDcFMʯձ[{r}{*28H UP:έ q%yT^A|QS $2'ZlΓcMyyylĹx5gCJNuh0xC&^\(Ւׅ%o3($VIO9biJ@Y7uۧHݳEP6BĻ*ne&8PR:X\E~ ºfR\w<7$eJڵWytfGJ*V'Kbˊ ~r´˝vfQNgھRX8 7k\J)$1ӎmWΰ;s&X/&""4BGFy܎ڌ3e={Y|{T2%(A]I1BÇ*jŰܚJy28%qZFW#|܋tAd =# @dc@B\tރC㍐]sVB)6x.v8=]w$`۵w^{3$ƊO UzCJixb,,\LFwc*KhZ7<}׽?_h R;_= S꣉,<VCU>Ie`^rlwcbHr|)HSb6/%8q\w&N7tڑK>lJ1xREq 튗{g, I)걎#4uZQ$G0wPld#xr-zI@9&vMKϏH?xsr>e"+?$8 ~d>?ǵ{f3O `5̋Mଌxqp{=ߑS[=MH`#ǻH%i 5py*ϗ_vj0*5>,qVKe  ]1E"`dHJ\2% F5ѧ{S*٣%&[JQrUj2NK!)̚T #j7\mS̩;u1_e=#[^77F x,g2Ir$P0~D]|<)NӍ7"x#à!QzeFkuD9#tHHD)I*r"- kY))COZثtTXʾIO|jb{itG؝=0BWޜN%teY._qyٜ '^c i){΅uY<%_]_,g7~˺,5UVԨ}gѕiRRD >,\MSqP%^k UϞͫP[?5I'S.ɍHrbaity H+`N 7e ݞ>{IkYRhU8F=RD\>$\,CDXRYLLE]F=9N;ZT?j 0K|ùGMr0F6M(N}3g{ӏޞ oN 5s;E6P9dN}| #6~gt1=`:掉4Q?5L\^ 2 4.{)e D?w%Jx|e(;o!q, NJ%QV6"9)筷r\' N8tף;o]M`cV`NzFIg/ :ϣ/17 ok:ڛ$.q3yh"QUmww2^Cը,.з6m\$?m]KW/9H,Dְ #KrdI+S7,+Ŕ֫0 Ӂ6D[7P }38/Pm➉K$-} ;qJDpdƢ8"r&%B^/ -%J[-mLfP4"Ag$v\-b|kއ^IC.#ƨȲ #UI7`& Tyƣ߿jPTrpf|vᅠj-xRkn@ɸo/@IM:-s=~gCys=ǽqs]fNFc2?1q -z窝 c 6JpOO|.r1F,8٥o-|o|_8- EKӗ?wOù~!%㧍?}36m͌/W\yTs(sS: _/K Bw$! [@ 9J#5%knu۹\gKrvM M$Je$2Ȫtu4=$ޝuEID\9BFӘ ]*m[ne]5ʚx|޽׌ӕݽ/[WMzHuAjxy~a=J Y iQ. }aʤ(82ESнLARbV IDATvkیHj헷nDy Gvϸijŏ^֔.FHpT1m=Ehx%Nޝ&%a}/uv4:I[\wN)foN\6W&Ȓh9hВJɤHQ$pm?G4h,k&%EpLWh-Qb>nA%ӷF.{:QS3s}!`1iszZ>04d)5යO% a$| G5᯦JWX*L"PrdL K݊ קD` 2A-CRz@V)Q[ΧJ朤ñOwBf"td-oJz8Ⱥ-ŁqW탣9 R:~|֥ gC"0A-u ^u9 ^k/W?/{eP"h`d>ae_W_rS z(f|x[#M|BW8W^şo72^bY;t |n&_h1H@ӗ_9ǝ: $[D?o_ umԭ ?_/OX cw T]DܶIʥ6T;.gĘ)\;0q ]G =2SXƟ(/[nqwتe< Kn7``xFГ9^+1JPYU-!Kko(QqQ",%,|\4ѻBnTFgs;Gf(9Ⱦ͸42Wq-+3I1k ʩ!!Wpf0/Q|w7{|R9 zԽ2tZ2r^i.F9%3K4ei{ugk¯=샷"0H.T l7x*^-[ Y5,)1mTmI}Pr .WVR$e=ei!auÕG܀ 8WN$"Dz#A@&"ѐ0wgiݎn7`j!;~mLR I2"o2 lʒ)$2!6ԝdF^W̔u%O3~Қ+W O5#bRwnTh8WvbX(!}ht/ZX'8Lr8h#33rxu/ n͵5U҂4݇A=١5))26RC'2\Dctq3ϸ5N9i%J(Rh`H -R4\/~#x<A=@3"GkuWR.ߛe1h7;k[ǫAU7sr0[^ ryb#d!GaƽɿQ]219Iq G,|f~oxûBzQU{WjSZ%X}$io ˅//yJIXl0"Qz6\3jablA7$+Dc`#E&Kd _fuYN.&oPT J1} Z؍aϸLEGZtZh J+l]py2bWc`=y)Zk3>#zFdvu*- ^z0X4Zݝ?QJjzciAmP,#Bjr%^]2gD( Mn]X$wDy,K̘_I{9jE33f2_dqmw' >,t\ ,P;*Fq,xX^#xkdtm6Ul l,aNdV ieQ I{=ҽX>2bk^[|2;-E9_O l$ɑ{]r)TU{z8!OWE 䰧 4jˬ[D*G%FrfG=3u֣┸IˌD[7ۂ6$ŶJ FJԍalfC0nX&K So^Q *eWY7;90;lEt#ں7hE2Dܒ]90pWO,+"XƘ[+HA\ǞIA(3\z[)̉*'HM;μE%ݸOė>l x=0jks$E J +rv,&L=4/_ut\h |bYoG{]F64⯱٧0n 8lshH4@X88 D!ڒdUM=|v)+ZRNAdD`l^~?~Ż[^-l[Avɔipa| ?@ yKF >4>69v)Edv?="6쒋qͮ߁!ىx'wg_ {g\10 >Ěx@,9 %B5Xl?'nL8a64yoJ݌lӺ뿺*yJ|w M+yhïxj|.)?>7.U9dvdmrFH3nwUcRc#oŠvܘPkNS\ -ƴR؃}7|:Hf(fr,ӛKyZh4| zOq}kT#Qd-5GeS %S$Q"$P[Kh15˙^`BburDSr8_6veA݄6NsLH1کpJ |6_cT2_yuSNLW [-k>ɼ۬Z;W^P3?+,с5\/SFgBKî_2摮Z@=\2d=\_87H Gqն[zo|h]tsCDIS Pנ.JN9V%/BF'G譣ސgUu,RLBatLɧJe79cɓo#Vf'ЄLΉi餦L_ndu>7֢D)#ڣ]=4*lCWxFRkqR[=_+ϩBoc 5wɓpۖd{s#=a;6l)EAWMB9)^\UKxm꺾оmTڶѫ6xӿ*\jsV S27͵Q(UBJK( ?aavZYARks )8{$ QxZT4<ﰵ4Ϥ\bP"rKZi AvT4hqcg0͇-IDiBx:X< v-Z% #<ي Y㮻0&swdlqK^IHL#R{͏/ȏgx} MӅyz~py5MmVgEUR+y%>Y%|[|Q:!>ò@M;*v=˾jcOA=É_wר>LQxuL,EAN\o{iqϝco`znpXS1D3aݔY8miwt7NOu㳛Sc@wb{[%H`E^}z7̰˅8.\.`_,od"гЛ8Ccځ| 㱪޿)Gtmע(@ɿ-8؟vNcVɐSej_RV.S}:+[*{l+Oğ}qcr66 m6W7]d WR8S3fUkϪ9i"6nTF<)nP2ǹ`1$]{[^PC%S8j A'\>iJi7mh]*>tDKh [p%36oi766?zPIG|_![6J|%uƞOٟIE#)z½4"]1*n٧}tf6O\6pdo*Hn"d51A:ڎ@^%q1bUxH=CgwOctJ[ę`NfFᏏ|?-_}qOSv>Ogn,<]:DޙwΦOغ8 jxJp)sʇKicc7e*ǃK5Q{GN!g CjYY{? PO8 #Ď<Ց1Ou 9e;SZC'%_e-_=Oy|+)oyɼu5C!4`ku>]FaO5#_IX%@͍͗Il(y&)x E̯?9:HByr ]0[ zo'^$}Xq4HuEq$<*筻^ >>b23rb=IHSM6F^ńt), P A|iX x*%Olѣ=*4k,q9yUSsx i"OMYSHkO/V.ԠE i^EkJݓ+牲LX<5\Y YBP29`B\K^]!n{nՉqĚd{(8H^ H ɧjT%6"Tdap{'.ZU!K#pg$?;8.ՑW M{L Be.,UW6ʒU%k;xZzhM#e\*>M9r.H.:+prIL2Uֺ9'Mibq}GՠnrxL%_ZR(WV*⭬ո-3SΘWk 2;=:yI)ͽUz0(#F2"tz˜D],Zv^m `^<~@(>9 r}oX}ocS6a*T@[fl)[k1[<=3pT{xqo㎵kJĴa檀9)3Nl=c֪p:9c5uɠFP} cXXZ'gr[rx{?+tjиʼnf?[*9ZuS3a\]*aL62qKms&IзӋq\f)Aͥya`Rfwljۙ(o_}yǔmve5jzE/Skg*np坳Ki"2 .O SZ֣soyt϶]͂&HuB bK敒iD$L@V0]ȬV>"@$h ߗ<50o_ GWoc坅Kv|k6?dܱQnbV3Y -CCSZiEq.,sF&R B7oC5DAeޮ8%?5 162yzs ^lx#)ՅEF,f`iжv"v6b*AXmz"*?W]?##&q [\6I] $ӆe Am@ IDAT o5jw3SSzs N!c[ceӈQ^_c:_~oє(RvZytәŸϝBĥUP큯>ܭ[޾UO +p{)q>5.62`ՂA jXxH Ez+> Uş/a$9+/^ 1_PpR&ʔ97>?1kiᘡ \ 2=zM<uZ}vg44%5:؈N*݂mi@A[ SS WX RRyC4oԟWP׍zɠD!u$.p=eL )ō$:_V,GB gRWjkѾ1{6 G^ᬗ\P͇d0O g೶LxfHh^F),,S&>Wc2vmKH-s]aiє;ڏ縭 7H ?{o}J:BT{Ď1W|󺃃4v6|H,pl$+B.ZuX78Ck oQS+eũzUˍj{L?7` qnaه"kZ8~ܹɼ0A>VݎѭVg3*|p _<=V6u5iJ.0;̅*U oLod9{I)8//?c+yyudX4.Er)٢HIJruj}H$~r7 Vakⳤ)9q9m||\}rn,)Ro;s/y[n}Bpg/pͱ[IE2vB m8{'-nN)0 W5w:Q8aC>5qJwul_kFTg)IɌm$I͛qa4w)Tӝ&UoӭaڙKa9.T@S\z퍶6#vdM J^y2(m=é̈́thC["WM*%6ܥ:m[hUx2 MIނ)h-Z$Iz$իK0>>^9ěb,bF}5Y} #JCﭙ M%p8#ک;o%˟E캙ÔX7%&lނ_`)aɱ4yO\Ps/ex@TsMT)i2n54r~bn][zs91ܽsVtT Bc"ef3KAty ֌G'>W+ of*2gJ)qHSnһyL)¿+lqbe{W.NkHb2yT֜7}8u"Δ)I?wK\o>\x9m8z}x4faOQ\#GedB)KSKM%v|/erD,ݛwR8p72hXD>L65.EFg CNM4ht˙i*k w 9[!\t=׶F*~iH.8Ku3Fk:]CH?/~q!h&әqgkRPRWRN73h6oO7.9L}J|@hX"g ofo۟1%rW|kM8ܢ$j8 NltԔs՝ RWsW1 wČL9dE?f>{+{3"ӪK|ITrh͟SI¯:͏NkWKéV'~|C槯QUy:m9]`?y!M(CFjb2gL;e''z9vl5I: OYz,z&,l3BS VW|2)BXL-4,δh򩺔}[() _Q )iׂX\cd5z`1%\I%%tr`k0mD\j9xPfY%=Tc!xE+a &N J U0O]{DMp3R:cZAا %5$^K4WV* 6Ej#O,j=O &T77LB Y/jѲoVQ3qQd'5UJkJ]B3m=JbFZbĂlZsMBG_z=NXǩg+״Th[T6ch%gxXn^a)DZ[aƝ޵TxG]⌕%+,9bH#Ojc'߅K{i{ FsL-C(F$jޓ3kO4@)}>|8X텀EQ7;1.{:hZa R͓œw/o5OT2Y~'~k~k7~Lax>SoӔO%8cMagdr1:1`Jۺwq ƺn0҅۠sE^ܱysᆒK<+?>]xّ9Kcvֲ 3AGH頮ݚ0ϐkRk0~p2rL0B6?c7+oV"Gp-I I2al:̉iNnUq1#)i 6aki]o6,B*>*=a$` ނ? q0t& F%oS M|:U}ck>00Wœs^᩟ Jzu=u.4mLp{P ˛&ٺ'ea,R4%Ew/1 `1X376X?d] 0XvZi[\9CN]#@ Wtޖ[L2zož;%a<8Ά 4ܷ̈́=hIJG)4-ڱ8sG/;8Ž'7>2W,P~AՇa$c a mJ2S]nb)(|ᑿȇ>4 -nWS*>s3g|Dm<=mԮL7kcIxoQ7F U^z y4Λrn|߼8:|t1(,HįBM~\C0^QOO<~$ dVSRRߞ_NSzuOJv{bq$gt廿~?Ӫ"@')x;%󪼬گ>0WxjbJ>x<F=ضxaB,ə>_ަw Xz"vT28ez{D/TjsXOdNmm2e.['QMvС s:@}ױ!割6-w4,,exH 'X&eIgw#}ceksZ;b1zG|1DMaj%]UbY)/++%jGX`=]g})ZZH!Cc k-@YrSⅧ,,e=ѪߒvzXQ83[OOg>LJ+]\Is{>_#/ep>piFoT{<ٸiڡرjSS?yu`ɝG>|x?c㴺#7nW||O|] h I&WhHWKJ)Q[J1U`Kc(e ?S[maABj eNw#8k:]]pF7-a~hѵ&c[xiuӺ_.-`l)C$1l$Ėa!? 1TcHz?]2bhv%`k{wSX5d#h,ʺ3{;$H% b8 \,ra1elx|~A~xBrw, mukZyUGyX w̟zF8/|8l5bO||zfpPO?mNQHurDAR_QRlPׅTNΟy90Hr^Ʋn?dJƫcPe7A\H*m}xd~Wo(O߾ת EQėqƺ6߇+٨uEj!z ИJ^8S&tZ8,%rxfDκv-cԌ)I w4 l"߯N1=k뾺zKp&4Kj$ zqʻW3"|rY=g6H`mwO+ǙT͙D ˀhͿ%ns j,4݇D9}l;k\*"w >~I ,RZeZ%g昆sd$ضnTLhkQOW8pXr33ۙi*TS ❓>& e"lXDž%& $j#qDFvAe-Ssɧ2(KIy6TrMAI㔵:0?;0Fַ'AP'D2erʺ5ueʉeb\!D"SR"O2z08?-3O Rkg$(KA\vFO;$q'2Ȏl{k͊`Ғ9t{a_$t{)G5ڋ:%(ƻ ]ߥH@FdLF0~\[z3U6:M"Ε]$qQS@o= q#w}QRsv=Wdaj /;L1=H'y E+|Fm, fWm|h"`\+sw}*udcgI%~:gxs6 IDATwk|>r,˂vrn^8}J8%4PIFdO_?s>9/^3xfx:%ڜSiU݄Bu$ӮaKE,E`^|0XUνǯX7n_6*[1[3HB=o,Q$ AS_QŹ^#$x!dY|Nl8qT-V.Ƚvn߼AQ3Qk sd @f 1$PqH"{rSUjh8r@$3'n I!-np,O>>b'cf6%Kƿ~ȟy5Ver(D3>/+{odՈ#Ƨ)%$( 덾5 7aKc;2yvkC90ΔiFrW/\ &@'j> BRam)֙'s)sY ŗdoQm&Xs1홦D!!fV}_xAW}'CUbS8)j}u"aι6R v[^iFؕ]3i]!+NB [(I, >F p^;u4b+{| 9刓ޭ϶-a/Xa>Ly#3cFL8>&%Vkm!FAKEϥpK-%,lD\ARn0~N`זJٓiPʞ ]9 _ -/ |.~14H߸xD]+`%I:MBNHSr H~2WO %zG68)'e\lnfmt]gZW޾T't(Hm^E AwYe'JQ]n*B-kC'~=LRgV [J|_ߙn4B4O hJN#ZQ~̇;R PAUr2ɑjTC4ZKZJ5@2D! T30XZ dx.JnjXHTc{(%ƫ,niǧ o>`2ޏswg`2oNp?]r:?|?~VuNARb3͑hgџ|wdnke9ff?zhyq/Va͋N7y,~Gdfe  8-#2I;V+mLlM 4@U9Dw'-s_UUUf?9M߭B`gA?|t2TtI FLGպ[|zu"/ G/#nΒ Lg+Kkrٸ6VyYX9Α>{o_Rxo e*֊5g0]%!ZȵAsor\0lļb cpV 9ey@P#.e~ݩ/8#/g~c .[rjo5j0) GndŰwE3[5ε@0j>$a)Hew0?(y`SďZ'g_͕]˜_ŨeЬ*Izd!*{/4j}t$Yl|FYHf~~ՆxKB*J,4F YmĦT"aFm 2euc{jY:tq/#ݪ@ɨ_W4w<@2}~xCr ϏXO)Kj6mW?EW| gmBt\E^1EuJ!Pk%-圡vpM}um*141bk\84N@`GX V[#zJRg!=*f1>jHaI>M;Fy]Fu0O`8c!>ML 8 7;#qZlxKc&0w6gsy➫tLME}ɢъx!s^{hyA1=1 !($y?!z(Fk)`:T:%]EJzwsePgA?@*V2bנFp2jb͛Gn@uY'Q#hXBn1>̏?ySR ~[v`0b^XmW>\!ۧfi1>`MBR w kSwGqrL ?#jQK[;jm_2EtjZ0|ŧ\n|ӥpݕ]Xg_c:)@39/9%O?<*+C(yd"|-HF+)-؊U\Wl~n(̼AJaP.A4 . mvSƾWԘ+X}w?OiUV/ g}vsN_1SCVa4v͝VncdvPܿ]h2o.~K?O }7L"f:?C]@~ t7t{ՔC1q u0NI E]Nw FZ0 ~Hvr"ިo&'IAYFRAF3B8Pu;L>4ȸ)H@V6hhR&,'LKKsixtn6 {4Si> nu1kUI5˃Q\@qov&c GN \6J5eG.1-,oG4K8 a9PB%&K#'Z)}@!̛zAHI_Pz5_ l| ޏ E12K8h!gbJ\j*paD@;cxCP97j x?f*HFDB(ӻ0tpQ}(!Fb7U9΂ coAU PqqdvN>Ю9$Y%AkWXg1ELkOs1>$! -__ӟoLΜ~Tf~Pn F⭴\Cw| e#wP }ڹyl#t ̺"ێ͹)4r#|g]O{wy]׶1ʶ]Gi:iK"ߝ)Ӫg0rr2#_yFBYW([Z7%m[UTa9 _[rFiP ޡ 6WvHy,C5&s/[߿}V"b⣫qtCc&pDl)?SJZlrߪSTr054J~ HLJZR VzK\'pwp k9ĚB@AkBSИ봈S% .E9pN*rn犵VԳËOfЕ챕l=l3L'_xM)NuCLT^a8\.iUBvZN LN؉ƃ,x6F<'‴xKԾ[sPtHy[5nU,A^f=,r9}`iH&UZGu)hFZT!v30x8G8bԣ`Z-(I6ʶɅ)?1P Q-)@+{pa^\bkfYjmRDaX\H#怂EFO) 9)Uܑ`$GI̾9:bt^~%2&L#$c='BBY{jy\?G9gdk>?;T8{e/ۦ-3[0%;D^$pja`aQ<[(Jq2*tR%ak ڮQdvmP湯KIB:ɋLM*ț9u3u̿G,=rql$g<^zk#!APu wTrB M/0Pգ[ 4>HKd 7;kJK%-u+XԍAyF+y=c1#eBMUk盇˶Qe6_7*)χ}׾6#鴲1ߝH9bwTJъbb⛷Oܝ2KX$s}wGk*6j" &0A?B@Sx{@/;OvIȗ M_cŁ18&IYFBk$fd+qi5 !R` ~6Orcp:/nBݻCc]>q.'i]U}rrƸKOg/xtWP[?3qȓ0Ĥpu9X֓Fq~֓ Ԣ)ִl@ܰ!/Qʍofڐ-{bz92{`ס!6( 4 @sA Z!x=R,gN5gbPM*@q`M,!|Kz.|qfrџn FՋOMҏQlޱ1T y/>r$MTMMo[i\6d]D[,d@?䎏_.iiJ7n&Vaۇ|8>o}W'~?%o6G|5=E߰gFCgRuQduq^X8ݨwo>Re:Ln4<" O;*!<9xa4T.'̐ڥkaSӍlϙӋE R9gG"ek;3TYtb Lw9RxĎ뗫 yjEƣZfJDD: cQ݈1A݅46A(MPJ⨎:%eJlB"d#m97Cn7/VR`Xઊ8E~_kL$Y0Jc KN4^i8+>ELaևrA)oxy6K q4&3%'']ݖP ;I|~0xɺRC-h58dmi۾˧Y䐽YJG"xfn%)3L#ރD윋mof 3`C1L&ǛY(8x76eBPoMES#Fr9rˆĨ(uڶ*1*Cg]Hܝ).]ǣI%r>)S7}x[Zny Bʋ~{>zϯo TYdXRfN7/g4RǽR8^+]cR8Ucx,](ZRc3gN uCpO N%GN}<^v FWs2qmC|yIBۣX'L'E۟SbY9mͻeˎgu; {8]%HB uȢcx짟p{lcxݥPːcNBܭO>^ _~uU3Х Cػգio^EsۍHbnU=~/"X1uSSd49ݘApprAOJZe`MA`l {R1'ʾ2Tw,)IK4$zK,NQ) FA|=C#4j=Ntxig2Ñ ޅoPj ၽc-32)<88F6N #Ơ ܓ3FVhO߹8|6#Rut SMY^.\(^Fdw{C6zmN,~۞CAť%w94Nf00,C݊|hzU欫NA;£5~Ǥ).VvZDE5mbJs@ws10qiR𴉝}pp->2'.uea)WC ֻΡ6g) p"otBiBnу $? &qj֋뭰7% }[ZTRQ IDAT/3$P_WeW2UE |wcߥ~"s٪d!Gs\#όVoY֩B-;U^򒱔>*VRUT)SJh/ tsU6BʂJBRvKfYWMdy6 e[-2طΈznXCBp,f\§Og/+[i,1@4JԲXMAb(%XutRk_EZsr)4[heͤ 3!JR.ZN>Uv0#erݖr.j<ɍ!zRU|PFH^3)'ʭk~{ɟ_ ~tOiw X}զ1Н֪pB}/\?^E#ule跽E׳S͋SRD 5?neŻk}(O1HovCDgƨfzL_;Gh胴 v?fqb$%/˜+`)D.o1En'󏜗D:e'x|RMC ZãLĐ ު6R֘wGJ kFꑴиmR D`:0V&3(0'aVxΕ[1GӉ3UnۮE㑳ô@ۚ`tߌ\) zwY 3ŭ-XLd gHBZ췝3,ڨ^(&i<,c= s:[C}ZqhHX{[@ucb1VMHPÕv%M*OPk<l3"36Hox-M,ݺą.Ќ<<#3 v#N~ru>cp``H1Qۣs+B :Opc@qT9~˴H.'xk΍e`5!,Sd?_DNiݟ.%]6f[uzز-(VEgw!>KcX$/~DR<"b͙Z<=kZv (w9ۥJ*DUWlhCcθ@b7^R$ #݉<0ۆjkm\/7rӕC 7!짜?5AZDG*NZL EmS!++#;L qx/š1Pט8b$UhhD"yM(Eَ}^lZw΀i$e{髐L^ *֘ 51'Q\9!-MjrdHZc4]ֈ 9AMs^y"4~Ad`J m/ԢgRLNMgzj0p"0  "Μ>kTVeњ|/Af1T>pxR 64MOhTs2pL߿ԎFy{RZs0Hy9Z֦ZϜp͹2zi 2.wW=5h@8Ƅ%KTh ɫ 1% nA"~Ap<*ĎͰu%,j:Z{!EҺi *soFv >R!Kk>m,1t'?_{>TJFEB9/Ӫ")Vǯ_=-!^N<xه<Ѻ̷\˺N_ Ē\ƈ)uW?D+Q6K(aĜ ! ^,x l[췝۶Ip``¶z6Z5&/6eG(+sp׺XVz8g)4[- s^pe':]i I;_XBgWH~rjYs$#wt8 wP(4Ǩ3cc&{/wϑOyyTz뜖H2Ru.ɽ,-P[e0Rw^2'g̥`Wf֜RA2Qqj/O^~ZotXO*s0,kMN,YiiT$b]ÒPG[}zIa 8.u^1UXe{g?Kܤwj iؒp Ws%:J}8b$OӂѱV{Q_a[2 )1pJ'n{'~-`USGd8li :s8-8}up;P yb0#J),tb0*o^(ͣ'3p!V$`eDbBl͆)\: Эh]1{VXvWu͈-ы22gc~=+A0 ELWT^)/N繓^iof"JMc>!J,l΍G ݻ2rtyO%a"$ p0{ܻ"OZk1 0 iEYdv'|#v]5@_w'ƕ??築N{~߼y?kR4^zgHN^CY />_OXb|}/ρ_%/ׅϿś _=ܸl;a. 9*Qn7FO '?W_kU8r^<ы/{ڸޔj~!Rd\}!-Fޯ,,i"XTFbe[h~^ iy>U*lS\Qc^avRph>QDsݧ[Աs:y9}{ɋJJ9gHyQa`T *b;V5r+o*εI0M6==O=ܬkeCA)>̇/N|ᄒPO}6I9n{!>fcM;KENlC/_$P>XԨS}RB t{3>t֛2Ve?_/߿g/ G'twb*j>1 'vkpnڪȄKAIf!C/7sw+ە`tcU[dSAR\[E,9*K`&B".x^h;CbXWuR`V SEsv3[9g cʾWqh"򴰑Lp2`2jmA"x4&K$.f,w(FYc1ZdN,J{ŢԚ锈tAޭcs Hhph@mB;Myc !pҶ_ su4|! ᪾\yJCE?]HAC$G1oWBZ{12>临 /p0[%f{^S.A(,%R)򌺤RJN')4/=qXRdOO|}b~Of5'oo(m8LkWRL|p%u[ >DE-U|YQ’wG_W!`4Gy]0hM:wk$ =!jMcl[Y#;1޽Ku[?xတ+&1űQv胘M-KFЙ;5^NARd򎘌O?~M;_WO8l3tA^SN+k7՛`&A_-U{9N0V뺪*73ʊYs"YbFJBٟ?n  q{l[w/8z_#Ap~Gwer <^ Uhs @X *IZ=fcr1.D8i5ݑϻsfF|UO{Œ9zVyu\W!s1jzT.$["W_'_5o/ >f DFFF G-!=z4lDR2YHcPE/HQasua9鰈Di^ r",Uh 6c͛pLAJ|pZx/Rz+{gyq}}C5ѴLy2$ē=h8:S=b֗<t\íG_Νd9$ χ rhp3s9OVj/lH|ǺtҚ_@ .eiI*9 =^G4>jJZb=RF+!֤3jbT1h)Ȝ'~bG8obJ"/E,T: 'F߯gvn0**|LO /f;'L!!/g2%,JJme:c4XҕFxG e#$A50z"1K jŨbn0ia9L0֪LUf8$WqQ慀L5'D+Jv︢CZ"q]({3ّ7c)hb IDATԢ19Өȼ8Š8; &sYҚz&伐R"xPUe\l!/E߶EgѤ Ρi}d"ċxyJ|3?7zݨ[j]-(`7ʫD4}㗙;߽ek%pYukӶv[ |LoÀ8XNftcfZG`,*v ënٱ!۾GϯC+vttyoK&A{h(MÑL(EXRmÖrn2BN:;->ZFcqZu.+(I)&r$Hk [J!H^RĘTtfxTFkν*E ^uqCȤiTEC #p5sZ²,iޙđϑ/NDNW-,ޡ>~hRv>wW>[ծ%'<톞m^$_ s_]L# bӰ*u KWW7>zuBn͎V|ܳ,xiM9B〩zo+᫫iM%L4!Th5 **$ @o/>lH) ͞RvS7`K{BcmУ[8eDaBU (X'&R$ 4NKVpNģA ɹi!E?Ar@IJd`=w qL!NI1Rܞ{-5$.) 5Nā;2q2RRAe디t!}Ex31D eujDv릏lx7HdLrn V+ Iqݹ'y`nHQu٥CRrޥ n$̈(U4٩Fuw*~IFXW܆@`0}ZSȴ+^:xǛ #E5/;k5&?|sSI:uR6I+2ќȞ+:>c~)^ Ʀ/Vxo)GYʴA/aYEmC-3XGJ}G9Uͭ.a ƑU}XS<\x{KbY4nJ ^/Bt^vU0F{4BশfF.c$9z(&0(33@+1*C)*&]tMܶPn;s\N75N1?w)DK-XP:9'ypUNKJzo}+ܝ3^f8*O+[={7e&U|nlMB gW7 YkQcO Q ]E7 r['T a˵Ё}<]:KԽͻ+?S2Ji7u-zk |px介ɄN/G2}p惻ĺ.\gU#ŅV+{,50}"OLCG[` ys Wy$v5Y^H u5U 8Ao'k3S0zBl"[2q&Keוw{=oNUde2JVS5o;!44ՠ&Q&" `w~ikJ,?<ܯZRODIUsLe-B)ybX>:vFE]JS_|`9/&).}gSEEC+zoQIΡ͑)4&\I|]dJA2tU^;=ZwfrBw(L]tʾDKVj*(R Xgߺ7T )KPJDJ\si#ݻޚ;@|l ȣn;W-W@B7%_̍)jJ)#@FRE'D畆98Dyb pm*Cfƨ\Wm]>@ءֺ8nDLs3%43}os\Z(uXb}znxN K!~R*?ٛ,co4NK)i6%XH9Rr"ƨB~x6C 7*!ch'(vmH&41CE0~ m6AD.ڒA o߳]eGP;結Jmĩ}Ʒ?n<*¾~6'w׊7EU%6;\ɷE,^A߫s`8Cx ʒH?n.՘uGv@O7yp_"6 ) xׅ>?= 924NTXfrHR!ם~Y.o)=+̃O=TDKj e91QU'l)5{*.$\E]c9ky]^ogKЩUؾX6*)IܩR3rgl ~|DX#Ug(1Ld<'Oo؀:Ln|*7"8`z^&%՟y<󭉼RrgVB= 7H`-ZoЇS MDRVj`%0d0fuwC@Vn$wo3&>;x 7AuЪPDJ_3PqGAѳmxshfo[Α56d< 'X!GRLYRl_7dRJ^z!ER}ԗpUFӸ8«7'+0mapݰJ13 ad z﴾ob6'^MۀmmyA?)HH0h>lgZ,6d[?/ެ*n,EZ;c3>HXYK|<I, "-ƻ ̴ޫOE'>" n]_KZ$uicYm$r l͸;%&(EO kWX /ՌSg'@Ӎ^Kޮp{|.bZ0hָ=:%}Z ɚF'υsm ""-+ravMDѴ/V>d|+d_bqL^MF`@e{`8nyAdaPdG50xN!U&BPD甖LΙތ6I[\2T0񠦘4zqF+qS@cޤP&ONf0m"J<+E˸`6ugw}u!hÝ :љ? FU]٭IRt"杓$}TW7 g_oJHZ]aҚ;$V&k-F\<)mMʚT Ol_d(guU!h P݉ I2 ) shU|_9,?=`ی}p%!Z%dc4 snO4cq} &{Z7IQ'ʂ<T^y2!Qh#}z_/7nM)Dޮ25SS+KcSn[w*' _]9-K4sz%[֎m~>C'DYO;˅(OzݰwZ ˉ|YfT7 &2TcHƼA cV3;x+>gf(tFA%FzbX?Fl`ϮyDM*x$4_Vo!͈%k"q8g78kG#<7AxR~VJ,3s9ti=~T8-|IO9%.UTa@Fy1.yxGkuXhxzxz8T0\)Z5f@ʰ&^Қ!$GHnBs\{22 v*|=/p})g`L;09<֝mF#˭ y$\1QzHQs*V0Kݗą:)!89i檝@o?eT]r 3 r,:`^ވPbLEZ`sыsZIR1jQnh7c4b7ѿ#v[_(* iDa-MQTKNNTfGrOZEb ."YΑu͔(q|;Iu(JUPk\)cFB\:߯ܝ"o;|م*A̰^LJm6x;߽{^?5]}i3EWg_2vP]/,rnU:k/ɤ輽K֍:\e&J$j C#51F%.Ɂk_Qx،LJ Wŏ,H^N||~;'J,)rmo?O}`_~K:_0uZKmÓ 3Rft=o#q卣Q$k;_6 ;~*xk9^0pp0# ̢qrJw{9+ȣCW?ЗgWnmg`]?(f"iKL#a)qTYԯq{Y2)h"qф=H-BbYWb{80ҰX41?̅}~\(G(GHގX^8 /Ų>W3Y88pD!'bwt'!N;sszZ,RD]yYYc:!9TwtRycu"J1i٠%*CZS=xk\dWmL  Α$>'1Ȭ@oX(Nc1Fc]=䀏L˻k]DͨlTBɶ@k5-}Ha6R:jRA W"q:7?B4R,IBgh Bд`? zCMZ|zC/c35sӲ e±{Ɔ#])37lĮgYi|}*c=r"ڛXP92wG?A6p^N%kS"+iQNm#U{c+ XTJZŲd m<>t7>m}ٙӚ]!ƪf=>];7!_RJOçRq6uB;az|<:M6%W'~zG;,9yu.|x)0 vyjӣ~v@mУ6;g^k:HקLJuck͐,N%z]_>8o7|ŵ="d9ׯ+כ~+?b.M&U0Uij FJJ:Qi~+ZV\9OdX1 cck@sW|Dd0 4%p,-ufJ< I:)l8L7Y@h6K P/Zd'8ܜ0]]T#S]:z29riߓA/q Rus'/7;-:ϰ1;rd'w6Ҏ"7p ‹?kۜΒ m>9*:L?tT1Ǹ@s8>$,gh1;/hyCOa IDATQܸd72Fe-iX^63m߹m; o 4|F%IF CmC$U*8| '!$T &}D?Y=d8i)MAg:,HV~62X`>j!i ˼;Nttf7&hM:p Fx}1yJEv@$UhAnakk 6[7B,rZI!FG['&o4%kFw B&dMj@X¨Rlr,s>|<)iS*әrZ8犮QÛ8X/jLẼ7y;Fg\?>5` Q|*56EcUmI)GJ vŞ*s~Ir1 b*כ5s3^/>8-Rpޗ,IrI,Po\+9h 1#*qQ:r &c n4Q?1dב$}1iۺU.nF)D!Y. +.F<$0ɹ:FGA^&1\/ncƁ@3dPqg1`f׫#sw`ns/!Ab6*FឯȠ'Ii#>:Bcq ;YM^1\=83,Ȫ!Y(qf&2zMйS6Uy=sҾ(+ gQ< hFB-T콉<hrv? XQ?hmn]ͻdslZ%%T#BZDJ8̰{8k$J91Ƃx/9 w)u7p teL;(E؂#~Utd5T)1Lp9VdY/>WөE^G1ɗj t>L3ښeE*ao)B{h=j0%jض06GwPN7i`0jfҊ/Ch.n"9 8W&ʱPU(w[/0_(1,פtLt j-6>zc\>.M*/Wş/^^9eIjnןw?y5yUrмɘMWDiS&'x [j0>Dy2N%ȟ­OvkW9ȲI6?G9d޾= OKt.|zVys 9ۘK<"%%X DxmK)鵱?@X2!d,.Uƾ]δG]7;;o Dg6ҁH{’5N;uSD<./b?wϼg׬P| a۽E/*0_4f!9Ù;Oخ?> ] fGM< v<Ϝ(ٹɎ?{c>[DV7T:^ܞMī[ynJcPv=֏_I89hB#4]yP:I|^{s3${ԘV!G^>/#/?'pwc=Iz<+(sUcLzѺ.Uc;ճvr &9ӓ.s er@댶;\hNB~4Uf+jby][ !Q15BlcFM/O8vghB`p4К'!LJO\ <¤H93>.wq3\o84G#"'jskfG=2~hb /Ͱ!/'dCj(~X4ܪ!iKue;"@AvCEk YfU ZVRV8sB{k]7Ș_nWtk©$zDrnЙB4IR!Rf\ˉTd;ցP3KN>aV E{Ctw&,U.jr2}:(wO))޶Sɪ:>q?w?/~vi ڵGv]2{?Zi#rEӇV@+xغ/V R :'6Ēi#u:/%>87O'^_?sGƒo tx`كwdZy.ʎ7kI6^Ʒ%/<^sXԧ;P5j!pK. 7WVtn}L5; 1ˏ6yÅ 1h"dQiъ/&J YY< /p}k5 9VH^O |DRas gC 7X  A`vcW)||AY0^KvNlj`y3tWZ{^4)ιF=^aX"AY#i]ó u 0j%;zlukӣdLeՈ(X\<Y`)Bvk?Qp Ѵųs.TȠ2!ljbp*;.{u 1EEX݈ XB ².`Jfٷ+RZ&<ԡw-rukfzϟQNĹ~)gOz#eoSQkv[O˒h> }H7ZtW9 jN*{ݶmDvmu_jSH.4b2J˱u0͂bE2B3M]W~Zs &Xj)T)r^(2/<&9[e\>/k Ai[ow~v5q9'.s}gXo>ѻtn۶০ S{'&8/˒#糱,)~H)B{SΛ7_xE;c(5ӋT&**4.6(dSP^ݭBwZD^x5%띔o}̹DƘL *@Y3?'j} EkT [D'aUڰu憢qvLwL\#Tnݗ ȜHyw1 ~계)9-̻< 6DoZ4}bhy]xMh9 WFZRH a3~aG{] RN,;OcC1漝0GOs܆Hd,B BԨǦyܲp51^{}HZVxLk5IG7 $i zJ޶O"3,Qt^2{!?n")Gܮ70(g\XK?I!SzJ(]֚Z(%RR<]CHGĂ+1ei1Z:zW-4ڲٮm+*SRZ1 E{ 'v31Cj]T/^%x'ۏZo+eIT<>gޞ wƷlYR}S:xu.|zdZbo|x޾ZX5)o7EuMS&7<7Ź5C Ē]+N볯OҨKʇOs@;>˙Z1F{o3c}m!2QZ-R] Fp2<fGp=<3ӞAEi8-8!i\' !! Uv$33e4GP8Q);τO8ijEsGĕDOe'eZuT5fHQw|0VbϰLUeD, <61qnGnە\r@Xs3aufZM֩aaКLΒ1$oG#F\o7Zf.Ӊ.M=+(E{A,ϗ,jkXˉ^s n8#,N1=Y7x&h>sSh5VjehDi20zu#‹{J3yAp^ ^[Be+9}I7ʪ5ȺjYd4ھӣ$*"9= i{6߻̗*LDBAlЧ$> 3"uAbdQ䗒Yו`>VwZ+B8AV&Z4"/nÉRVfs?(ԅ.Fpc|:*ˊJZ{g>Ɋ*O>j_" (&ztޅ&-iLv')@HU)ڲB<8!o&=T0S.s,kgc9`h{FZ8#t&L3\=G'N0) O7Ӎ_c {&JԫQwplU`kO;~_XTu%s=M>xte^82:7x5`Aɝe0K8lI|~]D>]+[WB€g~~U㲂@m "4  F^]&'oº~x3t>~zb=Vuaܘ3mȉutR裸?'oK+~$EUG@tt= LA 0yh+DK$L %.?^!.KyWyM#H[“J81TtqJuUӪFn3K^(iOb-c91i}WX'YJ\J펞jRTw^tJ"KRۡQf@XM=i "/(;;Ȋ -) u1.C#bdd 83*3ۮ h7&)PЙ0E/7f}IfY_ܕl[5u"䍕rb=eفdXĶ+Al׫| /FnmЫ9%Q~CX1:6wDBΌѹ]oz(>H=Kg*ÀgYIgY3iu"n^T9Eq]Iwqc*3B}gAhRŤ_Lz?cTe!=J8*R@TH[HcZf$B%gdL<^9?!`Q=dCxTՈ7Ô|)ЬӆmL1zÊS:S(PJ* F-UueY/u6GSY.+DRLA`n&ՔҀɥ=@lϾ ZJm'ڛ/:(rN*OEe뒸5#Q}57w;>=>n SQ.z tVe?"G0Tx}wrLw ֍s<Ӷ?[o?#hi EAoo?_wFr #F4\yRŨU8R D_]K9HhK * > Za'$3zD\1Ȍ9;(xxN%׼ zN> ɢCAqq?IAZV F—zW?e/ GWL*R͑[7Rd:OG.z?ͫ$[53Hڑ';W섔uh[}1ڠ#N^.q3(cG5L|=LcJLCtRmuĬ3Zhb!s NR#ghV#%1ʷW,BILnGmc] cvi\L^H%c":9G!z, !gSpFD0 wۂ5} uV!8b>JHwow/4.NN?x/݆8t&;s-cY~EILN64}#(KtwrhLQGԽ2DFugI4 >֝QۜQ&?z :|H:kƾbS߉F*jryr#@>n~ no\tI1zxƼjlB }UV=C DYºgGtr~'/(y !zyMrDP.Ϧ9~^L(! {ZL>G؇ bֹ?,~W+>J!?V{ڠ)1ڸV}"[Jd>?S|;򞯾8S|v>xsMt(F0Aچxt9DX3,K兏vҸmEGbjv_8ߞ$yzeɉiOTn뵲 tŅ}mWߪtz$-4w T%W'Z׿u ?JvrR0&A㣂1c/ϣ"0-!a'ZtA?<((%$jp#8/&xs)r0ݯc8Pj7Ogl" W =>xn&|9f~cP_'x"{kNwR"hrN1J`&~wJq vØ};VjSeK)(6eBOa s⅄|f2(*[LL̉z}bX褙 '+ A_C"ւ&)vl׸f]XbݪGD%o"HMVa靜 @%=7g]"Xu1cEIp21_u~|?71du]Z;u4JRYuGlN}n37dȳ^I*BAi CS ܫj?>R0G:G|4J%cnU)Fr&O u($0O:!o.\۶kPH6<}R~6e=N[`ZtZxQخ]WZ{p !9l`` IDATW̺.y oMalS!DM}ۍ ܒwi]3GSDH%F^Jsrc\+9 5 WzqZImȵ)޽h@g|oǩmgFqJ%'员"K\VY||\ wYgm|=)EnS5ȅHI!q_"$uttﱲGxuo|]Z+=gW)~lkͫK|: ?~⛯_,n{u{uu9 ^_jx ޿#3)bYIK֘Dq&Yo^͛;G/Wip<%<( WA:[ 7|dR=}QpyP5Gv&sDxh6)\8|Ag&,㷨#щJņzI]I8| E]"7( V~Aw0!RƗJ&EϼVډc"/ Vdkt{]7rШͺS&PbZSb6,IXf;:KҨ*D;PQvJwD: zW p,Po;iWZs~Ĕt=;Q*8sE:iJ)\[cR 4,uP>we)*Q߆U.SS4ϳ?39 &^(W3闺й?=wSйNEElrAFi1p LY,39\\^0R2) "'J)i%r|bHDw/2rb=YN ihtN:Ib~IE|s4č/9m rnPJ۴x,k=x;Gy~AZM!MHֹ`:Fb05'ϫ`nFY q.є`aV̂#|*lGz"8ݢ, ˅7Rz_AБ$sJA|`0x;yd*aK "y=Y#Le03ˆ #"=έv֕Nvj_/ެ:1nh5º ~Ji߇O?{$̧qnoNާruS#~ xe dZ2U^OVxU~pz3B]#?;Χ T`wOےiFCЍo};e>{'o{~kzTINY9p>xA++?}Sw8_3Jc;+kI11m(Oe]񰈄9y>~q"#A&", l(nC0U> ϔ:7񣘽AnVYy_5QtĜ}{sxړJ$LOu^?f6Cj31F JJ,KAޱe~G{JjFNc”ҡZOU@'wdBة- Xr@"t}Y,PV#\3)`wwm hx._Ie>b7дc郔 H\2Ð^8P*!\& &aTDv9‹e!'Nk&cMt9AMy!fgմmC YVS'HRiz1J{LYMQԢ\"musϖ"uCKp{ޞܾvE?r",dNfaD=EG^M:ܷMaAƜ{ %s^Y |4+1xEzk,IK먄8'Rv}h&WQMIV{#&8 )si~'T6\l c!{gn>eF@ t$^ A~r!fӍ\uɍ>Z zotU† 7iU>t/vv e!׍vb)e,}RѝpѼ\?Yo,[i}ntƻ>+  &0JDxFL R%fPѤfv2Pյ.ϵcm5kW4sX&ݝek](CeU-9&7V>U|EF8s:}YG6،JNʘՆ'q:s?MO&)7|ю_~qC?&&g:{j&N͙ۛ+ },^p{0cxz9p8& pu1P?~`,=8tLKGW{n.n6<}rӛ+./vg_'xsy9"4uhRsmy~aEWɉbP/=ʻW'nv.=W<4Uņ|9mX܎y0tų~l9͛6 K/d|W/oQg_wT;fkՉ₅29n <+H=ˌqG#@&J4s;RV{ĠUmJ{c?jR|l3ـTW vp#O,WnF tOF5oX.&6:ڲ?n&r"rF6cHr. 5A?#lSŀxv!+TӴX1 15]*^VXe=Qf h "޼`m]gK1m`kAõV } Ibٶl@lٽ}uuב>wZU 4zsBdV? F%) }I&(4dٵ~`!#x رL4#ө, 5pc)wXq^o$F"'Zi岉0l7>FˣZ v)G(߫-_O .V.HS;c86hj-L{ zst j"-D3Ll^;L EOުw=Bgz@ B6}ojK-ReL8ԬFgNdh2"eNhg2/ 'P>~AZV7fB)#-uɁ ''/R[*e#w}F{Ng'^H6t Dcy6!xLֳwGQ$":c0trge:I>SX>AbTk|(Us֬%ȰJ)AUP1 Ю2)j@9K|áT;/m95*m]ɉ F 6~ 4ZDn]VnMM9EczA }R|sys$e\9΅׏3{~xtDU>٧2_qR~߽ɋ >~:rwݫ||}Gv.ґń~76]EOVe34_?( 9'1}niݑyXTkhc/Ч-,Q/xU%g?7:c1:G;v\Хqa4vfîuC`ͷ5 2a}?1SO/H_=o9f g~w<<a^۷߃VRvR3Ѧbkn?yDZE(ԴZjB}AhKVZk3 `3|dʬ z"Rg\#qrֲZU4`>bv4$@!P5ZZп#A5+A!-WfDD3Sx34{uzi-#*g)#:Ia1+A&0%iC=w Stbfv\[VUDz@5D`܎g:yq V)+#xYN.&8nFcge!TlQΙfKU8hYVÎI$t}OW í̲n4r`/zW߁e^8T]ȹ+< =Sd +_β֪bC[ճ$nO]dA`2O$Uz1='rG?dEHMŬrXURsou5Rl_º:\lVtěp}S1d\(5UWP2ǙԳC9'rLbʝ8oDfXêVkHg(.f0OGAqU%0rgZc][ڳ$עX)Fp\䙖Qqf˚=]f7RZ^H 7(gZJݫ8>dqXoMa VT,󰳝tfUƬl;׼?o]C+Wf$m,]~e~wo=lL2]Εf䳏 QLfӖ1snmgo׈S,bBd\Z }F ~yO-/~v`1XM ~v߾=Icar_|~/>M^Q+3f<ϑh-?ǻiv%OW||W |Z\7|<2k3ۋ|Pj-,{{+>p%bT>{q/9+oeyܻQ-η,®Ff:w2j&H:z(I| R `E^~ ֗ . |$RE ъh3h"wH[>dݢRP״t|ϸk@Y󮂇YnS\+o&N>\MA.iD\֧>$~훁#[LeִȪ)@`[`r1 t>6?c-}M|L,RwI[k6CjY0tFDT%qy,}յw: f I OjਕHKYi>M =ԁfd."))9AfF9Iq$ͩ5tɲ~MgkX+[49W8h_AE۾KI%q٤E @dݍVě`F6+:,nFT~v Ϋ<lazj]ftKB^^T%*0vg-ld:DڱL3 .ʚ%Mkma=ڂJpVxv~'(t95{i.0&N)  eމ6 ~f_fLP(cRn/:6񂯿~{6}g⪞J)#y(y |BY?YB+ ÁϞoV,Z]Դ9M-a'W*v؝]_n ɢ=2.:n6d>6k/~oav|z2cHm˯&xW?~ ߽z7/+\Y0+{ttwO+W9,'r}I8\t{\۠b_JPcJAgfP ?5_y`_NO.iO׍q$;8ZqMuD]"iGYIŘn-:􋜄ɸT#()_~$3Qۤt~D['M#c=6<p13P_,-R(+eIq# aOn6/6G׼x~ŗ˿)ZhCoÿkE# Ų9u^") 8~WVPqFE^AImzO{t/t LFrљ*ZVB*h5ʕIֽ'>2ƈεf2d{ s.=`}73D*E,xMki^T#;ݺlVbÓ7Ҫl74':\Z .x{]9u"K5"cxsɲOw9ݎTNN:"u+4sвMVx;kA`ũR 6~I` IDAT.jTX .$b~x&r|CjFUX& d'R*9 RE ];ݾRldҸ0 YI*`-Yt܎tf%X|?߷{ȆvU,X6@m=Z@9^S+!N1_luvL?_߱׭睶+$r^ wBߏzӛ˪WKa:ˢp?YYy[wn6Řrx*~ IVnH#W.w|W{ \nz.6͔mz^yqΝHvT8]jak޼?pw,=N'g/DI(T{6_)ko}n?QVmMhrDPEH%Mը>DPxwo.sM.wG-S>Tzi1ҺbulLC>Gzz587aZۯ!Xaj,E?&1WղI"URyBk@N6uA3){Z`Y$SW2:hIJ"QNe4ndV Ts%gjem;%*gv7 ?gYӵֵ#8vha࿲\}S.n7%>ycv0;vqf`2WXVPcQ'v\\O]fфH^ 6Pi1JUNK4W:Sg:.!Yn6CϨD]tVP q,O ߽z~lW6L$Y޲Φ:)VYиeMg)j?208U_쏬6}rR#OlL']Ϯ/*VHc]6ê<::&\Zpe4`|, WҤzv?Xf@ڸE%i)f$k+/%d Cb@m  QھZ}g(.ӏ2-y3Cΰ,,>shoO29gI^q$Ӊ4;6<mV!Ņ)H}k9+wȺ{~0n-j5I3ҥyeY5q-ՂD\mCrQ`g^-dGQLu.HCæe-<֧lqm}Y¬ &٫@UpkTH4d}&J ߢ)?:W+`LdsdVЙmϟnϗ8-d$6zN w\a8gޝLW{>姟zc_bgVMߪLiZfN=o/nw:牻<닞a4O9O Ǚ_zd_` F2,2+L6ٽ8v  RNG^к_zų _Hj)3ŗŀQ*>t}OJpv\-"Ac:~ (FReY*2:$#ԑϣJ}fVfx->iq_u9Y)C $ )[WsT&(D'Lj4JHm.\윟Y{( ձ]Y̊8;^g曾/?kG\V`Ϫ96D"6b lO_\w;̖VtUvpn7p,ƥ`T`+w=ۋ_9}gqNԚ-˿}I:O?e7&7 tӴpx{}oޟtÉo^>d`ūh",wcbttȥE`:M-nI-r]GR9}6|mFyT:5Bm7Mh`9QXH%`iK{mTJ KR9,Sa 0?0.{{@|+(ʰ١y@V_1= : {&@T ZdvYͮ,LPӮ|HwMh]Z5Reղ:Cq;o}؍Z܂$E? wD@mC-&3&QdmC &~y4 Hd{)9jlVLZ.^""M&UH9vsgxT|$"HױRL2g@N:r70P#ud_JU&sQŜA|p4XF!P>\7;ܮz=u;'g]aʻvl$J:qVfYY@+id1ninh`0$fsSp4 cuS+H%u 4ekmk lc=l/ꙭ{yaGQcEi<ed-'> jp$MKЊaRe+x5aH{6wDcI|?pӏn:?x~{_x{*W_=͆O6<{gxܟ(}x _8VXH9fz˜ILYB!CmU5^L*f0g_JxwG:squӔmN~O|«#$aNN}1֎~:UϯyaXrrxPT Lk:d~̶TjD^B9lcǛ{Dr2Q4 YywhygY~ymtiP]X\>"ٸ6,hMW0 ZT7t\W_ӀB; T_eO~K%{M~u6TTˋ>1ȹ͈Y&vFpFԫ1[96R=ZmޚW_NiY t<)bx ikH" 8jߠP3Aiꆻj;w3:3$Q4sV@U"aZ6ų֖!Νsg6WݳB`zD&xmPwV7Ay$ awSo<)縡&>'p n5~; Dʉnzji_ / {S1"vƹWq3@%}AdՂarbwSc48vbeϫW_ ZuCr~F!ĊMz!8bhl%1|kgM ~Of=fk[)tuSW2޹./-rTeimvay|톥urf2R YU,;/p Inw<c'|{gŬHTe^zI-e.w\zypHBrO2/ wAg}˿[Ϟ_O͛=_9/`f?Uw{|MXCaY+,dH٦8vGoBjVm!Y<%–]wPv9qJ6O?e>GtہON.GO.vJ:#Áݓ[F9RHtC9.ɖ2X` uߜxn)L3[޿0l><0wڌ[[C;[ebkdmǝG%q)`1'&A@v![:o8g:4hQѳK'_qF5Cg$^Vߟ!39mϟ䆟w'qf|6D*\nmբ5VyH8ʖEt ;0"}O%CfX 1QT=_|x7>y 0"B]Ιo]Z7N JsĂEeʙAh\#9פJ j!QV@:=Y N+H6!si&0LrߣATjTlpL* ՑV^[Xp`|a!G҉kG̒r*ENt>dFU[S \qښ&V΋?{qj4ƐJ'9u []Ĝ}b\h. -/NT$&Va*,jVr=+~g[ z8[?e^;6Oyx8.ܝ Ky>q:ƞI;Owd7t 3aTig;_mx5)~ʰ)sg±=]K[xy5B8 .g3XwfY̓ۧO3aϸk$w #>s^:q0\>x)@idfr8<߽bG72)D8f=myx v^.ef|"[ꮫ R܈ظ֮a%>cш2JGtH Y::xcAze[WY9CE!Ρjeh /K57X|,sj3n9ΕӴgm3tRZLT50v.*a7{sTfۓ,~,HXZ$p aRD8Z,{g]DiT3N0e%QeQ Ffk|v*^vI}vb朝o< =5RXZP)A|YEM+$T8ZZ+:PikFe>u;wơ%]&1gٟ[Flw@3]yERц΃Bbhz}xH# Srs4A&GFIx}㣜LAqI{&5ǏDK8z0Yɿ ؋n7oU llg8pLA c9\gH!rOF0vt~{"?ek?V*|q$ZU%$7.=w?^ѕa]3b|.Ý%<ç dig/Nfd9\V5yV쟖9s<BG[ IDATb=+R!X;Vrz=ܠeri||KD 7PΎyk4 x4+~Rg#F܏ϫpgDMװJQΏxVNQk&π1L:F_%ߵ9R.vR)'+EjZ|&Q mD{%d[mׯF覧O6[Ȉd+^g]9f^nĻ~b*~*jbIcm`z'ñ2e.fj9&TEb]'r ]o; 9q5f5qz~?Ԝ;˜fi ŹO(ye! BT3Ls̅~ :qNgtc:sOI=If?n+A̶|* ƮmE+(/fS]h@y1.yԓeS* x ZD=`Z0<躧K8lA(2MԘ^L:o5n%4MҌrKK:סŕ9]?Ǹ$- knyc:MUFIϒ;YYNNuϺq^~f[ ֶ0aFƣr;BI9hX?Ϟ$ !tH\AemZm~FB8Ouݑ =.2sΚ$WV[J+`MeA(Xw YO'iWxvϴS&f:v]WY!H9NY8TvNÚi#],l_IT9 P gj,W &qSҺslU Gw=AĦ ,s%?'q7r%3mG>{=PG*V:bp[gdyz6Ɇlɺm'?|Jevx}?ZGsddՂJa29m \lb:*ZMq ?{ t$]Fj]D蹽»7eN'\k<{ C;V1*WZHǻRp2`~$up)bݻ-2 }4oG 7`zw0o;:ydLJ{!;A:.2ݿԙpԅ~ܰdPu6 <}?~rfԥa󬂛ۨFvf|;\u32gurV睵֓ՙ>K5+7dTꟻbۮ'fՅӜȒF!,wHʦ)):Bp$ULjCŢ(sf?\(U7+pJ+7*x8D /a}"vC( !iDoo3]@5.GM!tފ;6} #x8fCZJ{m460qH+﫝|;4}}J[o= IhVXvEiT(0HRUͳXlk{]ΕA(JJ[3M!M2}Jt MGsjx<砮{@z8K27 8ţlm*z2O)=V~]WJZ,}2>Q &'Aҩ,SjrzgOF~O^79prQZh)>#Ϫ.L3nZ*KqGΨ3q=yx+_~ua:(\ 7[^?,saq\SSXܖ#JF)g@ߝbO޿"閜M4S4)\;ƭ+rJ,B7 O1 E񸇲PwEFgs%7۞y*t,l"~$%-Ǒ"=_~oS{~n_v~uU8zTifJw٬O-YS7FQokWUEJKXՐ 6z#9LQe騵rL(4j-Ne}2),VjrM dkwSۚ5:3ϕ޹1hkݝ6UX6pҒԶԾc؊xC(* ($D<7vVe0܎lڲC=XiDƞ8ǚ2h`fccc}+M ->)J XȢeC~a9UˇJPnٶ6|e=x8˕ڬe6Ü6o5v x-ܮ9IšA F)cvpP RC/lQEy?]m0A[&׳TRY-@DY羽xUQPϲz.ﺮ-,),t]쭻FVo qH/C+s. r8->}C~㉫As氘 d&7+ޅRTJ%'aȐ{fdL]N]-]ʼO͞oWKVExsdǢРlrJPB )Y :f$^P|+7:%_"UigIMʐ sARYb䙽3J\A✦Se&pRLRsEeIC$UOZy%TWs|5*R^b{eS*2}׹DL kDR>Z)|~*DB9%,t(Y2&, 2qFVb7*LJW` ֒ZеYjg.f㮒TVUU;LG+x8-HT#D>R)8F%%\S@MhgY#LE͖MگFn9.U8ZX%(yr^qqmqϽΤTDU#1%|pNKh1o{&Ec3Pc9K y!̑-s1M@1&ʔUbDh|ML%(ϐwB4JZ?#X7lN[~#~@iji.8=ұZ-˸fLR=0yNAX,;@x hl~ף }t$^ubVahcM =LG~ٲ)_-'?|/|aʕyYN@c0嘲3!۠t ̽P*sH3-APTv/vQ? G)y2o4,S“+Ӌ52| /nَ4D~i/UEx)y] լZ3H{z)~9}rM??D31$TLEQSʊ=AM4sCJt%a F G..΢T_S-iY*ϔlAesjTGEg $C뤆M9X0'hTz^BTƤ{84H)@=O tA#ooGáPFtڎ)GGCecӄPrHJ^)i&ֻu+&f1RJ8 f A|ŕ)%x̷k8 j_ik93GnX#9(J3H KY ^q$eTeiMT3s3ߤzJ TR9JG˘#A^BYފDN#vHf@NFܥ(GLuĺLq bOe_󲏻KeNJYQ417=KA↪KNqVozC'g<:_Ep<N]su4JٸF1M#^ IA}ǑnȘ4 ᯊG׋'w#V]Pݡr?T(~d|SR-iB "(G.m ׬S*=NGL"hkFn~dGkY,F:mKTA<[i%[ŪmƜrnCP)a{g!j5\5ˢ]}ȩFHfd}O#'<`/?|rn!*vi/Fhw ڼ )K/}mIm琔xY3gL'z1{ %fVN k!~Qg\0J@ib4݁g%f"VaL>0@bK>|v4auxY'O/{&5?jd@qA*īI57UQ7DɆƓ[\UAMo+g4=y^YᖨUMyd&g/˲ y_v3T ک|_REVDFlL=R&JBy"ϨJv(gNeNʺ!% k+NeiV#*gRB^W-I)pVxXM7)وH4dRI*!2"MxzanO:z0Yw|w>QVaN|_Cҗ* hLֳ&Ͳ0eTy޼aE!V,iؠri7;dNbb2GRȆRϨ%`3Q옧%9Sp( -#C"O$%ܷ"gJ#&7)rUJ#Ri YvZɵɀBC,["*},~Z鈎K}+:(ɑ&kj`|?:VyD^&p8Dqhcd ±Ԏo_xFk syɒ4%bW*:Èa?xi[â)ۍY+NZZTY45uZ,?yL. _Fba1jZxaAk+L ΝǬעCc{vw DF%h)aS6 V (ʑ$JO{sOPnYgl$JaKbovl~9rs+ۡ$+cSx3ՊO;>y=Ruͫ;!jA:QI;n@{L*F_MեOU)*'+0=*O[*]"Z5g \-pV|bE7)*JC`ـV+ IsAZqeъrV'ifyIG,"qVѵk_=_߼侗 DEA^tٿy-JdP^?yU&=[棅s+G((s+!1sͳ)F9bЫ!MLN;Ï:xޏ$"4 A5FͩD"3m^dp39WD:PA)WH nPsDQ ceر71?>ҐתrT gqX67{E63 )Re'"`A(ytu[|&V0iD"f":ފ`.V>=ա${3wȾjj8Y`>"29*>^Z"a N5o]r%54J)%SI!l -$_x]65E'ixdSGP1w4xZ*c9F]C D"`,֮ JӼsxisf ht҅9Iqk3LcOӶ gBL(Y8N#ubjw4!$Kc5 t 18yF+%=_~|υmhβ{0Hy80޹<8k>q /#Gpst]VraU1UoQESw2 e хkR 3S>i O4VZ6%JqO煢Tf>R9 >xTS|JsTR:RgC"S0'1 Z J"UVS̻옖Vy|;AgQP ^ 2 #)(toJM7/qij\x}FVa|)%%8S42RqY):s !Eėfל$`Y5M?ppdZdb0'8]5~djd _6\Z~([fN VN#|-y"2[GUQ'Ʋ˵Y~lE?@V&X-ZE0F"E\=咮]rwG x=hcjEid'+dݶ$RX#l&b|{u\i]K#|d:ANK'cDš_7|5?;@4l6*|fa/Da.~9G/PB>Tdr3.q1 mR]Tp\%:,l#קugIq׈71P4pi1GI2|@ٶeX%Z>sR00xtӠ$A7Rf#2rGA[Nfa8aNpKlNy_ԑrLJj(F"WzQ(?(i[ATl**gI=/[2X3%n7r4ߒ@i͢ 5 COV㬑EZcuIdY1LlSi hrl'rp}IH6G53tkr4{|jW#vr16*#HO f}6dݬ `˷R΂:%)F hJ˪M^3gesʳ!/.77j-_89%8S ٢Tx]T[ g5 # Lx8:ͤ1ƒ"٪ W ᳕Tw%{_d4e99z1q'B,Zz|:|a eGU${QVrg[8]j_9[K)c4Bu0HᘜA80mXn͎F>G#J?Jf;`N4" {{g2#A AFUƴ-cLXx|CaΡE+L_77ٲn7| hIar)G]-O8⮟x{1xF4~ +qu4]:Nϯ!%%Vmq¨{(O V+5lo_PazR!& XdO4+ZQ2Q%{H~Ghg1ΠcOoa7[տglNNqMC6ƉjVmi0r7L۲}{Vg]K봄cOO ~V3JHJ2uj/v?:ق)|%fR>"V+6dݲY2:&RMtvZf6?MC@!ӳcZ5%֑aL(9 1”0ƕIYĀHд={~;ZC޺4[?2> (9rNr/Fv"GZJ4⬳ @CyMZ|Fo]:Is9E̊VX~8k좕>5I92f:2V3HhL:2LsU< dekH"O=q2vHLΏH]ю{爜BϦȭ| )*BkUsp2>DIϦj$u${%tOyw;Y$\ *3(j9䂆V4 I k$S6X!JW5yhև*߿Ⱦ:J , {$ϔD^S-=|%gud3s8f.T*𕽔uYU.:_UU4V\W\rq,e DLZ-{ꥄ RArX>&H1u-èz_1$Xvi8eÇW }~x A"(uYCq՝bX`lCDVAYVsO! ǐ>n jdjŧRBrc0YW#Q[cr1隻Ge2 t]KBẖHfZJ%Dӵ6 drYBuWieYbeGq 8lh\C׭xߡE[Q5W:󡐛cYmPZ@2iMfZoMòkGxڰ> <{7_dm=W@AFnD חkN< 7T<]]UNGl$x4 2#kҤљQeNl HɅ1_dQ28FRcu Zg҄PQ[Zдta)qJD<]2^GK C%&1t)6yNk~wƲuU-{/}b׳WHHB* *BqArUU9g'oyj5c0e#8*p|Ok"&1#)aG$2M}df}a'r!zpjb؜MTRLLa\SoA]N7$|buze,;V(H=FޓS6kn#41[8 g#|;nxwxz҆qg| 1qzzn#3Kb:5 g|w=MiM7+(5DrGUER)HYPcL҃9ͺkpڈ ՑHb(ޒs?Ȑ&qv9~cLD:J~חkV>KII<PHvSaH|-57=7ӇX#')&gGЎ> W^:GJkT9__<أ9%.ϕ@E+<ϑ2GR\v[% L*[=iUO!f^E&!ʹT$AW hllP)6,2Ru"bRv-{^NS 5m^#$mrwf.Sx/B/ I\&ХC`sH6Mg7~ s*o^QS9dɠKEgw壬ìFny V"L2&(LLC'%di BeT9ډҦ(Iͥf_#cr &!XX ?߂/ySSl*/Mɘf#b"Ai+HIL~Mbow\p a4|)C=]ѳS KZ ֱ^ݾa Dp#JuZ+<,DM O[vϾX\MAsϿ_rNsy՚i8pv}`mC?C9䐞A"|N v )V=C<  DQ2 2Gt2Ր:YK%DgْR7ӥŇ"S"i؜,I:1%c,-kR!֢ l_ߡ:˩!CS -y%ԛ"^Pf$yJqP˹q&"#S(1puLXox3M#Hi"?Kn8==|愮i 6YGY&Tc厗=_~c;rc,QTOOtf)Ԙ:nCAi+Gӈ!HHijqJ5i\bj,F)9YIH1:f%)|*MК4ΓgCƘ<D\ !U`4?~rBHWV<,ۗIY+H mSb /&idLz xKK%Gy|5W D625SNs%IM"Jl\THkYOb\d)P)RJ-UVI6o8=edq@UV.Xm>Rtg~r||s#h;+ِy\1.xrvzTG'(ƜF],q%02/Ÿ+$[6ъA*J]p%N0JejuS"yZ`E6OysE׆H,[{B,΃*Z3JQRdf%$| e0}3u\gU֬q%U1]2Zq2-.Q (+:$ev&gK3}i$Nh38@ ;mqz" Tђ1$Y8NZ܍>K\K|g7hQzxvxΊǾq1#]W|}b&)Q4 >y~ο<0'fD&Z˨=c F%[ۉo^<>2DHzE4T%Ţ?_ёh">&&Ģ5tˎ]D 'R׶)@ZB(K4GG!yY%Bcm{Q~i!خc^X$\G [Ha7K {bͮde&'?eq8HQ;K =!)z#n3N2pDDF_w+OIX)0N'Rv9!R-(=ig0 ܀%ݷ''FfyU3f4J933Z:ydM\)Iuy0R.F[j|<ҒCyBRurxmx " ` `9UGUP ̥ƃ[[ ~QLyq^J?$Z?Įt # =HM #$Rq(y/J\BQR;X Vk]ubg-?VVHk4F:ft\vrԇr*bM堘AGg 8͟=5mcwe 蚆U$R [3%2[x(i{O@ [SePeVȁ̬C"1HP8YRágMp86$G2XgLLѮ?=Z{#ϔӧQjF(jB^:r+a5GnU;Fj5u>9:Zi`SVnюAW)|ea.k?d=rpD$ ju ͲˍN ?S~ן޳ ?z ~{BLDG$my^jYɦAxDH-J&KN جg 1ɊxRX%i,Z.\LPY ϺZ[>zg"k$d|^IE!HVRMf *cXnZK%"2;Rthh)S4V:\ uM!T.Jg g+nCfJ* P'&E 9U2e];yt:OUG^*`Ditvĥ/+8GʼұŊR^JG !Ā֔{y'ΰs d덕ˠ(+(YOMT @sc|bI3 HcAX =]sjXv>?cx $Q(+LͲh>4N+6lSq"  .Qe U,3˯IZ_l\,/l$'!{Btq6_T{5:ޘ2QAcReL2(]uvRcy/L#%xy4`볖vݎ7{~xȋW Qg0diZ{ϟGO+6b %Kw{NOWG5ȷVuǪuQQ]oZ%jtbcyzAceq{?aǫ7wz}yw7 }~U7߿_aղMH&Ok~ Wk |#w4)r'l0yEz}s%/)pj)$ )}\F(&ZKx,D=c9bq i5i:GZJoI#BL"::c,G%"*zG[KX-w{pMCZ>8kdKՒ>dx;|\Z>~Ekľ|-_ĿŊ _}1GbHŵM%\3q1wHL L%pn8,F;NI>4*OfH׀ IDATD\ 1aJNSO,iÇOVxe7|mb&]j) zm 8R٣[-JϪjtJ#Wf?W"VNOv|p Tyney#LR!d$ޚfnȭ4գ%6gʷtH]p2g$`@gr-Q,)B/ૼ*6X|Və^3X"Wm:]C4jx#w +T^G# 89l Se[fSޣ]&<,H)2W*8h,˸.0HUpjDrBϛQC!Y֭#+!!Q3hŤ"e r}2SmEsTkNGPsDOb)*H9^#|\G$ N҂ًwcdIu]XRΫp~决õcFۜU_WҔ;~uNvW*}o=If~ކK|)%"k[7߾xJZt~cZ7 Ŗ!SP8#8mq# ivX QL'DH,59ю憻nw^9R2KKYGtY,+І5.!1m؅#񎘢c嵑4Iɥr e a'a""Sq؍IipvuE\bƁ~q":M.{dE=~7{&y#߼z74pvZRn6&.Z^?D \]1|?}B{zpzڲ\u| 7Os`p0uΠ(maqB'O&Vˆ9'>jxdɳ>`ohR L@R|7Wx\^Ᾱ0 h-id'');)'?~ÞsKHYr9u{޲Z8b`HTf%~xワ3(UQ,xr37w;^'ͪ$%)(cu&=GrH^ᯍqcIimM$Ud=oGR6!b&jY*/(EU3i&)W<*꼞.V!Tbbbٲyʽ8I)ڗFz Hʷj|6LZͯTJ@(>(yW Ť R"1AF^QiY2M*oH;ZEtf1&uǽ1,>x#QNNp=10߿dZb5'W<v7b[+Ư/6lV-۞i )ҏ#!X7;H%eC7?d`|-C|=mG1:o_Oc#s/ٍejś=DO(q?D=ْ݁_%Wе1X$bm {~*}\:];<`w8?;eXg$`jVYL-Ҕ@;p9%'~cZx:IVa:`}jvsrYЭN15Ƶ "Dc V-k~K^n $B 7mUQG|KLK rW-u[aȀ!@ 8![IYbL&w7Sjn̵wKq>{c9г߬y֛a.krEDΎ ]nzI+:gA(1F~DPI2 Nu='?"3\,sN R4vEQȜ2pDRCv4L+ΗrO>dqQ2IHL.FDc@^Z^jm8-ZBQ܁bӏ`cl5dGmO[3F(`ނN+5aS0{8ܟj5tkp4NO_J3]áDs#YE;D' G˹a9+hͶMYƠODQLC\J#z<OW˃HOz63&iHN- is7dHOkMgσ9)  Z?}')!PZKNDEr!D6-/߭ ,yt6sCUhbx{ࢀݾw/cV[~ۯq.py~ Db2( $W1,* ެh:ѲOXֆvӓ4ofKUG9Eƫf=w-дw_}wjGkSժ?bYdQspb%U!IL*&AO'՛k6k'i!zyn9]ʢ8Yn65כ;qYw݁1YBjΙ1'3i0)նtp6iyNY.zP}`H$]fduմQk:+2A+})~ Za<̐S ۸.8F#tBL^1JKp,O)4)H_GsJqղ*nC䧗KٳIN**u!Fz÷\a*慩8= +ZC@Y `b.%edY)FES$dz$Lt0 fZصAbҵ-tbS M-ca-de~81% q\>03?Id4:tNO=ܴT/Tr QFQ5slV3;BShÏN?ut9a۾c߉c@e_++qy~&:Gry6(eݲ.7{6{Ѣ٣ G˙m8;Ň=8F+M5=ŒEM ȴ(X&Q>W/WRm;\FˤC$en']"A;5r6'X`w M=Z[#ˣ#y65>Fяb.-\ďPy*j11mTLQ9ج$y]ac3ikEo;Lx;D_s=~w(iz~/?b PVmdAzY8jtgk1VQ9EsYM^hvI6&xptH]hg'KNTE<> Q+20,:cY/2. _R2S_UXDiC) $l%?CZ)䅴"84J[1(NQELvZM-Q'MOe1Vc:# cu**n6-0=h`JRYC(쇁c>l{^^Yx+@^Ӧs`WFTNuLpd?X+qAƨI]W9BpPԢ&JYxrYbs7=`%]*>!DE/34u ˤ]ϮuTe&sҹc9:XCaM}5a7v4xzO7xc$ JMvϢ4|gynsKUe9GۉIt8^Vݮہ__S嚾Xt{ʲ`-''*i4CM (RW97]O]<9СxǜeFYX6=]2w!DRfWܬ:5'G5EQA󜺐d{}%/-׷{yȥ,1FdEiX.,jah5sg>9(R_@;OnH O)<)N.mD 7EŜM+-r-Ȃ Hqf!ZEfݷh )eYq[9nb@ݲ۬;: }K>rHhR[d AA Ɏ XmP"C۱(r ͞n(-mR4&*'B d&R٬FiMzH"Ff ddQ]fԕe^gU<||Qh^ȆbiC)( m`,j2IDp}P:Zɺa$y9M:ȍؐTJFhY.mTz$E#0mnqp*\p}Z H@z?P'abP&@>/PIbDE?!c:Gbs`0)CJn䪴@6|5rSh L)iTbH'D"Ǜ)/F`W~LR4&a)+*E^h\s;Ewv?[YJ)6 (6]6OGQJ|ШhHa鑵K7e\?@9òrӿHIHƈ:e,Ow^:RXk1T;M4HEI9!s\Vggsa@.MX' 04:e \ZnL)ne 8Ě>X e`5%]~wGr|z$6.r󋚏3ҵ=ЋU9mͫkr9;+18;]Ǜ-՞7vΰ6H:޾_qnYk֫oݣ0uŽ@YU'nU]Yw,%O.3W(fW߰mH78: !B5ww m8B@^FY][|j9;-ح\KL}4YάsrR}/1L:%hd4S H6 rdIi]+ %Ej--ZX  ̤ZbzhD~Kl{b0lw[Nkӎ8"/JŖsxeD >B(f%Hr̸1(.2@ag?P:ͫ7<9q~qJUO,Ym{~{a$ArB R7d&)yfyo!v51*Aw`X%XΏJ~t),OL꽈Anp@.r2k7=:LD kD3%e$n乢(eTB(`l:˒~$Ѓ65EnpIftdbOU}zf? Sx89HQ#'%pRVMuUz ^3DQo#@k󐱒-SAՔ+Ȅ  mV3#:]7:u "9T7P:BY&viDrFNi:CwȺȿW"}ČMP6y(2ĐO":tJAnR1YhjYlZS(JK5Q1,i\LĸEPXPG=~VF$f*}*#?#HO@9w!Fs̘ ~E#DqaFR_/Md:`e]3,@Щ!b]XE:F%wG=zrTIR4^imҔe5HkN kXt`LBD'ԏ[Mڃ2 Ӂ?qu1XCOOg<=ss x+"uidܖa IDATv슈:-hcyTSf9]l~qͷ=Er.|zOKcł8,uV_ \/{6'9?[Pd9dŠ:?j~~x ym"7U, (hG86t'4\T4;Lum?`s4MF׿EyAil1_0-q JK;HǤK+JRV(bNYNQځ#tN4\ٌFa<ݾziz0.]Sd-I#A8EA-ϐeRVʂ㣒0Sׁ87&"t訓 Bf>xdWXEK/^'Ѵ} .!p-эRSfW䚢XcS⤦"@,3y؏(IK!CHN. eM()kE.T;NTzbF0D2YJ9HyR4q F,h!YJbN:FKi; y] 8"N{1R!@{` 'B1=Q 8>ds2O{Jt!JS"*W)xk ,#^J&&1xbIjrKt<|Q ܁1POͦ0ȚJtV2B74C/FqqA<6 1:8W%ON Bf8 4FqCCtRոuL+:%+#6N S~sQ&ӓ]JN05( w=nsZ:mqٳ ],ɕs Σy #->apދϙr:ΣB==MŬ`9/p>Y b9+["3.yC)EjR}rN-Z6hQb8( oVXkC.i b0g?DLȥnQӴALJF㲤gT. T|pK h .`AQыh85tju23).Q䕴[c)났d3E ̊X;xVũzl>tC47QN?xv:q;'G3<9$9%3L(S&o1" 5複JA6 ҁgI^8SgXQ1x !ki)N:/5X*, ũ>29XBrJOІ91 jRdԒ)y Njg}70eVf|GR5l(X'pRynÛg|35ei4r+\LZ#]mH)kZBJONMYH N4F$NN G]*٫0o~(򸯽+ o&zOl$R!01(JV8*(ˌ{Ʊm<7G-|O?:YBs0,+Mfܻ0oE)OP<*ҐtHc$S8>Úhjn}ҳAtD9~@T*Mcc2MV9Ȋ \{?ӑ0\.rض,<>WJN}QKK݈i&Awđ T'SJ0pDzI햞;&x3/'M?pׂS.D2=/߭|!BmEn5oo-:tY"EV 5= 9<77w=/o]ﹻ7"Ո"+眝|5gKʲizfQ-7};w[;#tT(&3t 9?.yv*-g'5Ϟ3Wl |*3ӫS>z~JtM eQ<~l%Dke YŹ@]W,s_#ܼͮg%!F/.8 t6t-!!D6ӘwIgW8)9Ԍ=$SqCP "l6 !?G⇞n;44wc trI:1ZJtG&1iJFj'Ύkr3'_M]XSJ+G#UCXגĄM#SIg|&.x9cbcM䑡<@Yw?N,R7FQgLn*i ! NmOůA 8vc0IFX1a )yQXq7֢2 ZphkѩkjpÀrc0lO/bj5.f.Jiţ'ueȌfY#y#.#nFi|^q=QMS'U8IDl0GQ&T ?A{ >X!aB)3τAL" დ)(cUj1ԥib&6F0eIU Gt/e6˄1%Mb댒E{p6)=!@l>qo=%Q5'nJB/t zä0c0b"µf Ew(G|7A@mshuk׊.xȂYƣ3BWq(yz1<|xƢRap!J5<:FźgԁIaZ3jjrv4@8ƀ`ȅ33u9+ ȫS9v&+~=XY!FN\RY6bJ)>L9YI]@x|$.i1 N-$yҰ8{/BrȸkBd= *љjw3Svn:u-˚ke85Ktp$FaJڈe=jT㣒64COYSnp X5D?B$+153Vj:,.{wݰޑYjz֚9/OXV9/^nfg%\In-2#/ iJ}` |Wg|O͹ShC^H ׋"Ϧ(EU#D|bɴt n;/_5˿b?sXՆe={bJk{s*cAP z?vxMA|̫PR28 Q:t@EFwێK ԅmZUM<}{q+%5odqi1WTRH>UNmy[>yfw Qcs>m\ffKDpN'Լ#0]MVD$<꼤,O]|;w Q١٢`(5k%edNC֋ a/l.ک\̪٢( <]?@@@Q*"1EƓOpfǾ`KU.p^s41_ی0bY*#:O ,Oèu RhQRhIQgEQeV_~~K#pD 8:csy}8 :̦C=҈bIzYǑQ:a,өd?Jou Xs~ȢY*1)|3*yPb['~Γm,Akxԡ3TEDYV!FO E%{9/Nhhgڲ+GG6<GϏ%=M;-]7pvzf)Zw>FZyZJ3OקTU_ }@V9>˳Ve/A)\D>e,@ euY1Ŭd]wg/2inJÃ,ȬdPJf <pV Cqaxf9o t5?dqQ#b;GMjK^`,OJ甹hfo:z'Vo?mz| <:^};ƉYLGDDHIxDUYGg|&7_츽oJۀ 32$, 7dbT;χ#/ϸ7='%힗;W^||ݍ0)K&_/c]@+KĉT 4OB> ?鏯 Ek%zȣMMZ}9;rW|7<.*k_ ͯKf0qW߼'ҸR% YYgAWΈ`6 S\/BAI=H;W+泒((! ](ALHUjvm L;΢*j|t#+,Hoq}O=/mFvw'sI*"x.ëׄي(߿ݞmӦt"o?XoZp=Q\\rt|LtCU Dͪ* 93e:g5γK-=+š㒳JdꆮI &5X@I6l^/z<|ɂ9G'5<˰P9yvyQZ|ܭ6آƖsq!Pɋ e3@ynZYhdXxHV+xQۓJ>l*?[J48d}rʦoh4&u`槃6Le= #ǰXp0wczGJi(N\RʌXH升YRJ(J>YJ%ں8 332mcPAX~ $VƲ]1T[A50n(/b ǵ=3ٌ"(eƂvӅJe%`4(cw"9/ɲH]fnɌ>?c*È2ٌu#7&rvd`tmާq9i݊[ynJ)͢<;/yrVҼmxa϶q8?0i|&6+љ(rM!II~kW5͈TD2mE =eYdz:/ Le Kb!Nq|NHF˕pNy2SWWAq4rrfZ'vwbGt&WFCZr[9 L=Eulqbc<T6p-i2&F$4aQgl{^k9=5ݾg97 } @~9A@sK\5q2ϣ㚡~?0JC01`rgVEb.HL,2>M3zbFԚm2fU yyvnh@mŶbkhjOYY:F:p', K" Zs(GmZe֔eNk mĐ4#j1|2KR3t]8OY+KBT|vvS!ϐcS|U1lR),V"|`;*xP#IC*3RcD 1Ā:c'(^L*I_nJ,@;(H3ePҴRhc$hED gc$"pLe+xzVsyz77zAe"![sKF)i|HQo ]"nYsq^ltTE9ٛ%gO}0鯜K%yaٳ9=]P@`^;>l:DgRc1N%QT`z}d$G Y @0aW &{<ѳKuZvɜRUiM wk4&2"cFfȞl*P vijӋrOqO#1Ӿd(d=ƔCv1Uq’-iKq@4dJQW`HZ/1e16<9wi899˒GK6ۆ%.Ndo^ԅaQv͵iMtvk̲(M#Afג6.ȴnK ͮk{./yl3N5uSWjG7R|fl;\n IDAT]R[XzOUd*5m<ٲ 8OUgpbitz2wh_]^ZWdyN{O+߽b?.flytRqu<''Wtw,kX+Lu*64r٨æ!W8APx 4 CV4M=m)4:ij,>x0%Zm4-Gsm:7(aK 9s"7x%Neحp}6; S⍊"4ikԝ9`ҩJ.o YxAG>x(koh>л-C|)j\ Y.klChY?GKo|ݽt:Dh{}"prT2s^]ٓ%B֑ӹJ2q/>L #W^K/VrhZG?yzZ[f9E kK(s+ɀdiǮcY<{tʫ7/_}`^Ǐbp5XU 8%3nQllv.RS #آʘ0$ˠQR2 ᬶ\.r1l;f`:)M9Q o4dVa頁G3>~4o_/^T7sȖ"g?zt2nY;FFt?Ӛ>g||^s^ٜ%_~MsuyA߿-!U*m- A601#k`%YMߺh-\S1C+1`tNK b$6r)S59o/90NAv)i]7WV6(It<?í9YA"2r/Q`q#S:錮pdKޛT"ĔG=>0$/*t4zJ只5_qbO~ǽgQ߇-]NYdfi'Q;+slm3тen7#׷5,̛a`>yF'1ZH0G'3ίܬ>2Bmъco&F!h~EդY QLe"s]+uXyͪ4IӅ!%}=*ϏUji-֬w G".4ՎI)ryfZ^YUq.qJUi^%ʰe3uxj[c駀Ur(|`X^<]h٠2vg;_؎Pn|ե):ʰh+felыjo;vtDPgD{Yf<>h1"1eJ:hR4 5hZm؍h+CّB+KMf+|,a npǟ?x9=?F?HGOcR|&EU's捦if/߬qO~~j3`-=;VplpFt,k5|-Su;(R}/8dO_ÆW,g?Ipdv?pU%i'OX,Mӗb9k+ytѵE(QHd`A[ucWPlZ{ R <&=4 l}׃Ҭ[fD6ГL̕nq ƝO( D c-i o bkI c?rqbbJtL:IF("k!A$Hk4?xˣoOWڲ"nf7ҏк"Nų9?x;.V=ދI>޼ R̜ a`?U91xSYBgav+EVjtFob@A契aΑHB<r*Tiږ 443TV6Udg!G *$U&h]Hѳ躎aVc SZͧ~ \޲V /~ 30i3?jYجGGs.ήy~|ǟ`s,f' D FtZI\$`%phyu>p+v? _Vd,̰?q,kU1fBA.fCz8r0I3v 7 xF|x{2o,)$T--/vlܦG3i8٫ylR^[^\ ~!F/(Z 1$jk؟W<9l*sOdNX#Q74)xƑsjf-*g+nnXu.臞0竤#]%~4Sm-M/j:?G[EׯS ̘MK_拚\I+ 4N[tSV~ G<3>Gnw, ]TT@&yŶpqqt֒HM4UV~!3N㚖zM#|hZ1@[0b=$D!3ٲAkH"g~8 1V)JSLYL4BNCA)Vir 8eƭCҹ Z5Yڕa(1Im8.<F&袧`Vg*'Um*Ag6]j#+dxٌRt8zfᜃ9]Ƕ"1z6m'Q!1IH9%ΒJubaH*50\Ȓ\3ߥ;b&~|}ǯz;|uЋŶld)cz.2$ʔ ){.l3pz/uh @J!"{%l1K22$RR#j ^%Xr^dlsS ɩʶ1P6`29<cz r)fyu!''jN1^/9h[4&H}f0ڠMr8pquק+vC$|}˓qXcpFc5<>^~lwo/TƇ4Mh,yR>ޣÚMEXb4\NY _% P (*@#T5Opauf 5)+<0&?۠''{rڦD}LT6VaS)ܩ*+W|0 (q9305jINi^xvؠ˦e Bߛ;Z.hVkYjnWkz?lÔ ixψr&q)2t?WW_Ce@dA\_^l8ڛRv__xŜټ-Eŏ_2oޯϟd_|3n9=Y#9DIF,'OOI!|b6]MRնs$¥H3s, Mf3x)Hh(Tl)ƇTx mG-!tEc OJ2L95O!%riu]7F抔0N X9ٲ<\bs ucd<|j &B"\tS"y娚=M3iRZYv}̴p&'0%r1`4T* m 5YTr̙)FpQZ ŠLLN19+}+*[Y6-V $n{Kp5@hږXڶb69kc ϟjvݦU5Ϗ[v[imzFΊٰ]Mjjk*>T}HA*Mv&ӴT2t0ḒYnQJ<(m1aa"@pr0z8MD=8*15>0J^^SZcEӓC U}0CkgJL==fs/k^|tnf;&ArvzNNǏ||o6l:njxÂsR!GYB[1K>},FED h^s4<7]f  VWKR a=gT,IU柀Iem,|5VK90'b$* (X7U\:eO?端O龹 f `64a)t=g9_Mlj9kSĩ=$KGx6#aL!q˷ ?gTe&ף=K.IifVL|x![@? TQCU38iz -qCe5W{ 1kEug4m/jinF~$a FM yȡ €5νZhu\an"Ā#;jшSD;T)]"Wz$3II*J0'%P*= ['X.I! PcN]s~q~QkO3[PYwgߛyU'ޞuL~ćEz&f1pv~I笮hOysvjqF>{U;Lc ~3KU/o_5Oɢ2C՗9Xo'G29'oޑkdΚ5*+a~i&0*n%dc!Hh#م^[LcPNZ[r KZi*[C'׶T>5u 2 ˬWnh+GXk99>DY+9GL]j:cnYX3۞D1ƩHs1PäM0hv(UbJHf#kJ<{v5|{K]V|#!ͨӬljx1zG4%F,) ݜryܘ1<5߷rY%xH~DAJR"Hi91b,V>ybH)hg34#0Na:)-4s|Bb8-/T oRđqΑ0sW6X[7,+>3\U3fي"Ϟ\8Բܔ-*a~wfGiE)bU3oٔ!b4$? ҢKJQ!*b.A)ɰlŬu7= ͹Җymka; A,"1?ZiI*Y9yZ ֢f{(߽jCGʠA'kC ׼>妏䨸xrp!D JT LvED EjM8LV?/T(d|9FA"?u4MVќ0jyuwKcaf99GMm->J9_G<89^T"Ѿ@Q“k,*͓G5PVžB%G3n:V Ϻ.2YE &Om,)GvSJY4تf2ǘS"J%nW=kǑƵ|$lUVCă5(k!1)7ÖL!E)7FT"ƔJa$Ychik")%*e*mP7lH &7fff 8ɻ ?L2dޓGS.xy]V,hIYQUӿQXYDpO&xMb77oxa=gvӑT|rd䋧8_g}u >EkpձND򑳛ϯR2q`Fy}oѶᣗ?o^Í]{ԗ|8 GHV0%ZA2FHR *WWFQf8XqL4i'-2y h婔Baɀm[r6+neӲxI"vǬK+f0^$= .?\f 9Vg#fGG ꖾ {n @ V͈Bdb'&R+|8 9f-JD_&ͪȅ͐U&f@.yrX喠u&T)V R𩊧E~6J9 R[E?&톃eͼ1 /WWt>KYE]2t#96?< IDAT5}?:RJc]+rH<Hʊ^|2UE[񒣃=v;P@V8H1b<VCTTVRc%ŢeooI?L6H A&nm/JJCPKI$ Xp:)lY*zϮ#Ni=%CG tݑx--~~D "/sqsK7|dOxwvxAHd?ځ0z!5V:/EtCbwwH6`fadzQ׼XV"QjmPJ`RabQJ]7 ~HꚪmJ03c?ȳPI ŔeCl2(ge%绋 13s݀-72hn:|cd<3mݰݍr3".+&}=\+<߽E1#Fk9+ɴ΢eWM7)Rו H>IKšXl k-ovl&3 smEpQ1wH6â2OlHP93WTTʹ\RhED^ߢ361!g4ǯ_҇@vXmHf8DSf$y| r1;9k-޽/7_q~71Fc&%!ti*R-!hd Ng#Kq l"ˍ ɠm+8@U5иH7N^@ֆ~`jk20]uېx̲blon lm1M3Em uΪv_d'|()>{'v9<N-ZE>h|00e:P 7Y r$fLHF L \L ,*d(' kK>hUF (/ʂ1 !i0l F҆cdcߑc jsz~)S#~~[yuͷo/41#mM<, Yu^O=ݚ߰8։ecitcħ}bNdR !ٌ58ѶxoV5~O#n`ż*p$}J)1#?MԵaor+֑DZK٣%]߰uQ^ J(E&hj9RLTd)vUXzŏ~~Ϝ_rg3N>'[77wVk98Ok>;ⰱ|nE?$|/j"}~G>pZtͯ9yO~ e+zR!N'y&CIyyb?*0z>K9-M7w|{O&K=R&(eNpB+2dl 80$ҘTh!BtH?ʀpX0I٫s#_''d`^)>ctEOL!x7dHVţ%)z}?Kۅ\jEg%0{mCqX[+Ґbnaokg׆}xx n=gAʴCiS -PHx43D_OV}*p?Kuە 0El%i9˜-XTDOgp#1O0{<)d:SZf56•R}/ļmבdB 5uʅI6\(6e0q2_˷[Sd.x@713TCȃǸ,mF%MTX+JAZIc9RXkqJ(SʨT.{brjMl" E5I>&&6!BwRcg47]r2NW‶-[~|_[Sg= e.8pĬhH.j )%B  8+QgįGT҆RvұYA,hBЂ Qrq- g!L(j 'KEFc\4gw,H8Wa]t@0b9ww f˖#wKISmeA02SPԭlYEZHIJYLQi6ct¨L@n^(j#Dv$.!ˇHy$Sq xEk1-b,5(aǎfGݐ| .X nd7yִmk6\w\nHSG33]fwدZ{3c9wBdoپYyp* Azv[7o1\ΙjjWѶ-MSIK} n8=_ͷxx g|9'foy\_x?W_Sǀ<~rQ1#sT<~t̟B!x>|8, "z;ruw;z7?+nvL! ()xbu҉wZcJ>AGATv3R.{ԁ3fuF8I#sjKҒ|H4r@ NJ1#bmkjk5/,hZw֜]*X;~gRO#\ hk8XX~GEQ˜LA6S;Mrʤ0=6ZQׁ͋o_\Vxn nH܍Tb qǑu?qvvHOpN1Fx6 ۞ff)$eDG u[ Y^ xyG* ly=1HS))ۆG'3s10N=ďYJޣL9 Cf0|uFNR;OTS ¹F}*[@,wk X2QV"^1!M3TV`̢mPF%nمȘP\c( ]PY` ? ,e!r-7۱lkJGxoƓ1_1j2|ƧY]+Hx)*3*0fBYV!&[Z|$(=eQF-G_|?!=o~tr8aFBc:nv'Plv/!*jDODD೦Ƚ$"!$kM[W rΧr/ b'uGVhmƹې6riF,c3[ F *Qw=9Pqdsyq4dRh\c+B,+X8ED:PP7c7R0ۍiv]O;β-t#mc"G"xM$ r B":\I:K~TCaÍV1hm QL)წ%ե ͗r+W ?OHݘҊP(,5)2')zV[䃛5gJ?S!O9G_NWlwQ६;iͺ\*>zq“gϰYIY#deehm7^Cf3OrEONh9v@$Dmj'u#(KLGT&!>D?F}XoOYwLc`w[S~QEϸapyݳ&׼x}0g=Mug⏥3`89Z\Tv3_Jn)%)H1L\_7ק)C==r9Slw#، m~S~ f5ތ|}2CTy[cv80 F1`)]b텾TT]d|f)A"7H1 )MP59Ej;U+Xx??jqή;R,/34mME B w->K_7$>bBXER Tu6Y-P߹W,.)?Ȼ*f@ԬೄJ|la0&9*᭩PF A6:E_4TRP[dW+Wa\?۟-caL#*!1N,,Rm51y'{sr\{R4yC<'hhFI(#V?NCOt ?R?DƋB Jn 'f"X%M!2LBxWhUue&^-(PZd!$ bk4(7Z+3B((@%:r{DLLSf }J:0سmW1,>=K4'RGO(6D"72-m=Wk^7v`bU Vgga->]?2N16UZk!' !aicP h#,*G7\lS9Hchrȏf3pza;LO׫ׯcrn1qrVM|zނ6$麞qP\d01 D\_)Z3>9A)NH ϟ[Fm'3G -{sX ' =^r? ]?r&IJeF;@i)KN>Q$0!`΢X{yv}hֲ9 Q HZ$Ępώӟ>e15]w\:bXίw,ۆYI(~)VgVj55JԽ !xyG9ЍA:n:)͚8Iߛ-o.¶Ħ[cbEaL!G145_~{N]9=F'lw#nctW[*yd-Wk/V(ϸ"g2_Fi #Dpߠ*$Kʙ4a^LShbVt]v\Ӎ8]m>J(˓%Ĭ@?b$}&IVC!g):dD0 /*Q9K(VKT#p X^ldm-VAp" TYp)+&򧵖ża7ΗN9T.8rmET/8pt>{³MW{Z+ ˬ_ ,Bb'_Gr @ݔ"wnnu#c?THuȮKo,/ZO<+CDaq h7l4v[^}wo\y)s7Fn@Ί'K15c?*tۑn$Q|n,.RRݥ}OtZ-t`tf&"[ E"{km 5YT9b @"adn F VT C%=vH0Neji"Cҭ7XBTU+S1o5[Uq)N[(¸ge㒬eZ7L{1GrmCH$Fv+Ib)$I`D*Ո6bpi9YmdǝF̆bh1eRfZ3Jcdye#ЯեE y$T`$}߼ 6YhKo\3Đa34jbTJâYe`l{1? c0M#Z}tǟYlf7?+.n،#]V\;g?dIvw}I$"R$%$iPIS=As4LVFIFUEH Fxxxwi,o7"܏7k}ϟ1ɬ'tUqyyFP N\-'LSvs[vO^!k *@kYXaW1 I~_<|7/C"O\8 傳(@eNYTZy6khyo^/eMcX6\lyucEB,c ^]1 !aFt8 }7pv2#ww2YG)%8HQ1D2)jd1rj$J\q7[}䶛pƕHr&JNRfBIGØ7͞|"^Slثe(1CUNVŚ36%4191%nڝW~7?+r/g|i]sESZ6쏢sz9o~+SU$I3o*z}"#M41I࣬`! t wLQ&Z'i,_膡`F4ǣghpbwA~g!&&sS7Iѣ,0J*\ %LK2$gcX ?$`D$YŽC+cmYw*EqzWc@Eh-c/kO8@swl*"rO1.aU( ̖v}7+Q-n6#gmĹtMG͎4H>0rr޼Ѻg@;IE>4mx8T=@%TYK%.4)GqR$91EŝX}p^ yԴ5TBGб:)cfJUV2z4VjU U僐zPؓ?.Q& ƈ(R#Te7"C>!oj~ԙ8 U޽bP%jI_.̬+Pg]Cdwp*jg@B]wn>~Sf%Hz@F\X@P Cy-O.=4|'(~/'$sY"(P,NNͻ5O''315);?yDe-Fiai&;*kif-nź{Q >rjX#njG?Q ?z~)>Yy[3kjjW c̓+5ee3Ͼ'cQJJ=L=5P34bNGh 6ܮ+~nG@{yUK[Y IDAT qZ(otaMmIMf׉2,ix~r1ë7%P I'}i2wa/8+&ٌ ݔI$L%:\\Y;V*b!iE7 lդT'"@P sx՚g:¯_O?bPZb\hBjeU~,܂=)3v0k6nfSo[fh5UܱM'/:ǛDVhm S=8 UeYgU#ie T]U1 \b> Id,.DS7psCa<>>G^ȰD"r>d '3GTN횿:dOmn$&’"qwEK>=+IM^> J,80E ^WDuyMҊ8q"HJt*'jHZhG +Jƶ5c֌S"fCH2I>|&!ΠE}fUV(r l#z `(t'XP'XjR\而2D #8tmAT 7Wъ(M49l1` XA?+h >y!3loh3"ݑ{fRz :HsqPbaFH`:S(*\WRi43cY6XvsS Oj4g:?*KS[j# iB$rD7/Ey)*ݕ5bwy)|Zl/xIWA>ZFTP@3F7F|lb!4gJkR# n;޿o9gNj*?~ĕ7k*֌sJB߿ŊˋK%OΙ-|ҢLş+j8e&1/őqd;;a3Y jn?pR3C")TY|>sf4QiBj+^}2tIg,&X|=E/k0C&apu?grI&!Gqv)xd9G-mGZ*ͬY1l6[7_=/Oh5E՝تu/IΠՉtv0y;ΙaJ|{8L|\qlшDȘEf ]ns!1tYkOo8!e?;%NQVwY\O_~tO>=g㧎D@Yq]yMi020y}\'Z%llL%_/x hO$n dZn=o>@c*fU˓an70,1ч)p>⏾\LE*Hayأuf-U-\A4F)B[K7mDϱ9Xqqf͎Uc !L )M͖n$he(&$-zX4LXnV}-˅D͵&$+wLYȺ^yPT\'g-1'}8ʄi6sJZXZ%om8tMyC [ y1Vq>keG~k&u?/=7CfL'~)J5Rln2Ev!Er/ޯ.fHwnYᧈBVØ=H6%)"S(0FYjm >v71Mq7rohUJ.Re>x1ĂR&'>,˽/hz$Z6FǐVI9Tat4ބ ٘Udk#T.RI85HP\@MI s9cmՆGԫC%8G|Pܭ7,g e֖qTvšа\] 鈎[&XB5 }$)r VTPF\!X!+z bPL}DH7Cʉ!!AD i5CJbVVɋP"{AdTTFƠPY4[Z˃/EiPP\#_$\QKdB]aF+ 2 RHy?%!$6(9=0$;}g.3[6-S//@N~ny?qVRdY#k V?zp :->'{OVdkYQY)qU;JD("e5ŊTdYV̖vۻY%E`c؏ivp_{>zJI*)$8YQ$jy~l#ʢtpdseMkֿȿ/ӧ,gլ֌x]TI4L~H>=8#iL!8kn(Uthqɠ4Z'LӋ9/?fϛu')a0"g-dqUVcUfF~xD]r/^ G5䜰&8QjrV }$%n)4ѪtfIWeRCir*@RԳGifO(irՆ>g|ݚӳk |jjӼyNjgOp"wݑ`pVSE]69gxe4u/*rNlzۉ9 )yOQIQ*5'3.Dn'tcrE"ȝp/ad6uaN +Tόdcf M1kFҘb'ae')\4 Ä6nsPso)r^ጢF'?/-zQUx)o:y{&f7~57#&|R[.k=Ylgs s>ℶV]o崩GcO ي!+kpDLYA_eՉ̅O(h*u2I6a+5q- U~2m mNAFd#U25ZAw9VhmBvd P9 04U $ЅGQ) W(x/fVKs'$s/ŁJ|B;E0Gɱk,R\ N<@Hԍd J.}``ћY)+ /ISfAKRJEkKeiV!HwTlLIb SSFK Q^-q ɧ‐]gqJG HI m#P/3`|fUJ!h*YOYrP˪4p}YuN  Ʊ9epsW/ܮy1>=NVpN5(˛#-Cn?QiwoA))j\m1(k"T$6Mh[ gC+bVW֝ј"uZ"M[q2Yg8+v S@[LcGT"M4YaxYG캞}wd6TMufp`6P87Yew;fǴZ4, {Ht]vW7w!)]C?rq~h[K?RdgH^"(y9TJ0 ;|x~[A-U+d>D".V%.N/\Ԏy%tbl6| htXx {a#~4ƈ@zV%LIlT΢U-] [MRƈGF>Z=*dVɆ Y׆Kw(ϛC,Aјe `ϨJT d"KEeBt !)[o!"$ݱ-z)Dv1ӳ$BTSɮXnxYI$"t83 QzD\}jrd%U$*pj܍tQ>kMqw[>'/X%upbmE#8tġ[B&%m0L>TSt(JazX9]*BVLn4B7߾~U1fw/?p%\Ø I$T΁ Yv(87|lɇ۞~OT A.T2nj]-ahTr3S՚dq֜,+2;5Q0mJ3Fl>UmS\0#J6mP|3@o 5%TKm&4B&91@#}7pzhf0L-g'g60 :]ZΖs95'ˆ͑wb "U1H"Gj.i 8duA%(hDCT;YUjRWN&ZYMtT.c}3$E'穛ʔ*sODSYjc7 D[7W{OBEa']_7 ޽!?[CĤ;C4*W3q@%|i!w $'t}|(XaT&@m- ̓LZ&R_HT-)/x`vIDt1Fa!K: 2A>TVlA߬% $I|@sy|*#!*-J3&'Te0SF -!NI, T0N$B9 af9_IHȜ-yKW}¦@e%c5j J\~lNIV)Z >~U[Ø=싵,L5UĊp¥r2V4DNAe$/G"t\+-Y Z!L$ZE];lUU+kg^D% J"B6T69OIzo#ZYeщ1U\~~p$)Fv?&RRIxXXnFQ+yqV h$+gR"U*"l6{| mcb=abݚ/oyS>obD*"՘ǻ)jqUcCfMU3. !dB>d '[xKJݕ3R; *8YU6[6!:Yж5MekK#痧pb}e:H|L&3zA){4Db(% e-0a}wI)"*."HHn7F`I$MX-STCbJ4mY58Q,(lTp]$vBX@b|MϾ8ݮRh5K;ϟÎo?n>_}?ӁƎ)enF%xrB_@>y~6[q%A|iǮCY*-:l@N2TZaʪ8xoZGcϘْ?C!JalX8~<[Y.n>˚D&1$1L4ReE8ҘJaT[fL$HLY-E3V)@rn$oedECe'0%t V:|}hT,7V/RؐK'jʈ>j]bikKee0nv=! [KTVjbTnjd-H q^d^Ie%!\iɿS(䅰lSJYz0$$H=tZ"RN'H3EhS_9"$EjW2AHWzS u}??ps4%q4K+Ec%c0~%W5?~۫o6k_ϙ/OXftg 3J(`(b V1p,BxHWT@ G8pX&0Pi^úS;B!tQXw=4YjT8vh[X`Jq8aM1+Bzym:/SnQ4ydBbzkTp}ø]J9VJJne*<Ȼ /vgv_nۛ})SVEt C"1BDb8IIYqD 0-VNbb`{bR$)Z) q ~B%#F!hgcY)z3g30e|Ղ\e%#BlWݏ!rENΖ|>o82C7Oi*3/Ámb^ C](56ϟ8"o #g'oq{8@ִe[P$9i : H6J]FuN/._UErhkHI12W` D㥁*& Cb e[s0#&H`+1i_&ީ<Њc?2lw/n*6?Sْhp͛ ]|_Aﺜ$Xe9URX98rQْS/% :üC*3$R˰!B~H][puu˷6|Nfxr 1rvJWGFRNTN12RgQOY+"N/^z.-mc9:b L8i%WF"ni([FP3ێaPiON&KAU IDAT2  *T9+*އʙVT֖=L~I!0XbNƿ&"r''5))l$p-<c"4۲6FR!B2w("*b1IX΂RYWMD(n1NiӓSVDmr凜 O"0 vXq{]Bm2Met)re$ .8e(lBYoznՌ*^ygL-8"޴CJ?hP"3fK*ՌO.XfݙgBHE!,$S{xV"TJmeX5'iAT?qi-$Q=%!EF x'.T¨I塸)q8 NMMN4E,/K|%>LYuk- VY1ɡ#~,2CG'N3K00})my6ҚH N >0=]Gܿ)gnww<{Oe{Vpt?c0zڦ'ggm,;L@o<k򱰀-%A3 Sxj@Y>G XL| #ƗYpnD#>rOJ\v||Y])1z7]`4~%6('ihK1e8'>}~¿g-l'6w{a'hDr8躑f)|y0],y~9q~m#5LEf!r`-}pw6sQWJEBbŝs$v}?Pt(8 e$;">EQtΰ=N|zÇ}Il-O=JCSiB#()ʨ}Z:+}Ȥ$nLݙ\ZıEq .()mKl \%%V}HY٠dԅQ:`9eU+BW!1YX7J'>~4O; 0bqF1p:cf>~rμ5sLC丆$|~qO^BANmjΖ-OW-3I/>=dUtf7k]⋏/l;d /hG^6~J44ka_DcK@ b4 Uq2Ub zI\.\CڍhWhYLIFnSR`9-~(|˝-I JJ\UqG%`O) #O T:)y:HJh_Tj8yqPYפMJ4FN> г1}!"~V#CTau$jQLIQbA9C7<΀Qc U LvL g9:w3@2FW뢿jVmm0D n 2 G)R7 FlH!c d19'nnsdww ȋ珉)p4bpчĶIGOYNVsVU]af90fQ8MS S$#W@2L^dDi+2qtƷێ; /tvr(Y)pcι=Lv_L:~cr8EϾK6]/op8mF8)G*_}邿wGtffA?!K3![~w%n8e*fn` $zd TPsi.pIq~z41FqabU)燛'/C]fg81)Jմ ՊC7Q  ~E6I%3!KQRrtE+a TsXRO>(u]~dXt: D eDU,TgѪ(-{Vz)ycLH@! Km k%Ho 2*ZA#vhx#+Y+=X+# OJbΒJ!ze!?[%K 9"fP֞e.TkAYʘz& 0Yh,}71Y`b WYAY12gmE֊Ձo~uUsP(ql)uD)M8k[)hJ2|fg ZrHS,a\SW[֪A$Hha )NTbTfU"t>OxDOE \J%/캎tͨ*ˬqxz{+eMK0!tK;g];hpe݃:3*2v`Ѷ<=k9vw6Ye9m<4/x_}nw-5mꟾ?ry O~ݖ~è ykxʓӆ~dWDƪ|@95f/M0xi53ԕeJVehjK2eU\b ^=/eX'V1&vkwgOϖ|V<[5$WH)jv}UgS)*F 3 cz2'Mbu8h܌qp8OL'39 [jr˳˚ib!3&w/?jbHohT1,y2A >h`}B9~TdJP=(pX̓&G:J5|+@~r[ o_~`Qr93ݑ!Z$-fǘ)g UOr`S4eBT9 >!Ս0CNrhx!Al:}}QH⨬'_ as`9s4Fszz£c䷯n&i{sv*3Lp9LypqZqrZQGݞq9=Yr-*'TUe[1 Z4 goN| Ñ;Ǽzsz'竆>pX8_s O!;}dۃ5ۆvdw{+tJQ@{!}A"F!I6W !D)EYw ɓK=]'C `[D`%rUaYQ SberdK(R$+\H1qu˿>]{+Y:w7mɼh/IrAc%`Z-1Ifv=mh^G=;shgfPgѯ~t\$*͆3U ufE@v:FyQYR> 2!9M bklQ rٱ4$ !w|֠~BUd;Ҥ!TRbZ7<}9N*֫n`}Rꞧ&C.*NXZ{8Knw^ :lF`1YL̦֛=)x́1Fxqq:P;:/|syiYӢ&HSX*c8YNyxQ9.W~ &S:'ͷϯu7[-?F'' e:=A>0@`2:ta%9)& -"CB黅aϡm1ƠF}("xl u%ޣ\*iXe]QBWD\^F>(|F*gĐo))Cld i#ț:NBwp s#Lz5k$$BR!`HQ܏fz`N$G+1=礍rlJѥ *L6X-;)jUQabn٦ ֣1HJ\?YT=9혞 JIJS5(Xzѣ~bRGI2݁zo)m9[|} rQ(8A+VPZ.ž]REB1RTɤ.K6ԽІJ񹶂1,H>SGL -ڌ-YG{ƞs&DЉ⤖[jMa qT5Ǭ=R #C1(iioJbLٵw>&ٌtG<:L1wpXmzc EwjJ[A ٢”5!q`Ct Z)Y:Admj84D~ *IAd!+++h`9EYC$&ǐ4>Ha| B[jK&) %{Eц5%h^rô,)'vv1C:BK2DBJ>tGyG ZV#iC]rC—]Hh ^{{Q>ЏD be~.qm{0 wM]RU I֦5yee[|}dp~&m1@R_,Z^oy 82_,7>^ [(\G#SI"x'qC'r(kT i&jH%#)i\?3bim) Mh(*+χ0NL=(~Gcjo}8>[ptWoS>x4a>-ݪexO"0hJ:1qKt[ :^^Rه8#G' uaxh|8=oP7LS^_\n)%Îv_[hypzJ lV+t] 1Z cKY'% ?PJC"w[|t&T%ղݍ8PZ~HY"61|jvi4ZBQ21*R}[ Qc' ,HcL^,UlyZa)iryjJ( e]ntѣ HcCI?PH2 A Wrn` E?II9& - EZT  %?kTFWhh"ڜl+ }R{@NŬ$z_Fѿq9E )9tf΍kba M 30*Vۖk,m}O{G H%1Js6!|biQj0)-C 1R rq]Q1p~~JUUJ1)4(y]'?"sTuMUj{9UA(Ju%snƘE:KڧY6IayB*z'FI1$"0:mtJ²Oyb a0Z#b+1B׏wϗRa^MF3&u\2 UKGu6e#:$~ o%3K)d&sNB*s>uim%ː.;{0,jCp bjP'˓:clgoVFtgn)-fnO(P)2Rj0D"%]ϋͽ,$t2g?%*T⤩__gm5f| >E>-.\QjvA@e"9&ub1˯-w'9t,L8fӧ֗9NSF<MHTEduuGY$0 a9qkԚ.(th|+MEPBuu+1 -n{`IC3BPQ> scVSFa-QL(L C*EWQUQPC@zˎK1t3BRGXVaJ,* [l}?KIܖqܡ"% IK=)*D<8G[ma6_}H)i"6]ͩPR䪶Is(-uT "C A1ӂnOpc>t(RzJbFǐ%hF;|aҼa6ہyRkɤIlZ2g eiH1qTLsn߱ 1J\% ]P75Uuʳo4WTe 1UEAR@+ytNJ>A]Ȋ!o°8?)_~?|ox$t! ZKi y}f2]kdCSJ~FL9R+Ie8_4HXёpgێ+ϗLsF7+5g%ntݞGS{|Ƣ3iα(,HJ.]QR*Ł [\?DZj+)֡2U*~SLSYA;$V2* IDATQϿŬb=<[N]\,@[AB0+K4 $ڶG[CSX۶c(5#II]X֛vMSqvrńחk2Y6ޓcu@)Ӊvu#\82ͧTp7$ȷfi*Bb$$m98eUILv-,ggs^f OeLQdȲmUՊs rR$g׻Cϕ;G$ *9cX6\?˟ֲ{9 "P/ wva Rki[Yg<8=2<8QwyvƑ1FbaIk\] 1[&܏ [01;0$?jJ;ѥf 0 K%G_895<<8G^ꩠ]7PJ&MģȢQ6D?2lBYIӡkssceI6M*NF!iFn|۳`KQF,.cM+0̈́Q@n1bLF:c ( j1g1JNSńώ٥I2X,NQ $qǔA\Kr UW-P7Kna}cSw$b̓T 'G:PYITTN =s<}Cϡ'5MOzj$D$g9l* &m۰k[6a`6P&g:5֖LŔٜIG0kӆl|>AW|15pw6mOkN<>w|/wQI]7zHd/ ݮ|r*&|2PV *!'. MU4rFgŊݺ/88Gͮgeag4GF<",,GLxsvc{P\mz- 8?aVDIbQ~f2<>a?|'\p0#Ӧd"b,v()v:jlMSY>7fݡB DFR&:UX)?בwDPNA1? IES }_&E>%6mn RUX~gnZOQ*~)'૯dƛ+=Zr~q'^n9=Ol-\Q"brQʰ?+ڈ(k"*],-!X'gXPګƓTA?w]-VN4A\$Gkzڭkk#:&eVhFI$: DQjLj*="Ŝcgb[録n(Ք8MeÏԲSK,'U@<]F;)H'aH@Z ~|ɬFfiKVSZt]E.>\) v̧jb*˹!l.E&eL'jg(KK2B +4CJ9$U7&~saTTFW<}ˋ;ֲl nOSow\]y??] 74nDX7||ζl~݆|DB(֚JR5%V~tlZOO=O>l9e5kͤ8=xv`6|饱Tv{nʎ!g>cqx7$}uM-b" HP޳ĤL(뒲e%YDZ~o}΋OMFs r ]bi0 בɔ;>Zx>'H6"P q{:S*l3p_x-K"V4;4MY|PVŒ߱ٷ,kƍtJr*:o3UbW[,#IThl˳t\ٻ@m$aݍhMk2ġib$Ą6o{f;0x5|?)uSя!&;W)W7u{<*Wlwe3 =g u$8@u|zK5!J"e]fp#:$85l?IyNZ H#RJ{8/D".ouFN' e|.U.Zo-Li)ưmG~ i]rh8[9[Ny|>m!\NhQ D]$Q+ycגJڽF}xuaPDrv[㳉2j^w[bLAi^3\zOC|kS̖5VLKEUϸhw#/^nGŔG'5qQ8a4脲.y-F+{DdZH&1zq>ӘRDz6 T$$ 4& kzd֊#`Z)j(y$!x؏"b+ >YS?#$|(oV6O:.8zbUTcL{QiɍRb=L#މu,%E=Jr+#: S`!x)*l,՚XbFߋC+MYLtl#JaݐeE4)E댂!mh!%)XrK+4@ ZnX^rhBH1jw?ѻ1ټD@pC#ᙫQrʌ>h\c@kI(q@{b7=EQRZW_>ǟ>?'mvRmZ䳘K\,ym +tQẌh=7+N0xGw0HIaK~/xJ:p>9n7OX,f2|΁ Zr2(1$h] <^#=yb]{ ]˹~G2q"gKZ4ߣfI)4{9_ |QBIcF꺤n$m>=7wnZ^_hL nwVS RD(]Pސp`D:tR<80 7(>ssĈ&*#.#b# p-*:{4)q3z}`Lެ%0Q籍%XN+Jy _^jΗ ;&tBߵaE0z$AUb87ES|jü.]?/EFI1GӹƁ资jv<{9m/Ew@@5Ê>x_v<׫˯]IEItŏ,?^;d1(-VRv試 ]#TUE]IOvc`/xׯ Q1FI'%RjL2!IaFΙC oȯb:҅rG$D%́SIw=wxؒ#~gyzʁC4$InV+0+ 5/.rqD#c&%ci7-׷;"#aH mC>aK)KK׋:g4ԇu}B@.H=#%dzqPl( Ap#VVR'H0"KXjB%ƱN0`$)ْ4X5-H=I0V Bs)QQy%XdxKreQy qIP"!p#$/(fL"N|NU1* IIY=z:ȕB q&ߍ惏GaoI)4uS\6:/'̦ }Wh,>]5.hTɤiCǑ9~4 QlA jVbn펇TR}nPMC嶯tBw8?9=[ro4d|IGƠfq<ݍmD9̦if7p%2 y)%Bm*kټfhö㮋eE5ͺs1jBY󳭲K\"#ET@p ԃf(5ߐ8׸$z$n)YOG,ȯ-&LȇesQDxC1#>^LGD]cݳ,O=?kbܭ665t΋uAQ4rUIax{Ó5ٴ]^a=P|'9u̧Vh쒙QdIЭ>{TdžSٛ Q.: jH{Rߵ\̸[mXPE$OΙLJ mb/OJyK,bzq>H#ѿP( H "s.|6!Ӑ1GZ~>E=ţ7r6tcuP$؈5#V{? Ám۱t7L]q-ugZ8#ZYs'8!,S˂e`"UaYN+lێնg:1s e Iy|>[iu 8{sIw/1x]?FݗҼzs>yrŲaEՋ\:}77LzWyFQ&I. qx4Z64c6hӆn55cﹻ]JX)zH)`5Jp`m.M1ӎ[#bt7\OƤ 1@ie86D GڭBBw 8̩8pm#f7 >k/RR"XI>?&Ab#Kn͈dw56Io% MTԳxGRLХmxP~$ Z 1(>7+%#"e BcT`qcF ^PBR"_LJ9I C,%+*T U>ޖv e jALdшoPLQ)ܮϿ3qyO/.q'??ᇄٷ[yu Վٳ;~`<<ѧO>bOr N3z7 Ww=W#`,[ơ^.ٷ#$ɒTAe ED++42nv%%Φ.a$>|:{2RֆG5(͡%x(D.Gd 2y' JPYB'k#QQc )&;W yB٪94J'3@JB z $L V{O|Sb,__:ܠ =IIXi99]\$g$#pD027 E]%(FwQ;:?9nwxmW_/psg{Ji 9-ƔP2]lN!c;Lj ׫eMlbV~`Db T8~h{3m%KCyJ =jm:^~?R|1TN^϶z.e1Jlo7&n\luYMhw-]L\^|Wѕ}BǤH?zi܈t~$YLlghŴ.`p|wuEc!HMFau|f{##MLK=ͪ*f ̓SN3nzv`t-^n|t_4k,#ͪozM>ҹ1WdQr@L$Bg1hTaezr{>2,ݑR@G{,dPڣcgyz$n 2XK"ا F0B")xS;D{-Foy?"U`Bzиc7߾YIuS l?80*Pe* EHYQ*^]|%M3=TUn+z8Zn(vq.ciQ&S!ZG ҕw1m*RBL䣇<>[b@a&s\@YsA]V> ޳uwTcbERX#.\XFHؑs*. J88֔V\pVG>~а=7.|H,ڗ4E1(rϚBd>"69%E>OJK3 ׭T&g<:pNϿ{o|f_TjϺw Za󃔹'źa$,Cޱ#¯imhm}FEB#(2Z9L8|ȡmuHvB$D?,[%ޱGO_~G="DY$]7),i&Sm9}C߳wt~wG] u.Ő5nRikpN2TF. ]rWFD/#ʜH0@MLvE#n޺0F1 Ogz8gV1|.i-!,<:s}ɃSwϺYP)Rnv\??|Kj..rNJ 췬霫ac2얺Ґ\:)Vn秧T՜[٭wl /h36$PrgOab@==l!FLW10hWȱ IDATȢJ6cDmdY)% 2 U (Y/ }o/C9=Tgyah|Zn3E.R\W*OcY) m)Ṡ8b1b(CöZ0UMp %1z62/IH.k/}I18ߓ*\yUbt- 5ZAmB2,\@k]BS$$X1"MFgXYLJܠl .pv`1C!D-*S6иak.d\wD[?ܳh^2kj)Q#I AߗB& Z,^/8[IJ \mZT2<9xV]ݍGAzAQțc` 0Gfg*BBtG#nG)Ѩ,$M dx@P*(ױ|N,lcjKkcr28TȔ\xzq#c"%'2\)U07wk^ONJ6|Ͽ⻫˭'%-JG*RIt)hK``vv 1V.;ςeXQ֘٦w#3F&gQ=Sǟmȭ!TEIa5  F[ͦ[~ َ ~ƛ7k\H-8<:v/R{\SVr231 #w=ypCYhJq> _c.zӧ N57g;^]o:G4UQP*9g磊tٙ泏FǛ=' 'pw<ͤwknlgy~}豅4,oߖ *yF6o^dzhP-Hfz(mL.OPryݷUI4H\tCB;TjҒJDrg&PVe7l=ǝ"؄*!*'Sh@UK7DiP&QP&i9p4xĂ= %6DfcH JE!T8 5ɀRDŽMJ#*-!:R @#VESmHiqE%US, P=`%ѕM50t!Ff@1$oU(%IIƪ%8H T2̖Vsh\kSVFnO?BU̦ʲf7Ͽxr6 Vw>mH``pcĔ ZPYɄ*h[ Z+>D/:>=?z,!)Cqz2g1z_,)"2H C !H(izBh@+ {$u87lT\Wјs$,KVܽ1c̊s8tt}9dbUT}H%abLCO/N8]-x8&v$YOfږىvVg/׆GBǽV$At#c82I KQb~ # RY$ho9رXmYERSݖb5>s˳9#dHQfTް9 Aph* Є{Aoqc{BNǖR*:+M_(S0d\6i0/^ق2ͻ Lz0Wֈl5 kMxD㗂¬uTU(̄酅Ђ1hy ^aFMsr˓CO7$N3>Xr}|'|s>Ϯ@I;~d77~o3s!b)s~:EHXbIbdPJ`pəc]irIR-XуFvRA HP 5 nПX\@4~izrAsSWDxI-Q@gEYy %w|)g"mϋDyg7\˾pqb^1*04Ye7⚆{%]Kf+E4RQahN5h, gmCJMr,!o^qyҲ\Dok?և@3Stƫmgs$>_[0k~v_y]ǮD^ҽw(ѰXTCF"k&'?e|'6f'-MGqsczG r MC;0qTd`oсY2HqJ H<1F6eet*EPL&c!; =(`JPҷ,9CHo&pTq%d呧%K\*18!]Q*ǎP2ZĉBu]% İvtÁ6xr3 ^_ 1KbߍʳX-B?쉩0kg5⌬VnY8.iygpU"j"ugS(AYNk<+qU0k<"g1*$z`^h%A،Gw\?bƛufۋ{a@*`"8R35cy)uD7izw9ƒpde͙YPUwCrɒ%K6=w[6;ElZ$gaT3c!eK0F }ptØ%V0p}}JTq7br >t\-sՋo) TN.!Y#lE :>2?EYXq oLMTFVU{`Y߬yze\8Hr7|ޭ:Y?yϿ|>(+a* rje D)L#?sș !1?Ϩ'Cɑ!|˯?oAb2eʂ).Rdb`kzf""@R8\,*)v|$=P4d w9I8QuFӼv<=s9Y6ٟ|yӟCw%5}F],agL֜ԞjX,[nlH=N@3;p#݁ݚ7{և ug0bX-x~W+WCY:w^>*6]b{80dͯ^pF$mAhe[sҏHNQc_A*x4,$L֑'SFT2XO 9UTπhDpڦŝ U%d@C"b8A/RZ.tJu' mT@^Jl{F-[pM^6M ;d0W2@tVͰe):9h'lVJV]@c%U$mh >2Mwa"Pb؏ HFqPVRw=c78ɕkkpz1cb@e)\<א2~6/J gC%z`+7eLSe%>m fmK6ªYKzg>SC_ŚE^xi)9H 6T:؇Wo}ٓU|Y.g;fe5ohJbI a߱kedOTՌa`my۱\N3NO9#[nwtDH"Ó id,ڠ1s,sNfXxzǰ"on5<;'8FQFtC"NO[t<-ANT C*2v2RLr%ɏ.f ͈{bB̈2]}q~Uqv\Dc¦D/^m|trn\KnwqwT(DG,=C'ᘴ,$-(*r.O'dAҍ2 F .W 1@MĹP2ԼQ so*6J6X6j/zٳcQ_$ƥDW;FEǐ07R)ܯ{ `^M8ld^>yr3.Nd^.EDwc)oݲ46nnwۑ!Z(5h0Zk!*A@w"~ zmnTǥ-) +\LVͪ9WfjP&UcG[^p}lYϑ/?ܲĕKԎ1AlYdk onפRxy\26g@;[~!vlk,dN5m]zױm_rFrf$۰?j;q Do bm~W58G8 e^ڰϊ FUͬ6|}?_ϟWF 8 T+dr`{@Q&Kxr,՜wZ`J)+ @6BVZWuɽ2EYr)Jo[ܰBV5ރޙkDZUEWMYcdN뉅 {v a߼{#FB A쎛UN9 3yr+VYRlYqv9D!1 YWc$cs籏 a\! Ylh9uJd-fL>3,M$+ٜv1vXm*mckX9eŚ<̢piXkMR55rH>V5||ndęUHfG e|D̰3~3 13)ᝥ+ںaȬwr:ȇY(K+nd @0%\^rqvs%C"-=Dhj0GZNW V' bJ7{>p7<ܮyvudIp!7n1{US>-~i7Ҕ˦8C٠`ergZkf6'5c[icb=1y}r\ɇWN*\阯:.XD0r$wk,xfVyJ$-,1YL`:-K/8_4gGnny{t@ #9b_~Ə]&ds4MDuƛU>0G-lSB̤fgP xmMZYTyjMkTF}bI1-ram˽$VVY8[8_.78FYEpг>2`Dxghw;ϸqU߼gİ2-Wg3kadT/wru>XͰdں 䃵a lG p9 $bD];+52{SU ͖~#g<>?_ gܾ^>a8"^2CȈ٬dE=!~0|^ 5rb^]f`Rl5;zcꖓɂo! ŬnM7!Ke7I.(rRd.x#·^[BImsv LXIZ":Y? XdQJRidL.œbz_ 'stwIʰH~, ϊaʣ*"ֶǘ3PUN}etԎR3KH!D"C!c!0G~Y^o>p~௞\8n7` p~H<\Rf6k+Ͼn]麞痎y[$zbL`ԩHK@ %ʡp.amr!Qil k a}BҳĚklXdD ظP( NfY Q>x=_Yy'w4N_ HK+%߻a׼|"/rt6'(jb=)VbjcJYnOWauA#1˝(1;0Y"c9,?vĪ)P&z'*2OEm1mC Ţ1V4״øgf&Bg sRw=@w?@,})CI ~X45rzvSj>13Bam-n;]';ǡ&+Iԕ#qʱƊ{w+>'?~}gׯp|y`7flY-Sv_|}{W眭b)la -?\_B \.;W/On=|b #2:S˚g 6'L7/`' $spdA3Yc{agƮg)iH^%EֲέPL8ɏZ CSI˘"?aJ애{~ҢH.čh[#TwQ(>E* ",ՇՖQ)A,Uq< $S,Drij1Qf,3FOIsY8묡254NhŅ{O;oBR!8&R 8V^/ =͹".E@`chxzl'l@E,5c,K9G4mꆴD&$NU>ԫ2|Rd/k ֲw&ܔ&,m)WhjI@ 1*I囁n7Zv}a$gC*oYU~?x5"TΰZX. zax cvCI_T,f5M0żd!@7bHX-]2k)%Bу#79? rj1ʤ-bf،z[}_ƤbRWgz{OV<{vgj/]_+n`vr˜ 狊gOqK rj8guL".sneԤS2k2ĜnFD)gM`;JlG97Z 墝XmU$.( HUg#(\sU%;1 0 1ui)l7Cf .=xXXvϳ'紵_.yc7^-b rEuo2=28MӱU }O`t畅R?u|6! ?%}3,ZreۏBfgcXKN.g3k]?pqug+\bd׏8]0#cߐيfNw$ !f#q %58=л9fټa1tytZ]WN#hjXyM1q^~v1 \9[- /pbb7&!1 ;>ëSlհRW;iͩM%LL\+p/Q^/Sk;B,|2&Ήn #.!̩)Fװd EX10r$=念]hp2He+6,NG=O =2,#ʘBUW+bteb(:m145ԕԋYC/0>j3~O-~KF9;m9J_sh4 0|%ni(Gn<0{p?DGw=)G R"E9.srq ,V>?lNe=uD,LS?&R5uݖLXCfmGR1dr-^y];5\_4k~g{@hZN5Y͘ĩEq__nK2l gDtWK~o IPmw'k罋9çx+orIZ J1Xo`WS*؏.%)ĝAQ i.ZJL#m,MF84{MhcUSfЈFrI"Ze#IaupsJPsFض%*Dl^.HAKuET/"VyJWZlJB7R }V~,5D Lz_تr(mšh=ӭ7]몉)1JnU,O޿*ʪɈ0v ۶ehs%I."od9H~aUWBKAzcԶp~2!\8Dq,Q^@KC:R񦢹8;6fr@81]z3Rrfh(H?a 8G?f^x{Ǖ,@2dA]ע#L#xx* 2 #rb1o8Y-xrqF4tc`98[apH7DBHB۟͘50%4$ ;mj}&&#-K5ݠ\c2wS!r*8QZG(-|wacJ:zzpsNX<[yx:(H3 {X#쬑>ѳ*=:ji6Ǣ&91ҢB!+ %[j*JI)\fI:XԌ;$*-$c. )3dF؏:Y"S@RR^6!pn# ;Q-fq wUlk.NT{F}9IGFdsw@]e9 ʎ|}WoIB}mYH(nN銫C#rmgKn;~:@ ^X!H!XiM"ہru7w{1 `EbqRLalXrԕgSLonvQ g-EKL*qƻ1I+v"1laHI?͛Q9 ." ű\hrnϯpЩ JگSD/z0 cQ*y⭧A4%C car\-G-P)xD[VYVlFxِPIN帑*Sh<8pgESUU8#4 %f+xOr)39&qR%tCQTwE%=V8JqiұVXM*HfQS-8+HEhRID+?= e%pVh*C2EH)3d,7o^d8?]#cH #w؜oq{ R4lҖhquVAJ?9snz^bQ@Z 7'%S;&m;Ok-tQ1o^8R+OITh 'eq"w'h0JܾO)Tں5B邓h_{cbS(iGa$HVm5żkæ; .f<\tc0RIK7>d4(@˓K|EQ>|M;ZdM}fN"T`%z"-ސ=B"yR狆?db'WOhw۞#w>~_{çgx+3aJ3qzj^4m#9VߓN#FH&L<|-擬#gܽ*/gԩYY. 3c42uuǠ#1F*at3Ĝ`Ebٰh 1oj^&r+6n]t Cdx ,,uQ٥Ŕ砼jո Xj2Nf5uevhˊ.R#$GC%SF cÑL vEx=WxWS 㛛;0ItO3iҹ@GBȔpP׎0&QfdH*\8hC# (ԕZiDžYRXYJϛR`"Öw7w__j* 4v5g63_,U-ZckP-w\& H Sj=ȽXmES~v|9w0\$eFNX %E[oI1zeR)>koi*KU  CX岥3^ANrbc 8dqD]4|%e >CmZ|]aGi.[Жr25iþg޳ڶb>oc~gNO朜cC8]"w[a di% &ZK#J *)^&1_LM,:N-3O+>~3󖏟_yW7,}1yg?$mEE/#i6BNՄsQtZ7ERvΠ+Vϡ̙ѽjr6Kh1C{(LᚓKN8t*abE3Xx;i2$Ķ 샴cʄ,FaPߴ5T^]zѵ.Yaֈ;iSXgkOij8d(1M]㬕8 ʔgT imc`/h%`,hٴ`ݖoJnSfT̼c hsrU-bci#U幺\q)'>;D- 11SWzz -:Z9+ C1솑ݡgl,CG]N2j(O^tF՝m-ݵ>r48`U 풌s*J9GȣVSTѤϔaZ{sYd(V45<=m8i a1S2JD8B>swǜ`"ܭaVˇ@.QuZ$hzBUu V\2H\EYkehC[I AicKF^rm1 n 44F}zJDN7y]9Ko<9]`A@.eDT~ [s{)˳%+uP= S }?@̚ `Y[VSxupF$Mvw qrraV-aױ]oltC,UUcĈ(SVD=$'I1g!1$t3݀0o MFC˒DZT\\.=Iٚܳ -u0naĔjrDOX+8G𶋼7+sՄg5'sјe2ۦf6G5AGnoR8LL`LtQdfuf5S&8gج:r9rXIU5WKW|t}rƢlr~.NDv-c2eaI-s7Mj)Q"9eQ$+/Boc H$4N"P) ^Huo6,͘&ɀDkIg sXH7ģ{+GVjѮO>f9wbdF4aVWG+F".zo1>j88t?CdI\ J ! YCfm=?Nȥp DȺCRd9G-"}!ł5iz+~Ya*N5W'?xj?}7ErnVsӅKJ ud,I=ؘIQ]ٌ"n6^S1+Ӡ |Z裐D2)K =rD^ۄY+ e0GP4Iݘ*Dp_TJO=0ń(: 1E631{0s@$F `Dl13䔱RJCKO){c0#E9Vqa #eev`tYXqg=ם6J2 +ɚ#A_aAkʱZԬNW4g8;-ޓ Y/UtHzt<@c 1&Uii)FSxu.r'.82q {pqԵ#+T$3%$ .Ydi=(X뙵f#ټwO BSRS ]bFoB?{jIm ~􄻇f2K,ܻ=?KΟO7v 鑼)ћ ry+r)(a#GGIyhGbf:Ma曷x[O:.Nf-0o~O'Jٌ'|Rń`}"D[i^m4iaEFc&;dkG+K)MIhxǸ/Mֈr233V. )Rxz٨x’#”i)߯'ѨTk,˶œ3~S~g ߾W}v^v2?QFQ)̍LxZeX=yNPy>Ң5"f~f\ oH.HANdU^f.k_8;j펫L)}}-uY y{C;MIBAagκM.[e9 )C.6gMLQ:-"[˱ `4LXUb;ǙD9-MS30QiѲGϏIӁGK.'nds?_^۫{)@奛E!L /RN }sNg'du3M`ēE 6ZRt;RJ٤&BĪ>ȁ$;<+91%ܺ"I $]/۶afxc"MD )i۩m!枳LatIhcNg}AeńXJ1RyW]?#Do=SXcTl2qUΪO$`;[cI$!)(%$ݠYٿqq"խʤ&6*O=*6ԨbfF'Xij*WTlARYǒtbkmqhV@NmBL]Wښk{KabxaI )P{f3{+B>r&BL:XT :J  d8FlƝwA0F/[oyU]w'+NΎۋNinUySm8=Ŋ.d^̝BfYEmI"HaH^ hEDKZD[]*w|2Tޱ>Z9C?.Ox%L)r"SHb0hkţ,-6 3VZr!Op&3 2* gr?t]6v$!LQ,9 2R" E1S%#j0aRшa#m-8a?O>sq~o_nL gtt^s: "S6ՃɨIKBz^ 6~.Ň,ExX'-\,9?Yb 5y[O!z3p pT>sw7Ֆo_Ֆov쇤q[ꨭ4l:$XOZDf}>&5<(SF8S$)z č Qܗz Z(~9Xxd^՞%3nv"(w%Atau=2>Dr JJev0aZ)S$IӪFX$ bg!3I'}ƪYxm1N,&]Xk5ozVqYJRX8(Dz2n\". 0y*[sFLT*RM q!D5d 8k'|䴚N13~H\V®8+x)w6VU׽QT%(3jQQ-eAZҲyD'-2:-ﮀ9WJД),G 1Bh-$\ SZV떲 ۊUkDogdstakÔdJtdOpئ L͚qֲ\G*먝1ߔ8M!o>z näђU]K 6ejg!g$mRǷ u-,]TLY-By+Gr>&W4. ݤJkjχa g2my= ..Y6EӐaIrLڅJ-&V쀙 f?6m~nmd>L7 A2J)r[S)@d wy&`AFޝTTe Ѐ0f⚇)b]StIR+I @$břbK!Y5 Wŏ7!'+GL+~kK~yFQ@C,f)>I r)D)@#DaO.Yw?A7J+`bb}1\gGX['uv=˶.g\To^׷t!sqS=ˀEIbHtGi^7xo7__q  RkӧLd N3UpIL?' 04^U'%XC$gK XaV*=f{ȢӈwSV"F`SYhR@-T\!Qs!YYhHZ>;gg5(~`ܪRXk}: /x@IarbQhɗ3 ]+"ڜrd\b]~&H$-W'8ߡzCDtW5V2!@2M舆:tcC 8KJ8 ibyBݬ!yBT_5?M*hQ>jטH$BL%[3/;Ώ\|Xp7ŘY}&Ockhu⼴ [Ԏ)6\mih~,g-?\V<{Tʳ@`fqo6n'k,U-T04u cLގXxIuMee xS [نprWݳ=)?'lS~s`zǫUv86ca62Z [8ugX95)l~$ai %rlIbӸKON<M#;![&dl\y0D\ml,m|nFmq+$)Ei%)2%و"fpbYZ(MΙ!L'&X*6 _8Ҏ%RZVl! ;Zh2L :)e{RO11Fw$ʔLCF '_Ɣ\77?7BaՆD+#IE˥;1&\(,4MYt$⬱A*a!!IRՑlJǶpzT3899:=;s.k1L>&胡&Uka\.a3}˝kKd#3ݐ9J`alwEq[X0Zt*=`&7TG+9ᭀgӫc/9#aNlbw1)%b Y4nz{08>^S?9þ^v.1wx-) MAE|RwaBTf#fiY';WߥBN4pyŢ{4MtN&a )WUd89>bʆ0q=;;n=]3&dgUޥ,n ^i&<Pzi벶5EZ |90rvmbuFM[XS!B0KlN*G1=B"VRvZH/,[(Xng8;68|jn†97l:`U޲h4ˆ-.@(=$]8Be Box'8`gȹ/҄hm#̐d1”8j2JK%ʐe1!;0hZ|SK+YzFv .DGئp0BF?n{eX*kF~?Jp2Y+H?=lw<숛[ݲhjk,|͡ aHuY-}! lnG.Ώyh͕o:L@Uy*kXxǓ.FQ Y2NI[F JHdE'Oh;lez.킄amahcNY-[{tSL6/~S;9Ds )Pt(N]hJ>}ΙSX>Jqͦ(rA H-8'I6F>cʙHcuXY/kN-.rp.SDԝc*. L6RCp6|ӓۛ=g'?'nng-MU :8av[dT~19dQ=y/9t'K|;2/VK@WIEˑ&\ Js2s|9rq7Yn6|}ͫ?Uѓjڥѭ+|򞅇G+='?1Ϗ|ͷ/oxFYxuM,+NiMȺ4RϐH:72`isG|Цr9e}RzxL?l8)OIӑ6a%(+gg&cdp46:ŹT]z(A{_<=y/ 'K޾abvs`Fķow\q2 puEVW36 $E=0K9*0 U+!knQJc9Yi8t֑rb-1F*9e-qhś-NDK~2L wpcB˷s1Zw|sY굋nyV&dE,YDo'#Ԝ0:V*Ɗ駳DSr0/x#8eÁo'w7&M? ڳh HQKh`n&/ (Ԭ3 +-!%,(+rlTLuE^i~PaRsS*C4 Sʼ#Lk'Q75FY.hZŻI/ _['R>$(2Wʳ0B?', 9% tWd$0E.tHw䥇BCBNVW}w_O4KBX]meLfZ>{9c Xr=\~% ׷ h*V s Zl^u C` h1Oa $dI[֎O߿Xx IDAT)joYK{o}?~oG2qy&ëC du/׌_W'<82uR"'˖ݳGAsL@u?F -iQ]ZM>i/b!fp(!M6^% *1JYiҵ0#Lo-\a3)h"kv @DUY?Ï~O?}ơ;pė_^3mnjөuX쨫XzazXŒ*}U3F{fƽF r)ݲ7[3*(zB*:XAmX̽h K~ Sn眓xQ]l}u$D92$VYcF?e}?َ̗L :d KkY;݌,gLrx6ࣗЎFWE <.Nq2l0 A)9C".N8c Rook3bX*|qL @-;B?qe1rNHm6U!Ӊû++^1 ~9ɴˆIn? tmw ״̦vӷթQ-+.N W[noD)`0"EYH$:J[Ja͉]rgG4ְh,X1F] ZUk@C;*xRRWlˢF*1EH/+穓iclN ^VgO9=_sjQ,4PUp}qrvB^QU]bswwޒr☣heȠrգTNٝz&e)z,= ԕhE<~c~cmE>e)kԆ%i t3h 6q}͞_\) /^c.iӆ?z/Kv++L.iW)ϢMQcX88+19Q[M:byi,V&V[*8Ud]/\9ޱ+q[]g?{9s{u \g/?=waSz2LU~{{nv=}4Ge+%PImVT63bN큡YW7enR`ryS3:@T6 FFK^ɑR$zc\ {lv\o7[8Zd_jUyvN෇]3F {S-lY-rJT>J;JviD;#ht,$wf4~Ue O֔b"Y9rm+`A_Mƻ!_oHBw'{9ApRӳ3޾ym1S$Y(Tg`+w, '%NڂaxqRYD.${bHJ:˃Pam#.H; zQ-E]LIjh7'Rwښؗrѣȫ Iںd4$ Z?[szR"хKX%VA4mbJϲ>" sىkj+v dMO:YC+r͞GkEsD֢%mfyzlMw&4-}6p~VsYp臢͑#%-3e֔e۪E{G?y!kڼe7;p'6vR,9V'g-Y=28fDkdU\w/ȣc ~7,]?WBKIg(Y.vl)[ac;Bqy㟽d]Mf9ps3ۋc˖ngcfx{H>Ѧ*qV,H[pXZl<(R^00H\LIiIq6%噗w.:aQ0G) %d%FVʖq0KT~#H]9)Mb; 0Dj 1J[K}V 3Y%4ݽ0Ni!d[wc8PnȒbJW'p ={`hb )6,9 L)8N ;-mw^꣬UEyޑ)fp.hrH{ c,[ֳP+,C JcW,IR{e= F5Y+I||mO6՛Ģ1b` j`2M_ML6y~'g/oIXl d񵪭aִzr 㤀ʀS&ct:яDۮ0RMCN8 o =Aʮd5+G c$ɯ2=`SJD}>72֑|kt8%FnU۰^. ,qi`Z,{κo $US3@F xBpDT&j)sӏn9?i9Z5lw;5}+~?!|w-} ^lS,8u Krd0VҵגejwrR沃Mg2e]-j.CF+a 4vf2%LJ,*K]9^w}zПQ6yXZZi<>'G,La";nw`2_~svs|zD[y wIW'?!v p$e'deFOcD3Rk)(2 |2ZʟSl 0X̜c+ "'BbsPP] @"Id,$DX0ZDي0<Ä Ef.&X)'CxF4Q}Y,w1r^5/qVV,stҏ{WuEVLO-$Krw؋ؘHޒC eG΋^Yo :EgnnP jo [Nz\XbN$l_{2JFcPR:/[ ⮊!qeP<}OY[xy)HhYrHb6P w8%Q'%Kʥ%z}$2(g%rWk,h> :88X51$IZ @ mjiE( :k89^`9Ɗ[=Iۂ'Tްl,1mUq=&Z||s5:(1W\u|}8 />n=DFfv%H$`CӋ59 g,ݔyu}Od&=he!5MB)fexmX)Äu50¤z0 ae)8HRvhezhK*#9 L (Ay++|u[qZRUdw#m[Qb,ȶyue=L-M'4EUy3{qqqj!hkVZ*t01r_>O{.jad;Fز.<'$9L=DS7Xc\pybM=7w=)|ΣS?}f?_zū=j- 9!h3u e Eyvb _6= NḿQVP:Y-nLyD }/oOn)úm9O/Oȫ=rT[Oxdr8NncF:^>՞0OI%{a_|\qqfힷ7;v%8M}WZqFd!%ά 1~=&V I[r,%9Y%a,D&btê ig-zo{I S?YxV5U6$.N|9myus~9mmI!Sk|FV=ktt2,}3Ľg r1"j֋+q2=.,ʃMbUX.Bd.1Š'䇡#f<wXT (rR`e)XaLI&f@8)ֵ _jJYPGەh$gLQ 7uASVS=oZض5yOWrbUWar2)Ŋ_wwMG<[m@f')m5@2_/jIq$pf|>EyXFkDs4q _䵏$Fk#V]I.UA3/eZJF<( U#`lYC l21zأT2P$A8+5N< 4$QXZ  eFȈoE ŁdXԲOZ9=9],)TVEbBQ˴L1#Y% l Q]q9Q[KmFL;+MU-?V}t@e9$t̪zX1ְX,Sv-8rkͬ'1ݦ8ezj;etV[3P/K>|qzwK 5۫}@ٴNVTeѩ;jgm5(eJSFGJUAsZh 猯,uI) P$Dz8>ZR՞~x}uam4 )1Nq]!qQd!n?pjNIBDc&im4W<09!$*=d] (~rqb:l9n)Þv8tS0 xcp>{b7onzy{0$w|d͟9m빾3Lg]5r2yhN}I6j?A J[IR f!x'l 7y?߽F l3CnG-鐞V,Lh=] )#66D5rS\:?5v>aKFN;S|& PGVJA1dq )Er9+e*"'R5?aʙ8ep黕2sfN` Ǵ[^Gp;汵6i c: IDAT/gX/u`Gi3hjTVd,}ȓb|ݷ:d}oSʩъx"ÑT2]W׀匆cZp8^S %9$+jJ{OY "v^ zRDR}=hU".C@Ciu]LPSq B;ILD%+ZRRژ )h赑N,#D2N"5"UG|)m˰?P/VTsBcE[#ƞ9#$붣*TA=DyF9MN0JT)Y2E NǬ1[Lܳ$@Iݡ]QԮQb"Ka3̺ ;MćGϕ{p$̫wG^_y_ُϟo #VgX3Ɋ7m@N{xGxyIsB?hhUa2QK~JW'e*nl"N#ƫ`ԇl**椟'b^y$ 8!rooWKakݠS̔.!V\/n?{w%&n0]?[ˆs>8aw 1χ ޛc*vZ*>!.sEuzt9EZ`RkVbkMI}a/&SJ'm.t8{ u`XijNW >~v妡xn<}zSV%cK׏#o;{~{^.pg ^'oΌ)~8{JJJ߱L%RR%SeZ-ZfV%=bITc/4a)(t)Ydt@rD`A/VK~xpqZecSSdjO=y(!l.%5O*ʡuLT)OAbנfAQ4b">*%hJl.ѧpueݎA"Tc:=DdZ.!T^WثpQMmu]mj$J6t ' fZ>CÑPEE|y~CCy\;~-EM|wGg<3ͺR+ = % ~~#o޳XT\^Y2 8 xX,8lX,cvui"ub Aybk;2UfuvSEodv}ݢX4PI:xu!c9j)nSӳ 8۬ 8r{O~c |nۻq`{n]sط1R" a*Ι^Y#j!MTr(!9>h 6z1#b{M0!:;}?R{xx3Q\ 8Vii73Ɠ SѨ/(A+PHfWݷl!VnXcO LS&1T4fF)ڱ}O)a`R8 ԫqnGqoݿktKTDH%+2hd`fh*$KّNݎDX%?=>%FYL3&#$U咉ɛ2#xJwsŒFqI}yI12Քm2RCoXwX':db4se4}HuA_rgSئ^4^82:].وI&9Dhpzמ{ơX.j&L쎔e}1bvԑ9uEΙ~1۬[4YPBZ;dиU pmS %Bp1[H,ELmwޱXU*T^x|Yžhc麖\2cxz›竞Ǐty8 5|wwatqF,pxd(E v E8!1Y-X.jnԍJ"> ,I!dHTBY5M{ݓ~t'2XPQ,矝SLXE&BQԶvO>~ӧyw;8rzk6r&0^ʶkr"Hf1 04B#=Dj/T^cBR!Sѵݞyd\ڎ8j>gr3qHWlN֜nV|Gx{#ԛzEy-߼~G ޢ[R(B+c(o'X8uN#K~rq|ǘZݛUMS5 W[.NN6Ku[PsvGolH"T1GK" ܶG}FNM' +(04&B +QKD󙧆=Ŧ"QkA所i)9sV+pMnRMEv݀z\ʜ,qc8K9 (?ZZXM(^4|(R:b;s4Jwtcj>7]CE ˩ڄM+=Hv`P&*^Xm]G8vۖU9 {beXa{g_hUEDS $H^Ky)dA x-y4ZQSF-j5Ĕ2Ϧ)zÜ-/ Nd)^ppBNgPK;%1N6e(`Hz@W˚f!Z" ߑB>dXJf"G*yW xx; Pq+q4M6o i(qri#} `$GnoIm5Z ^ MI*1"V'suYR =daXCDvg(:OŞ̏I<REYraNq.g\SI` zX6DKkGF\xǛ-w9+O,=8p~vƢYF@%bT;w, N<.b )]zp ͘xR%|mHhB]AcT" OzQwp_WHp^y8njUTuWLb2R"B e\TT'5E #J^7Y=NjGT]@Ct)iYqfLا̐yyTt]n6*YTԋaǑu=lFA:W؎\iϜ)-7\8lG޽gQ89N,OW9tӧdN4K1;bc$pۣ= FQ$(N0BPl|X7)wY{ܿuMB=ƜCjUYV,+d=oqNGO1v?:p{.SˡoHքcjsŃĪX e" }Ƒ&4k.gx!mr)'\l(N+/( PQlԦ{r`FG7P't*{F$e?5LG3EQ7 "F$5mk郬MKQ?ٱh/瘞{5__=P27۫=C-'2֎vèYrVb3ȺڥW66{^I};0:#sT"4hGsfʣ3#:ޜ'ŋس+ a"G!DU eC*qP&6~v-%#NMIc8#?ٙy0ꍋЂ#f&Nje:^lY-EU)o8ZPt7+ JFNt4!i@R!ArA }2O{*|qzrR+sBdlvRxqyw5/_s;RK-]{!4eƨ61&R4uzPWUfiê Zh8)XTT@Sx8ȍ >;]#}?Pr*TH)3ƨcIalF 3.'!8:ɉ~Sr5KmΎ>r\\G#)P+쿝*ZCZ=cdK;RRwJ<c?0;JI$5GK.N u]℔/߲w80DkMM@eдWIqH$[AJVcE]@-l5MVy):B^&*IH-3 xçOWOjjp9?]?Opun#m?nwtH11>M&`̌~LC#2m5Pc Sv:~XMaL蒘[|BOE em*>rAޔ^w yyE^yKdGbD^<;t(9jĈ4a3;MTXWk{ =$aA|f(<"b ;&>Ȧg2$P Ndq 3!u8~SB=ɚ]^rjᬢWDGu^ _*jo{b}1L݌s_7LqI?Ȕjbo0cG):RGZ/ 6mB1KR R%3&Ƥy<-XXVJCVxQJ>уlP)%E[\])F!.<2{@bm|HdL$f2:|R#ˊrI7- ` LO0f7;͚i/ӵrՒ rpwKߏS8=[نᄏ?x\j $.eAcuM*5O %MA.5w j=H3}w)!5|#lv;g ͉uEwc'^]w?yOzndsrZT׏)%ڱn='+?zdٓzkȦIf.VdG)ZhxrL1j.ս9sAQeSsvQrM*aoiGv=%B O.*~wۖ:[q1 g H|zK;Fn=ۣSF^س&_R&C:g1wq6>XSGffճPh&: CG\к/dOP Y%I'¿%d;)yK6t Icɠ420{}]=ZiEӌYY!7SI14bBGA ( KP"/_\ͣ88Q^jvNi1hFϞӹ٣3>i? =XXo֝B18Te\5- 9*J/hQdY *dQwzFٵs*Uvf/D?,vwqeU!# l$0-T# 82w4u=0(,k"ר5;%*j_ 䨵\pqVP,rʼn@ -C[q$E5IIb:3 NU9ZUNrXpdAGƣXw}i,%ib6±뗋P9-Kj'+!{ꐨ*wGUb3HB̵!kcD|M]_x"B/?O@EuthGvo֓e՞:'42W;vg'ԋU+J IDATv?p<Csq(0&! Yժ>͍J.fmG5"T2 !3q`HĤ| x|HpvvqÞ\ ǘy?|w͟}_|5c1l*9Cnٷ:&uNdW(biJ'ȫ7w(YՍW>{\,06Og."9n&s=^{Z/؁BhRj%>,ޱZ-XmVmCyƽzݴ6(EFq*b05Y(t}W\/Y=mə9y#c6kEw0:U*Ji*O{+[QԼ 1AXtw[rZt<2={gkc5)G6*ۄvR3 A+%l~: {LΌz MԶx DAE$jHwTfC3$Q 'Yldù0x\UѬO(v9g>ABxfvXG sy"_|x!UC+J8Ǒb\ :mP &pQ%?_2M&w!;a4D2o޾ڎP8tAZ;t|œ5kǑ}kfڹ=yzAS;l]?v2 ȡ'ȓVDqZ΅E(tpstӊa"'xW gk.rh%.sg~wovןO-mxRG9Q{O'摒5V/??59G ?6(ɢӆ4D]Ӡ{HT0:L%Y:SP=롴\ZcfR29TN6$4j x9/x{s?xGgt݀œ5i А% ON캞m)r=F\XZW`4 \kExzy9|==W/8ٵ!4Wu.VkvWG޽zH;DOϟrs׿Gh@}ʙۖ4z;V ܽLhD27uy0nVq6<7 i$չ@8ƄPbHdsSU0q *PUٌ:! t+7BSgJǢgQQO._(yFr3JLU:ij>1L{>r](j*iL:qx.02.ii>5Κ;§+\a .e~Ƕ3q C;2)L=I3{3Mc؏[]Rp@5~#xFKnе(aPYf`pR<yEz'H(&Q-<$Qc\T 0f nX,TG-șk(:VD\YL2 p%h=zHŇJ9]d )*'2͛iLSqlhVkہc;X`sd1WRp޼|-)(LѓӹnT8)Dc1֢FLUiu`Ț(ƹG&$-}cQ3GkW]UPiGFY+!R.VE JiV gKw=gm]PD׊epkyuzQ38d5n i>`Oiͻ-exG3GNּ븾븺X;*wƞRcsl{1DLAwgKN3kJ>3]aœ4>wDHE8lN6cdGv=n|3֧'^|fQϹ<ױ"w;n||λw<~|b@~58q|[Ƙt"[;YylDZS0[W/d$Kkʼ3 '{V5W jKI!/0;uj?S޽ꖳ@}dAEıO8 f#߿e߶4kQPlN㝢߁G'~|g{OUNMU[i3j0{Ca%P 5W*䘸@iJf]bT)قw*J*Yf*l<.]?z,4(GdlvJ`=ism蜁Qӝ&-|okB b:Wb$CL?_2+|/ 1 uQpqhV]^] ǁ!ai*-)"A YEMC㨅FTk3)e&Gv${SE\v0B‰(f8QDBB oظi!v^t: #' nm}r8t}4f^!x]YyXWZjreJͭH0#S6&@@@Q6Ly^8=]e&W8̅s&m#c.&+ZtN%FLh] %F^:Py[1)- =Kcęe%DΌqn~ ߂sԜ6ĐUu5X81s 1Ӎ"ɊAEdܪғ4hWWhoNS)zY/+ˊ@n#yd(7BuP.I:Y#.+rin%ἰl*]^rzӏquߪ;e2C˗oGԍ|A»9%>qr䓏\-lG^!1͐~#.8q>SRdFbdqt$=C&wxӏ.EY4m*ֻJ!rPCsqrS9Ϟ?W[0tG~kS>{c5ó5?*&PsnԢ( hGF)'N81K@#;sл:Q+V@cB[鬨dPL${&O0j *LL7 %SG$4c(:iH1StV)9K1$!YPFHao.̆B)D(F!Zg~w`#cԕlS" ., ޔWFY,/Cqw{4$uѽ}YFkk$fAg,VO#gّBRB~rn\? EÔm"h8m+RkVmSƫ z㚺HGU$PY?0`R'#*qc<^ź JR=`SxNfXXNI.HEݰ\/iGVbVu8bĢ]m10(VprvVlYe%Oi BUլ]f+Yu\T2{}g?fϟ\ռ|snءM/lN/li@zryH~gl}tɶ|~G4`:rвyrqdx_pZZD?ԁ.RG+Arv{;칸'>{񘦪~ɟꟿv%3_4-`oŨ8)'MIOs\5iq pTTt6b0CmZxpFvޑKFZl َPsDdyrjgnZC{FE\Q'V93+3(͘yUqޚ}N{S"LJVt'X,8^I7WKL &0+9v c7qOJƬimK1=/fDTA4;Ixy>WqX*hZ/-'INJv'B6 {xyiLH0fjQ\yi~6NOԄdR{݊>-hnI ;#b~%aHa\X45'3N..899rCXSN -|ʈQ;L1HSՁ*}׾IQ%E*WC@ qQ(YSwTu#OZoNX6Ĕcgb'8,e=ᖧMHΫ)`7F<$9bqN7@ 71AQ2=S)EG^axC(,,kF˚j`*g*_HxT4oƶo۵ʙBeY.V%j *1FI|:8{9aHjAXqЍ1f_ݑcsr.lz#W^GTutk ~_8'O61qi{-4^Ba D9S*yyԕPy}fɊ{eMǗgus*:EMJ*Hx縼8%8=N2=?eZrAא̠KȎ:NkqUGJ˿w8\NwT}d{V(i?Ag} K}&ۗ 2AԀ1CC-wqQƶCO*#=O{/?W|t -~Ǘѵ{ Oߵnrv-1tC?2ZlŬ}w{8&rRﶜ9p<ݷ=cK Q{ղ&AJق5?1*TABlO Sغb|x3P0"r bW Bwy*P/jjC~$D]N ^H)c&G F"1enʍ5jG+$ OL ŔM^WT 3!e$'lP&{#H'L:ʘ'f8٫tkj ;Z>`{Ox֭oK6z:x}a-}g{o/\^U`lHż&32R>=beEKh{F%ix o>]dOHqD X9z[5^?yWٿ|`W\.W匣[o2{|9# 6,Yt FiJ$ c m`3V#a|ِ%G71?ͦg1P9Rin '9luϫrm[v<*gg 6Rl].5y+NX4FmTSx.) 4IҰOD%aw[NpJi.f!(#$8:7=sV ȌM*!z%*^0\m@{IA\&Iźźf64pp$)ΘM+ _Skz;!I(nF{Bumӱ4[ǫ-/O_ś099C7HǯďnO_[:lirm h}ۣAş=woqք,JǼt,jŵ/ܚ򕷏}+_|׏h{9h ڍўQq !$W% ,dZҠ21b.5,մmzM;b̭(G==a15cA#q6q 58흱UD?徐n9f "q>x5)"z{h4\"8&`Rq΍D&z8>Nl6Re!1hbm"5ưRy .% )X?QʋIZ"r|n 1O0t"J/74Ђx.ă\8Obu.F=$pO11 :3Nh3-̄ÐLE1dʓdFEd`2]POgdE# }?Rq񚸐"m-}37NnH4qU%UG/-O)*!SNf%q,I hA@]LYPR4(*f0$-YjtD,dl1]IJJ%x.ёO1.iB!},TQayy@GtI㜥\6w`92D¡ۓi8;Ysz^M&,]E^Վ&I:*yhzvmD8adhQn Η44Cۣ9vtbl2dXzj4XhiLL:7霨W}DS)U`ceeDD#>p\n;,fj#΂ >prz,,r>gܸq@j!Qr8_m_Ff,&GQ=/>?>w8 -~Tt]g?1kXx/!JyTMљ{J A#68`m{ʪ(KIv[YBk*R5޸}- g?C)C]i7)}sai,!I2;^1|yOO_+t&"7M㳎$/HLP:n!n0]l99pvp~tWX8 \0a$·9=5t" )un9>],W84z6-(TVH:D3ecim3FJfu%$j,F;ABEgF&!( q#zrt8# '\\,ٮ|ւ ?b0s2e6n~j>M{I k'sk! o -7!X|3,Q(P8bV3(Gr J˘GkM?pnAI(,ْ빶 8GÃuwdz[SLrHѵ yŴ(d(c&!s"Eg̙T9u<v] 4CJ)XV8"+cDCYOa/ܤ*J>9f^iF^Zձ@B+>E@lgL 8tJd,'M3YB*Pg< NJT"'Yړ=z3bS#I\v]$exh!)?n`rz9Ɗ/q\QWR.d|>U&*\6pj49u1j1+6J!bQ⭨UBsqw+J-$-IBJnjh l~Ew CouyFSpr󡮢NZa\|nݤ)x \Gxw$]$ˊuj4$I||| qSB7+rq*`ŏm{m "DnJ}C97۶h1>ʎ$GaQsxQiHL*&E' މf2=> 5iZ*dzZzk;XfCl !8GYad6u<yĽ5*Co|[ELyz٧<'<'#Odq*}yG>JlIdw`oR4 xV>gVIB]ggS\.[|\;2N!R;"a8#/HAsء*xs o$N恽yoƍUHplV5ey4cRfĐ%2KV9Ӫ89OGKf\^i^s)"w/MR,#1 [G%=LP|%m#6n+N7hAv"ڮFMYb8'&!I4()Pi a%~@k۱APLB=i^O^DϽ RAEnGEVRUvll"E}n%cH0 ̮U!Ҭ$\wjձ2SLF1lظzwf:T˜)RdZ ǚb 6DvH\[/(vEbx.NMCZhL:H:BxF/(ߍc0$]Xŀ$z`Y$ :|4<ApB‰Et ]:Ewq BSnt6VR;c)F$uYas(I|6Ek-^nvс*q\a{1c+ꊬ (*Xh[ wde:C+L.f dkLeG{TUfMjbxͦCBzIBg cw4ێ yځ)DyR2vNLN \rݸ8h,vxgA91҂4/iBb{-[ʺ&$V\'ѐ*EYh}#~g|W$&ew(JDaXz~$S 0 ԋT:~+;&q2O0XRRI]Xw<:7Xk4#뜘91 O`1/q^hV|}'q,IIM US;+z23B$[]o8[{=lNa\a;"JL:ہ|ɳT^/SEQk_W+9Ey>E$喳ۿ>W?cNN n[pS=S<5߾A%t]O{~_? @=6}w oxⳏy<}Ҭ:7>`$[$&c&Ϟq||19{ >\@Ѵ=g+ak-EEc^l1IxE?!M4W/_%{{L ]:0 . ? r/n,ދ_at뤴= ZL?yN׵6׎{;г *8T EVp/yꔏsrhہ7[\.7UN$4NJ4M]b֭C7ڷ w^%b#D J_A%[JrB2N$KwA5I*K$8 h{1/Dl$X sUOnTѶ *tE}HދLc$vΪwT~c\#Bk8s!ԁĦc D?g#A^ԁ\9 8K(/?BШ8((VNǺ&Z.\s`T%X;S)*sf!YGw(WY"Fg  x'{b (7* rAǙi9.q#AD8$s+HVj t^L("Ng҉֞1AH\Ԇ,O1&rҬ놝&K )ٜdRn!@ݮX/YmW\V7kx Kʲ 5 wqi!r![)v*Ҳ)S9ʺ0y yQdF"40N&dUEwKȮaR"mzfqFRF|B6f7&Zn{IX/ȊB\ +D׬͖z9mעbV4K%XK`VX\£/k޻ͷ[+/w~`yí{}okw8l6v pB–D9ν;|A9=dn8;=Ӈ|3x+o>z'ӂ|2Hyvqɓ/98g^ub98>JhClW[õk ^?zmnLy.ioɲ+Wwק=oe%Nfy=|k_<7nssiQA/,%LDύghCs^mGጬ\O"go,h^lA5ݯcQ -"LF *ik6NqP:~X"t{A _#*<QWql!oTzivv5{W+@bp"ҤTF9]k.hq Pnf*R+54)Hу6R$)|Ju=R'yt"ynHI@g(hC%c. K3fYbQ+FsQ<($ .0mߡnJy͘D7h eK՚oI$I1F1=(a{ BHɼFazvenZҼh:!˅w$M釆zq4#+%b, Fa(|/YYH,RgU'mZaR-6mZ>29/W=Hrg,)ƈE{ν7nWX_2AYUh(+1fDvBL2-HQcF(Xa  ։U)-]Y Y?i,bP:vû~3eF¿W-s;)ͲYZf)Im]= V>/V6~tI?җ/żJi7w䓧l/]y׏&SBFՖý ѧ}_7&[?{x m~G|o~xe8??g^l/'þ%SoYnI\Mz6%Zil#9klct[B1:U-ELꜽOϝSmsǴ(EUC*RcІkL PNg8j˛e=2Duq]n7uzkIfES'`gΙ9Es|fplM)yϘNJ2:d>}䣇'\?z˧a7|?? ͖=ܿWt{f`y 'gj&93l iVo* @^&t%8gr BEKd%ѱXs !G.G o`wAPr#:Vxp~`u;դ)`{wEXyCYBB%8N.iF+N_hj IDATL詴" դfh-:1gIܠHs\e7I C4MY5 h- aUURlkjE/ ُmRd~V]CvB^ͳ"S&5E^?WY/ݚ~uAiF}0#ISUCj|U]5ՒnFaQhtRpl>xs?yEEy[iPC;83S%>*>f@G!x$j;mN}%4$k;ůy;!f;zfzDSZ,Nj0[drefhr "dY$:Yѵ[k{u>||,yM2tVl K4Es|=ٳ%ܼǴ.sȨ${Kf*K!C+H3^iEX YUK޸ub1OGhqptDg~[o%|:_y睷y7YQd٬Vx/̓VAkVu努gb';vݛ|;ο]h+N^s~qI^'_ph7gO\ϝ׹q ^a]Hilvv$(}Sbx?Xf˦xyᓗܺ4ߛQxCNgSRLC`G.M]%ܽϫbuBi-ϖ>Ă$ygX6$0\~yIӬם$-CX7 ~I4=zKxIOk5+n^?bI=gYǿ}n:&UƳY-\)m; '/ gg3-bq0'_>?wz>씟}K^(1:ar>z VFb$Lq4uB&,E9rC0|ϫ µH^g,*{< 5Ur)2ˏJqZDq2 KB5xi-1wE(Dݓ.DPGyF\*%KbH=ұWE|nB*ZY/ezҐQëKQ.S('Қ\ށ~B Y,z4ГE*|\ GFG 6LRT_7j&~*MFW1iʳn[}/څ, ɰF{mj2ƀɢfS͚9] YJQ0y&tPah*D߮u6RdIpYc ѱ,ͅݮ2A#AۤdyFVdUJ4U=n:J=XSʲD'r+PJRhFr Ql7gW(f$.gfDa(Vq4BZ:ᢒ,Cm0ITS ]fa:9,۬O1*0W[V[xxL]__ Nږ$aL2@%,8x􂛇5j(A&uB ?&L_<ނ TyYo6h2LJQEjEdA44>Q?@1Piɤ.PZs|lPMjHWZEUs(30e fCY.;>w޼sfe9/Űd^XRT !c[9:c2- ::n丛p^H4gO?OO/?(!f7[.^>Ŀ^pu_zT=ێ|r9YV9.88\p?c 7W~Kz>gO>{MolV4~xq~8~׎x pu6}GG\p^q _;^0%Ւ(C/VDɋׄ?7;_`?ڵlzô.f:u1 'EU:Y6+l,/N&x?DTC^(Lc@B:nH|4MlzHsC lG-\^n҄9?{uƫWǴ,jng>_p ^҂żb |=}߼ ^o.٬[ u+ _:KzrraY<~i گ1jލ}~"e=.["rUӃr1Z%B!Sz,ł8rT X7(?K_ eAedcy[%BvհTn 4gn")'əۍdfip.fpBXWA`h-mR1O?PTEPڡq|rW\!A;=4R##$M.&UR,NPF%łr2^mFY\o&>v2΍]6;Zb,x< 8+ 4KeeCQp`oq}{KNY/{ { ٜ(Pfk7xr IhYeZ|4y]>PV7QL&4X譧tZF~3QT%tJslY2GkC IQ)Qn`}TEQ2!g;iC9?;, z^a( T3+εEŤLs4ydŪAsWlC\|v+tRazeH$I&!˝Wъ]szrB N"2PH:l6l-YN?XviZy.H cP8 ]vqLbB?E' =Ӻ Kej\C8A_V_A;h^JllȲ,qT3$Zm\&|w1S6@Y|~lއ?_1|q3M8؛8*y\ߛ7 >pZ8g{t>av'!v舞`)߸Ub:,S'~-)׋D[98ضd6s"eRgeJ 9nÃ}x >~-IVKLRM&$Ji3J/ :IȊ9.^ڶšfZgU7>i;|4xm??~O\?ڣ*軎<{}I+w{3۾\R=k}O@aҜP-=ݟgK%o/0ۦ"1A Oj4Jŵ?Dh~8S-#=|xQ ZN쉈JY>b( PkUJ*1U'X4w5{S*Sѭ%v&*BpAJ]Y y)MQwqS: 9cYqX*#rQ&,imYBOⅈ~v!YNƥ?`ɬ; MMRI@#?ĉR NExDi$N X8 MQ8=EYPVzl +R) <`=K0@mXs/%\Y'm(DhѺb4S !>6ghУhCߓ&%iY݀zBLpwֲ\XoEPAA'ꮕԶV _AyڡGGߜѪk:4(hf9ɔ&?;g)Bg&K%nB7٬2;h6B-DαYoIթ|QOv=ˤ03 Hl^RCJ P4CHjqCiE%f޾MoMFo8hzbٓG?QEJŔ}/_ t@U i<Z(1 ư4dR|zV1\VWh1b.E⹸˾^G(˂YϞe G$8~ lP,[CHu?9UC}4%ޟ>ٚ7/sPn[`>}Mr0X;)evxbQR'890 ż|͟=iVń*V???ԛ4[vgzZk}L$H@`OJ$Ֆ\9r3oph`eWUrT%; =ͼ=nWM@$2o{>{y$ ~Fjł1w8™j2TFT]*Yu|\tÝ +ݽG3mo(Otk OjqBʜ}'Cg$~fVLij\}5 `;AYVʜ0bWNCQ"y= T/c}}6,)xGf6_2[yN'?+)˚ Ko.^mM}6Z+66>f _WX_[m@l`:qn޼΅-0=b]fFTe:/(D9O swK'~L%6x\'GZ()HJzg[C>x Dʓ3Y;)D)L0q*rAT*DJTwԫ)W@m:ŪF4KZQ5UHJ$u8yKd4e=X-*'V.Q5'4#Melnѱa>]TUV<{h:diFU7p2;qxG։Tm U^;Q#VFqm`}QAVmm|6'xEX=ڪ%zR 8=ѱ!`m]ږ;-~c0׊ kCPچ1!Eaۆд4bn- ںi(|rG|͆hGKFW_5?.kںjmY. ^N &Y4Rb_8eW4 Gl YѽCNW<{: CX(TH*8TmR4hpvmnz)uUP5USP>)Vx:Y/&FsW]JWѺt*].i#Xqn-g7OX _=`Z-t=r?ã)1;;NNy n;v rOƏ~s.|$…9h߹&_(:qRvykQgssqQc ~ǟ|yE6F}.]goQ/ɧw9:"t[ %LtZF!ˆWZ>Rg$ӣt[-J/8XEuJ1wiMazVCr]`OɳF%=P^( D[㌴/TM%eYFiyn m5\To P9l0q_i#7: tT2!ILy84xi5Zm#CM7QVaU(1.ώOaW1-!Zaui"2G@st8 I?isN.Z۽u SLZ:;kPYUT ŴĆ(IYJh-uX ծ!H)Di-mS]Г)*xzkCE SBךa(QQLkSNYp, IDATbP삘UGt õEo-ih9>8*v`8Լ]? |oya׷H` {lom5dww=U-ˢ&6g 's~/¶\@NgZ ,Eg%բPAۈ1hp hEgcN*Zk)vMC ax,<<)2h$霣XL&%'GW^8OfFg3oB(eCYu^:Р֫;|˯w)'l|k_ś7!}z7[/_}+/p[F9vx:RWsXMIׯ_s7y7quP _⠤i/Tق_᭷d}sW.238flsE E[3 ~hKVcu8˹x"Q$@ǜ.<>8xFI7qOQ!pɢ֐^8tɌ %v֩1!NɇC:sxk99QՎe)֢)j4efCkLZ}ck-YO("9<>dVbI@ᒄyxzx$y%id*R<1@ !m#4V.xy+ ,Ei)ʂ&/gwr1]T,+ˣS޼uKlmo)0(mRUN33ZS9e(:t71gm#=tȢ ̗ ,LTMKj&ӂ@wg M6Drj NDcCSl28EuyV'QGRKaZ [k!jE[ 1g>~ʅs GdH*u71fh|r!xp<ߢx~o  F\|=e D-DX%e45Gԕek0r ^ӧ?k'xyx>w(fzr¯? N^xy/gw'S Oߧ o~Ws|z]OOZ,Skƶ 9UUG2,t:w`.^WosqktV'𓻜w.w{#jrŵkxjQG}拖N-Q[oė2ӊ}{S%uբl&("B+4LW_%DmA{YTxOlxYZɄ#ay@(cx̟DqLd Жvx_98hݾ'O[o꫷ran\u*Ό Ġچ{N" KCa##s3o5z<>9<ctFoXQ8Q%kqN*&#JIϬSŖv{h/>gI@pJq+& 6ByE uMg .ϐϬ:@4ZE]Pu%ğ38-1cӪ~Ff*\w?w *DP@Ƽ @("* Q|҉((mD 5"!#*( q~׆oGdylg6@iM,ia?cc}"Zm -Yu|vS!ZT<=ºjݬ*ĉ$B'/m*0Q@iLi&%oz=Mh[O&, ZSt5fETZV/ !H{9*$& h#7!8 $S! \>Hj0JZ'nr̅TwU0 b'*9,?\A'Ҕ$KYE+4"Mb8D)IH>LŘTZ,߄p^1ڐd (Twyб\tLX:MKSئµ +,#icH"%L3B^WH'w%w.R%JqU.nweyFζ~Nf=67FlF]Иz\dV3_ܸFq<.k_捋|{»=d.?oŶr$M8HMjJa4Y?a~Bט~Cc_C[ ayA/[ek12^doDyG1Fw.39|ѓ<|P҅* uyWX+_KHLh%TDPJ ,ˊ'Lgs@Q.\9m>>ni͝{,%'Gn?>{{/koN|v6}w;Ԗbx{d7^ֈ]`LQY9) ƤDqRrdky)چO.}ts z|ͯ ܾsLf ŜxktjhO?oί>b,@[;JRVMw@XkL2՗re)|IcYKB[zQg5HGa7(Ξ#^EHo?+*/ٵ pf: \z2_an:m6h8 3[OpA]Ǩ2Z;N|tvi!A-!V8r#?_a+LT|T J ΢?c5ķ- bf>[⛆h08>8`Y,Hk[$O<弁DA/#lܹ@Z[ᐺ(q9R%*f(7đFvCGX.$z*t8e2okIG<(H+ϖ0Y0̫7D)D>e-iZ'뼭`֞>Ee=CU9LlnWѤa9;#n?{̿{6G9TN3לp7^Û-q$rt-?S-vLB$Nb40V-&l1MېRkkLk zi=ʱ,IzyBXP367,4:OdA7_ɉT5ʶ;=^:TSrnsqgV?fv|lA;\8K#hOXڦMj\dRN'%MYQ   궡b 1i3sSaMeފyYJɵҭWsjCݴYa$qe+juZ 0@8,5hl +W{"@ya4 J:gwy_elrdZ}n2y~Ix)uUe\sMф# 'GNKj)dN/|_W% m[q4+9S9mqn{͍ł}.唋*h8'rĐf$5g@z1#c4#WE1Iji[K[[ˊ٬)?>Kd`''K07F>.\`}c''SNOU6G~nNv%v6<}d!/xsv(xК}549,9:¹ /]t̝G{(6x/bdWxw4`{Yq=m Uh[bu]1 8>^pz2GK9q)Oykw~ƢE@+9Te-i@/e)f~Y ֱ,Kـ͍>XX7^[FY8CTQgL3 KJiIJhl 4i[9(1hhj2Y?'O"810 Uk8o-:fgw hk^/\@sxp7_agk$I}g~rKr*ühZnmېD;;ԭ?O>{ȅ+bX"xILzxtֹ>c7.fܽM$4\qs4UO=P'iF? M2rmJ d,,}4%z5>Xݴrt<==N'}G}^%F-<ǘ0y7yln uǿ -x:G=J% e>>_}5{" p5e/Kn]px2a2ʢ2Cv99>nZ>'*(h*X(ײXVS6 [o_o>侤uJ5)rsXR%Өsg,ଽy辜ƮQf%yJG(;o Ȝxo 8 !tNPPAT^{HKPLYBW7i+%oJQ/48geU"4)qJ4I/VTDM"ʢ+Q}$w`!J3)a%M$90t&w:ZG0!&ˆ̋bk TcI!INД2d0xdgqXMGu*b-]42tPRIhõ TgʻǶ%M3g y.:4>Dr tÙ QhHMNRݠ.VR]&Z>< O9LR(m+5I\.uEh-M#|ZP0tD5~nSmӰ`׬=I( UuJٮ\wEKUgvöM"'IXIi!@pwf׏ IDAT(Z{>?;x޸O<l02\94xqw?+d2WEIk$&݉LDS5,e QaJQ\6}-@H^NoCQ<Ͽix^ d4`MgJ ۣvië@8*OY,&3ʲŵ2iƚu\K]̗%F+%1 <8"ō!:eAQ>8_cLUp=.\a}V TSyW^ū_7_" wqy_pQ|Qk╋u;O$6p/Cʲaf6+`q>1?;hYf2s%_?O< Mk/3ܿ>: HZ҄$ˈ,KU].lvid4q+阦ђG:.YLFe\֗o$坏w>arx c賻DqlQ-$j"24Z5' ܳ#3m+GY4XkzyxYXT`">}x =Ōr1#OS41{3/mɬh(ZQcmxxZp8"YɼbmKO /O6Ӷ%r@mg&Tx h@~ $^ve,g]G&|-mk)n?Mxy޼uA_p44 ebL`"VgU\  "l\Ept8/+:)=~eRZ!vBERŠҮcR m[ JՂ5Za$%0BqwbZFhv7f gt2q*roO?ijQw8k(5 [amOHSC2H*#2ypVE ڝ͈;frFs5lE;XIB Q1\['OR|\&FxM7(ΰMgjNjuU+R >(Dk"pMs9l|:X. 3 ۣZj|ە]*M~gŔ@pc *B(i]KPNV 4.˦u̗US'M#HSLilH`&Ph%0gI%5iVeYsX̦,kQZ"6GҽkwhjG1M֐4ə/" s OQ*,^F۴,q%iBst8(*zy.#?VI񦴶 \K7$9Ƶ Lg3BQ/b{Gh:DO փ(jrR(ꪢu5(LD&'$RJ9YG$X穪[h[bܒ_>% Ofd_>_ӢރCk[|_Xgl8NQXlvy=6wtms=>}ӣb-~z{3[,A>/iᐼe1?p){{O( X"IR6,KN'sfjjđέ]2qlHYFGh꒣&'34e00yt׿ k:f1[wtý<{ʢ\bۿ`29bʭxWxѼx^2Hcbm^ /=t2DQA6 msK4E exxV,ƣ!"[0Khʒ Uk,essHpu%E`Y8!3泊ppt*ȗR8.l|z) heŬ$pJ{Q[Nxz8Sʺfmq%6G%'45s`8,*dDX(Z9<2[ D{ڦ-}OW6Yɣq֋ض|/^/^bQMqZwc:⊟FD&p܀a.jZdI. V&Qd0J(MG$IQiEek)b\ aZ%^1yvSur9'C )$òXm,׆/|iem0Lx!,aș!''t'שJHQ2NSU{/@>ϳ,l/S)#u rjdEYd'Yߥu-H~Zs3h|BqC{ʪ( ltRROK-r.m D8d09P\d:f[oТ?(ZU4MC5;{eMh!AUW;ESJXVK)c1 B,85ZDƪ S4XmsQ cR%;{L3H ,9;=aGOSD/Pn3d$oPmp2Q#_lZ ҇p:t)%{i߯ʂhbabjƏr'/7\J"vdv9C.YE/PJmM^ϢpHQYY 'FOyVa\Hd.(h$8e2!4eKמ{OT|B;h;k oq^սpOS/f:xttsb~nŲGkC7&6+'k T.@SwJ9Bcl&Wk|c(kJ[lD-P:&z=4+ ^%kw C0xVH\>;K|?wwxq9vKſ+]կ}|h>kWP𑗟#/bZ7,>/>\l6w?3ti( ׾5"O|)=J0F…( +ReqCV6^vvwŨzC]ueG?vT. :ʴ<%1(JKYVTՄm7tmGU\z@e5s?7^`yŢS6}j1v1G;V۵4UU.޻dt#{wrLNlNohҔ$=1<3y<{:'ˎNji wXu@Y-P֢`LY/ל/6l!dN@'GS*Jvr)QG!!Ƅ.qB^Qy62pК]tBB&j+DFy"N_ra1/N p)4UBU):a QlʦXٙ6V5/Wܾ~d t|yu)Dke udRzxīxl˟8ѲRJJgP?DY"8-9:]ۏ c"r}1fɵ[CFaayCEDMľ_2M)[yyճSy)0JGç^'_z{Yvާ[/E?ӟ|ISз=C?"T4Mpu'k>?I!w<Nɻodp'i%W/֖cP,-Ԋ)& D:_;2ݾ/__Ww_ڷW_nsB6-n?5Դ,Oy'QO"ƀeقhmfTUъɴ◿2?sO~/}/bZNNBV"/ 't5{ɄźͰu C1pI4)M]68Y:BSdgVzlÝǒcv!R A1Dͽܹ &uM]OJe!L88N-4^bH$qLJjt:GK+kц(!%bΥR U"{{eBRb6#4%L*CY(c( J)w:.5|zi6˞~W .q]Gf]RSS|˗.|TuOuݠ+I^lw^;8YpشRW!IAGwX*1Z _*0\TnX6CdE\Ҹpx|DB-g"aR{F]s*HZȈ EbM䉱'Ljb|袠0oEv3)Dр1 E3rLUd;D'jr'wΤxdXd@KA:-"b\Xϖ4]ٔnx;8B c@@)&)ƑɴζeJa(q8ײMZaheRB֠D=+ma@ç#cFӵi2Vb2첪L&; KL~F"vpLyS &"EֲYn)k%: c C3t *у Db|Lm(cb9TX|7OKD Dyf$F2 ׎0 .崐E9YH|'Q U^ D786LF.ٝMIѳZuBj5S]hR^i%9{>gO(eS3POApAtl6떳uǵ W. M c*-źdͣ5 K;Fl{GU sպ|⍷ޢ4w߻K%(('cdSdL2b @E6goN_?û?D'o֋# ks\@aSPD^&-}eg7173ILkMKla>vL%Z90տ(fLWأc]֯U7](ˊYw|;<|=~3c2OS.Of!ǏFNW(SǼxݻ]<:|)RIESH9g{EU5Z}S?h9u:2Bm>`GXek2dBT(qc=ZIS;L # qouf]ֳ3W~9r~87r|wU~:/4nG oOxuBGCo2+O̭o}UrTIr[=P`tč>sU )xWGvf57r.'G:G>)G ~}3U.0:H`ˆi]qi6eo3nδ)WgJҠuryegw)KK3k Aqr,zm|SO\tdD)IK!)\́#(UgVbĔXs$ ZYrSRHm<9gg' A᜗ౕ*s"): 2qk@[KRYVd-F+b "5UR-[F_!3NBq &D-E5IEBށƘy0T^=|2t(lL$Lł4h*J#\yL CULt)u]f|ELUI0kANLG`?غ@| "bj@˞4m瓑%@RO{w݃} uYRbh{vf {آ$*ӌDs0û=pKp)45%}̦D9-'UvV$1O1eU tHIF+oZ\w34.)Ul-Tor Mra 9fe֊aN(ʊI3ŸY$MU  IDATih^8#[Joޝo/Hs%Љhf![3h;9nÇ|[p.o/><=Eˣ#+iµ7)u[l0Dp>^wxx <>Zq|zVSC T& ɒb ,06bʮG_䋿Oɾv{ȷ(5_Vnt][k%H#m;ݒg*sI0GH90fG> *r~by̾]nݾׯ] ^xc<[|_7o{m7DSy%>S?|-yݴ\zwqmA, ?ۜѩ-U MK"QvcJ (.p|f;ƠQ(S].x9xN>YbAYE2f|M+~t~rrM=DtvD)CUm)R)-ŏuO afIKtnKVc^b`1|kM9Y,6vhc|"(NnAS7>bl Ϥ1:*(mGl`lG5EQ|{'C5U]HGggF'J mBL l~`0!MEDȎ 1Ia)bl#Ǽʦ6+S(uN&䩮ʫY 0߫K뎡KT"EW/ES2x74%~Đheyۈgp5nsF P9eaHRNJuEĐBu=1 d LF IBe}GJ1Jъh/gD Qd&u|ExE~f^nEL!L'5)D'\1մ8hߠҷ-I<O_A|㷨߸ú(ˊM7^zNNij854 V{'o*5Vir4uIUr ޏ8:?0xn@GKU:]K ;P*"\Pk|ѫ?;o‹xꩧۻL@Qa%#)9*k0ƍ'N*^7.Ta%x71yx;zo_ [a~iz:!o3N8[,C%9EQ剨EQS»[ÀF`QV Ie X+7Rz7ym@#su}Ǧ;'ƁMdtLMV/8Y,zR7f n) SXtQs|v Xiíó=Nj/wO~MH# U찳w-.ӳs]P0U%JC+VzoGo?d7N*;b򴚔؝U(m8]\LhaGDOR1'&E=Q 1DУD,)Ei Ӫ*.! *oٛ\z?:|js}}c cO楢*`ҌˊI٪c:kEz2  Him.J&5* |dzMɼ2tCVOeK)1zwc,ںZ\(#@$1Xx<{\ qE[\ъgPB%ʢ}= @9cH)k:OLjr]UO2sGٮP:ahxeWrg (|n.Z #)2`d|BEz:=Y3r^ QDaFƐ(' !BD6IJ gc)|GpJY1BՑK;0thRi 1EAPZ8q$),%mʵk5d!nt9 XKg 9͇3)+t$.7e(f3sʦ Qi"հQ4M0sLPs·=}KJ[EHg6)FQ7 $/0]$T!؁lTA͆:*WF.CV-k]ȷm@:[cKh;5Cp,[BEf hEyG! If* J΋g\eK!ud1!rzq]$(gVCMKqiC ƶCX|;ݷg7?~KמFaٝ֔"BC4e`ZEԞ*#h ?Yw<~|')6Sb>/ e]Ud~`7'oy sgz @;LU{pʚ̻C^x?UO'n=wڵ}^dgJZ~urNw+' ԢPԍ6ͿZw>UQVZ*h)a"ZH] -.Hr ? ??thW?&t<:h*)z]e.DU+p1zǸYyMz}~9?s?+zhk?~G'w&TEd:7_K/sQ5 ~K ]h1}v}w&n$f1)f!*FEšPt} #ʖJ&<1x~LƖŚ{gnD[K@1%̘ o;AʹwzƕADnĕ9'+'hźj4.DIN7ctSHFo=Iiہ;Gx*=y~ج{v;[ eҢLvv(D8>_Kiٛ|W~jv@k>JCr W'ԥe mXp앟#l?3RQSCPO "!%4ryQÄ!Zzn8c(6rqlzGQ|;ywyFٟy5w}GŇ7@ k|pȞ/V0JK0 LJ6_jP)4[ UBuI[Q6Z{f506D8b>UxnHtCcX._1k8Obh[Bjʪ–)0^N{Gy7"޽v8ڭؽ4gxOf;\DnߺM]Ti lQ ߾ɼ(8>(jdţH8-cn:qHYkpnCHⴡM72m > H>c,QD9/jiv.r0kؙہǏqH31.7u34ZQ%QYHr&0C^'%׽R8d\l0yKtqz_:gV2`QrFi2~psjb2tv)!+%7"{YF~?3VzdLeƑ.J[Mm `%Vk,N.覮8=?0\Bb)FEa%Ęg>!@Y%57cj~[Ghl-4$&I0JÊ7!G fR*Cr)y*RLkuBFTL*.18L) 4@ʒn#>tF\21IQO'l+nJ:ↁ.QuG* IZ <'y+[ kB|Q┲ޅ\ WD䐨ȼ %xaY7FRΡ!J}O{H) .&D$ OSL,Oќ|R6zgrWֿdQQT" Gk 1R`\1? QFZI5pOҍ[ضxtx;w&*FLgS1Q˓{r`7>6nF_~GLꚦ(ܺ4IJن#BbMbWQت qyeX\ޛR/Qh /{81o_6]K3ݙ35U5/7(m ﻰ7CGa3%F#7h0V+tKҁ(?Z S⪴U)iႲ#8Nr?(-Q#g-+{ @7 L[}IG>V99gް.,.%KrUQU eYqv 'y]G۶Ҝ9t5ⳟi~>B=?[1*J9EO䌶mT hMYXUO_ -HC&CbS>@ H\T»,JB׭K]XwCXƁ;(D!ADŽ0P8OKՊ;NN6W^aoj2,ƈ%Y`23rNp5ZSEp1{'|l97ٝ7cdkC]FŌAnHs[ (IPIڎK~J] y|'89?{?#ꄓ.SD-F(Xm}*׿ T4)@Gry^:E$vat4rb7&k4)Ɗ =@11k͕rU4~YSQ8΋^i[f* 0 ՊV2њ)=:8[4֣B* *al5)H6 SHib1m:Cd=gT"HeW#ѲJV.`t6vI{;:R"y!ڂՒ&O]ĂMĵ=DOQWTu3R}f)ptIlߥޱ"*WjIg&꒢[F;wBn5](L?8)C@a) CQZN8* q")14f3vdZJ~sBѢy"=\'%є&i+rE2⃘xɀVIk|-#0T/t1VZ]@WR%/TAgfGE?+"rRt k.Q'O}|(ckz8o&& 9UXѝX-oIޏ]8]/9r ~?Y +{#o33obbԂB-ٽ,;Ú=^{>?~mRӷx?EYV];) 4L0Q5%)|gիWϦptr|w+kW/3Zӷ?oY/@ʪBQ\ti0V2)(t黎F)  $E :FjCH9'"iS1 -0a<#CAfZhmp1cQUr[u V!iapg;wve:M hꦡ*زT=cODvplJ;xG sm C}z?)K.Eaxen^AG~XEMCPQ t 1a\4!)*%GN)b$D\fz3 0Tւ9V{PcU$Oņy G5! ˁŦɂ">9j(XޝDeW#]'+:㕒)!UXIIn2mezw!  $x4UQ3!ێ §>iٜnJuwzO/W9>p|8HsB@,0d3 Z"dէH{燯Ѯ!E]Kw~#l){[ nя|"1-Ǐ٬7-؜̶xM- sL'Sz-z- R[W4("2R͖e}oNoDRI߸ɟE.[XHf}I;HVK=ֳ̹?^ь}9`ox{yϸ{VT!|g\\\0[Hz (MBގ/m >CJ|K}{EgjBNڒEytwżxsI1#E7QhLe4Ĝ=fn|wǟ巿yU% fh LYʁXeޣ@*I6Ga IDATV 1bs$6 _Q'_qL]솀N2BN|'/ !pxp˳K~dHsĞ!(!EbTĐ(|7M{ F>ɭszAs"BkM@L; f>M&^& FFiL5&Q C&02.M3Ç=U[ ڠg3\v] ,fXgxa1Q5 *-<(C%S5ҥKS}$б'Ę||Ya|hZKBq'ԫ9s8leQ5PDSD^*isYQSZ mY[YtxKU5na[%%05pJݴ meKʒ^Wk]SEl/F| "~c5 5F p%eރbo}F"zOH: mk+|ybwKƾUZb)`+fuHA\2U,Np͌}׋{aIr'@e:ۣZ̘c Sq{B~u;]ӷW;' ݻ' aG?)i*&VyvŪ*cRd*^ePNIrUl7([;kѮfQ*spptkEiL :| 7_o#...M̕LLSU,! e2 n1o<~=pp  GO9uʝxzBLuŝ{HqdyrX,"f8o^|~''%J'Mƾg8J<ȯR.nŐtL _Ee ZA |+ ~U77sϽ毺/U2 ][kzAh5nhڦԢy"!'Oy3'jIfߏ<,ϾHJGB?{HW%_4h錶v<{bS (. CY#jGԵ`>7TFӗC c4,j;89YP<-!frIrlbw(P$ +c300x*`q|9(T1XyCdD4sz¢yF@1%Rl`\Y#:*wZ0 s"GJ4XpPI#I1IigOazJpV1OIf -$HpT"דÊJ2;Ų$˜'y*mid&{'nĘdr1=˚Y@M:\# Bas9GF\ʰ;1MCȑHfN ,K129.A52Mw=8Bd0BstHQA |?k*vަΊsJ))&a$EomUĞ=1D|腴RILV9Im-nǁt,UbW2 }s.&JZ`CkeM$pM)OV)3uD˥d1a^2L0 Ze\N^c,zkGcHKz(lbfʚRu@L6Tt_G5 ߏ JІQƁO*lʆ@ ~k#)0l@UUl !=JOx7.0v,"*46S;A|iWQҌ0<&A2-YfryȢX6 ?JEblQ_?og`a25hIRV vF5~+tIT"*V$696/?嫧z;—{1Z\>rt|Ľwif =ݎ'~‡|̛7WѮF kq" :s"k+Ƙv2}/.K*P[̨*+h*ìvP&Iw,3d7%/Ŷ>ۜ"sr|JK93xʢ#}EW'6JqzXQY JnX^cp]7.lWߞSx]V*1[NZ)1q458Fh4' 6G+fx/~?GwxCǟ0x1|þaiDkW($]O4c ݞqkSӹ|Y+W)" 0T~ DM$eYËrtk20@r %Y8S` un]jb>GJtb%$ 1_]2 med2>fF˚vQ-!%v}Ͼ1hRҧ"D7f$TqDu].9{[5:8!#'#uUrWUܽ=wEd>Opqq#_wd5r|0I&( ?B^υ2yKD5iBfsdE .੍'+îB( YP7aV;= ϟ$eiB S\G9t]C"@-9 (BJ҄o֞nǮ뱕 cgiTNE*ٌbq)e%&s./7I3TQB) C10fdCgr&A}ڧ9X:zrFhݤW}*c[-BA3/xez3O:j CFs/{AO7 M:j&Vµ:ڑ\HB'(E/FBhmK:3bv8ߒQ8 G1M*YVYFDE],HA=_^s +XP-9mGΑQƐ"=<&+=aUUc]E]52|kyKuyVPih>~"XU9LT7(V=H6%BK*ϬT- Z1E|?265ޏتƸeĐs TZ\;1#̸C`״!LjN fϮhJlֲPf\~-q f<9y^υЂƩ(? 1^_R/fdϴfLHF1cCA[9OGǤ\",疺4mF꺥YX.Z^?PdH9ˆSȚ!DL/Ǟ~1AcZ\-%[ G^|21lN*#qE)t܈D3)3F)u7eZjSxPD&4mY=Y2 jh(%1 rl% DŜbK{/1F!W^) kZ_v>``#]KU]v>P)d9e{۷Oxc?T./Z;=mWWOO>{O OX1 RC32 m`[ d嬹uPq|qvѕ?MDqi2uc8\-qrK"D! !ٳW<O _~0:@`Cfva$'0CU"y(Q2*5SӴ̕4+m9N1 !12;xZC;k0];50È Պމ,XqD7m}10;8`:,=a|O75*E۷6jaO3)3nG>qq9 _|A9GG2\}M|?p{gKrsXM&CA54N[?>kq@&S7v(HJrHHWwOx.XmC6]9C = A%~@V!!/ӎ3T3gl=1J1E#]=1Q*Ϟ><}c%q A |NN5͚ǟm)8?G_ѽ#?+ě ZoW k9cs[,V y;we1j$AeYSVؖZ6?!h-vS2|O^_j"Ƚw}`y{U7tQX'I6i'59>W{Tj$*@R' ?lt.'_9S@r5~]jޭsk~ş، s]W߻K7'dٳ6ԻA92擳K (ŔNZА)F1Iȴ&&d[(I1XR74)kJ*Ey3, kg%rճ[F96n. UklQi-НOIӺf^8%Z*q ȔM? &*D_n`%hSJ%r @n**Ŗp 4b'IxE~7$)4~ gZA# " )ǣ@5q H"zO ]$f%v1tlH9!:lܜ "MgJXnʜ$1OS+$X Ucߟ3AeRzP}*5;Be*XHVJD\iufG_[ŌجHn8sĔ @zl{ϛ .&(mjLҬ}8*ޕAg uZa”DF剚-Duu9)b.3RJNjZZ(8:J F+j}]Mi; b6!2SX¸2% QWY6nZh+XWUAn$xV%AX_zicPFZ Xgx)W?}'?~[_S&vj'3Y0c!9^vxul>j߳<gw&KYTt/{|JbaTʂad6/jˬH׏d,ߕsV! nhR:;>nqc7QBzC̜ov Wo.xufӁxx!IܻU%zbx$hJmN <(F,L:*jj4a ( )W0ZfZ>"BNnA[ 4YKn TT,*L)tdW heQu=tYm4Iev(+o<=K'E2"jDfJl ?7ub`(KLլ1|L hJI4~4)KGx!1.6{]"fhIbQN @ &(麗HT'XiĈ _6& YF\ f /]29&$+Kz#'"%\Ş>:vFնRYCE 10t^4.3_P{n&dɑPe`q\*KhRnK;/ۉ$G&,fKZRe@ޝ)-A>̓8brPq[M>&/kcUPU9&1q"̠C`eA7%ATD<)ny|ͩM+A~` =Iu|;ʉ_X`^)9:}c9wOx _o}]8$ HSN>ki*ftY {B+M=;b=JBn UUӏՎ~wQ! 4<1K pedQ`Y+Y}l\!֢ݫZ6{Ba|{ϯ2ZdrIiqJI3okokuΙ7/_o^qSKĂEk'Zl2/qmɭCrt=ubJV/Wx ? "-Й. b:J W[ 6R]G:#J-Zk65PHLmҚßG-ہyc"и/ 33u19 XF0[Ma^״FnX Z0.ZBabKc1ohBLʛGM]9*cY9kŮie>=Bw"*]A՚<]}-GIFIIۆ$l@D?#pN3lw hb dl"3Me }!mJQY*iKG3͎aΈ*0QГh'|V杘Aε׎I5ӟe鶒>%IĒp}$G.T,;^JInRʉCyX3ET*r򚶭9.*Y}!irȒSb.bLמ(Q-gL ,$JX-~ifg ጇ_M}㏱J[>3V[+">4RW9n5~Oܾ}Oy5)ݼ,ٜ֮x5/^EkM,mmX-jwK[LJ-bZ|%8{RWJ/mm)ζw{'S9S?~84ƮL^+ x~!~8"17/˟۸a"rŲɅ@_ghk ^(Rb_B'>sS߹bcr~yQr_~>0:6ww>DxEm5~qv!1'_|'NycV|<~/;yGoU| -<Fm'yfK'bȜ.Zg,4v?5Si<}&%M4tj vĵ[ܹ}OF%~R,+hF3o+#=1 W[? {pɇ]1(kw$G+EӶ|+qJhtYGLƙ\" XEVe?H":Y$dr1.]HHq #b\Hwٕ1I^ D($J (*-S1R:IJĵ>))ʴ#Ei2Zit+.(E S:}C+M%S k{}F5wq,(T\v2Vw#,Z9J&QbdVeux %C :՜a64sքAn$kKɖ $'JhgQ֒Ծ/Hee!nnj4>*651xjE~KfJ\U1vj hg3f9 cD7 JN;P8R|!]H R2b MhPT(<*maӪT(U&v&/Q M[CVlA6儊:ErsNB4e I9-'yj_2P*Z]ksue, cct^*QqQ-j/п=4}ɒ&r6-SV4j+ڲܹ}|3zNn~'B35^5ܽ{ld hQ+Vdn_jXjW*M GB:S9b^GśjOqSpfdz~X8ؓ]Te4C\ |=Ȯ|Dwg^ka5H~`~,tJӹmI?'Oۏ'x{v+bj1f<=*a6ϛ={ѻF=%GGGֲjoڪgٚ#fI8dtR4I%I RF$.yrjI12@me 6E]7-@.;Ao@m U :U !i>zzۋ-_CۂiJjbamw:͙ ʲo'!į309)vf1|*!X5UAZ%,Keu^5Wt0=ex#QCFB(DseU,ٖ>ĦL\IeF#yQ 1iiI.`DJ@3M͘3J!9([Ь ]{[D;AcF^vS̪}dwl0)=$26(n;r2!S/>QQەgHa JaXBhc9iVN(~$@ȑ4FhpY*TXz]sbVSWa̅<,_vurZ$DA3o88d`KknQH2}%*)+M =J- rce[} JIɄ\gE\\8UV ߣsBf@Ӏ7"C D TnkSN7sqM[h )9Z%^ eb̥@%ɢhT.nD*"X6N%N5<1ﷱaQht<{WyNp>'|{%8>f|/ytm8D~@)b9#%o5Nf'xR-Fe:aVG]LGNn\c/NʪHP| x0] )FpPu"gab>N꜄Z69 $zSJ\3YTRiUI,fl87SY+&*EJ"Rs'4ku|JgZ:4xa:P 9yD _ I$*o$!7g"#(rوRI~IMDR,A2 dJ%YN!/Q(in.Rk"]J cE[a\Mq .;3Ӵ3sJyz4jVǀ$M 1^oIW[X&]CU/猽'Q2T6"Lum$HsMU^3YQ+_Z$ ُ=͆Cqh+ c.V[ԮftoZ'3aѭ/$޴+BI[5m Q}Y@NR #@n \jK1BELD ,9@f){YB 1')e)E9=p@kQ= \-I>UB2Ev%ۡQ"ժ ESтC:埉p")IMYB$(aT2[h#VM%sՄ(SB:#!IHXe! A}lfjKu\]]G?yJvYo\S8h}9' fVͧԵDysŦxtw;9:8`V\79Y^DdCjO?m5o߾Ѷہ/u'K3 )RkEӔr0\=}޽#f\pt}Zj+|ȿ*΢^GßT7]QZR~ܻ̚wbV _V6Oӧwxtw}zȼMxvrѲrޡj4ϯP+*gY.T7#vG=_#mSq)x n#r?!;jqý k ޜm2XP)2hːu˺. VHVH2axL x#: ( ݖĊVfAv#&f˶]}osiR%V$9We /JʅJʉ$K)IvwrDT*V<k9[U^+Q$%^@2\ګAᕓA λv >?$P4t?yNk> 0$((+- SaNN>eɩt:-*gTt-TM$UFREi BK{$EJ))Ǡ61-Nـ)ҤQG$S^4ު絖dh(A¾ӒHa(c!8\i Ir&@'ǖіh=εJ|JZH rsm.y(u ޷1de+FDa`{Y,,V"3)G}[{:^"W!ڈ^I!_Ih85 bkÙFjZ_1V ku4C\7EX}{|_w '7ޑRŢ#L;Ág|W]b$KʾdmP?~OfIxqo>₟S>9;ݞ~i[YBZD0b0JZ8OD2ȝA{?pi5ij}+7>[s%~Ow9_GZr^5yć?kD?GÄ5z`SmG|{~5ah+T)39攰JZE!AUHfaǥ1Z0duNA}{蒘&E@nJKvE/}-]j P0GƢkBt+]j1 LMKf00 ᔨJP]X9|㜘"yJ$4(.:aF+ 9bU&%0W~TJ0uX\=YljdGڥc$(qcjEXw@;~ÁBfWL} )ŷkTJcЂ$a%$E5gd֙Վ{tK<]^/^s#juQ#Tx5P= ,{rW>!j8GvkBڲ^Ym_,g8閞;B,q5^> ^JɍbF)ժi!&|ӲZ]Ck9'\YX]a͒펻kJYb&L N>| O~G6(ضk=9]wջ|5E{h!'ͺe&nnn_Z3M1F)) Y mY# чflFsxse{p=!DL|8{!Pb[Ղ g̷_{?^4PiPshx6[$Jt]=瘙FQyv7́/b~wO?~6  IDATy˟쫗#XMdqJFl %sB8դWm~rS` uJlE~:"bd(R:)Teo)K'pr}PByZ \D丄pܦ JTX~ Fq%%ZЉ`~ 03̑C"vj0F1a(i`uDFq6rwnB%DLA\p!`3!3GiZĖ6k2Y `02큣]+hG kRGǓ6o?)(%٩db0* t{sM T]༠X:Zc)uG<kiy&ňk3mY3HT4ӌ^Vn_ R\De+cv!yJ2@m0J6\RMTc~ɗ"ۚA]K W g_{_Vˆi1VSi08)x1~KֱX#B|ۿ?w7/*)10ƠwJ1(R޿aిc*ؿz͎kW+̣K.6l_ް;$aa,)0޼zՅp:ˆ31bq+R9RL Ter~"<){!zǗϯ9 /[o~'? xqbՑSd:铧l{t%]}W\^iZC*0g^=W?{sK]\9,iCxpٱZ(&=?︺\"0" R`8 /`5t]w yf")v{sX,d8=։ h%.fow0V+/6&Xig7Q Zs[CTCʄ0t )д{JzJsį6XqN* Lb.*9װWC",NoR!R2 CH:r{bZgɲ\x..6 O\]mXZ-UĢcO@ g3гX4\b&8!g5aΌsfMcxxb,)E~0α4?\KN H񔂤G]k}J$̭g5-pղe,kh 1a?C1 wO~vͫW{o;a`'{?7$=cly|n"PI}8 H UJLI-.leB\4*%YpuJrj)IzUJIVD:+J&&k4 @H j2<ܢ9hnH1Pp PRZ%P"Ӯ햨We8 6c|ZCκEY2db@VX@ Cu1Y2HJ߮VK..pw~ۓ(+]vv.C+ )m-YYCʙ9[UB-i XK a% sL㐠T i-YBBZ\ VmZ/#)E)<48''FZfrbȃ_uAQȹc^ K$[]6gc8+Mt]2:ȧ3臚՚e>5=cj 改|HW77;1?;.(q瑲|?▘_Տ@{Lr#Y#\*, =$ĉyi ^p0 #~?+\?K/~&ϫ,ZpƯcP!\<;{{JMݎhODy]Ku(H8 8=VJ!.0FaZP8n{G [$9#ZƳlRrDiCa/0Kw`Zc-8RJ":G-eyo8JPT!c,U-qǔ|d۟M9G\F~+ZT$rB( ZTmk(!R7R+-_~uMyK֫ ya/^^3M3]жНh|>r< /^O/?_deJ"CV6FJW͛"K`9ң{d[|f*s\ ReڵGh+? ,ـ.$JFIM!nZ)@\!)9/ţ1ͺŻ"%dLh43 4g./V^qj=(.aGEz.. @ ߴXxzq O<$ě-?yh?P$%mm:,N adq jJ8 3a˳M))٧/ݳx.ZrMHO>^7glys^ٲ#%abggX/=s(m  Y(2a8q$&4VX48{]Z SԆc-}=eKA411Jkq&GPqX:] 2Tt!{·c-1ֲTH)-vae*Grs.i33;zp͎8G<}HheA}ߓR@pΡgRu3 3:µVVӹh81O^:!YD4EPEhmcc=Ղkjצ\2~dg-Z%^)s;5 D3Luot6R( 4C[.P0c ͚iexҘ1ϳTMd{0q8X#niQ!FWX+;U@Ï9LU`a:gL10Vӧ2cS |K å&EH30@ "hZҊ+no|OZvfQY7{XB glytm0Srf\\"AL ~(l'> g| oo#_d3qF$K!gLCg YcDQե(:J EuS9etB'Z*DP 4!˅BhEYM#@HÃ?P 5hL(!X)DkcB +ё# P&V%Lɱb k}6NzUv"{UD+sUEոVp㜫Нc%adHa[ͪkl61q'@ʥ5 J ~?Э:֛ndw/?k82(6F׃O(i\p^Y*Eh6LP*@I.2JPIMӰYQ\s2)qu M-觓 n@eeXJSXi^?-dyߏ%S2%ʹM;1BD6 Z6]K޳Zt|wl\d..8;[ /Z./=(qi4h4r2gC 5\-qL*DwuEJ)%ݐ!U+!kڮP6jy@W(_S 㲥ќi 8kJf!H\\@H)嘬AQF8 /M[(l 9?γ"5,mwj״+OHXzwcZV>bE۵8#X9.\N)3Abf ZA6rj#e1q斜 c N)%|H¾shkBH|589$[n8{Z, uL!2NA(:N8`ÄknQrCڬm+BERqL0 䜘 -zTkEҚ\`"0⽡k=F)-"&I_,mG2+%z7@3A9j762 Z+=HB"ƚf]sX$cРE6%o 5U#,hh1kE!YvR9B5t4a+:|npV+}P'oa,g\םPk9V kY2%9Fi5"R ÎK.^|!94&R., mpkZA #/_ي)ጢqF,:hk-0f1bnVKS"e% vnwg?SG?-!GHp@щi.e!}"[SQDA$S 3:1e!e#wYQ% Ն Dq F#.?M@3QaΩmD2k)sTJ^Q9j28$i-N88[-%ivh6D”O_mˆNJ}T }i:qѠVQt]܂iqی4i&L I͎)_YeI Tiö' r\L*NJqȄR!Nҝ-%Kk+Sb"(o00\i;H)hN 1oYM<7sP4ZYq$ժ(̲|K>I1W eyrе-KN kc%[E5jz,jL>EVGq+ǠN+VV-zl0 iLPGh#^ *JaU6QS*'1oȂ`TdĘ9D,8q{yR65t P]iF20fE[a zr~[,m՜ǔNRhK΋'n]Ҵ䢧ȑg$FʧQŢ(=M u{jU"RU$Y}9^1E&q^a#R0{ag8 ZHte$Ըc8τ9❥lE}$\u˩u6y&'y@\^S|oۢl =ҍ8Q|}w{{.h㰜:OӴRc&5sddY@IϢkX.]E@.qCMde5bBdڨ"@ӈ-LV5MVMy]ם*^IK'1חjR`2W2)hTNl JSn?:TgqqC叨gHH(B ǜ,mFKv8]}?,]-6%U~GT=%9gBufT]nn_[VaZ*,E+CkDrz4m`r=S41)L\g˟s;^a?bf"4Jt1b*OU9:S6K$!ڥϋ=9Annme{UJQе Be,YJcCPGE(SJ$H+e $1V0H8DQmHif5]Yg)r'yި,O[鯕j0Hԑ&jmqռrЭd혧DGJj}A&#| X# PVҶc(kHs5yxs{,$e xRF)E#Mݸ@  PIfyIBޘrS Hm\D#Ѯ[VY'߭8:s :ϣ<:cixp3+wjM#Y>>+(BOYg1 3*9~Rg`(U1bar(V{PJh#_c .|8@g W($U76s cic!'%918"Q¿Q9t6ޟc%:pdR 9+(28﫸RASPR!Dq2]Riq 1~H)\m=a8}(U/#BgYq)Uєzi&XQJL%ݶk01d_yYoX|QpgOMr~>iEk-~i}f<9ǫL՚C3zQD"Za|1$+E4YJQIuhR"IP0(EJ;:,'*YbU)$:j9{ARvJmNAaFbSA ue:lNP JQQJJfŢu(r]__1]ײ\.NZ㯜 A q1J;0L xrg-cQ*K3ө~M(.#Bzu)w_g˨$CchM-sb8a,""hTV:&:u*%DZ.н꤬sa(r8D6KJ&4 4Q4)GRJĂr4qL"j6ܚs9mLǎA,-quar ;mX.Zr:}:n UJ1.]~-1SG)JtKJh>} rJvSyJ'E*0cKSp!Qk+oa:\W.r]2xY4윫s>}9ȫ}x%M+ɦel3\pJg~JTF0ahEH֒V9Ū ;dqhmB;r1,NKMMhWY(T}tC#=zd05֨sFkYnYP"S` ouBó=^˯EWq$Eyٮi{M1Bb{r4m#}Fyw+k- 1 te2<γTRww=uz}CJ" <|fT vByBߕw63'-Qߏ~}K8Z" iH9.mu^έJw{c,-)<]Z-aE{7HY=MԦ<wYA2Vfۻ=0T4U"/xJEM:qM3,wm1m8;_0S-nAAgVx/4GaZ*b1:ysc(G7HZueC'(:ZSj IDATSfKuui7QZ|ēB >U$XRWϳS ":giZC%fu*U}1F9:>)f+V)$$gew圑o9Z6p:kqhfk=9|e9P꘣hG;}_>qs}`zvacGqؑ0h l~^oN9xG|b0(^_'W/ļǚQ8n%)D8J"Eb#%IZN̈́(\dAzѲDQR&Dy:W r,ĐY.{@ ~9A$)&rmNJc.p>QF9qK(Xkh,XiW yABIt9RJd& ;J[ջܫTRk sJ&Ǫת[ҵ+H0Ns=4EZD4,%k./θ<{) UsƈQT;Y^sg%XuI=Xlš~uVc5s!y:k1UXwg\ՒŢ:RңtL*@`sYBc4ա3=Ct>BiTSF dFWͅb#m%Z,Wں/*%ç+VQr1Sp QZLdO"ltlת^21a5!I`2UaH_4XIEڠд aJ^P'"n0 XcX-t%m1ƊH-Ց*u/l1 P<,P7hc4ݎ0.׬ дbIQЎ3/_ Vk>x g+)wyEhll#v Rݲm~G)keM4 :NVJx9JihZk|qixIdh^4S@1DR}JUr=/39Rww[{rͲrqG뎔2XRy)Dƹw|+R=~~`j"qHE'J%4ꈓj3qTNF n冚^Zg7t]SPScJU7UN˙ Gɪ#,M'K5G4N^0U'o(\I<`Ni<Ŋ]( 7 WEK(oI<:a?2 #MY,[(Iƕ@'>0źqsmmP4Kz览b1s"58~a8Vn=@7+VzMʊ>{E,yǃ@ȉ5FZV̩H决i10Ge?/~3+iOX& d/ib-n{`F:hCZ40EAl )-nDRKL^yhdYr&mhbEӴ1jI3e^ACEt, W̳8Ҵly@FEޓ|oHy˅ۙ}/Iu咮;"ꝣxRc#<4mS hrJӦ"ǗYScMFyf͛8r$9|@¾Zk>{ :nF^:BbjaYvNЧ"Rn9l]nXn6U/OnTSƹt6ey&Tk+ VSJTM u1\H$ˁU)D93=]֚rPH`BKPAr״[)1x`TM<5R3ݠYDֈQuNi }Zb];HZɹRTt$LqiZO:q\DsYkF(S}9++Hb#aA.0SU !EklV!#^RXSi 8c !F~"R3ju).7P ՊB;*+mޓs)h*r4|^o{T:U\43`Ҵ MbTyõnE h#<%ьs晾Ai˥P9L ֈ>+TQ4˥\h+$`VtgfTzmֹkԸ3ZqEC)鲜+-CgJ B\ªF4b,xi{ԘR{8I]d%BQŊF0TX^Lj)xki1WUlkz!EVD9GƱ\zYj8nׯ︽-x|#JNgmc6"VYе&2V\99bRt HԆ܄6.+LX(Ftxr$Z O# /i/s$.24"DZ^Ogĸb/#k%ʹx{@)\(j!㺾CH5D8HSH UV=NDu]c,r &.<#޷o୅UoDajC(Xeu$ʖ=px~Q_ǩi):t]h6Jµ&b6ğEŪhl߯w3seٕEGxCGo:|P3,Xu+2-XU)8  Η]czT_1 N ^^-üLqB#}ahQ@Ϯ/cʞmkp,()ZceXc{< ֈ[p9_3qo4Nわ?\p8V:yNu2"ORh@`(7m#rt1CSxS2ea0K(g9 .3Ro w/ %O_G?R3uBk;sr\eNScYpдwa@Q`LuqE* C bv?{Xa"EN (M0G4"PQ@tO ՒaBR,$ȂZtWk6N&}e<(N2˄/HbA}O*$'bD 휲)[eFLEu1.i>z)4 󊶵hvۃ\IL_Y9(1%d:m% R j6@WtNxIgSHȝ Fb;KFQF_ʕԟ cJqAL9r2Yɦ`gB)Ơ,3eAJΒ ,6b*<2иD#p9$2̋|&ri8Q!I+JVxxn? G MZ HzD<'pbedfoJQtG A`R+ ~%fMNf(hp˙tTF˚ \5?B+r ЖE4Ą#9i)enL{mÊo`O)\TE\\))GڦPjtK#CB(/O__bb 7hVDc ϯ$E11FX؊6Um‚ N~~GH%Ca| Xt} MUN+ E]3WPT 4V|g]icB"o%4* &%( 5R꧕"V9,КHk"_΂I UMFD70BrIh8*Q3.Al#WD?Ѷ-ҘYՀꦙa)rfe1`PT"ӄ,޵?oť`$k:"hh8xf!t+arGu|G-B*#=Zr/#Ԙ 㷿߶}6:D2]XUJaqc>q v,$R۷X;YgtNf9{,G#5d2! rE).EbSI9%, cmkXdhEd9']ފ?<>wPB|<3XWVF3me_P #(IDATnuӸ!B-\cEWfjYb#nZ1%\#R8;Aԕ3 7,M膞WKM:r gۦ)dfXY3$N]3Q*Aq$ߚN{8W &1F JSQb{坮 =;|R{T[q|=Z~ŘuE+ڮǰ!ĄyZལ$"FaQd`?YGc_ ܠo[噓G Q)(Pfh epő(QHS>Gż$Lӈ0Y)fCa1 ů/G9%A l8cP8:=:e˲;Tf1O3Rb!|D=`-LY,KB c*E$[4RH4Jw&S//'#Pe\4rdac4D˒Tn Og3^Jq*N󄔀1 @mzds/!Xq(LIC&f P$M<:,8G( C%x57ہ,Yd!;zzޮyi=x] M}4xo+h1f!7#$fcM8V1tMw*//A xߡ(׌?".3MX5/3)W募W5MT,@ ,Oo V$9xsX9\u_ <㳦mMrS(![A.Xׄ3oVҖ$Y(YKDccڸxTBιjR A YJKFQY%ӻ,VIRoj纚R*穵(@1T 5[uF։4=N)f(LӂI-" UrKکb[#|E"XmD!rR1ೈJLU]-:Ce@D8G5DϜ,˂~~7:~ 4F)[uWDOqz< j #5fY _"m:W f *Z@%DL@.aK6ڝGQA8RRf JFFbQJe)YS$1-୆/#_=7/,jGaCנ9ڐHsZ3Kąߛ؊,Mk04i`[ BvF)Qhm\ %Λw쒵 'hS&htCOgrrCނ,"lq~_>+GrpUvxpz=awCɟuǜWM" Kdhp,#.-J\Gة.DȴأCXW9tt =-f0heպ9kW=w݂ ~kXѵ_}ݞZGpAR3Zop*Q=8Nӫrt \Lq_KKW~ĈrR9d/o䜩7=J-+:"@԰BO)`LQ&#,eA[Љ;Ƙ@-3Xd8"ȝ;"YQZ7H_ypHi!"(MJVbQ 8~ד 2Ń"sPmY\88Mc iW9r?EXKW :s[Ew1O+~Wּy6Jل۷px,4t0O}~(q pi3U<+^OgL㊇bP6F3ʰ0 K8o.+v} 8*CFBw01ϸ˂d'hn ڟPp9O(9eXotLWt!љ/F״q"JVhZK-fV;"/Mo~A)yHbV'`.#\bZfv臖u!U2W)wX \)^y7XUr G%t4OOh[_r?BAkyDۜsdjB)#9gR0G(C:OX#i~ߡ("m,]%u:-(EF?|zr.q\e?Pukh砵4÷?`:.3 :{GJ 45=6HkAB)lJ+5j96P^٫"%9-Bq]`u \{SQ# zpa,˼q.rpF::,9f㨨V qNOj-x>Nu}ڮcJ}8<ƢZq)MGӇv ϯq0t mM7R /h[a?M>mU~/OGhpWB׷$7NȌtQ՞ ,­70dtc IKR:!UH9Jڸ$\}4áKTp95z8)4)ѿrִD"J? =EC?t-tSH]VD o"@*QձۢE|[}p~_|#F!SQ "\ƔBB$Zl5iYf@DSM.969e'M>~x?\cflH\} ,:7Qk$pO-V@Y*]+i4c> A<PR"8w[-@4P W+.fsFQ,kt4mR"ĈG5q:] ; Tcx^cAJ+RC|gB w-E'ж rL,@mb&jP1e"yl~{x4H9`a0X14m'هjCQqfQNegRl|RBXREZÐE1 >F;N=Jaiaw,JzkZ >EߒC6)AյwmMx‘uT1:a#rJ74!ma%rJ8]$Y?FɸZP/3Iw&IZ#e^ţo1[Ij/EGT2 eo81/U`7^f7JʥZQ K ٹ\V<<34c:^4t'?;#rQSnGb4#]cqwNi;< ddۘ_>-@tmW阗a&Bxt#9S ch;%cYΙ d]Vۈ9$:LB8hN..9AR{s=e&e$6rCZs A.HZƻRF[}ElȉE 2&TYg`A.K@;,44M/%&ex܏vx u7}bhӒL <wПT5BXk qsMl2Η 1D8H6M5&7$ِܟ䕟wԟv"ZO:?sբsO~ jauoISw'N'<[<>>^@ċZ ?9e rWļ ?KM~WJYg<0aGp4bP#ſ|7 Lq:o>E1Hje8\99Deaa/jQ+bxmA[L#hDD`P+Umq~?rn`f@1 R7sֱLJ:f x|/*9\$PK@A"=fd$\j4! e1\ǍerΊRȻ,TSL3ljy OL,(onB柕XjBun ,,?W)sxy>r~0Z*&pZ@ΔXF05-a^VX֌9dK_kCOPiNZJ7~_.ΐcSb6Xy~3~=22iE ݎy9 i`R( l~Q~<+ĸ_zq^NRJYBXI솞A*Y{@=WA<(t%Q󐫊C;(OIf )FI4tVv -41pa=|Q( TN2dY#i:.4%x}vJ, ? FU"(`Ś2.)'8)zH9߀S:R;g<ǥd3.첺r㞖q$ O3b\e-4{3|NRj~ 8c4֋5hXLf98^_/88 8zAA7tF6ߔ#`& i~v}|C7_ׯYjQdJ~+6 2Tӟt0qŇ_Srlܸ` Ӵq},nX׈u<8[4G} JD$Eyhf?Ԃ$XP- +:BɈƒPn9/h2`^!F(# ͇}zz Et0Y1Lcua gڌX]3/'\ Qc$ C2D|1Q|*93OU% o%sRF4%`gLc Z 焦ҀVbjZgnn?*Ρi+7jioF${*4M; xZZG2+aUHc?IENDB`qmapshack-1.5.1/src/pics/compass.png000644 001750 000144 00000023262 12527654570 020401 0ustar00oeichlerusers000000 000000 PNG  IHDRYtsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxyTյ] CTD` |$1jPB NqN53qpAQǨ & S H cjNW:5穧SUu~j^[T27m_}[:55=c#"1aoCgZU {<~% cu lo[uf>aQn1-RM9+0-x-ʥMLI ZD(,4`ŻP ;"?TuK ڊO_i[H`p&0+FܯKB"RnÈxnӶL#"{c}8,ŽOrAQ ƟA` 0 xZU7jX=1C?\rIQ ZDc.0 lUcD|@(NL'Ε& ӻ0wn=iZ@Lv>;OvZ#l;w@yGT^{h93l1pL!ىB~ے,;?{5[~PE> ~ oCEpD1JUlZ+Cۿ@%0HČ~ o[R^!k^"o-k9i%ý~8o`\cy(!ƟNxU]INPuq@DC⻠Xg03C.Uӗvo13iƪc} 9DPj<#!jX͠oU~ّ+v9o/k ZD.tVg!lݺĉ;] oqyZDNF`֭+++~ےMTm <+"չ<N= ]Jbؾ}{~ۑ =8^3A|ny'kjj-^S 9\qֹlok!?tm;6YT>)˱%۫ESmE}sk}"}uAH ._&`,'6\D""X "Edc~ZD:/ w9?IG{,:`$&eݘeoȻ"2Ʊ<DAm}lЏdDsxK2'Z8 j$d<mwOjB&]1"`#;Of#Nm <|YDDbWZ戝|;0c*3؆ ޿UUNNra{bZb~kRwtmLd'"Uul|3tfTe%"'xtFtqAU%O̗eUtUboq8JՌ^8-1J^t%߹OtGX lsص$id dhPaGaBo.v<x=o۪1Yɘ#&b.v֖+`B2\rhMM!3, -k}x~oa&ۆbҁc05Bn4#Y0?3bL$sv97S̈1UuTu E?nNÔ1X&L#5I|Omn\U(OibKGhc~cW|0{Ej1u?P0UdkKk5KȬVNJޤw0τQń8}3}u>7ȩlˑ氈Lx;gb[#dtxj"-Aȱd RĴj655Ĩ$8j*eRz?N6eeSCGa5Tr=tFkIT!"1AɞDZAAe\3AUOC߀xTr@LAr0{LzR)aς0ݔw'gZi"f ]d{YЉ8jx{ekWf2,Z&Vkb O4 *㍸XU z$CQLDM8Ls &$E②L+9ԝwBI +z7jcf Cr[%W"?~ţ0H+g:QHɘ$Г gR*M$E$@fMG}tVyC|=7 F%VPjҥK+K.$[AwqBG/Z,Z|MW llܸqݢEԜS{ %r}#G8p`!Bm*h鋙q\w-_|ZMM͜ykԩSd@DjDd'S(ڕsf灃F[CUcPU/;wn˗k׮SLaO9hllZ!"K]GݻPl/$jT}>F Wϝ;weVc``03!DoTVVf%ak15f̘={܎Yox8m>XE> lE/`, o)e  :eʔq!D2;/vl;vS̋1㈋%h):7F>y 5::, x9f)^:i*c>1m\]>x]Uw,`D=4ZԘ5V zɦ WJk Fq"5Z}]OK 0o޼K.5w=x2L3_,1jآRЃ1#J z8 u.bS) mʗ`{yرGTB 'b XAHf]}2o5"rjSSӒ QR${7;3ǽGD3oBKSPy8jC֪65^r`C,Y*j<S$i[[['+㽱ļjjv-Z.UrꣴNY,XOotʔ)Fߺu!WFD6vinbE1:2.ᔸ-ѡqVՇ*++yO\>McǎGnWn*P; % Ɔl ,X`X4J:t0 ٘@P5_˂O Q C@ MMKaTWWGļ>d5>{K{pAU#"7|(۷sTWWS]]sά\|;ڵaÆm޽{<1/-|=]l2>&"gai"Ǐ**dʖ-[ز"Bmڴ@ЫԩP5%p3Uէ}6(o=Ed@SSSsClrJΝ+*eҥ̟??}E:PYYe`;R:p!"?x蒋'Nث?0μ\蠪Y*sOlUUk9i5{s ݝ"ED.袞***755 @ULPgo߉$"ki-gߞKCz`mc RܨV՜6L2W8/"T[dIYBx7H%o o`q}D3Z_l+we wQ/kc )+"! 3mI&LPވ6y(x(Yf,"buy}Kcsnk.W'WaזE!dҤI+**Kڥr|WZ+v R,TD.t${ F,n=rz :IDz1#SNk׮޶(D"t<#F0fu"0=:^Ǹ[Ucvؿ\gep}AԩЎ;k׮UUU>6n696H8nmۦ:tHX!)H6mv566~ G=١O>k۶'՟u F{ P36;v{,]^wDDmcX.](" ycZ).T99DŲFZ{UP(tfmXv{,XAĨUO|,GxnCTWW[ lӗ `_T)+$6>JWDںdYoӋdC>C'Š}.CVE p-Ey\b/ "meINipN$E0qm^8kodj,bh"G{ŞI6"XLxI CD&ORa<+ o!%/:N\|GNbw ,vwi}`LDp"&kr.Jksȱ6(AbB\ t;U䋴vZL,gžZ"=[Lע&ZyGk޽uuu|j`Scc#uuuxV%(xvx#"`01[755Q__O]]lذaGCC)="چ}Dl"{uk^UcVr*azk"lL>SLʨ tB.]hhh[om?~|6mٲe NQQYY[-c=G}4ӦM#'9TU+R|E9Vx$Ph!.:/`Gq<0Y+zF蜚7Y[+GJi(oݼU QBc4F͸ T)"6ԸZ-1rBB*aGRXo=[GDݭ[:=xV_P`0&Ԩts#p0\US$IC7zXo-"ƍsCV͸M-F`#^drg @(Z\D]Gp,0.@0\%FzN 9R͌4 CzI{Iy8\֑l%h; ɅU"j444lty0XUoO+;ɩvb6ir|)]Ͷnc՝x(V垛b)<\U3VL.muP(t0\D>t:TDcu.h(pĭT(a[n0cƌWX Y 1$ -tbܸqGuc a5yFp~_t0c˖-̜9ss8~iZDF7SnȘl/GX"^(6ljΜ9_ڵ,{e'^È:UݐUKP(uW^ <Ѧ+ ODPO2nV5jԠ+Vlضm9r^7k9P(lĉ͛wpΝ)l݃0t%xyrCmmGs,fCAGx>C:thtc*p뙨o1y;rטiVX^Cf8SO.ψ^D*&v>\9ӊDC=tsxa^v !^Z&8 m#3K7Nr 3c$# ,{c-M[Bl[b.U:MJ [A%GE > 3(?o.䝇vb:`L._݀!"k0< y!`|M\\BHW"Zs`ADNdAWBQD~Ok;wfߕ&%E /0]`\+"}6+.CUS1)iob MPP՝+`0X%"׋HMk#"^Ϋ–"EDnj3 V_0t⛌i8v6։l,]2w7\U'c*. .]22sTcmqW lLOžOaV5M1-ZT ")GIDPܽjg`fT-OUdS!G,Tui|*"1v,B,DdB ZnY>|w](f(RAGP&U}QUxa.jQDw٧hv5Ӽ@D{@5=ȍ1w`>U=~-wj݀ocz䇻0! ab=|wkCîiv[7k#m?A~~܊2NXDwJU̱}gb8 jӳ70^^ Y.TJ^Nll}8ppZ-,2 #&C;ElcdQûű %"\:MFD:,-b`[sK: ;s.uMUUݜ%t I Z=z٢,(032b:0_UɦHaRv׫~ؔBq "m17='E:JFGH[U}(}Se IENDB`qmapshack-1.5.1/src/pics/noMap256x256.png000644 001750 000144 00000046666 12527654570 020705 0ustar00oeichlerusers000000 000000 PNG  IHDR\rfsRGBbKGD pHYs  tIME 8-+riTXtCommentCreated with GIMPd.e IDATxw]Wy]kS4QŶ-ɖ+`B  $1!&!@K` ئ[euYm$iz;}>gd>9u?{zׂ0_A4_o! DH2uG4( p ~]/ K"prF s벫, ˎBЄO(tG!)H@PA< ]F!0rFEh8 1niYXKjfT0+A}C(V}! .3:gp M_o tu22E|P \ )(Mѩ=כ .G2V!C22g~-k5l,܅5Dc.ѸB>OvP Zec G8~9|TK` Z<,?ȑޣhࢍ/1,"9\yoX¥ T:H Pv86D7 É|ǐiRy!)Qbyj:b'҅P8VT&˒z!ÁQ ·v@)MKe\_5d$\C]pd8O'H{П,e`0 :P !R'OS<9>M"\ߓc4#+C *G$XZӯkqskؼ} s\<Rp&@s4%!*I&!P>J+tJ ?a(J X/F> +V$#08# "NB[ʠr50`@6*E9\ko]oM7,$! %*XX~Gbh<@!-)JxA!$BH,a!F#E!&d( P_6̩*4{^bsءkyd8Q{#qaUHwnۖ m$lXK.ZuhB 䱥CĎ!\45rp,!$Z.ĤS#BXQU@i-mlicaxN nh Uʋ/vϝDYLc!E!AF>s5w_+^XB00β1 VςU PB#"}hḘg|8e}!$;k4^+y? $UH,I&pzosBR ?}`/A[?=ƙ2_/,V-B:|$wm=ܫx}3}yX7-E5>Jg{B Bͧ174A:t9 ~ƒvdrXxA@yҟ(ʓ}4--h18iZi]=o^%Ǐ d=pUg|CFEK*g}+gȍ/&W!ja>4% ,9/%mn-RN0?cE hn_"&1f8ӏ|bnH d| ]q@s8aZQno\'$dKan1Rc_AZcfQEF:69hu`;o|: ma}Nn^%e .KÉ~ XP EĎNE$"SH+oܵ|hH"G6?9f5s ?W9FraPeO@0B3\&Ѱ}n| X/g+Ir(͸J񡋒ޱ\bN Y ¶p!`]Jd  *( 4J,ʰ^3'pjD1 })[uC=nK DDpgkvwRD>":Kֵ}-km E~׵ҋR#uQPxA x+J(@|BL?#١pg*2̎gXZ/8Е1>\,u[zj!p]Ad^l{Ѥ R+%| w<7n8=nk~G#/T1HdG "--3(u`h4 R^P[²:ش)Iyk* L4`F'yaׁ \{J~>ΰ>7eE]/@J%X |@ϒ.:QSe&bYT= I1Ӱx73.v&(C/w3;EZǾx'фIqm4:/BR+tT@P:EKM Y/K̼Oib͒1? k\SR#ؾm=yN %G:[>lb^3V=Bu7.S}Ѹ>64f˼z`5dXbNY?] ǎ2rp [TǪגrfGA:KZ+<Pm`]s=?:GDyΣ˜@tJdO%`eDB:<\§: yxGsf== V1~ zjꩍS.QO}hcw}%F!(PWkW2,ln>HT[:Ꮨ!7x> j߽yKu zpAw-'v}h @mx$AĎS*Z+DY.Fs#a]/K#䩏7y&GOUkImV̑[=]!ٌ< f"PD>{XwlA>Ej~>tۻ8^L|Pԋq-X&ĨSk&^?aHQ'`f<2ykZX2k9ܟ19J5U ,Sϴ<6XD$#;=L!_*/ 'n7p']?{j9?wA3|H#,n-H8vtܨaaϹeJ #4|&15$sCs%b%b3| U<^sj,*<ϐiaUǟ: 4D,1E0oEHηm-#pxN?w}M+zR_,&f׮&тv8}ɬp4|cg^cY/!HդB^lR~1~eqeЋm1+`}> V `ּz>; ,3cO[Wn7n 2,JS>JV|jWQ\mE9Q']]!P"'z @MrK.:GIXTTH@[̫]ώP//^eұ4B̏3iEkAn@iDB81RQ6n$%2gbRl|ҙ!|򚍸Cǟ ?~`@71t!]/p<׬](zSg ``&Qk=ڲT@K_#?x9[9dFxw!k#ؖ;4LDe/"H=&l *1!"@4$l T@V5R ?(pct%/5D~ϼph'0 O3tnc6>7v}3{vCAxko!R%]h\p ;8y_fohj^ͮ?yWWQ6xgJ dR c[6 Κ BF{jF>@.7 ah囉^/*%K|GuT$[8xr[ޥ9b \r>͋rb'7p>‚%Wz#_϶?nI`΃+6i = pGQ*@Jח)oP @JY4zQf2%_Sg!ρ8ZcC8-=p> F`3AJ;޲[Y̾.8˞} mrg9 O sZcM@]b1F A}4yAfڀ tDz``GTX\ŌE(6*9x8_~ؙؼaӭD i5X=B BeЛ Nj`nn1ˈcjlZy'/dY_]~&~4eia8!me~"F݌ _5ōrd_we% ='Ĕ!tA#n\D*fҏm} d> Uu-SKxɸeѨ{0̯UP xqN!XrG&P/N ,,)cDUO) lr߿<} nlf~""cM+7U,ͷ(4n!٤*@zvYJ'V!lٍ%5PrSԀL&T2 4q]G8A,jꪫI$N$.n*(VV}¶\AIexlFp,kM3S[tzP! 4R^'(](D˖#vm@E0pA&4 q.c$65l%36 ]R%=8PNS. %e4smm{hsb,Q%DRF +E6#SS*F9EXg,DZa_آu=D.;ĺ[vO?|g9Y,=^yhi.@oY+* r F5JB ^̝o޴cl[2kZNGO9J(X=XiM&\EWQH&XzN%DR+L>hzC=f$b,!PZ!$)t,, _>qC E6ǦW|=۰lgc BJH!ZM󼊰zõo: Ɏ:M=!.8qYRn~RT]$G>FZ608G~p16D=PL †,PUS͂%8~I~,)hu1X1e )mR̞FKG (Jsi\U[eޒDI]uш` ]$eq8V2r8%8ì8<];uyAY٠rX,nEmCXso$n,8ؑn4i; O>^~8A,[(QeT1XAiɷ+ޞB˼5k!PicXp" P9}LiK./Ka88D-G~v;|XLIӹ˵A(cx_{hj^AEf 3g)-k5`W fQ%u1FG9a`Y i'@+^g~=VMH"ш,34rL/< njmq\ H%&7`0W ,8v|]}3ϲڏ֠ɮ@(~B+RRޠmMT]R,YF6m`XBPx |m?v]ʇTҖH[RcR3i#3Cbl8GrV K+u~YNO?ʦmNn*&fq^3p}'Re;. 4 7q"\H9k8Fs𛀏>Z(o,߇d#_K0*@U!R P"*`KHĪhIG4we_v?k /\kW*zʅ 5ap`bӁB穭_u[׉UhH!|oy}8{ѓC4R7n|wH m[83p@i0p|?`?mA<Ķ<_#~uۍb2Tu_qM<굿K"^EO~iX_Gn~xv֮7C*is8͈ŀf{9l`?Q dju؎ +GgpVzTTy_a鰘9wDPXc A5,+:lˎc^~'iշ1ƺfjƱ&L, <\Xё\ec|k ˆO12 B %~EKJm eSȠ<([F^pͣifw߱{o*VWa%pv3(a`@&$o(C /(3=#yH͜?3YV-=Y^|_L3OOQ K;l2Cf&|m|_wrm ([N;,7*,Y+Kt+͡qAhX.?cM}I[r2MWhx {ZM0_t8`q}0ٴGu˜E3go%h6K M_֌PsAa2z֮=goI"uyAT eh476~n6TcC #6VQQ^HRBOS,0 cVKF:ܰh4 RaYoydy,&:2YTcLyl:/3H@*4+o{^D$?/54-aʷG$!t' ID Dvυ Q (g(KAZy曛װXLn)g?Hěi].zuQ g~1U 嗥t+V kM&;3?ki[:@CMW=$yd ǓDZc!Ūh=XǺ^ p:l~BYFD=D=hű}rign t<-gf^7PlC?kĒdpֱzg54oqleⷱq9z;<97fSU2~,TQ(e%(i䫏}r<ٴ#$#y(H .1AKw!Z㥎_uF#5>k/lK%Wzz\syqYDKNV Lw(QSO}ڪܴ|PK}aqh,Ʋm4a;`U{=6 &u\jd|ޑY7ٸg-[LT\ҊX$۰Ȩbɔ7._!ũ#}Ųaij&;ozL D;7~Xuqa#O.2Wk[0RzCL?E]1mDuai?G}2v+V#7_YdÒƞ˚rua}NOrt&e,/*$!Vŷ agwoZ`Yp@#op!fgFS,idԹ :9ַq*B#6|U{JJJ72&\3ܳ ҡ{x*Bth- 7Q, s[:h1CܹK6~Soqym |Gf7.p~E9Z/>~g;ōw-j @:ūM B*_Lg]!Փ8iUC:BY7IuVy0K,_N(z$끨|f>aY i n W_ɫ?fOSĜJ+ǎp<~l] stɯ/?TP$ç (;Xvw|?DM))Qn[_|g>A0. G3?Os M6@c<]Fۈ;! 8ٮ$*S4ӹN~ZaEi1uNOyy*ݚJRpXOKS,Y2 VZy7>lK-si ʩ`n7Na'{@<4ﱒ?50ݖRH,awLaI}TC/gam1YxÚ`WQ)"ng:$ 3q`vctݪ`S^Ǟb>v:±*s8T/Rꐶ{e\{<@S:CǚYk _U (*3Pt+\t R,=t^7zO=ɞ83z5Xp,-eQ BeʛS Sw Y䢸eWUan\S/<ֹ7.Ú.@ ^T`JrcgJO0M墀E2:[}JpmfQkfm$j'T2|*MTTnF?v1SpvC=_ƅp-^fѺ__y@SbD00ЪPϏaRGkw^Mg ~eůЗk#C/rhyϻ7.\ lWbj?d}pU_c; 1g;@ʜfPrzp+Q;zΙ?쾰Ze !’a Əss_L,7CW,: ōˣ/ŏw҉fšPo1Ԅ҃0A_v|d@~DQ\&P*mԎq283AL ;yJ5,azsbG#E 4̭ c8*+V/2!R<{YDRbmJ#"xð.,չSZ 3Hx׆ H4(7'FGG#9>rUaeCE!@ V@'iTx #$8[Yw\ʺ08X&g.4џ/4E41`l?@^cߖ$V%{N ~w~&?:OM l% *X@@ L~E(4Z|QZma0&2Uع*)-80e7n VvS@|+Ҁe#/(` ſRF@"Q(|[mh`fH ؕGw+_Y?;û>r#nZXŵpC"p$tfzsW?ޙuwwHM$XdYey'͊&Ccip 0/mZ(ZijZ)NI$A8jX2I,[ lŋdBj g̽arp2~{}W-|eT`ޟV,X[XdSYB¥ˀP^7Kx k5.s–[,o#.5X 6ޗ)a{*ʳ t3C> y4:qM˂LG__J&rՏ311kXNCPP .q^>+CܐC,W}xw'tڠys;gnc]{6sӧٙ4s;ly5T0J&eKoXKZqPQhB,G2,mVnMn(SS'8ssz0;@I ӚE*5)?hT)( l; ))YY q@G@ o_t p8<&~(}[egdwU᠟)&ƂA&«L,K&ݔNblwm:= &oFc}"ǎ]EϔL=pa/jrL,@1N'' vvlgEnzTy>(8_BmwH&P7.ߎг}wˮ.na-m4g@^\ gq,hi@sgMl6yh\uJyqN.qyp +'] ó\w1&/ИB= %pw7X T<=!y6vʥ=ÖJGw3[hj&MќMiJ`yڹeA>Wd#9L3<4LQ a*J\TL_n鼕+8" k^s q&A=D6@HA@$RX(:إ) Ι>$umlr  5R.oC,fzҞgtM(8OWNe x,FJDINo(O:BJGKW.omcw$[-RnDQX M.~$\08ဥ@4!Eq@S)T]W3῾_}-cZN)i4pk W` l@ ,!6Np[-쌅pyF5zFiaMiP**V|-smqj7?q'.c&7 -bˋ:o\,0~Ȱz@"_|;v8B)? fB$rbc{jT{-_:_@hQsoڙɍ'^?u}tw*Brq2'C/`Y|0%+@Pµf V ̰{7Tv9eU$ArGph ݐ`-#Y4<@^E S )Д P\|&=E`|(SfYQs]PgcQ8pgc}V E<0Q}9@_L 2`@44$-L'TjHvOud.L_&Q|e3LLj7br_` vt`=&I# ntcm5\k\G [C޷n7W5^RczdJ䢼֣ LSsLOs%I'yW]npeUTԷNrPL n`D/#EN#Ozdp1u꼾fgA;e|1<QiƢPwoiT|머=JR^b\S5|6#s5ja c+{]z_i4@^Un'hQ:+Pnvb:iW*WcԺVCg#ԇR6MmPƌOFC ?o!D"٘r \^?jr21b3/ D1t4ަ陶v"r$z1' D1"tg_x# ^^aG]潵0BytNبM=;'g>Pl!S6wmz[uHߢ|x6d! t@`}LL@\pZ؋"P_Qj 3VkL]&E8ʎRIJiǩ2S5 'e ,dD͌wP)["%/QٌjM(oI^ image/svg+xml N S O W 0..180° 0..-180° qmapshack-1.5.1/src/map/IMapList.ui000644 001750 000144 00000010642 12572350112 020045 0ustar00oeichlerusers000000 000000 IMapList 0 0 400 443 Form 0 0 0 0 0 Qt::CustomContextMenu QAbstractItemView::InternalMove 32 32 QAbstractItemView::ScrollPerPixel false 1 3 3 3 3 3 64 16777215 :/icons/48x48/Help.png Qt::AlignCenter 8 To add maps use File->Setup Map Paths. Qt::AlignJustify|Qt::AlignVCenter true Use the context menu (right mouse button click on entry) to activate a map. Use drag-n-drop to move the activated map in the draw order. Qt::AlignJustify|Qt::AlignVCenter true Help! I want maps! I don't want to read the documentation! true :/icons/32x32/Check.png :/icons/32x32/Cancel.png:/icons/32x32/Check.png Activate CMapTreeWidget QTreeWidget

map/CMapList.h
qmapshack-1.5.1/src/map/IMapProp.h000644 001750 000144 00000002356 12622435722 017676 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IMAPPROP_H #define IMAPPROP_H #include class IMap; class CMapDraw; class IMapProp : public QWidget { Q_OBJECT public: IMapProp(IMap * mapfile, CMapDraw * map); virtual ~IMapProp(); protected slots: virtual void slotPropertiesChanged()= 0; protected: IMap * mapfile; CMapDraw * map; }; #endif //IMAPPROP_H qmapshack-1.5.1/src/map/CMapWMTS.h000644 001750 000144 00000005717 12622435723 017547 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPWMTS_H #define CMAPWMTS_H #include "map/IMap.h" #include #include #include #include class CMapDraw; class IDiskCache; class QNetworkAccessManager; class QNetworkReply; class QListWidgetItem; class CMapWMTS : public IMap { Q_OBJECT public: CMapWMTS(const QString& filename, CMapDraw *parent); virtual ~CMapWMTS(); void draw(IDrawContext::buffer_t& buf); void getLayers(QListWidget& list); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); signals: void sigQueueChanged(); protected: void configureCache(); private slots: void slotQueueChanged(); void slotRequestFinished(QNetworkReply* reply); void slotLayersChanged(QListWidgetItem * item); private: struct limit_t { qint32 minTileRow; qint32 maxTileRow; qint32 minTileCol; qint32 maxTileCol; }; struct layer_t { bool enabled; QString title; QStringList styles; QString tileMatrixSet; QRectF boundingBox; QString resourceURL; QMap limits; }; QList layers; struct tilematrix_t { QPointF topLeft; qreal scale; qint32 tileWidth; qint32 tileHeight; qint32 matrixWidth; qint32 matrixHeight; }; struct tileset_t { tileset_t() : pjsrc(0) { } ~tileset_t() { if(pjsrc) { pj_free(pjsrc); } } projPJ pjsrc; QMap tilematrix; }; QMap tilesets; QString name; /// Mutex to control access to url queue QMutex mutex {QMutex::Recursive}; /// a queue with all tile urls to request QQueue urlQueue; /// the tile cache IDiskCache * diskCache = 0; /// access manager to request tiles QNetworkAccessManager * accessManager; QList urlPending; bool lastRequest = false; QTime timeLastUpdate; }; #endif //CMAPWMTS_H qmapshack-1.5.1/src/map/OpenStreetMap.tms000644 001750 000144 00000000632 12527654570 021314 0ustar00oeichlerusers000000 000000 OpenStreetMap 2 19 https://c.tile.openstreetmap.org/%1/%2/%3.png Agent Smith Map data: (c) OpenStreetMap contributors, ODbL | Rendering: (c) OpenStreetMap , CC-BY-SA qmapshack-1.5.1/src/map/CMapItem.cpp000644 001750 000144 00000014577 12622435717 020215 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "map/CMapDraw.h" #include "map/CMapIMG.h" #include "map/CMapItem.h" #include "map/CMapJNX.h" #include "map/CMapMAP.h" #include "map/CMapRMAP.h" #include "map/CMapTMS.h" #include "map/CMapVRT.h" #include "map/CMapWMTS.h" #include "map/IMapProp.h" #include QMutex CMapItem::mutexActiveMaps(QMutex::Recursive); CMapItem::CMapItem(QTreeWidget *parent, CMapDraw * map) : QTreeWidgetItem(parent) , map(map) { // it's everything but not drag-n-drop until it gets activated setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); } CMapItem::~CMapItem() { } void CMapItem::saveConfig(QSettings& cfg) { if(mapfile.isNull()) { return; } cfg.beginGroup(key); mapfile->saveConfig(cfg); cfg.endGroup(); } void CMapItem::loadConfig(QSettings& cfg) { if(mapfile.isNull()) { return; } cfg.beginGroup(key); mapfile->loadConfig(cfg); cfg.endGroup(); } void CMapItem::showChildren(bool yes) { if(yes && !mapfile.isNull()) { QTreeWidget * tw = treeWidget(); QTreeWidgetItem * item = new QTreeWidgetItem(this); item->setFlags(Qt::ItemIsEnabled); tw->setItemWidget(item, 0, mapfile->getSetup()); } else { QList items = takeChildren(); qDeleteAll(items); delete mapfile->getSetup(); } } void CMapItem::updateIcon() { if(filename.isEmpty()) { return; } QPixmap img("://icons/32x32/Map.png"); QFileInfo fi(filename); if(fi.suffix().toLower() == "rmap") { img = QPixmap("://icons/32x32/MimeRMAP.png"); } else if(fi.suffix().toLower() == "jnx") { img = QPixmap("://icons/32x32/MimeJNX.png"); } else if(fi.suffix().toLower() == "vrt") { img = QPixmap("://icons/32x32/MimeVRT.png"); } else if(fi.suffix().toLower() == "img") { img = QPixmap("://icons/32x32/MimeIMG.png"); } else if(fi.suffix().toLower() == "map") { img = QPixmap("://icons/32x32/MimeMAP.png"); } else if(fi.suffix().toLower() == "wmts") { img = QPixmap("://icons/32x32/MimeWMTS.png"); } else if(fi.suffix().toLower() == "tms") { img = QPixmap("://icons/32x32/MimeTMS.png"); } setIcon(0,QIcon(img)); } bool CMapItem::isActivated() { QMutexLocker lock(&mutexActiveMaps); return !mapfile.isNull(); } bool CMapItem::toggleActivate() { QMutexLocker lock(&mutexActiveMaps); if(mapfile.isNull()) { return activate(); } else { deactivate(); return false; } } void CMapItem::deactivate() { QMutexLocker lock(&mutexActiveMaps); // remove mapfile setup dialog as child of this item showChildren(false); // remove mapfile object delete mapfile; // maybe used to reflect changes in the icon updateIcon(); // move to bottom of the active map list moveToBottom(); // deny drag-n-drop again setFlags(flags() & ~Qt::ItemIsDragEnabled); } bool CMapItem::activate() { QMutexLocker lock(&mutexActiveMaps); delete mapfile; // load map by suffix QFileInfo fi(filename); if(fi.suffix().toLower() == "rmap") { mapfile = new CMapRMAP(filename, map); } else if(fi.suffix().toLower() == "jnx") { mapfile = new CMapJNX(filename, map); } else if(fi.suffix().toLower() == "img") { mapfile = new CMapIMG(filename, map); } else if(fi.suffix().toLower() == "vrt") { mapfile = new CMapVRT(filename, map); } else if(fi.suffix().toLower() == "map") { mapfile = new CMapMAP(filename, map); } else if(fi.suffix().toLower() == "wmts") { mapfile = new CMapWMTS(filename, map); } else if(fi.suffix().toLower() == "tms") { mapfile = new CMapTMS(filename, map); } updateIcon(); // no mapfiles loaded? Bad. if(mapfile.isNull()) { return false; } // if map is activated successfully add to the list of map files // else delete all previous loaded maps and abort if(!mapfile->activated()) { delete mapfile; return false; } setToolTip(0, mapfile->getCopyright()); // append list of active map files moveToBottom(); // an active map is subject to drag-n-drop setFlags(flags() | Qt::ItemIsDragEnabled); /* As the map file setup is stored in the context of the CMapDraw object the configuration has to be loaded via the CMapDraw object to select the correct group context in the QSetting object. This call will result into a call of loadConfig() of this CMapItem object. */ map->loadConfigForMapItem(this); // Add the mapfile setup dialog as child of this item showChildren(true); return true; } void CMapItem::moveToTop() { QTreeWidget * w = treeWidget(); QMutexLocker lock(&mutexActiveMaps); w->takeTopLevelItem(w->indexOfTopLevelItem(this)); w->insertTopLevelItem(0, this); map->emitSigCanvasUpdate(); } void CMapItem::moveToBottom() { int row; QTreeWidget * w = treeWidget(); QMutexLocker lock(&mutexActiveMaps); w->takeTopLevelItem(w->indexOfTopLevelItem(this)); for(row = 0; row < w->topLevelItemCount(); row++) { CMapItem * item = dynamic_cast(w->topLevelItem(row)); if(item && item->mapfile.isNull()) { break; } } w->insertTopLevelItem(row, this); map->emitSigCanvasUpdate(); } qmapshack-1.5.1/src/map/IMap.cpp000644 001750 000144 00000007050 12622435717 017370 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "map/CMapDraw.h" #include "map/CMapPropSetup.h" #include "map/IMap.h" #include "units/IUnit.h" #include IMap::IMap(quint32 features, CMapDraw *parent) : IDrawObject(parent) , map(parent) , flagsFeature(features) { pjtar = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"); } IMap::~IMap() { pj_free(pjtar); pj_free(pjsrc); delete setup; } void IMap::saveConfig(QSettings& cfg) { IDrawObject::saveConfig(cfg); if(hasFeatureVectorItems()) { cfg.setValue("showPolygons", getShowPolygons()); cfg.setValue("showPolylines", getShowPolylines()); cfg.setValue("showPOIs", getShowPOIs()); } if(hasFeatureTileCache()) { cfg.setValue("cacheSizeMB", cacheSizeMB); cfg.setValue("cacheExpiration", cacheExpiration); } } void IMap::loadConfig(QSettings& cfg) { IDrawObject::loadConfig(cfg); slotSetShowPolygons(cfg.value("showPolygons", getShowPolygons()).toBool()); slotSetShowPolylines(cfg.value("showPolylines", getShowPolylines()).toBool()); slotSetShowPOIs(cfg.value("showPOIs", getShowPOIs()).toBool()); slotSetCacheSize(cfg.value("cacheSizeMB", getCacheSize()).toInt()); slotSetCacheExpiration(cfg.value("cacheExpiration", getCacheExpiration()).toInt()); } IMapProp *IMap::getSetup() { if(setup.isNull()) { setup = new CMapPropSetup(this, map); } return setup; } void IMap::convertRad2M(QPointF &p) { if(pjsrc == 0) { return; } pj_transform(pjtar,pjsrc,1,0,&p.rx(),&p.ry(),0); } void IMap::convertM2Rad(QPointF &p) { if(pjsrc == 0) { return; } pj_transform(pjsrc,pjtar,1,0,&p.rx(),&p.ry(),0); } void IMap::drawTile(QImage& img, QPolygonF& l, QPainter& p) { map->convertRad2Px(l); // adjust the tiles width and height to fit the buffer's scale qreal dx1 = l[0].x() - l[1].x(); qreal dy1 = l[0].y() - l[1].y(); qreal dx2 = l[0].x() - l[3].x(); qreal dy2 = l[0].y() - l[3].y(); qreal w = qCeil( qSqrt(dx1*dx1 + dy1*dy1)); qreal h = qCeil( qSqrt(dx2*dx2 + dy2*dy2)); // calculate rotation. This is not really a reprojection but might be good enough for close zoom levels qreal a = qAtan(dy1/dx1) * RAD_TO_DEG; // finally translate, scale, rotate and draw tile p.save(); p.translate(l[0]); p.scale(w/img.width(), h/img.height()); p.rotate(a); p.drawImage(0,0,img); p.restore(); } bool IMap::findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline) { Q_UNUSED(pt1); Q_UNUSED(pt2); Q_UNUSED(threshold); Q_UNUSED(polyline); return false; } qmapshack-1.5.1/src/map/WorldSat.wmts000644 001750 000144 00000037365 12527654570 020531 0ustar00oeichlerusers000000 000000 World_Imagery OGC WMTS 1.0.0 RESTful KVP RESTful KVP World_Imagery World_Imagery -2.003750722959434E7 -1.997186888040859E7 2.003750722959434E7 1.9971868880408563E7 -179.99999000000003 -85.00000000000003 179.99999000000003 85.0 image/jpg default028mm GoogleMapsCompatible TileMatrix using 0.28mm The tile matrix set that has scale values calculated based on the dpi defined by OGC specification (dpi assumes 0.28mm as the physical distance of a pixel). default028mm urn:ogc:def:crs:EPSG::3857 0 5.590822640285016E8 -2.0037508342787E7 2.0037508342787E7 256 256 1 1 1 2.7954113201425034E8 -2.0037508342787E7 2.0037508342787E7 256 256 2 2 2 1.3977056600712562E8 -2.0037508342787E7 2.0037508342787E7 256 256 4 4 3 6.988528300356235E7 -2.0037508342787E7 2.0037508342787E7 256 256 8 8 4 3.494264150178117E7 -2.0037508342787E7 2.0037508342787E7 256 256 16 16 5 1.7471320750890587E7 -2.0037508342787E7 2.0037508342787E7 256 256 32 32 6 8735660.375445293 -2.0037508342787E7 2.0037508342787E7 256 256 64 64 7 4367830.187722647 -2.0037508342787E7 2.0037508342787E7 256 256 128 128 8 2183915.0938617955 -2.0037508342787E7 2.0037508342787E7 256 256 256 256 9 1091957.5469304253 -2.0037508342787E7 2.0037508342787E7 256 256 512 512 10 545978.7734656851 -2.0037508342787E7 2.0037508342787E7 256 256 1024 1023 11 272989.38673237007 -2.0037508342787E7 2.0037508342787E7 256 256 2048 2045 12 136494.69336618503 -2.0037508342787E7 2.0037508342787E7 256 256 4096 4090 13 68247.34668309252 -2.0037508342787E7 2.0037508342787E7 256 256 8192 8179 14 34123.67334154626 -2.0037508342787E7 2.0037508342787E7 256 256 16384 16358 15 17061.836671245605 -2.0037508342787E7 2.0037508342787E7 256 256 32768 32715 16 8530.918335622802 -2.0037508342787E7 2.0037508342787E7 256 256 65536 65429 17 4265.459167338929 -2.0037508342787E7 2.0037508342787E7 256 256 131072 130858 18 2132.729584141936 -2.0037508342787E7 2.0037508342787E7 256 256 262144 261715 19 1066.3647915984968 -2.0037508342787E7 2.0037508342787E7 256 256 524288 523430 GoogleMapsCompatible the wellknown 'GoogleMapsCompatible' tile matrix set defined by OGC WMTS specification GoogleMapsCompatible urn:ogc:def:crs:EPSG:6.18.3:3857 urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible 0 559082264.0287178 -20037508.34278925 20037508.34278925 256 256 1 1 1 279541132.0143589 -20037508.34278925 20037508.34278925 256 256 2 2 2 139770566.0071794 -20037508.34278925 20037508.34278925 256 256 4 4 3 69885283.00358972 -20037508.34278925 20037508.34278925 256 256 8 8 4 34942641.50179486 -20037508.34278925 20037508.34278925 256 256 16 16 5 17471320.75089743 -20037508.34278925 20037508.34278925 256 256 32 32 6 8735660.375448715 -20037508.34278925 20037508.34278925 256 256 64 64 7 4367830.187724357 -20037508.34278925 20037508.34278925 256 256 128 128 8 2183915.093862179 -20037508.34278925 20037508.34278925 256 256 256 256 9 1091957.546931089 -20037508.34278925 20037508.34278925 256 256 512 512 10 545978.7734655447 -20037508.34278925 20037508.34278925 256 256 1024 1024 11 272989.3867327723 -20037508.34278925 20037508.34278925 256 256 2048 2048 12 136494.6933663862 -20037508.34278925 20037508.34278925 256 256 4096 4096 13 68247.34668319309 -20037508.34278925 20037508.34278925 256 256 8192 8192 14 34123.67334159654 -20037508.34278925 20037508.34278925 256 256 16384 16384 15 17061.83667079827 -20037508.34278925 20037508.34278925 256 256 32768 32768 16 8530.918335399136 -20037508.34278925 20037508.34278925 256 256 65536 65536 17 4265.459167699568 -20037508.34278925 20037508.34278925 256 256 131072 131072 18 2132.729583849784 -20037508.34278925 20037508.34278925 256 256 262144 262144 qmapshack-1.5.1/src/map/CMapTMS.h000644 001750 000144 00000005111 12622435723 017404 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPTMS_H #define CMAPTMS_H #include "map/IMap.h" #include #include class IDiskCache; class QListWidgetItem; class QNetworkAccessManager; class QNetworkReply; class CMapTMS : public IMap { Q_OBJECT public: CMapTMS(const QString& filename, CMapDraw *parent); virtual ~CMapTMS(); void draw(IDrawContext::buffer_t& buf); void getLayers(QListWidget& list); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); signals: void sigQueueChanged(); protected: void configureCache(); private slots: void slotQueueChanged(); void slotRequestFinished(QNetworkReply* reply); void slotLayersChanged(QListWidgetItem * item); private: struct layer_t; QString createUrl(const layer_t& layer, int x, int y, int z); struct layer_t { layer_t() : enabled(true), minZoomLevel(0), maxZoomLevel(0) { } bool enabled; qint32 minZoomLevel; qint32 maxZoomLevel; QString title; QString strUrl; QString script; }; struct rawHeaderItem_t { QString name; QString value; }; QVector layers; QString name; qint32 minZoomLevel = 1; qint32 maxZoomLevel = 21; QList rawHeaderItems; /// Mutex to control access to url queue QMutex mutex {QMutex::Recursive}; /// a queue with all tile urls to request QQueue urlQueue; /// the tile cache IDiskCache * diskCache = 0; /// access manager to request tiles QNetworkAccessManager * accessManager; QList urlPending; bool lastRequest = false; QTime timeLastUpdate; }; #endif //CMAPTMS_H qmapshack-1.5.1/src/map/CMapList.h000644 001750 000144 00000003524 12622435723 017662 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPLIST_H #define CMAPLIST_H #include #include class CMapItem; class QMenu; class CMapTreeWidget : public QTreeWidget { Q_OBJECT public: CMapTreeWidget(QWidget * parent) : QTreeWidget(parent) { } signals: void sigChanged(); protected: void dragEnterEvent(QDragEnterEvent * e); void dragMoveEvent (QDragMoveEvent * e ); void dropEvent ( QDropEvent * e ); }; #include "ui_IMapList.h" class CMapList : public QWidget, private Ui::IMapList { Q_OBJECT public: CMapList(QWidget * parent); virtual ~CMapList(); void clear(); int count(); CMapItem * item(int i); operator QTreeWidget*() { return treeWidget; } void updateHelpText(); signals: void sigChanged(); public slots: static void slotMapHonk(); private slots: void slotActivate(); void slotContextMenu(const QPoint &point); private: QMenu * menu; }; #endif //CMAPLIST_H qmapshack-1.5.1/src/map/CMapVRT.h000644 001750 000144 00000003437 12622435723 017425 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPVRT_H #define CMAPVRT_H #include "map/IMap.h" class CMapDraw; class GDALDataset; class CMapVRT : public IMap { Q_OBJECT public: CMapVRT(const QString& filename, CMapDraw *parent); virtual ~CMapVRT(); void draw(IDrawContext::buffer_t& buf); private: QString filename; /// instance of GDAL dataset GDALDataset * dataset; /// number of color bands used by the *vrt int rasterBandCount = 0; /// QT representation of the vrt's color table QVector colortable; /// width in number of px quint32 xsize_px; /// height in number of px quint32 ysize_px; /// scale [px/m] qreal xscale; /// scale [px/m] qreal yscale; qreal xrot; qreal yrot; QPointF ref1; QPointF ref2; QPointF ref3; QPointF ref4; QTransform trFwd; QTransform trInv; bool hasOverviews = false; }; #endif //CMAPVRT_H qmapshack-1.5.1/src/map/IMapProp.cpp000644 001750 000144 00000002234 12622435717 020230 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMapDraw.h" #include "IMap.h" #include "IMapProp.h" IMapProp::IMapProp(IMap *mapfile, CMapDraw *map) : mapfile(mapfile) , map(map) { connect(mapfile, SIGNAL(sigPropertiesChanged()), this, SLOT(slotPropertiesChanged())); } IMapProp::~IMapProp() { } qmapshack-1.5.1/src/map/CMapJNX.cpp000644 001750 000144 00000026176 12622435717 017754 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "helpers/CDraw.h" #include "inttypes.h" #include "map/CMapDraw.h" #include "map/CMapJNX.h" #include "units/IUnit.h" #include static void readCString(QDataStream& stream, QByteArray& ba) { quint8 byte; ba.clear(); stream >> byte; while(byte != 0) { ba += byte; stream >> byte; } } static quint32 scale2jnx(qreal scale) { /* Ok, I've made some calculations, and got the following formula to calculate the JNX scale (S) depending on the map's meters/pixel ratio (R): S(R) = qRound( 76437 * exp( ln(2.000032708011) * qRound( ln(R * 130.2084 / 76437) / ln(2.000032708011) ) ) ) where qRound - is a function which returns the closest integer from floating point value, [unfortunately its defined in C99 but not standard C++] exp - exponent, ln - natural logarithm. Magic number 130.2084 - is an average value for (JNX scale) / (maps meters per pixel) ratio among all zoom levels in metric system. Magic number 2.000032708011 is a ratio on which our standard scale table is built. It is (76437 / 4777) ^ (1/4). */ return (uint32_t)qFloor(0.5 + 76437 * exp(log(2.000032708011) * qFloor(0.5 + log(scale * 10 * 130.2084 / 76437) / log(2.000032708011) ) ) ); } CMapJNX::CMapJNX(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility,parent) , filename(filename) { qDebug() << "------------------------------"; qDebug() << "JNX: try to open" << filename; qint32 productId = -1; readFile(filename, productId); pjsrc = pj_init_plus("+proj=merc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +towgs84=0,0,0"); isActivated = true; } void CMapJNX::readFile(const QString& fn, qint32& productId) { hdr_t hdr; qDebug() << fn; QFile file(fn); file.open(QIODevice::ReadOnly); QDataStream stream(&file); stream.setByteOrder(QDataStream::LittleEndian); stream >> hdr.version; // byte 00000000..00000003 stream >> hdr.devid; // byte 00000004..00000007 stream >> hdr.lat1; // byte 00000008..0000000B stream >> hdr.lon2; // byte 0000000C..0000000F stream >> hdr.lat2; // byte 00000010..00000013 stream >> hdr.lon1; // byte 00000014..00000017 stream >> hdr.details; // byte 00000018..0000001B stream >> hdr.expire; // byte 0000001C..0000001F stream >> hdr.productId; // byte 00000020..00000023 stream >> hdr.crc; // byte 00000024..00000027 stream >> hdr.signature; // byte 00000028..0000002B // byte 0000002C..0000002F stream >> hdr.signature_offset; if(hdr.version > 3) { stream >> hdr.zorder; } else { hdr.zorder = -1; } if(productId != -1 && hdr.productId != productId) { return; } productId = hdr.productId; files.append(file_t()); file_t& mapFile = files.last(); mapFile.filename = fn; mapFile.lat1 = hdr.lat1 * 180.0 / 0x7FFFFFFF; mapFile.lat2 = hdr.lat2 * 180.0 / 0x7FFFFFFF; mapFile.lon1 = hdr.lon1 * 180.0 / 0x7FFFFFFF; mapFile.lon2 = hdr.lon2 * 180.0 / 0x7FFFFFFF; mapFile.bbox = QRectF(QPointF(mapFile.lon1, mapFile.lat1), QPointF(mapFile.lon2, mapFile.lat2)); qDebug() << hex << "Version:" << hdr.version << "DevId" << hdr.devid; qDebug() << mapFile.lon1 << mapFile.lat1 << mapFile.lon2 << mapFile.lat2; qDebug() << hex << hdr.lon1 << hdr.lat1 << hdr.lon2 << hdr.lat2; qDebug() << hex << "Details:" << hdr.details << "Expire:" << hdr.expire << "CRC:" << hdr.crc; qDebug() << hex << "Signature:" << hdr.signature << "Offset:" << hdr.signature_offset; QString strTopLeft, strBottomRight; IUnit::degToStr(mapFile.lon1, mapFile.lat1, strTopLeft); IUnit::degToStr(mapFile.lon2, mapFile.lat2, strBottomRight); qDebug() << "Levels:"; mapFile.levels.resize(hdr.details); for(quint32 i = 0; i < hdr.details; i++) { level_t& level = mapFile.levels[i]; stream >> level.nTiles >> level.offset >> level.scale; if(hdr.version > 3) { quint32 dummy; QTextCodec * codec = QTextCodec::codecForName("utf-8"); QByteArray ba; stream >> dummy; readCString(stream, ba); level.copyright1 = codec->toUnicode(ba); copyright += level.copyright1 + "\n"; } qDebug() << i << hex << level.nTiles << level.offset << level.scale; } quint32 infoBlockVersion; stream >> infoBlockVersion; if(infoBlockVersion == 0x9) { QTextCodec * codec = QTextCodec::codecForName("utf-8"); QByteArray ba; quint8 dummy; QString groupId; QString groupName; QString groupTitle; readCString(stream, ba); groupId = codec->toUnicode(ba); readCString(stream, ba); groupName = codec->toUnicode(ba); stream >> dummy >> dummy >> dummy; readCString(stream, ba); groupTitle = codec->toUnicode(ba); qDebug() << groupId << groupName << groupTitle; for(quint32 i = 0; i < hdr.details; i++) { level_t& level = mapFile.levels[i]; stream >> level.level; readCString(stream, ba); level.name1 = codec->toUnicode(ba); readCString(stream, ba); level.name2 = codec->toUnicode(ba); readCString(stream, ba); level.copyright2 = codec->toUnicode(ba); copyright += level.copyright2 + "\n"; } } for(quint32 i = 0; i < hdr.details; i++) { level_t& level = mapFile.levels[i]; const quint32 M = level.nTiles; file.seek(level.offset); level.tiles.resize(M); for(quint32 m = 0; m < M; m++) { qint32 top, right, bottom, left; tile_t& tile = level.tiles[m]; stream >> top >> right >> bottom >> left; stream >> tile.width >> tile.height >> tile.size >> tile.offset; tile.area.setTop(top * 180.0 / 0x7FFFFFFF); tile.area.setRight(right * 180.0 / 0x7FFFFFFF); tile.area.setBottom(bottom * 180.0 / 0x7FFFFFFF); tile.area.setLeft(left * 180.0 / 0x7FFFFFFF); } } if(mapFile.lon1 < lon1) { lon1 = mapFile.lon1; } if(mapFile.lat1 > lat1) { lat1 = mapFile.lat1; } if(mapFile.lon2 > lon2) { lon2 = mapFile.lon2; } if(mapFile.lat2 < lat2) { lat2 = mapFile.lat2; } } qint32 CMapJNX::scale2level(qreal s, const file_t& file) { qint32 idxLvl = NOIDX; quint32 actScale = scale2jnx(s); for(int i = 0; i < file.levels.size(); i++) { const level_t& level = file.levels[i]; if(actScale <= level.scale) { idxLvl = i; } } return idxLvl; } void CMapJNX::draw(IDrawContext::buffer_t& buf) { if(map->needsRedraw()) { return; } // convert top left buffer corner // into buffer's coordinate system QPointF pp = buf.ref1; map->convertRad2Px(pp); QPointF bufferScale = buf.scale * buf.zoomFactor; qreal u1 = buf.ref1.x() * RAD_TO_DEG; qreal v1 = buf.ref1.y() * RAD_TO_DEG; qreal u2 = buf.ref3.x() * RAD_TO_DEG; qreal v2 = buf.ref3.y() * RAD_TO_DEG; QRectF viewport; viewport.setTop(v2); viewport.setRight(u2); viewport.setBottom(v1); viewport.setLeft(u1); // ----- start drawing ----- QPainter p(&buf.image); USE_ANTI_ALIASING(p,true); p.setOpacity(getOpacity()/100.0); p.translate(-pp); foreach(const file_t &mapFile, files) { if(!viewport.intersects(mapFile.bbox)) { continue; } if(map->needsRedraw()) { break; } qint32 level = scale2level(bufferScale.x()/5, mapFile); // no scalable level found, draw bounding box of map // derive maps corner coordinate QPolygonF l(4); l[0].rx() = mapFile.lon1 * DEG_TO_RAD; l[0].ry() = mapFile.lat1 * DEG_TO_RAD; l[1].rx() = mapFile.lon2 * DEG_TO_RAD; l[1].ry() = mapFile.lat1 * DEG_TO_RAD; l[2].rx() = mapFile.lon2 * DEG_TO_RAD; l[2].ry() = mapFile.lat2 * DEG_TO_RAD; l[3].rx() = mapFile.lon1 * DEG_TO_RAD; l[3].ry() = mapFile.lat2 * DEG_TO_RAD; map->convertRad2Px(l); // finally scale, rotate and draw tile p.setPen(Qt::black); p.setBrush(Qt::NoBrush); p.drawPolygon(l); if(level < 0) { continue; } if(isOutOfScale(bufferScale)) { continue; } QByteArray data(1024*1024*4,0); //(char) typecast needed to avoid MSVC compiler warning //in MSVC, char is a signed type. //Maybe the QByteArray declaration should be fixed ;-) data[0] = (char) 0xFF; data[1] = (char) 0xD8; char * pData = data.data() + 2; QFile file(mapFile.filename); file.open(QIODevice::ReadOnly); const QVector& tiles = mapFile.levels[level].tiles; const quint32 M = tiles.size(); for(quint32 m = 0; m < M; m++) { if(map->needsRedraw()) { break; } const tile_t& tile = tiles[m]; if(viewport.intersects(tile.area)) { QImage img; file.seek(tile.offset); file.read(pData, tile.size); img.loadFromData(data); QPolygonF l(4); l[0].rx() = tile.area.left() * DEG_TO_RAD; l[0].ry() = tile.area.top() * DEG_TO_RAD; l[1].rx() = tile.area.right() * DEG_TO_RAD; l[1].ry() = tile.area.top() * DEG_TO_RAD; l[2].rx() = tile.area.right() * DEG_TO_RAD; l[2].ry() = tile.area.bottom() * DEG_TO_RAD; l[3].rx() = tile.area.left() * DEG_TO_RAD; l[3].ry() = tile.area.bottom() * DEG_TO_RAD; drawTile(img, l, p); } } } } qmapshack-1.5.1/src/map/CMapPathSetup.h000644 001750 000144 00000002665 12622435723 020671 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPPATHSETUP_H #define CMAPPATHSETUP_H #include "ui_IMapPathSetup.h" #include class CMapPathSetup : public QDialog, private Ui::IMapPathSetup { Q_OBJECT public: CMapPathSetup(QStringList& paths, QString &pathCache); virtual ~CMapPathSetup(); public slots: void accept(); private slots: void slotAddPath(); void slotDelPath(); void slotItemSelectionChanged(); void slotChangeCachePath(); void slotMapHonk(); private: QStringList& paths; QString& pathCache; }; #endif //CMAPPATHSETUP_H qmapshack-1.5.1/src/map/mapsforge/types.h000644 001750 000144 00000002714 12622435723 021335 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef TYPES_H #define TYPES_H #include struct uintX { uintX() : val(0) { } operator quint64() { return val; } quint64 val; }; struct intX { intX() : val(0) { } operator qint64() { return val; } qint64 val; }; struct utf8 { operator QString() { return val; } QString val; }; extern QDataStream& operator>>(QDataStream& s, uintX& v); extern QDataStream& operator>>(QDataStream& s, intX& v); extern QDataStream& operator>>(QDataStream& s, utf8& v); #endif //TYPES_H qmapshack-1.5.1/src/map/mapsforge/types.cpp000644 001750 000144 00000003316 12622435717 021672 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "types.h" QDataStream& operator>>(QDataStream& s, uintX& v) { quint8 tmp; int shift = 0; v.val = 0; s >> tmp; while(tmp & 0x80) { v.val |= (tmp & 0x7F)<> tmp; } v.val |= tmp << shift; return s; } QDataStream& operator>>(QDataStream& s, intX& v) { quint8 tmp; int shift = 0; v.val = 0; s >> tmp; while(tmp & 0x80) { v.val |= (tmp & 0x7F)<> tmp; } if(tmp & 0x40) { v.val = -(v.val | ((tmp & 0x3f) << shift)); } else { v.val |= tmp << shift; } return s; } QDataStream& operator>>(QDataStream& s, utf8& v) { uintX l; s >> l; v.val = QString::fromUtf8(s.device()->read(l)); return s; } qmapshack-1.5.1/src/map/CMapMAP.cpp000644 001750 000144 00000007631 12622435717 017725 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CFileExt.h" #include "map/CMapDraw.h" #include "map/CMapMAP.h" #include #include #define INT_TO_DEG(x) (qreal(x)/1e6) #define INT_TO_RAD(x) (qreal(x)/(1e6*RAD_TO_DEG)) CMapMAP::CMapMAP(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility|eFeatVectorItems, parent) , filename(filename) { qDebug() << "------------------------------"; qDebug() << "MAP: try to open" << filename; try { readBasics(); } catch(const exce_t& e) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Failed ..."), e.msg, QMessageBox::Abort); return; } isActivated = true; } CMapMAP::~CMapMAP() { } void CMapMAP::readBasics() { CFileExt file(filename); if(!file.open(QIODevice::ReadOnly)) { throw exce_t(eErrOpen, tr("Failed to open: ") + filename); } QDataStream stream(&file); stream.setByteOrder(QDataStream::BigEndian); // ---------- start file header ---------------------- stream.readRawData(header.signature,sizeof(header.signature)); if(strncmp(header.signature, "mapsforge binary OSM", sizeof(header.signature)) != 0) { throw exce_t(errFormat,tr("Bad file format: ") + filename); } stream >> header.sizeHeader; stream >> header.version; stream >> header.sizeFile; stream >> header.timestamp; stream >> header.minLat; stream >> header.minLon; stream >> header.maxLat; stream >> header.maxLon; qDebug() << INT_TO_DEG(header.minLat) << INT_TO_DEG(header.minLon) << INT_TO_DEG(header.maxLat) << INT_TO_DEG(header.maxLon); ref1 = QPointF(INT_TO_RAD(header.minLon), INT_TO_RAD(header.maxLat)); ref2 = QPointF(INT_TO_RAD(header.maxLon), INT_TO_RAD(header.minLat)); stream >> header.sizeTile; stream >> header.projection; stream >> header.flags; if(header.flags & eHeaderFlagStartPosition) { stream >> header.latStart >> header.lonStart; } if(header.flags & eHeaderFlagStartZoomLevel) { stream >> header.zoomStart; } if(header.flags & eHeaderFlagLanguage) { stream >> header.language; } if(header.flags & eHeaderFlagComment) { stream >> header.comment; } if(header.flags & eHeaderFlagCreator) { stream >> header.creator; } quint16 size; utf8 tag; stream >> size; for(int i = 0; i < size; i++) { stream >> tag; header.tagsPOIs << tag; } stream >> size; for(int i = 0; i < size; i++) { stream >> tag; header.tagsWays << tag; } quint8 N; stream >> N; for(int i = 0; i < N; i++) { layer_t layer; stream >> layer.baseZoom; stream >> layer.minZoom; stream >> layer.maxZoom; stream >> layer.offsetSubFile; stream >> layer.sizeSubFile; layers << layer; } // ---------- end file header ---------------------- } void CMapMAP::draw(IDrawContext::buffer_t& buf) { } qmapshack-1.5.1/src/map/CMapJNX.h000644 001750 000144 00000005471 12622435723 017411 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPJNX_H #define CMAPJNX_H #include "map/IMap.h" class CMapDraw; class CMapJNX : public IMap { public: CMapJNX(const QString& filename, CMapDraw *parent); void draw(IDrawContext::buffer_t& buf); private: QString filename; #pragma pack(1) struct hdr_t { quint32 version; // byte 00000000..00000003 quint32 devid; // byte 00000004..00000007 qint32 lat1; // byte 00000010..00000013 qint32 lon2; // byte 00000014..00000017 qint32 lat2; // byte 00000008..0000000B qint32 lon1; // byte 0000000C..0000000F quint32 details; // byte 00000018..0000001B quint32 expire; // byte 0000001C..0000001F qint32 productId; // byte 00000020..00000023 quint32 crc; // byte 00000024..00000027 quint32 signature; // byte 00000028..0000002B // byte 0000002C..0000002F quint32 signature_offset; qint32 zorder; // byte 00000030--00000033 }; #ifdef WIN32 #pragma pack() #else #pragma pack(0) #endif struct tile_t { QRectF area; quint16 width; quint16 height; quint32 size; quint32 offset; }; struct level_t { quint32 nTiles; quint32 offset; quint32 scale; QString copyright1; quint32 level; QString name1; QString name2; QString copyright2; QVector tiles; }; struct file_t { qreal lon1; qreal lat1; qreal lon2; qreal lat2; QRectF bbox; QString filename; QVector levels; }; void readFile(const QString& fn, qint32& productId); qint32 scale2level(qreal s, const file_t& file); QList files; qreal lon1 = 180.0; qreal lat1 = -90; qreal lon2 = -180; qreal lat2 = 90; }; #endif // CMAPJNX_H qmapshack-1.5.1/src/map/CMapWMTS.cpp000644 001750 000144 00000052500 12622435717 020075 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CDraw.h" #include "map/CMapDraw.h" #include "map/CMapWMTS.h" #include "map/cache/CDiskCache.h" #include "units/IUnit.h" #include #include #include #include #include CMapWMTS::CMapWMTS(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility|eFeatTileCache, parent) { qDebug() << "------------------------------"; qDebug() << "WTMS: try to open" << filename; QFile file(filename); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to open %1").arg(filename), QMessageBox::Abort, QMessageBox::Abort); return; } QString msg; int line, column; QDomDocument dom; if(!dom.setContent(&file, true, &msg, &line, &column)) { file.close(); QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read: %1\nline %2, column %3:\n %4").arg(filename).arg(line).arg(column).arg(msg), QMessageBox::Abort, QMessageBox::Abort); return; } file.close(); // start to decode XML // validate content as WMTS capability sheet const QDomElement& xmlCapabilities = dom.documentElement(); if(xmlCapabilities.tagName() != "Capabilities") { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read: %1\nUnknown structure.").arg(filename), QMessageBox::Abort, QMessageBox::Abort); return; } const QDomNode& xmlServiceIdentification = xmlCapabilities.namedItem("ServiceIdentification"); QString ServiceType = xmlServiceIdentification.firstChildElement("ServiceType").text(); QString ServiceTypeVersion = xmlServiceIdentification.firstChildElement("ServiceTypeVersion").text(); if(!ServiceType.contains("WMTS", Qt::CaseInsensitive) || ServiceTypeVersion != "1.0.0") { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Unexpected service. '* WMTS 1.0.0' is expected. '%1 %2' is read.").arg(ServiceType).arg(ServiceTypeVersion), QMessageBox::Abort, QMessageBox::Abort); return; } // read setup of all layers const QDomNode& xmlContents = xmlCapabilities.namedItem("Contents"); const QDomNodeList& xmlLayers = xmlContents.toElement().elementsByTagName("Layer"); const int N = xmlLayers.count(); for(int n = 0; n < N; n++) { QString str; QStringList values; const QDomNode& xmlLayer = xmlLayers.at(n); layer_t layer; layer.title = xmlLayer.firstChildElement("Title").text(); // read bounding box const QDomNode& xmlBoundingBox = xmlLayer.firstChildElement("WGS84BoundingBox"); str = xmlBoundingBox.namedItem("LowerCorner").toElement().text(); values = str.split(" "); QPointF bottomLeft(values[0].toDouble(), values[1].toDouble()); str = xmlBoundingBox.namedItem("UpperCorner").toElement().text(); values = str.split(" "); QPointF topRight(values[0].toDouble(), values[1].toDouble()); layer.boundingBox.setBottomLeft(bottomLeft); layer.boundingBox.setTopRight(topRight); const QDomNode& xmlStyle = xmlLayer.firstChildElement("Style"); layer.styles << xmlStyle.namedItem("Identifier").toElement().text(); const QDomNode& xmlTileMatrixSetLink = xmlLayer.firstChildElement("TileMatrixSetLink"); layer.tileMatrixSet = xmlTileMatrixSetLink.namedItem("TileMatrixSet").toElement().text(); // read limits if any const QDomNode& xmlTileMatrixSetLimits = xmlTileMatrixSetLink.firstChildElement("TileMatrixSetLimits"); if(xmlTileMatrixSetLimits.isElement()) { const QDomNodeList& xmlTileMatrixLimits = xmlTileMatrixSetLimits.toElement().elementsByTagName("TileMatrixLimits"); const int L = xmlTileMatrixLimits.count(); for(int l = 0; l < L; l++) { const QDomNode& xmlTileMatrixLimit = xmlTileMatrixLimits.at(l); QString Identifier = xmlTileMatrixLimit.namedItem("TileMatrix").toElement().text(); layer.limits[Identifier] = limit_t(); limit_t& limit = layer.limits[Identifier]; limit.minTileRow = xmlTileMatrixLimit.namedItem("MinTileRow").toElement().text().toInt(); limit.maxTileRow = xmlTileMatrixLimit.namedItem("MaxTileRow").toElement().text().toInt(); limit.minTileCol = xmlTileMatrixLimit.namedItem("MinTileCol").toElement().text().toInt(); limit.maxTileCol = xmlTileMatrixLimit.namedItem("MaxTileCol").toElement().text().toInt(); } } // read resource URL of layer and replace placeholders by information that is already available const QDomNode& xmlResourceURL = xmlLayer.firstChildElement("ResourceURL"); const QDomNamedNodeMap& attr = xmlResourceURL.attributes(); layer.resourceURL = attr.namedItem("template").nodeValue(); layer.resourceURL = layer.resourceURL.replace("{style}",layer.styles[0], Qt::CaseInsensitive); layer.resourceURL = layer.resourceURL.replace("{TileMatrixSet}",layer.tileMatrixSet, Qt::CaseInsensitive); // read and replace dimensions in url string by default value const QDomNodeList& xmlDimensions = xmlLayer.toElement().elementsByTagName("Dimension"); const int D = xmlDimensions.count(); for(int d = 0; d < D; d++) { const QDomNode& xmlDimension = xmlDimensions.at(d); QString Identifier = xmlDimension.namedItem("Identifier").toElement().text(); QString Default = xmlDimension.namedItem("Default").toElement().text(); layer.resourceURL = layer.resourceURL.replace("{" + Identifier + "}", Default, Qt::CaseInsensitive); } if(layer.resourceURL.toLower().startsWith("https") && !QSslSocket::supportsSsl()) { QString msg = tr( "This map requires OpenSSL support. However due to legal restrictions in some countries " "OpenSSL is not packaged with QMapShack. You can have a look at the " "OpenSSL Homepage " "for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory." ); QMessageBox::critical(CMainWindow::getBestWidgetForParent(),tr("Error..."),msg,QMessageBox::Abort); return; } // enable layer by default layer.enabled = true; layers << layer; } // if there is more than one layer the layer list in the properties widget has to be enabled. if(layers.size() > 1) { flagsFeature |= eFeatLayers; } // read setup of all tile matrices const QDomNodeList& xmlTileMatrixSets = xmlContents.childNodes(); const int M = xmlTileMatrixSets.count(); for(int m = 0; m < M; m++) { const QDomNode& xmlTileMatrixSet = xmlTileMatrixSets.at(m); if(xmlTileMatrixSet.nodeName() != "TileMatrixSet") { continue; } QString Identifier = xmlTileMatrixSet.namedItem("Identifier").toElement().text(); tilesets[Identifier] = tileset_t(); tileset_t& tileset = tilesets[Identifier]; // read projection string QString str = xmlTileMatrixSet.namedItem("SupportedCRS").toElement().text(); char * ptr1 = (char*)malloc(str.toLatin1().size() + 1); char * ptr2 = 0; strncpy(ptr1,str.toLatin1().data(), str.toLatin1().size() + 1); OGRSpatialReference oSRS; if(str.startsWith("EPSG")) { QStringList tokens = str.split(":"); oSRS.importFromEPSG(tokens.last().toInt()); } else { oSRS.importFromURN(ptr1); } oSRS.exportToProj4(&ptr2); qDebug() << ptr1 << ptr2; tileset.pjsrc = pj_init_plus(ptr2); free(ptr1); free(ptr2); if(tileset.pjsrc == 0) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("No georeference information found.")); return; } // read information about all matrix levels const QDomNodeList& xmlTileMatrixN = xmlTileMatrixSet.toElement().elementsByTagName("TileMatrix"); const int N = xmlTileMatrixN.count(); for(int n = 0; n < N; n++) { QString str; QStringList values; const QDomNode& xmlTileMatrix = xmlTileMatrixN.at(n); QString Identifier = xmlTileMatrix.namedItem("Identifier").toElement().text(); tileset.tilematrix[Identifier] = tilematrix_t(); tilematrix_t& matrix = tileset.tilematrix[Identifier]; str = xmlTileMatrix.namedItem("TopLeftCorner").toElement().text(); values = str.split(" "); matrix.topLeft = QPointF(values[0].toDouble(), values[1].toDouble()); matrix.scale = xmlTileMatrix.namedItem("ScaleDenominator").toElement().text().toDouble(); matrix.tileWidth = xmlTileMatrix.namedItem("TileWidth").toElement().text().toInt(); matrix.tileHeight = xmlTileMatrix.namedItem("TileHeight").toElement().text().toInt(); matrix.matrixWidth = xmlTileMatrix.namedItem("MatrixWidth").toElement().text().toInt(); matrix.matrixHeight = xmlTileMatrix.namedItem("MatrixHeight").toElement().text().toInt(); } } // ---- done reading XML file // create default cache path from filename QFileInfo fi(filename); slotSetCachePath(QDir(CMapDraw::getCacheRoot()).absoluteFilePath(fi.baseName())); accessManager = new QNetworkAccessManager(parent->thread()); connect(this, SIGNAL(sigQueueChanged()), this, SLOT(slotQueueChanged())); connect(accessManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(slotRequestFinished(QNetworkReply*))); name = fi.baseName().replace("_", " "); isActivated = true; } CMapWMTS::~CMapWMTS() { // map->reportStatusToCanvas(name, ""); } void CMapWMTS::getLayers(QListWidget& list) { QMutexLocker lock(&mutex); list.clear(); if(layers.size() < 2) { return; } int i = 0; foreach(const layer_t &layer, layers) { QListWidgetItem * item = new QListWidgetItem(layer.title, &list); item->setCheckState(layer.enabled ? Qt::Checked : Qt::Unchecked); item->setData(Qt::UserRole, i++); } connect(&list, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(slotLayersChanged(QListWidgetItem*))); } void CMapWMTS::saveConfig(QSettings& cfg) { QMutexLocker lock(&mutex); IMap::saveConfig(cfg); if(layers.size() < 2) { return; } // save indices of enabled layers QStringList enabled; for(int i = 0; i< layers.size(); i++) { if(layers[i].enabled) { enabled << QString::number(i); } } cfg.setValue("enabledLayers", enabled); } void CMapWMTS::loadConfig(QSettings& cfg) { QMutexLocker lock(&mutex); IMap::loadConfig(cfg); if(layers.size() < 2) { return; } QStringList enabled; // set all layers to disabled first for(int i = 0; i< layers.size(); i++) { layers[i].enabled = false; enabled << QString::number(i); } // enable layers stored in configuration enabled = cfg.value("enabledLayers", enabled).toStringList(); foreach(const QString &str, enabled) { int idx = str.toInt(); if(idx < layers.size()) { layers[idx].enabled = true; } } } void CMapWMTS::configureCache() { QMutexLocker lock(&mutex); delete diskCache; diskCache = new CDiskCache(getCachePath(), getCacheSize(), getCacheExpiration(), this); } void CMapWMTS::slotLayersChanged(QListWidgetItem * item) { QMutexLocker lock(&mutex); bool isChecked = (item->checkState() == Qt::Checked); int idx = item->data(Qt::UserRole).toInt(); if(idx < 0) { QListWidget * list = item->listWidget(); list->blockSignals(true); for(int i = 0; i < layers.size(); i++) { list->item(i + 1)->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked); layers[i].enabled = isChecked; } list->blockSignals(false); } else { layers[idx].enabled = isChecked; } map->emitSigCanvasUpdate(); } void CMapWMTS::slotQueueChanged() { QMutexLocker lock(&mutex); if(!urlQueue.isEmpty() && urlPending.size() < 6) { // request up to 6 pending request for(int i = 0; i < (6 - urlPending.size()); i++) { QString url = urlQueue.dequeue(); lastRequest = urlQueue.isEmpty(); QNetworkRequest request; request.setUrl(url); accessManager->get(request); urlPending << url; if(lastRequest) { break; } } } else if(lastRequest && urlPending.isEmpty()) { lastRequest = false; // if all tiles are received the map layer can be redrawn with all tiles from cache map->emitSigCanvasUpdate(); } if(timeLastUpdate.elapsed() > 2000) { timeLastUpdate.start(); map->emitSigCanvasUpdate(); } // report status of pending tiles int pending = urlQueue.size() + urlPending.size(); if(pending) { map->reportStatusToCanvas(name, tr("%1: %2 tiles pending
").arg(name).arg(pending)); } else { map->reportStatusToCanvas(name, ""); } } void CMapWMTS::slotRequestFinished(QNetworkReply* reply) { QMutexLocker lock(&mutex); QString url = reply->url().toString(); if(urlPending.contains(url)) { QImage img; // only take good responses if(!reply->error()) { // read image data img.loadFromData(reply->readAll()); } // always store image to cache, the cache will take care of NULL images diskCache->store(url, img); urlPending.removeAll(url); } // debug output any error if(reply->error()) { qDebug() << reply->errorString(); } // delete reply object reply->deleteLater(); // check for more items to be queued slotQueueChanged(); } void CMapWMTS::draw(IDrawContext::buffer_t& buf) { QMutexLocker lock(&mutex); timeLastUpdate.start(); urlQueue.clear(); if(map->needsRedraw()) { return; } QPointF bufferScale = buf.scale * buf.zoomFactor; if(isOutOfScale(bufferScale)) { return; } // get pixel offset of top left buffer corner QPointF pp = buf.ref1; map->convertRad2Px(pp); // start to draw the map QPainter p(&buf.image); USE_ANTI_ALIASING(p,true); p.setOpacity(getOpacity()/100.0); p.translate(-pp); // calculate maximum viewport qreal x1 = buf.ref1.x() < buf.ref4.x() ? buf.ref1.x() : buf.ref4.x(); qreal y1 = buf.ref1.y() > buf.ref2.y() ? buf.ref1.y() : buf.ref2.y(); qreal x2 = buf.ref2.x() > buf.ref3.x() ? buf.ref2.x() : buf.ref3.x(); qreal y2 = buf.ref3.y() < buf.ref4.y() ? buf.ref3.y() : buf.ref4.y(); if(x1 < -180.0*DEG_TO_RAD) { x1 = -180*DEG_TO_RAD; } if(x2 > 180.0*DEG_TO_RAD) { x2 = 180*DEG_TO_RAD; } QRectF viewport(QPointF(x1,y1) * RAD_TO_DEG, QPointF(x2,y2) * RAD_TO_DEG); // draw layers foreach(const layer_t &layer, layers) { if(!layer.boundingBox.intersects(viewport) || !layer.enabled) { continue; } const tileset_t& tileset = tilesets[layer.tileMatrixSet]; const QMap& limits = layer.limits; // convert viewport to layer's coordinate system QPointF pt1(x1,y1); QPointF pt2(x2,y2); pj_transform(pjtar, tileset.pjsrc, 1, 0, &pt1.rx(), &pt1.ry(), 0); pj_transform(pjtar, tileset.pjsrc, 1, 0, &pt2.rx(), &pt2.ry(), 0); if(pj_is_latlong(tileset.pjsrc)) { pt1 *= RAD_TO_DEG; pt2 *= RAD_TO_DEG; } // search matrix ID of tile level with best matching scale QString tileMatrixId; QPointF s1 = (pt2 - pt1)/QPointF(buf.image.width(), buf.image.height()); qreal d = NOFLOAT; foreach(const QString &key, tileset.tilematrix.keys()) { const tilematrix_t& tilematrix = tileset.tilematrix[key]; qreal s2 = tilematrix.scale * 0.28e-3; if(qAbs(s2 - s1.x()) < d) { tileMatrixId = key; d = qAbs(s2 - s1.x()); } } // get min/max col/row values for that level qint32 minRow, maxRow, minCol, maxCol; const tilematrix_t& tilematrix = tileset.tilematrix[tileMatrixId]; if(!limits.isEmpty()) { if(limits.contains(tileMatrixId)) { const limit_t& limit = limits[tileMatrixId]; minCol = limit.minTileCol; maxCol = limit.maxTileCol; minRow = limit.minTileRow; maxRow = limit.maxTileRow; } else { // layer has limits but not for the selected tileMatrixId -> skip layer continue; } } else { minCol = 0; maxCol = tilematrix.matrixWidth; minRow = 0; maxRow = tilematrix.matrixHeight; } // derive range of col/row to request tiles qreal xscale = tilematrix.scale * 0.28e-3; qreal yscale = -tilematrix.scale * 0.28e-3; qint32 col1 = qFloor((pt1.x() - tilematrix.topLeft.x()) / ( xscale * tilematrix.tileWidth)); qint32 row1 = qFloor((pt1.y() - tilematrix.topLeft.y()) / ( yscale * tilematrix.tileHeight)); qint32 col2 = qFloor((pt2.x() - tilematrix.topLeft.x()) / ( xscale * tilematrix.tileWidth)); qint32 row2 = qFloor((pt2.y() - tilematrix.topLeft.y()) / ( yscale * tilematrix.tileHeight)); if(col1 < minCol) { col1 = minCol; } if(col1 > maxCol) { col1 = maxCol; } if(row1 < minRow) { row1 = minRow; } if(row1 > maxRow) { row1 = maxRow; } if(col2 < minCol) { col2 = minCol; } if(col2 > maxCol) { col2 = maxCol; } if(row2 < minRow) { row2 = minRow; } if(row2 > maxRow) { row2 = maxRow; } // start to request tiles. draw tiles in cache, queue urls of tile yet to be requested for(qint32 row = row1; row <= row2; row++) { for(qint32 col = col1; col <= col2; col++) { QString url = layer.resourceURL; url = url.replace("{TileMatrix}",tileMatrixId, Qt::CaseInsensitive); url = url.replace("{TileRow}",QString::number(row), Qt::CaseInsensitive); url = url.replace("{TileCol}",QString::number(col), Qt::CaseInsensitive); if(diskCache->contains(url)) { QImage img; diskCache->restore(url, img); QPolygonF l; qreal xx1 = col * (xscale * tilematrix.tileWidth) + tilematrix.topLeft.x(); qreal yy1 = row * (yscale * tilematrix.tileHeight) + tilematrix.topLeft.y(); qreal xx2 = (col + 1) * (xscale * tilematrix.tileWidth) + tilematrix.topLeft.x(); qreal yy2 = (row + 1) * (yscale * tilematrix.tileHeight) + tilematrix.topLeft.y(); l << QPointF(xx1, yy1) << QPointF(xx2, yy1) << QPointF(xx2, yy2) << QPointF(xx1, yy2); pj_transform(tileset.pjsrc,pjtar, 1, 0, &l[0].rx(), &l[0].ry(), 0); pj_transform(tileset.pjsrc,pjtar, 1, 0, &l[1].rx(), &l[1].ry(), 0); pj_transform(tileset.pjsrc,pjtar, 1, 0, &l[2].rx(), &l[2].ry(), 0); pj_transform(tileset.pjsrc,pjtar, 1, 0, &l[3].rx(), &l[3].ry(), 0); drawTile(img, l, p); } else { urlQueue << url; } } } emit sigQueueChanged(); } } qmapshack-1.5.1/src/map/CMapVRT.cpp000644 001750 000144 00000030470 12622435717 017760 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CDraw.h" #include "map/CMapDraw.h" #include "map/CMapVRT.h" #include "units/IUnit.h" #include #include #include #define TILELIMIT 2500 #define TILESIZEX 64 #define TILESIZEY 64 CMapVRT::CMapVRT(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility,parent) , filename(filename) { qDebug() << "------------------------------"; qDebug() << "VRT: try to open" << filename; dataset = (GDALDataset*)GDALOpen(filename.toUtf8(),GA_ReadOnly); if(dataset == 0) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to load file: %1").arg(filename)); return; } // ------- setup color table --------- rasterBandCount = dataset->GetRasterCount(); if(rasterBandCount == 1) { GDALRasterBand * pBand; pBand = dataset->GetRasterBand(1); if(pBand == 0) { delete dataset; dataset = 0; QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to load file: %1").arg(filename)); return; } hasOverviews = pBand->GetOverviewCount() != 0; // qDebug() << pBand->GetColorInterpretation(); if(pBand->GetColorInterpretation() == GCI_PaletteIndex ) { GDALColorTable * pct = pBand->GetColorTable(); for(int i=0; i < pct->GetColorEntryCount(); ++i) { const GDALColorEntry& e = *pct->GetColorEntry(i); colortable << qRgba(e.c1, e.c2, e.c3, e.c4); } } else if(pBand->GetColorInterpretation() == GCI_GrayIndex ) { for(int i=0; i < 256; ++i) { colortable << qRgba(i, i, i, 255); } } else { delete dataset; dataset = 0; QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("File must be 8 bit palette or gray indexed.")); return; } int success = 0; qreal idx = pBand->GetNoDataValue(&success); if(success) { QColor tmp(colortable[idx]); tmp.setAlpha(0); colortable[idx] = tmp.rgba(); } } qDebug() << "has overviews" << hasOverviews; // ------- setup projection --------------- char str[1024] = {0}; if(dataset->GetProjectionRef()) { strncpy(str,dataset->GetProjectionRef(),sizeof(str)); } char * ptr = str; OGRSpatialReference oSRS; oSRS.importFromWkt(&ptr); oSRS.exportToProj4(&ptr); qDebug() << ptr; pjsrc = pj_init_plus(ptr); free(ptr); if(pjsrc == 0) { delete dataset; dataset = 0; QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("No georeference information found.")); return; } xsize_px = dataset->GetRasterXSize(); ysize_px = dataset->GetRasterYSize(); qreal adfGeoTransform[6]; dataset->GetGeoTransform( adfGeoTransform ); xscale = adfGeoTransform[1]; yscale = adfGeoTransform[5]; xrot = adfGeoTransform[4]; yrot = adfGeoTransform[2]; trFwd.translate(adfGeoTransform[0], adfGeoTransform[3]); trFwd.scale(adfGeoTransform[1],adfGeoTransform[5]); if(adfGeoTransform[4] != 0.0) { trFwd.rotate(qAtan(adfGeoTransform[2]/adfGeoTransform[4])); } if(pj_is_latlong(pjsrc)) { // convert to RAD to match internal notations trFwd = trFwd * DEG_TO_RAD; } trInv = trFwd.inverted(); ref1 = trFwd.map(QPointF(0,0)); ref2 = trFwd.map(QPointF(xsize_px,0)); ref3 = trFwd.map(QPointF(xsize_px,ysize_px)); ref4 = trFwd.map(QPointF(0,ysize_px)); qDebug() << "FF" << trFwd; qDebug() << "RR" << trInv; isActivated = true; } CMapVRT::~CMapVRT() { delete dataset; } void CMapVRT::draw(IDrawContext::buffer_t& buf) { if(map->needsRedraw()) { return; } QPointF bufferScale = buf.scale * buf.zoomFactor; // calculate bounding box; QPointF pt1 = ref1; QPointF pt2 = ref2; QPointF pt3 = ref3; QPointF pt4 = ref4; pj_transform(pjsrc,pjtar, 1, 0, &pt1.rx(), &pt1.ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &pt2.rx(), &pt2.ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &pt3.rx(), &pt3.ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &pt4.rx(), &pt4.ry(), 0); QPolygonF boundingBox; boundingBox << pt1 << pt2 << pt3 << pt4; map->convertRad2Px(boundingBox); // get pixel offset of top left buffer corner QPointF pp = buf.ref1; map->convertRad2Px(pp); // calculate area to read from file pt1 = buf.ref1; pt2 = buf.ref2; pt3 = buf.ref3; pt4 = buf.ref4; pj_transform(pjtar,pjsrc, 1, 0, &pt1.rx(), &pt1.ry(), 0); pj_transform(pjtar,pjsrc, 1, 0, &pt2.rx(), &pt2.ry(), 0); pj_transform(pjtar,pjsrc, 1, 0, &pt3.rx(), &pt3.ry(), 0); pj_transform(pjtar,pjsrc, 1, 0, &pt4.rx(), &pt4.ry(), 0); pt1 = trInv.map(pt1); pt2 = trInv.map(pt2); pt3 = trInv.map(pt3); pt4 = trInv.map(pt4); qreal left, right, top, bottom; left = pt1.x() < pt4.x() ? pt1.x() : pt4.x(); right = pt2.x() > pt3.x() ? pt2.x() : pt3.x(); top = pt1.y() < pt2.y() ? pt1.y() : pt2.y(); bottom = pt4.y() > pt3.y() ? pt4.y() : pt3.y(); if(left < 0) { left = 0; } if(left > xsize_px) { left = xsize_px; } if(top < 0) { top = 0; } if(top > ysize_px) { top = ysize_px; } if(right > xsize_px) { right = xsize_px; } if(right < 0) { right = 0; } if(bottom > ysize_px) { bottom = ysize_px; } if(bottom < 0) { bottom = 0; } qreal imgw = TILESIZEX; qreal imgh = TILESIZEY; qreal dx = imgw; qreal dy = imgh; // estimate number of tiles and use it as a limit if no // user defined limit is given qreal nTiles = ((right - left) * (bottom - top) / (dx*dy)); if(hasOverviews) { // if there are overviews tiles can be reduced by reading // with a scale factor from file. Increase amount of pixel // read until tile limit is met. while(nTiles > TILELIMIT) { dx *= 2; dy *= 2; nTiles /= 4; } } else { nTiles = getMaxScale() == NOFLOAT ? nTiles : 0; } // start to draw the map QPainter p(&buf.image); USE_ANTI_ALIASING(p,true); p.setOpacity(getOpacity()/100.0); p.translate(-pp); // qDebug() << imgw << dx << nTiles; // limit number of tiles to keep performance if(!isOutOfScale(bufferScale) && (nTiles < TILELIMIT)) { for(qreal y = top; y < bottom; y += dy) { if(map->needsRedraw()) { break; } for(qreal x = left; x < right; x += dx) { if(map->needsRedraw()) { break; } // read tile from file CPLErr err = CE_Failure; // reduce tile size at the border of the file qreal dx_used = dx; qreal dy_used = dy; qreal imgw_used = imgw; qreal imgh_used = imgh; if((x + dx) > xsize_px) { dx_used = xsize_px - x; imgw_used = qRound(imgw * dx_used / dx) & 0xFFFFFFFC; } if((y + dy) > ysize_px) { dy_used = ysize_px - y; imgh_used = imgh * dy_used / dy; } x = qRound(x); y = qRound(y); dx_used = qFloor(dx_used); dy_used = qFloor(dy_used); imgw_used = qRound(imgw_used); imgh_used = qRound(imgh_used); if(imgw_used < 1 || imgh_used < 1) { continue; } QImage img; if(rasterBandCount == 1) { GDALRasterBand * pBand; pBand = dataset->GetRasterBand(1); img = QImage(QSize(imgw_used,imgh_used),QImage::Format_Indexed8); img.setColorTable(colortable); err = pBand->RasterIO(GF_Read ,x,y ,dx_used,dy_used ,img.bits() ,imgw_used,imgh_used ,GDT_Byte,0,0); } else { img = QImage(imgw_used,imgh_used, QImage::Format_ARGB32); img.fill(qRgba(255,255,255,255)); QVector buffer(imgw_used * imgh_used); QRgb testPix = qRgba(GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand); for(int b = 1; b <= rasterBandCount; ++b) { GDALRasterBand * pBand; pBand = dataset->GetRasterBand(b); err = pBand->RasterIO(GF_Read , x, y , dx_used, dy_used , buffer.data() , imgw_used, imgh_used , GDT_Byte, 0, 0); if(!err) { int pbandColour = pBand->GetColorInterpretation(); unsigned int offset; for (offset = 0; offset < sizeof(testPix) && *(((quint8 *)&testPix) + offset) != pbandColour; offset++) { ; } if(offset < sizeof(testPix)) { quint8 * pTar = img.bits() + offset; quint8 * pSrc = buffer.data(); const int size = buffer.size(); for(int i = 0; i < size; ++i) { *pTar = *pSrc; pTar += sizeof(testPix); pSrc += 1; } } } } } if(err) { continue; } QPolygonF l; l << QPointF(x,y) << QPointF(x+dx_used,y) << QPointF(x+dx_used,y+dy_used) << QPointF(x,y+dy_used); l = trFwd.map(l); pj_transform(pjsrc,pjtar, 1, 0, &l[0].rx(), &l[0].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[1].rx(), &l[1].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[2].rx(), &l[2].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[3].rx(), &l[3].ry(), 0); drawTile(img, l, p); } } } p.setPen(Qt::black); p.setBrush(Qt::NoBrush); p.drawPolygon(boundingBox); } qmapshack-1.5.1/src/map/CMapIMG.h000644 001750 000144 00000050346 12622435723 017367 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPIMG_H #define CMAPIMG_H #include "map/CMapDraw.h" #include "map/IMap.h" #include "map/garmin/CGarminPoint.h" #include "map/garmin/CGarminPolygon.h" #include "map/garmin/CGarminTyp.h" #include "map/garmin/Garmin.h" #include class CMapDraw; class CFileExt; class IGarminStrTbl; typedef QVector polytype_t; typedef QVector pointtype_t; class CMapIMG : public IMap { Q_OBJECT public: struct maplevel_t { bool inherited; quint8 level; quint8 bits; }; /// subfile part (TRE, RGN, ...) location information struct subfile_part_t { subfile_part_t() : offset(0), size(0) { } /// file offset of subfile part quint32 offset; /// size of the subfile part quint32 size; }; /// subdivision information struct subdiv_desc_t { quint32 n; /// section of next level quint16 next; /// end of section group bool terminate; /// offset into the subfile's RGN part quint32 rgn_start; /// end of section in RGN part (last offset = rgn_end - 1) quint32 rgn_end; /// there are points stored in the RGN subsection bool hasPoints; /// there are indexd points stored in the RGN subsection bool hasIdxPoints; /// there are polylines stored in the RGN subsection bool hasPolylines; /// there are polygons stored in the RGN subsection bool hasPolygons; /// the center longitude of the area covered by this subdivision qint32 iCenterLng; /// the center latitude of the area covered by this subdivision qint32 iCenterLat; /// north boundary of area covered by this subsection [] qreal north; /// east boundary of area covered by this subsection [] qreal east; /// south boundary of area covered by this subsection [] qreal south; /// west boundary of area covered by this subsection [] qreal west; /// area in meter coordinates covered by this subdivision [] QRectF area; /// number of left shifts for RGN data quint32 shift; /// map level this subdivision is shown quint32 level; quint32 offsetPoints2; qint32 lengthPoints2; quint32 offsetPolylines2; qint32 lengthPolylines2; quint32 offsetPolygons2; qint32 lengthPolygons2; }; struct subfile_desc_t { subfile_desc_t() : north(0.0), east(0.0), south(0.0), west(0.0), isTransparent(false), strtbl(0) { } /// the name of the subfile (not really needed) QString name; /// location information of all parts QMap parts; /// north boundary of area covered by this subfile [rad] qreal north; /// east boundary of area covered by this subfile [rad] qreal east; /// south boundary of area covered by this subfile [rad] qreal south; /// west boundary of area covered by this subfile [rad] qreal west; /// area in [] covered by this subfile QRectF area; /// list of subdivisions QVector subdivs; /// used maplevels QVector maplevels; /// bit 1 of POI_flags (TRE header @ 0x3F) bool isTransparent; /// object to manage the string tables IGarminStrTbl * strtbl; }; CMapIMG(const QString &filename, CMapDraw *parent); virtual ~CMapIMG(); void draw(IDrawContext::buffer_t& buf); void getInfo(const QPoint& px, QString& str); void getToolTip(const QPoint& px, QString& infotext); /** @brief Find a matching street polyline The polyline must be close enough in terms of pixel to point 1 and 2. "Close enough" is defined by the threshold. The returned polyline uses lon/lat as coordinates. @param pt1 first point in [rad] @param pt2 second point in [rad] @param threshold the "close enough" threshold in [pixel] @param polyline the resulting polyline, if any, in [rad] @return Return true if a line has been found. */ bool findPolylineCloseBy(const QPointF &pt1, const QPointF &pt2, qint32 threshold, QPolygonF& polyline); private: enum exce_e {eErrOpen, eErrAccess, errFormat, errLock, errAbort}; struct exce_t { exce_t(exce_e err, const QString& msg) : err(err), msg(msg) { } exce_e err; QString msg; }; struct strlbl_t { strlbl_t() : type(CGarminTyp::eStandard) { } QPoint pt; QRect rect; QString str; CGarminTyp::label_type_e type; }; quint8 scale2bits(const QPointF &scale); void setupTyp(); void readBasics(); void readSubfileBasics(subfile_desc_t& subfile, CFileExt &file); void processPrimaryMapData(); void readFile(CFileExt& file, quint32 offset, quint32 size, QByteArray& data); void loadVisibleData(bool fast, polytype_t& polygons, polytype_t& polylines, pointtype_t& points, pointtype_t& pois, unsigned level, const QRectF& viewport,QPainter& p); void loadSubDiv(CFileExt &file, const subdiv_desc_t& subdiv, IGarminStrTbl * strtbl, const QByteArray& rgndata, bool fast, const QRectF& viewport, polytype_t& polylines, polytype_t& polygons, pointtype_t& points, pointtype_t& pois); void drawPolygons(QPainter& p, polytype_t& lines); void drawPolylines(QPainter& p, polytype_t& lines, const QPointF &scale); void drawPoints(QPainter& p, pointtype_t& pts, QVector &rectPois); void drawPois(QPainter& p, pointtype_t& pts, QVector& rectPois); void drawLabels(QPainter& p, const QVector &lbls); void drawText(QPainter& p); void drawLine(QPainter& p, CGarminPolygon& l, const CGarminTyp::polyline_property& property, const QFontMetricsF& metrics, const QFont& font, const QPointF& scale); void drawLine(QPainter& p, const CGarminPolygon& l); void collectText(const CGarminPolygon& item, const QPolygonF& line, const QFont& font, const QFontMetricsF& metrics, qint32 lineWidth); void getInfoPoints(const QPoint& pt, QMultiMap& dict); void getInfoPois(const QPoint& pt, QMultiMap& dict); void getInfoPolylines(const QPoint& pt, QMultiMap& dict); void getInfoPolygons(const QPoint& pt, QMultiMap& dict); #pragma pack(1) // Garmin IMG file header structure, to the start of the FAT blocks struct hdr_img_t { quint8 xorByte; ///< 0x00000000 quint8 byte0x00000001_0x0000000F[15]; char signature[7]; ///< 0x00000010 .. 0x00000016 quint8 byte0x00000017_0x00000040[42]; ///< 0x00000041 .. 0x00000047 char identifier[7]; quint8 byte0x00000048; char desc1[20]; ///< 0x00000049 .. 0x0000005C quint8 byte0x0000005D_0x00000060[4]; quint8 e1; ///< 0x00000061 quint8 e2; ///< 0x00000062 quint8 byte0x00000063_0x00000064[2]; char desc2[31]; ///< 0x00000065 .. 0x00000083 quint8 byte0x00000084_0x0000040B[904]; quint32 dataoffset; ///< 0x0000040C .. 0x0000040F quint8 byte0x00000410_0x0000041F[16]; quint16 blocks[240]; ///< 0x00000420 .. 0x000005FF quint32 blocksize() { return 1 << (e1 + e2); } }; struct FATblock_t { quint8 flag; ///< 0x00000000 char name[8]; ///< 0x00000001 .. 0x00000008 char type[3]; ///< 0x00000009 .. 0x0000000B quint32 size; ///< 0x0000000C .. 0x0000000F quint16 part; ///< 0x00000010 .. 0x00000011 quint8 byte0x00000012_0x0000001F[14]; quint16 blocks[240]; ///< 0x00000020 .. 0x000001FF }; // common header of the RGN, TRE, LBL, NET, ... parts of the IMG file struct hdr_subfile_part_t { quint16 length; ///< 0x00000000 .. 0x00000001 char type[10]; ///< 0x00000002 .. 0x0000000B quint8 byte0x0000000C; quint8 flag; ///< 0x0000000D quint8 byte0x0000000E_0x00000014[7]; }; // TRE part header, to 0xB7 struct hdr_tre_t : public hdr_subfile_part_t { quint24 northbound; ///< 0x00000015 .. 0x00000017 quint24 eastbound; ///< 0x00000018 .. 0x0000001A quint24 southbound; ///< 0x0000001B .. 0x0000001D quint24 westbound; ///< 0x0000001E .. 0x00000020 quint32 tre1_offset; ///< 0x00000021 .. 0x00000024 quint32 tre1_size; ///< 0x00000025 .. 0x00000028 quint32 tre2_offset; ///< 0x00000029 .. 0x0000002C quint32 tre2_size; ///< 0x0000002D .. 0x00000030 quint32 tre3_offset; ///< 0x00000031 .. 0x00000034 quint32 tre3_size; ///< 0x00000035 .. 0x00000038 ///< 0x00000039 .. 0x0000003A quint16 tre3_rec_size; quint8 byte0x0000003B_0x0000003E[4]; quint8 POI_flags; ///< 0x0000003F quint8 byte0x00000040_0x00000049[10]; quint32 tre4_offset; ///< 0x0000004A .. 0x0000004D quint32 tre4_size; ///< 0x0000004E .. 0x00000051 ///< 0x00000052 .. 0x00000053 quint16 tre4_rec_size; quint8 byte0x00000054_0x00000057[4]; quint32 tre5_offset; ///< 0x00000058 .. 0x0000005B quint32 tre5_size; ///< 0x0000005C .. 0x0000005F ///< 0x00000060 .. 0x00000061 quint16 tre5_rec_size; quint8 byte0x00000062_0x00000065[4]; quint32 tre6_offset; ///< 0x00000066 .. 0x00000069 quint32 tre6_size; ///< 0x0000006A .. 0x0000006D ///< 0x0000006E .. 0x0000006F quint16 tre6_rec_size; quint8 byte0x00000070_0x00000073[4]; /*-----------------------------------------------------*/ quint8 byte0x00000074_0x0000007B[8]; // Object groups V2 (CTreGroup2). quint32 tre7_offset; ///< 0x0000007C .. 0x0000007F //Groups2Offset quint32 tre7_size; ///< 0x00000080 .. 0x00000083 //dwGroups2Length ///< 0x00000084 .. 0x00000085 //wGroup2RecSize quint16 tre7_rec_size; quint8 byte0x00000086_0x00000089[4]; // Order: polyline, polygon, POI; each sorted by type (1 type 1 levels 1 subtype) quint32 tre8_offset; ///< 0x0000008A .. 0x0000008D quint32 tre8_size; ///< 0x0000008E .. 0x00000091 ///< 0x00000092 .. 0x00000093 quint16 tre8_rec_size; ///< 0x00000094 .. 0x00000095 quint16 polyl2_types_num; ///< 0x00000096 .. 0x00000097 quint16 polyg2_types_num; ///< 0x00000098 .. 0x00000099 quint16 poi2_types_num; /*-----------------------------------------------------*/ quint8 key[20]; ///< 0x0000009A .. 0x000000AD quint32 tre9_offset; ///< 0x000000AE .. 0x000000B1 quint32 tre9_size; ///< 0x000000B2 .. 0x000000B5 ///< 0x000000B6 .. 0x000000B7 quint16 tre9_rec_size; }; // RGN part header struct hdr_rgn_t : public hdr_subfile_part_t { quint32 offset; ///< 0x00000015 .. 0x00000018 quint32 length; ///< 0x00000019 .. 0x0000001C ///< 0x0000001D .. 0x00000020 quint32 offset_polyg2; ///< 0x00000021 .. 0x00000024 quint32 length_polyg2; quint8 byte0x00000025_0x00000038[20]; ///< 0x00000039 .. 0x0000003C quint32 offset_polyl2; ///< 0x0000003D .. 0x00000040 quint32 length_polyl2; quint8 byte0x00000041_0x00000054[20]; ///< 0x00000055 .. 0x00000058 quint32 offset_point2; ///< 0x00000059 .. 0x0000005C quint32 length_point2; }; // LBL part header struct hdr_lbl_t : public hdr_subfile_part_t { quint32 lbl1_offset; ///< 0x00000015 .. 0x00000018 quint32 lbl1_length; ///< 0x00000019 .. 0x0000001C quint8 addr_shift; ///< 0x0000001D quint8 coding; ///< 0x0000001E quint32 lbl2_offset; ///< 0x0000001F .. 0x00000022 quint32 lbl2_length; ///< 0x00000023 .. 0x00000026 ///< 0x00000027 .. 0x00000028 quint16 lbl2_rec_size; quint8 byte0x00000029_0x0000002C[4]; quint32 lbl3_offset; ///< 0x0000002D .. 0x00000030 quint32 lbl3_length; ///< 0x00000031 .. 0x00000034 ///< 0x00000035 .. 0x00000036 quint16 lbl3_rec_size; quint8 byte0x00000037_0x0000003A[4]; quint32 lbl4_offset; ///< 0x0000003B .. 0x0000003E quint32 lbl4_length; ///< 0x0000003F .. 0x00000042 ///< 0x00000043 .. 0x00000044 quint16 lbl4_rec_size; quint8 byte0x00000045_0x00000048[4]; quint32 lbl5_offset; ///< 0x00000049 .. 0x0000004C quint32 lbl5_length; ///< 0x0000004D .. 0x00000050 ///< 0x00000051 .. 0x00000052 quint16 lbl5_rec_size; quint8 byte0x00000053_0x00000056[4]; quint32 lbl6_offset; ///< 0x00000057 .. 0x0000005A quint32 lbl6_length; ///< 0x0000005B .. 0x0000005E ///< 0x0000005F quint8 lbl6_addr_shift; ///< 0x00000060 quint8 lbl6_glob_mask; quint8 byte0x00000061_0x00000063[3]; quint32 lbl7_offset; ///< 0x00000064 .. 0x00000067 quint32 lbl7_length; ///< 0x00000068 .. 0x0000006B ///< 0x0000006C .. 0x0000006D quint16 lbl7_rec_size; quint8 byte0x0000006E_0x00000071[4]; quint32 lbl8_offset; ///< 0x00000072 .. 0x00000075 quint32 lbl8_length; ///< 0x00000076 .. 0x00000079 ///< 0x0000007A .. 0x0000007B quint16 lbl8_rec_size; quint8 byte0x0000007C_0x0000007F[4]; quint32 lbl9_offset; ///< 0x00000080 .. 0x00000083 quint32 lbl9_length; ///< 0x00000084 .. 0x00000087 ///< 0x00000088 .. 0x00000089 quint16 lbl9_rec_size; quint8 byte0x0000008A_0x0000008D[4]; quint32 lbl10_offset; ///< 0x0000008E .. 0x00000091 quint32 lbl10_length; ///< 0x00000092 .. 0x00000095 ///< 0x00000096 .. 0x00000097 quint16 lbl10_rec_size; quint8 byte0x00000098_0x0000009B[4]; quint32 lbl11_offset; ///< 0x0000009C .. 0x0000009F quint32 lbl11_length; ///< 0x000000A0 .. 0x000000A3 ///< 0x000000A4 .. 0x000000A5 quint16 lbl11_rec_size; quint8 byte0x000000A6_0x000000A9[4]; quint16 codepage; ///< 0x000000AA .. 0x000000AB optional check length }; // NET part header struct hdr_net_t : public hdr_subfile_part_t { quint32 net1_offset; ///< 0x00000015 .. 0x00000018 quint32 net1_length; ///< 0x00000019 .. 0x0000001C ///< 0x0000001D quint8 net1_addr_shift; quint32 net2_offset; ///< 0x0000001E .. 0x00000021 quint32 net2_length; ///< 0x00000022 .. 0x00000025 ///< 0x00000026 quint8 net2_addr_shift; quint32 net3_offset; ///< 0x00000027 .. 0x0000002A quint32 net3_length; ///< 0x0000002B .. 0x0000002E }; struct hdr_dem_t : public hdr_subfile_part_t { quint32 dem_flags; ///< 0x00000015 .. 0x00000018 quint16 levels; ///< 0x00000019 .. 0x0000001A quint8 byte0x0000001B_0x0000001E[4]; quint16 blk3_size; ///< 0x0000001f .. 0x00000020 quint32 blk3_offset; ///< 0x00000021 .. 0x00000024 }; struct dem_level_t { quint16 index; quint32 nPixelPerTileX; quint32 nPixelPerTileY; quint32 Unknown1; quint32 Unknown2; quint16 Unknown3; quint32 nTilesX; quint32 nTilesY; quint16 format; quint16 blk1_size; quint32 blk1_offset; quint32 blk2_offset; quint32 westernBound; quint32 northernBound; quint32 PixelPerMeterX; quint32 PixelPerMeterY; quint16 minHeight; quint16 maxHeight; }; #define TRE_MAP_LEVEL(r) ((r)->zoom & 0x0f) #define TRE_MAP_INHER(r) (((r)->zoom & 0x80) != 0) // map level definition struct tre_map_level_t { quint8 zoom; quint8 bits; quint16 nsubdiv; }; // map subdivision definition, without pointer to the lower level subparts struct tre_subdiv_t { quint24 rgn_offset; quint8 elements; quint24 center_lng; quint24 center_lat; quint16 width_trm; #define TRE_SUBDIV_WIDTH(r) (gar_load(uint16_t, (r)->width_trm) & 0x7FFF) #define TRE_SUBDIV_TERM(r) ((gar_load(uint16_t, (r)->width_trm) & 0x8000) != 0) quint16 height; }; // pointer to the lower level subparts struct tre_subdiv_next_t : public tre_subdiv_t { quint16 next; }; struct tre_subdiv2_t { quint32 offsetPolygons; quint32 offsetPolyline; quint32 offsetPoints; quint8 btObjects; }; #ifdef WIN32 #pragma pack() #else #pragma pack(0) #endif struct map_level_t { quint8 bits; quint8 level; bool useBaseMap; bool operator==(const map_level_t &ml) const { if (ml.bits != bits || ml.level != level || ml.useBaseMap != useBaseMap) { return false; } else { return true; } } static bool GreaterThan(const map_level_t &ml1, const map_level_t &ml2) { return ml1.bits < ml2.bits; } }; QString filename; quint8 mask; quint32 mask32; quint64 mask64; QString mapdesc; /// hold all subfile descriptors /** In a normal *.img file there is only one subfile. However gmapsupp.img files can hold several subfiles each with it's own subfile parts. */ QMap subfiles; /// relay the transparent flags from the subfiles bool transparent = false; QRectF maparea; QFontMetrics fm; /// combined maplevels of all tiles QVector maplevels; QMap polylineProperties; QMap polygonProperties; QList polygonDrawOrder; QMap pointProperties; QMap languages; polytype_t polygons; polytype_t polylines; pointtype_t points; pointtype_t pois; QVector labels; struct textpath_t { // QPainterPath path; QPolygonF polyline; QString text; QFont font; QVector lengths; qint32 lineWidth; }; QVector textpaths; qint8 selectedLanguage; QSet copyrights; }; #endif //CMAPIMG_H qmapshack-1.5.1/src/map/WorldTopo.wmts000644 001750 000144 00000037414 12527654570 020716 0ustar00oeichlerusers000000 000000 World_Topo_Map OGC WMTS 1.0.0 RESTful KVP RESTful KVP World_Topo_Map World_Topo_Map -2.0037507067161843E7 -1.9971868880408604E7 2.0037507067161843E7 1.997186888040863E7 -179.9999885408441 -85.00000000000003 179.9999885408441 85.00000000000006 image/jpg default028mm GoogleMapsCompatible TileMatrix using 0.28mm The tile matrix set that has scale values calculated based on the dpi defined by OGC specification (dpi assumes 0.28mm as the physical distance of a pixel). default028mm urn:ogc:def:crs:EPSG::3857 0 5.590822640285016E8 -2.0037508342787E7 2.0037508342787E7 256 256 1 1 1 2.7954113201425034E8 -2.0037508342787E7 2.0037508342787E7 256 256 2 2 2 1.3977056600712562E8 -2.0037508342787E7 2.0037508342787E7 256 256 4 4 3 6.988528300356235E7 -2.0037508342787E7 2.0037508342787E7 256 256 8 8 4 3.494264150178117E7 -2.0037508342787E7 2.0037508342787E7 256 256 16 16 5 1.7471320750890587E7 -2.0037508342787E7 2.0037508342787E7 256 256 32 32 6 8735660.375445293 -2.0037508342787E7 2.0037508342787E7 256 256 64 64 7 4367830.187722647 -2.0037508342787E7 2.0037508342787E7 256 256 128 128 8 2183915.0938617955 -2.0037508342787E7 2.0037508342787E7 256 256 256 256 9 1091957.5469304253 -2.0037508342787E7 2.0037508342787E7 256 256 512 512 10 545978.7734656851 -2.0037508342787E7 2.0037508342787E7 256 256 1024 1023 11 272989.38673237007 -2.0037508342787E7 2.0037508342787E7 256 256 2048 2045 12 136494.69336618503 -2.0037508342787E7 2.0037508342787E7 256 256 4096 4090 13 68247.34668309252 -2.0037508342787E7 2.0037508342787E7 256 256 8192 8179 14 34123.67334154626 -2.0037508342787E7 2.0037508342787E7 256 256 16384 16358 15 17061.836671245605 -2.0037508342787E7 2.0037508342787E7 256 256 32768 32715 16 8530.918335622802 -2.0037508342787E7 2.0037508342787E7 256 256 65536 65429 17 4265.459167338929 -2.0037508342787E7 2.0037508342787E7 256 256 131072 130858 18 2132.729584141936 -2.0037508342787E7 2.0037508342787E7 256 256 262144 261715 19 1066.3647915984968 -2.0037508342787E7 2.0037508342787E7 256 256 524288 523430 GoogleMapsCompatible the wellknown 'GoogleMapsCompatible' tile matrix set defined by OGC WMTS specification GoogleMapsCompatible urn:ogc:def:crs:EPSG:6.18.3:3857 urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible 0 559082264.0287178 -20037508.34278925 20037508.34278925 256 256 1 1 1 279541132.0143589 -20037508.34278925 20037508.34278925 256 256 2 2 2 139770566.0071794 -20037508.34278925 20037508.34278925 256 256 4 4 3 69885283.00358972 -20037508.34278925 20037508.34278925 256 256 8 8 4 34942641.50179486 -20037508.34278925 20037508.34278925 256 256 16 16 5 17471320.75089743 -20037508.34278925 20037508.34278925 256 256 32 32 6 8735660.375448715 -20037508.34278925 20037508.34278925 256 256 64 64 7 4367830.187724357 -20037508.34278925 20037508.34278925 256 256 128 128 8 2183915.093862179 -20037508.34278925 20037508.34278925 256 256 256 256 9 1091957.546931089 -20037508.34278925 20037508.34278925 256 256 512 512 10 545978.7734655447 -20037508.34278925 20037508.34278925 256 256 1024 1024 11 272989.3867327723 -20037508.34278925 20037508.34278925 256 256 2048 2048 12 136494.6933663862 -20037508.34278925 20037508.34278925 256 256 4096 4096 13 68247.34668319309 -20037508.34278925 20037508.34278925 256 256 8192 8192 14 34123.67334159654 -20037508.34278925 20037508.34278925 256 256 16384 16384 15 17061.83667079827 -20037508.34278925 20037508.34278925 256 256 32768 32768 16 8530.918335399136 -20037508.34278925 20037508.34278925 256 256 65536 65536 17 4265.459167699568 -20037508.34278925 20037508.34278925 256 256 131072 131072 18 2132.729583849784 -20037508.34278925 20037508.34278925 256 256 262144 262144 qmapshack-1.5.1/src/map/OpenCycleMap.tms000644 001750 000144 00000000504 12527654570 021103 0ustar00oeichlerusers000000 000000 Opencyclemap 1 1024 http://a.tile.thunderforest.com/cycle/%1/%2/%3.png Map data: (c) OpenStreetMap contributors, ODbL | Rendering: (c) OpenCycleMap , CC-BY-SA qmapshack-1.5.1/src/map/CMapPropSetup.h000644 001750 000144 00000002760 12622435723 020711 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPPROPSETUP_H #define CMAPPROPSETUP_H #include "IMapProp.h" #include "ui_IMapPropSetup.h" class CMapPropSetup : public IMapProp, private Ui::IMapPropSetup { Q_OBJECT public: CMapPropSetup(IMap * mapfile, CMapDraw * map); virtual ~CMapPropSetup(); protected slots: void slotPropertiesChanged(); protected: void resizeEvent(QResizeEvent * e); private slots: void slotScaleChanged(const QPointF& s); void slotSetMinScale(bool checked); void slotSetMaxScale(bool checked); private: void updateScaleLabel(); static QPointF scale; }; #endif //CMAPPROPSETUP_H qmapshack-1.5.1/src/map/CMapDraw.cpp000644 001750 000144 00000023454 12622435717 020206 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "helpers/CSettings.h" #include "map/CMapDraw.h" #include "map/CMapItem.h" #include "map/CMapList.h" #include "map/CMapPathSetup.h" #include "map/IMap.h" #include #include QList CMapDraw::maps; QString CMapDraw::cachePath = QDir::home().absoluteFilePath(".QMapShack/"); QStringList CMapDraw::mapPaths; QStringList CMapDraw::supportedFormats = QString("*.vrt|*.jnx|*.img|*.rmap|*.wmts|*.tms").split('|'); CMapDraw::CMapDraw(CCanvas *parent) : IDrawContext("map", CCanvas::eRedrawMap, parent) { mapList = new CMapList(canvas); CMainWindow::self().addMapList(mapList, canvas->objectName()); connect(canvas, SIGNAL(destroyed()), mapList, SLOT(deleteLater())); connect(mapList, SIGNAL(sigChanged()), this, SLOT(emitSigCanvasUpdate())); buildMapList(); maps << this; } CMapDraw::~CMapDraw() { maps.removeOne(this); } void CMapDraw::setProjection(const QString& proj) { // --- save the active maps QStringList keys; saveActiveMapsList(keys); // --- now set the new projection IDrawContext::setProjection(proj); // --- now build the map list from scratch. This will deactivate -> activate all maps // By that everything is restored with the new projection buildMapList(); restoreActiveMapsList(keys); } void CMapDraw::setupMapPath() { QStringList paths = mapPaths; CMapPathSetup dlg(paths, cachePath); if(dlg.exec() != QDialog::Accepted) { return; } setupMapPath(paths); } void CMapDraw::setupMapPath(const QString &path) { if(mapPaths.contains(path)) { return; } QStringList paths(mapPaths); paths << path; setupMapPath(paths); } void CMapDraw::setupMapPath(const QStringList& paths) { mapPaths = paths; foreach(CMapDraw * map, maps) { QStringList keys; map->saveActiveMapsList(keys); map->buildMapList(); map->restoreActiveMapsList(keys); } } void CMapDraw::saveMapPath(QSettings& cfg) { cfg.setValue("mapPath", mapPaths); cfg.setValue("cachePath", cachePath); } void CMapDraw::loadMapPath(QSettings& cfg) { mapPaths = cfg.value("mapPath", mapPaths).toStringList(); cachePath = cfg.value("cachePath", cachePath).toString(); } void CMapDraw::getInfo(const QPoint& px, QString& str) { if(isRunning()) { return; } CMapItem::mutexActiveMaps.lock(); if(mapList) { for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(!item || item->mapfile.isNull()) { // as all active maps have to be at the top of the list // it is ok to break ass soon as the first map with no // active files is hit. break; } item->mapfile->getInfo(px, str); } } CMapItem::mutexActiveMaps.unlock(); } void CMapDraw::getToolTip(const QPoint& px, QString& str) { if(isRunning()) { return; } CMapItem::mutexActiveMaps.lock(); if(mapList) { for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(!item || item->mapfile.isNull()) { // as all active maps have to be at the top of the list // it is ok to break ass soon as the first map with no // active files is hit. break; } item->mapfile->getToolTip(px, str); } } CMapItem::mutexActiveMaps.unlock(); } bool CMapDraw::findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline) { if(isRunning()) { return false; } bool res = false; CMapItem::mutexActiveMaps.lock(); if(mapList) { for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(!item || item->mapfile.isNull()) { // as all active maps have to be at the top of the list // it is ok to break ass soon as the first map with no // active files is hit. break; } res = item->mapfile->findPolylineCloseBy(pt1, pt2, threshold, polyline); if(res) { break; } } } CMapItem::mutexActiveMaps.unlock(); return res; } void CMapDraw::saveConfig(QSettings& cfg) { // store group context for later use cfgGroup = cfg.group(); // ------------------- QStringList keys; cfg.beginGroup("map"); saveActiveMapsList(keys, cfg); cfg.setValue("active", keys); cfg.setValue("zoomIndex", zoomIndex); cfg.endGroup(); } void CMapDraw::loadConfig(QSettings& cfg) { // store group context for later use cfgGroup = cfg.group(); // ------------------- cfg.beginGroup("map"); if(cfgGroup.isEmpty()) { restoreActiveMapsList(cfg.value("active", "").toStringList(), cfg); } else { restoreActiveMapsList(cfg.value("active", "").toStringList()); } int idx = cfg.value("zoomIndex",zoomIndex).toInt(); cfg.endGroup(); zoom(idx); } void CMapDraw::buildMapList() { QCryptographicHash md5(QCryptographicHash::Md5); QMutexLocker lock(&CMapItem::mutexActiveMaps); mapList->clear(); foreach(const QString &path, mapPaths) { QDir dir(path); // find available maps foreach(const QString &filename, dir.entryList(supportedFormats, QDir::Files|QDir::Readable, QDir::Name)) { QFileInfo fi(filename); CMapItem * item = new CMapItem(*mapList, this); item->setText(0,fi.baseName().replace("_", " ")); item->filename = dir.absoluteFilePath(filename); item->updateIcon(); // calculate MD5 hash from the file's first 1024 bytes QFile f(dir.absoluteFilePath(filename)); f.open(QIODevice::ReadOnly); md5.reset(); md5.addData(f.read(1024)); item->key = md5.result().toHex(); f.close(); } } mapList->updateHelpText(); } void CMapDraw::saveActiveMapsList(QStringList& keys) { SETTINGS; cfg.beginGroup(cfgGroup); cfg.beginGroup("map"); saveActiveMapsList(keys, cfg); cfg.endGroup(); cfg.endGroup(); } void CMapDraw::saveActiveMapsList(QStringList& keys, QSettings& cfg) { QMutexLocker lock(&CMapItem::mutexActiveMaps); for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(item && !item->mapfile.isNull()) { item->saveConfig(cfg); keys << item->key; } } } void CMapDraw::loadConfigForMapItem(CMapItem * item) { if(cfgGroup.isEmpty()) { return; } SETTINGS; cfg.beginGroup(cfgGroup); cfg.beginGroup("map"); item->loadConfig(cfg); cfg.endGroup(); cfg.endGroup(); } void CMapDraw::restoreActiveMapsList(const QStringList& keys) { QMutexLocker lock(&CMapItem::mutexActiveMaps); foreach(const QString &key, keys) { for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(item && item->key == key) { /** @Note the item will load it's configuration upon successful activation by calling loadConfigForMapItem(). */ item->activate(); break; } } } mapList->updateHelpText(); } void CMapDraw::restoreActiveMapsList(const QStringList& keys, QSettings& cfg) { QMutexLocker lock(&CMapItem::mutexActiveMaps); foreach(const QString &key, keys) { for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(item && item->key == key) { if(item->activate()) { item->loadConfig(cfg); } break; } } } mapList->updateHelpText(); } void CMapDraw::reportStatusToCanvas(const QString& key, const QString& msg) { canvas->reportStatus(key, msg); } void CMapDraw::drawt(IDrawContext::buffer_t& currentBuffer) { // iterate over all active maps and call the draw method CMapItem::mutexActiveMaps.lock(); if(mapList) { for(int i = 0; i < mapList->count(); i++) { CMapItem * item = mapList->item(i); if(!item || item->mapfile.isNull()) { // as all active maps have to be at the top of the list // it is ok to break ass soon as the first map with no // active files is hit. break; } item->mapfile->draw(currentBuffer); } } CMapItem::mutexActiveMaps.unlock(); } qmapshack-1.5.1/src/map/CMapRMAP.h000644 001750 000144 00000004636 12622435723 017513 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPRMAP_H #define CMAPRMAP_H #include "IMap.h" class CMapDraw; class CMapRMAP : public IMap { Q_OBJECT; public: CMapRMAP(const QString& filename, CMapDraw *parent); void draw(IDrawContext::buffer_t& buf); private: struct level_t { level_t() : offsetLevel(0), width(0), height(0), xTiles(0), yTiles(0), xscale(0), yscale(0) { } quint64 offsetLevel; qint32 width; qint32 height; qint32 xTiles; qint32 yTiles; QVector offsetJpegs; quint64 getOffsetJpeg(quint32 x, quint32 y) { qint32 idx = y * xTiles + x; return idx < offsetJpegs.size() ? offsetJpegs[idx] : 0; } qreal xscale; qreal yscale; }; bool setProjection(const QString& projection, const QString& datum); level_t& findBestLevel(const QPointF &s); QString filename; /// total width in number of px qint32 xsize_px; /// total height in number of px qint32 ysize_px; /// width of a tile in number of px quint32 tileSizeX; /// height of a tile in number of px quint32 tileSizeY; /// all pre-scaled levels QList levels; /// reference point [m] or [°] (left hand side of map) qreal xref1; /// reference point [m] or [°] (top of map) qreal yref1; /// reference point [m] or [°] (right hand side of map) qreal xref2; /// reference point [m] or [°] (bottom of map) qreal yref2; QPointF scale; }; #endif // CMAPRMAP_H qmapshack-1.5.1/src/map/CMapPathSetup.cpp000644 001750 000144 00000006335 12622435717 021225 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "map/CMapDraw.h" #include "map/CMapList.h" #include "map/CMapPathSetup.h" #include CMapPathSetup::CMapPathSetup(QStringList &paths, QString& pathCache) : QDialog(CMainWindow::getBestWidgetForParent()) , paths(paths) , pathCache(pathCache) { setupUi(this); connect(toolAdd, SIGNAL(clicked()), this, SLOT(slotAddPath())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelPath())); connect(listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged())); connect(pushMapHonk, SIGNAL(clicked()), this, SLOT(slotMapHonk())); foreach(const QString &path, paths) { QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(path); } labelCacheRoot->setText(pathCache); connect(toolCacheRoot, SIGNAL(clicked()), this, SLOT(slotChangeCachePath())); labelHelp->setText(tr("Add or remove paths containing maps. There can be multiple maps in a path but no sub-path is parsed. Supported formats are: %1").arg(CMapDraw::getSupportedFormats().join(", "))); } CMapPathSetup::~CMapPathSetup() { } void CMapPathSetup::slotItemSelectionChanged() { QList items = listWidget->selectedItems(); toolDelete->setEnabled(!items.isEmpty()); } void CMapPathSetup::slotAddPath() { QString path = QFileDialog::getExistingDirectory(this, tr("Select map path..."), QDir::homePath(), 0); if(!path.isEmpty()) { if(!paths.contains(path)) { QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(path); } } } void CMapPathSetup::slotDelPath() { QList items = listWidget->selectedItems(); qDeleteAll(items); } void CMapPathSetup::slotChangeCachePath() { QString path = QFileDialog::getExistingDirectory(this, tr("Select root path..."), labelCacheRoot->text()); if(path.isEmpty()) { return; } labelCacheRoot->setText(path); } void CMapPathSetup::slotMapHonk() { CMapList::slotMapHonk(); close(); } void CMapPathSetup::accept() { paths.clear(); for(int i = 0; i < listWidget->count(); i++) { QListWidgetItem *item = listWidget->item(i); paths << item->text(); } pathCache = QDir(labelCacheRoot->text()).absolutePath(); QDialog::accept(); } qmapshack-1.5.1/src/map/CMapPropSetup.cpp000644 001750 000144 00000014275 12622435717 021253 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "map/CMapDraw.h" #include "map/CMapPropSetup.h" #include "map/IMap.h" #include "units/IUnit.h" #include QPointF CMapPropSetup::scale; CMapPropSetup::CMapPropSetup(IMap * mapfile, CMapDraw *map) : IMapProp(mapfile, map) { setupUi(this); slotPropertiesChanged(); connect(sliderOpacity, SIGNAL(valueChanged(int)), mapfile, SLOT(slotSetOpacity(int))); connect(sliderOpacity, SIGNAL(valueChanged(int)), map, SLOT(emitSigCanvasUpdate())); connect(map, SIGNAL(sigScaleChanged(QPointF)), this, SLOT(slotScaleChanged(QPointF))); connect(toolSetMinScale, SIGNAL(toggled(bool)), this, SLOT(slotSetMinScale(bool))); connect(toolSetMaxScale, SIGNAL(toggled(bool)), this, SLOT(slotSetMaxScale(bool))); connect(checkPolygons, SIGNAL(toggled(bool)), mapfile, SLOT(slotSetShowPolygons(bool))); connect(checkPolylines, SIGNAL(toggled(bool)), mapfile, SLOT(slotSetShowPolylines(bool))); connect(checkPoints, SIGNAL(toggled(bool)), mapfile, SLOT(slotSetShowPOIs(bool))); connect(checkPolygons, SIGNAL(clicked()), map, SLOT(emitSigCanvasUpdate())); connect(checkPolylines, SIGNAL(clicked()), map, SLOT(emitSigCanvasUpdate())); connect(checkPoints, SIGNAL(clicked()), map, SLOT(emitSigCanvasUpdate())); connect(spinCacheSize, SIGNAL(valueChanged(int)), mapfile, SLOT(slotSetCacheSize(qint32))); connect(spinCacheExpiration, SIGNAL(valueChanged(int)), mapfile, SLOT(slotSetCacheExpiration(qint32))); if(mapfile->hasFeatureVectorItems()) { frameVectorItems->show(); } else { frameVectorItems->hide(); } if(mapfile->hasFeatureTileCache()) { frameTileCache->show(); } else { frameTileCache->hide(); } if(mapfile->hasFeatureLayers()) { frameLayers->show(); mapfile->getLayers(*listLayers); } else { frameLayers->hide(); } } CMapPropSetup::~CMapPropSetup() { } void CMapPropSetup::resizeEvent(QResizeEvent * e) { IMapProp::resizeEvent(e); updateScaleLabel(); } void CMapPropSetup::slotPropertiesChanged() { // block all signals to avoid retrigger sliderOpacity->blockSignals(true); toolSetMaxScale->blockSignals(true); toolSetMinScale->blockSignals(true); checkPolygons->blockSignals(true); checkPolylines->blockSignals(true); checkPoints->blockSignals(true); spinCacheSize->blockSignals(true); spinCacheExpiration->blockSignals(true); // opacity and visibility settings sliderOpacity->setValue(mapfile->getOpacity()); qreal minScale = mapfile->getMinScale(); toolSetMinScale->setChecked(minScale != NOFLOAT); qreal maxScale = mapfile->getMaxScale(); toolSetMaxScale->setChecked(maxScale != NOFLOAT); updateScaleLabel(); // vector maps properties checkPolygons->setChecked(mapfile->getShowPolygons()); checkPolylines->setChecked(mapfile->getShowPolylines()); checkPoints->setChecked(mapfile->getShowPOIs()); // streaming map properties QString lbl = mapfile->getCachePath(); labelCachePath->setText(lbl); labelCachePath->setToolTip(lbl); spinCacheSize->setValue(mapfile->getCacheSize()); spinCacheExpiration->setValue(mapfile->getCacheExpiration()); // unblock all signals sliderOpacity->blockSignals(false); toolSetMaxScale->blockSignals(false); toolSetMinScale->blockSignals(false); checkPolygons->blockSignals(false); checkPolylines->blockSignals(false); checkPoints->blockSignals(false); spinCacheSize->blockSignals(false); spinCacheExpiration->blockSignals(false); } void CMapPropSetup::slotScaleChanged(const QPointF& s) { scale = s; slotPropertiesChanged(); } void CMapPropSetup::slotSetMinScale(bool checked) { mapfile->setMinScale(checked ? scale.x() : NOFLOAT); slotPropertiesChanged(); } void CMapPropSetup::slotSetMaxScale(bool checked) { mapfile->setMaxScale(checked ? scale.x() : NOFLOAT); slotPropertiesChanged(); } #define BAR_HEIGHT 6 #define HOR_MARGIN 3 void CMapPropSetup::updateScaleLabel() { int w = labelScale->width(); int h = labelScale->height(); QPixmap pix(w,h); if(pix.isNull()) { return; } pix.fill(Qt::transparent); QPainter p(&pix); // draw bar background int xBar = HOR_MARGIN; int yBar = (h - BAR_HEIGHT) / 2; QRect bar(xBar, yBar, w-2*HOR_MARGIN, BAR_HEIGHT); p.setPen(Qt::darkBlue); p.setBrush(Qt::white); p.drawRect(bar); // draw current scale range qreal minScale = mapfile->getMinScale(); qreal maxScale = mapfile->getMaxScale(); if((minScale != NOFLOAT) || (maxScale != NOFLOAT)) { int x1Range = minScale != NOFLOAT ? HOR_MARGIN + qRound(bar.width() * (1 + log10(minScale)) / 5) : bar.left(); int x2Range = maxScale != NOFLOAT ? HOR_MARGIN + qRound(bar.width() * (1 + log10(maxScale)) / 5) : bar.right(); int yRange = yBar; QRect range(x1Range, yRange, x2Range - x1Range, BAR_HEIGHT); p.setPen(Qt::NoPen); p.setBrush(Qt::darkGreen); p.drawRect(range); } // draw scale indicator int xInd = HOR_MARGIN + qRound(bar.width() * (1 + log10(scale.x())) / 5) - 3; int yInd = yBar - 1; QRect ind(xInd, yInd, 5, BAR_HEIGHT + 2); p.setPen(Qt::darkBlue); p.setBrush(Qt::NoBrush); p.drawRect(ind); labelScale->setPixmap(pix); } qmapshack-1.5.1/src/map/CMapItem.h000644 001750 000144 00000005531 12622435723 017645 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPITEM_H #define CMAPITEM_H #include #include #include class IMap; class CMapDraw; class CMapPropSetup; class QSettings; class CMapItem : public QTreeWidgetItem { public: CMapItem(QTreeWidget * parent, CMapDraw *map); virtual ~CMapItem(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); /** @brief As the drawing thread is using the list widget to iterate of all maps to draw, all access has to be synchronized. */ static QMutex mutexActiveMaps; /** @brief Query if map objects are loaded @return True if the internal list of map objects is not empty. */ bool isActivated(); /** @brief Either loads or destroys internal map objects @return True if the internal list of maps is not empty after the operation. */ bool toggleActivate(); /** * @brief Load all internal map objects * @return Return true on success. */ bool activate(); /** @brief Delete all internal map objects */ void deactivate(); /** @brief Move item to top of list widget */ void moveToTop(); /** @brief Move item to bottom of active maps list */ void moveToBottom(); /** @brief Set item's icon according to map type and state */ void updateIcon(); /** @brief Show or hide child treewidget items @param yes set true to add children, false will remove all children and delete the attached widgets */ void showChildren(bool yes); private: friend class CMapDraw; CMapDraw * map; /** @brief A MD5 hash over the first 1024 bytes of the map file, to identify the map */ QString key; /** @brief List of map files forming that particular map */ QString filename; /** @brief List of loaded map objects when map is activated. */ QPointer mapfile; }; #endif //CMAPITEM_H qmapshack-1.5.1/src/map/CMapIMG.cpp000644 001750 000144 00000304732 12622435717 017726 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "helpers/CDraw.h" #include "helpers/CFileExt.h" #include "helpers/CProgressDialog.h" #include "helpers/Platform.h" #include "map/CMapDraw.h" #include "map/CMapIMG.h" #include "map/garmin/CGarminStrTbl6.h" #include "map/garmin/CGarminStrTbl8.h" #include "map/garmin/CGarminStrTblUtf8.h" #include "map/garmin/CGarminTyp.h" #include "units/IUnit.h" #include #undef DEBUG_SHOW_SECT_DESC #undef DEBUG_SHOW_TRE_DATA #undef DEBUG_SHOW_SUBDIV_DATA #undef DEBUG_SHOW_MAPLEVELS #undef DEBUG_SHOW_SECTION_BORDERS #undef DEBUG_SHOW_SUBDIV_BORDERS #define STREETNAME_THRESHOLD 5.0 int CFileExt::cnt = 0; static inline bool isCompletlyOutside(const QPolygonF& poly, const QRectF &viewport) { qreal north = -90.0 * DEG_TO_RAD; qreal south = 90.0 * DEG_TO_RAD; qreal west = 180.0 * DEG_TO_RAD; qreal east = -180.0 * DEG_TO_RAD; foreach(const QPointF &pt, poly) { if(north < pt.y()) { north = pt.y(); } if(south > pt.y()) { south = pt.y(); } if(west > pt.x()) { west = pt.x(); } if(east < pt.x()) { east = pt.x(); } } QRectF ref(west, north, east - west, south - north); if(ref.width() == 0) { ref.setWidth(0.00001); } if(ref.height() == 0) { ref.setHeight(0.00001); } if(viewport.intersects(ref)) { return false; } return true; } static inline QImage img2line(const QImage &img, int width) { Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied); QImage newImage(width, img.height(), QImage::Format_ARGB32_Premultiplied); const int bpl_src = img.bytesPerLine(); const int bpl_dst = newImage.bytesPerLine(); const uchar *_srcBits = img.bits(); uchar *_dstBits = newImage.bits(); for(int i = 0; i < img.height(); i++) { const uchar *srcBits = _srcBits + bpl_src * i; uchar *dstBits = _dstBits + bpl_dst * i; int bytesToCopy = bpl_dst; while(bytesToCopy > 0) { memcpy(dstBits, srcBits, qMin(bytesToCopy, bpl_src)); dstBits += bpl_src; bytesToCopy -= bpl_src; } } return newImage; } inline bool isCluttered(QVector& rectPois, const QRectF& rect) { // if(!CMainWindow::self().reducePoiIcons()) return false; QVector::const_iterator rectPoiEnd = rectPois.end(); QVector::const_iterator rectPoi = rectPois.begin(); while(rectPoi != rectPoiEnd) { if(rect.intersects(*rectPoi)) { return true; } rectPoi++; } rectPois << rect; return false; } CMapIMG::CMapIMG(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility|eFeatVectorItems,parent) , filename(filename) , fm(CMainWindow::self().getMapFont()) , selectedLanguage(NOIDX) { qDebug() << "------------------------------"; qDebug() << "IMG: try to open" << filename; try { readBasics(); processPrimaryMapData(); setupTyp(); } catch(const exce_t& e) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Failed ..."), e.msg, QMessageBox::Abort); return; } isActivated = true; } CMapIMG::~CMapIMG() { } void CMapIMG::setupTyp() { languages.clear(); languages[0x00] = tr("Unspecified"); languages[0x01] = tr("French"); languages[0x02] = tr("German"); languages[0x03] = tr("Dutch"); languages[0x04] = tr("English"); languages[0x05] = tr("Italian"); languages[0x06] = tr("Finnish"); languages[0x07] = tr("Swedish"); languages[0x08] = tr("Spanish"); languages[0x09] = tr("Basque"); languages[0x0a] = tr("Catalan"); languages[0x0b] = tr("Galician"); languages[0x0c] = tr("Welsh"); languages[0x0d] = tr("Gaelic"); languages[0x0e] = tr("Danish"); languages[0x0f] = tr("Norwegian"); languages[0x10] = tr("Portuguese"); languages[0x11] = tr("Slovak"); languages[0x12] = tr("Czech"); languages[0x13] = tr("Croatian"); languages[0x14] = tr("Hungarian"); languages[0x15] = tr("Polish"); languages[0x16] = tr("Turkish"); languages[0x17] = tr("Greek"); languages[0x18] = tr("Slovenian"); languages[0x19] = tr("Russian"); languages[0x1a] = tr("Estonian"); languages[0x1b] = tr("Latvian"); languages[0x1c] = tr("Romanian"); languages[0x1d] = tr("Albanian"); languages[0x1e] = tr("Bosnian"); languages[0x1f] = tr("Lithuanian"); languages[0x20] = tr("Serbian"); languages[0x21] = tr("Macedonian"); languages[0x22] = tr("Bulgarian"); polylineProperties.clear(); polylineProperties[0x01] = CGarminTyp::polyline_property(0x01, Qt::blue, 6, Qt::SolidLine ); polylineProperties[0x01].penBorderDay = QPen(Qt::black, 8, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x01].penBorderNight = QPen(Qt::lightGray, 8, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x01].hasBorder = true; polylineProperties[0x02] = CGarminTyp::polyline_property(0x02, "#cc9900", 4, Qt::SolidLine ); polylineProperties[0x02].penBorderDay = QPen(Qt::black, 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x02].penBorderNight = QPen(Qt::lightGray, 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x02].hasBorder = true; polylineProperties[0x03] = CGarminTyp::polyline_property(0x03, Qt::yellow, 3, Qt::SolidLine ); polylineProperties[0x03].penBorderDay = QPen(Qt::black, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x03].penBorderNight = QPen(Qt::lightGray, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x03].hasBorder = true; polylineProperties[0x04] = CGarminTyp::polyline_property(0x04, "#ffff00", 3, Qt::SolidLine ); polylineProperties[0x04].penBorderDay = QPen(Qt::black, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x04].penBorderNight = QPen(Qt::lightGray, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x04].hasBorder = true; polylineProperties[0x05] = CGarminTyp::polyline_property(0x05, "#dc7c5a", 2, Qt::SolidLine ); polylineProperties[0x06] = CGarminTyp::polyline_property(0x06, Qt::gray, 2, Qt::SolidLine ); polylineProperties[0x06].penBorderDay = QPen(Qt::black, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x06].penBorderNight = QPen(QColor("#f0f0f0"), 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x06].hasBorder = true; polylineProperties[0x07] = CGarminTyp::polyline_property(0x07, "#c46442", 1, Qt::SolidLine ); polylineProperties[0x08] = CGarminTyp::polyline_property(0x08, "#e88866", 2, Qt::SolidLine ); polylineProperties[0x09] = CGarminTyp::polyline_property(0x09, "#e88866", 2, Qt::SolidLine ); polylineProperties[0x0A] = CGarminTyp::polyline_property(0x0A, "#808080", 2, Qt::SolidLine ); polylineProperties[0x0B] = CGarminTyp::polyline_property(0x0B, "#c46442", 2, Qt::SolidLine ); polylineProperties[0x0C] = CGarminTyp::polyline_property(0x0C, "#000000", 2, Qt::SolidLine ); polylineProperties[0x14] = CGarminTyp::polyline_property(0x14, Qt::white, 2, Qt::DotLine ); polylineProperties[0x14].penBorderDay = QPen(Qt::black, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x14].penBorderNight = QPen(Qt::lightGray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); polylineProperties[0x14].hasBorder = true; polylineProperties[0x15] = CGarminTyp::polyline_property(0x15, "#000080", 2, Qt::SolidLine ); polylineProperties[0x16] = CGarminTyp::polyline_property(0x16, "#E0E0E0", 1, Qt::SolidLine ); polylineProperties[0x18] = CGarminTyp::polyline_property(0x18, "#0000ff", 2, Qt::SolidLine ); polylineProperties[0x19] = CGarminTyp::polyline_property(0x19, "#00ff00", 2, Qt::SolidLine ); polylineProperties[0x1A] = CGarminTyp::polyline_property(0x1A, "#000000", 2, Qt::SolidLine ); polylineProperties[0x1B] = CGarminTyp::polyline_property(0x1B, "#000000", 2, Qt::SolidLine ); polylineProperties[0x1C] = CGarminTyp::polyline_property(0x1C, "#00c864", 2, Qt::DotLine ); polylineProperties[0x1D] = CGarminTyp::polyline_property(0x1D, "#00c864", 2, Qt::DotLine ); polylineProperties[0x1E] = CGarminTyp::polyline_property(0x1E, "#00c864", 2, Qt::DotLine ); polylineProperties[0x1F] = CGarminTyp::polyline_property(0x1F, "#0000ff", 2, Qt::SolidLine ); polylineProperties[0x20] = CGarminTyp::polyline_property(0x20, "#b67824", 1, Qt::SolidLine ); polylineProperties[0x21] = CGarminTyp::polyline_property(0x21, "#b67824", 2, Qt::SolidLine ); polylineProperties[0x22] = CGarminTyp::polyline_property(0x22, "#b67824", 3, Qt::SolidLine ); polylineProperties[0x23] = CGarminTyp::polyline_property(0x23, "#b67824", 1, Qt::SolidLine ); polylineProperties[0x24] = CGarminTyp::polyline_property(0x24, "#b67824", 2, Qt::SolidLine ); polylineProperties[0x25] = CGarminTyp::polyline_property(0x25, "#b67824", 3, Qt::SolidLine ); polylineProperties[0x26] = CGarminTyp::polyline_property(0x26, "#0000ff", 2, Qt::DotLine ); polylineProperties[0x27] = CGarminTyp::polyline_property(0x27, "#c46442", 4, Qt::SolidLine ); polylineProperties[0x28] = CGarminTyp::polyline_property(0x28, "#aa0000", 2, Qt::SolidLine ); polylineProperties[0x29] = CGarminTyp::polyline_property(0x29, "#ff0000", 2, Qt::SolidLine ); polylineProperties[0x2A] = CGarminTyp::polyline_property(0x2A, "#000000", 2, Qt::SolidLine ); polylineProperties[0x2B] = CGarminTyp::polyline_property(0x2B, "#000000", 2, Qt::SolidLine ); polylineProperties[0x01].strings[0x00] = tr("Major highway"); polylineProperties[0x02].strings[0x00] = tr("Principal highway"); polylineProperties[0x03].strings[0x00] = tr("Other highway"); polylineProperties[0x04].strings[0x00] = tr("Arterial road"); polylineProperties[0x05].strings[0x00] = tr("Collector road"); polylineProperties[0x06].strings[0x00] = tr("Residential street"); polylineProperties[0x07].strings[0x00] = tr("Alley/Private road"); polylineProperties[0x08].strings[0x00] = tr("Highway ramp, low speed"); polylineProperties[0x09].strings[0x00] = tr("Highway ramp, high speed"); polylineProperties[0x0a].strings[0x00] = tr("Unpaved road"); polylineProperties[0x0b].strings[0x00] = tr("Major highway connector"); polylineProperties[0x0c].strings[0x00] = tr("Roundabout"); polylineProperties[0x14].strings[0x00] = tr("Railroad"); polylineProperties[0x15].strings[0x00] = tr("Shoreline"); polylineProperties[0x16].strings[0x00] = tr("Trail"); polylineProperties[0x18].strings[0x00] = tr("Stream"); polylineProperties[0x19].strings[0x00] = tr("Time zone"); polylineProperties[0x1a].strings[0x00] = tr("Ferry"); polylineProperties[0x1b].strings[0x00] = tr("Ferry"); polylineProperties[0x1c].strings[0x00] = tr("State/province border"); polylineProperties[0x1d].strings[0x00] = tr("County/parish border"); polylineProperties[0x1e].strings[0x00] = tr("International border"); polylineProperties[0x1f].strings[0x00] = tr("River"); polylineProperties[0x20].strings[0x00] = tr("Minor land contour"); polylineProperties[0x21].strings[0x00] = tr("Intermediate land contour"); polylineProperties[0x22].strings[0x00] = tr("Major land contour"); polylineProperties[0x23].strings[0x00] = tr("Minor depth contour"); polylineProperties[0x24].strings[0x00] = tr("Intermediate depth contour"); polylineProperties[0x25].strings[0x00] = tr("Major depth contour"); polylineProperties[0x26].strings[0x00] = tr("Intermittent stream"); polylineProperties[0x27].strings[0x00] = tr("Airport runway"); polylineProperties[0x28].strings[0x00] = tr("Pipeline"); polylineProperties[0x29].strings[0x00] = tr("Powerline"); polylineProperties[0x2a].strings[0x00] = tr("Marine boundary"); polylineProperties[0x2b].strings[0x00] = tr("Hazard boundary"); polygonProperties.clear(); polygonProperties[0x01] = CGarminTyp::polygon_property(0x01, Qt::NoPen, "#d2c0c0", Qt::SolidPattern); polygonProperties[0x02] = CGarminTyp::polygon_property(0x02, Qt::NoPen, "#fbeab7", Qt::SolidPattern); polygonProperties[0x03] = CGarminTyp::polygon_property(0x03, Qt::NoPen, "#a4b094", Qt::SolidPattern); polygonProperties[0x04] = CGarminTyp::polygon_property(0x04, Qt::NoPen, "#808080", Qt::SolidPattern); polygonProperties[0x05] = CGarminTyp::polygon_property(0x05, Qt::NoPen, "#f0f0f0", Qt::SolidPattern); polygonProperties[0x06] = CGarminTyp::polygon_property(0x06, Qt::NoPen, "#cacaca", Qt::SolidPattern); polygonProperties[0x07] = CGarminTyp::polygon_property(0x07, Qt::NoPen, "#feebcf", Qt::SolidPattern); polygonProperties[0x08] = CGarminTyp::polygon_property(0x08, Qt::NoPen, "#fde8d5", Qt::SolidPattern); polygonProperties[0x09] = CGarminTyp::polygon_property(0x09, Qt::NoPen, "#fee8b8", Qt::SolidPattern); polygonProperties[0x0a] = CGarminTyp::polygon_property(0x0a, Qt::NoPen, "#fdeac6", Qt::SolidPattern); polygonProperties[0x0b] = CGarminTyp::polygon_property(0x0b, Qt::NoPen, "#fddfbd", Qt::SolidPattern); polygonProperties[0x0c] = CGarminTyp::polygon_property(0x0c, Qt::NoPen, "#ebeada", Qt::SolidPattern); polygonProperties[0x0d] = CGarminTyp::polygon_property(0x0d, Qt::NoPen, "#f8e3be", Qt::SolidPattern); polygonProperties[0x0e] = CGarminTyp::polygon_property(0x0e, Qt::NoPen, "#e0e0e0", Qt::SolidPattern); polygonProperties[0x13] = CGarminTyp::polygon_property(0x13, Qt::NoPen, "#cc9900", Qt::SolidPattern); polygonProperties[0x14] = CGarminTyp::polygon_property(0x14, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x15] = CGarminTyp::polygon_property(0x15, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x16] = CGarminTyp::polygon_property(0x16, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x17] = CGarminTyp::polygon_property(0x17, Qt::NoPen, "#90be00", Qt::SolidPattern); polygonProperties[0x18] = CGarminTyp::polygon_property(0x18, Qt::NoPen, "#00ff00", Qt::SolidPattern); polygonProperties[0x19] = CGarminTyp::polygon_property(0x19, Qt::NoPen, "#f8e3be", Qt::SolidPattern); polygonProperties[0x1a] = CGarminTyp::polygon_property(0x1a, Qt::NoPen, "#d3f5a5", Qt::SolidPattern); polygonProperties[0x1e] = CGarminTyp::polygon_property(0x1e, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x1f] = CGarminTyp::polygon_property(0x1f, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x20] = CGarminTyp::polygon_property(0x20, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x28] = CGarminTyp::polygon_property(0x28, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x29] = CGarminTyp::polygon_property(0x29, Qt::NoPen, "#0000ff", Qt::SolidPattern); polygonProperties[0x32] = CGarminTyp::polygon_property(0x32, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x3b] = CGarminTyp::polygon_property(0x3b, Qt::NoPen, "#0000ff", Qt::SolidPattern); polygonProperties[0x3c] = CGarminTyp::polygon_property(0x3c, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x3d] = CGarminTyp::polygon_property(0x3d, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x3e] = CGarminTyp::polygon_property(0x3e, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x3f] = CGarminTyp::polygon_property(0x3f, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x40] = CGarminTyp::polygon_property(0x40, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x41] = CGarminTyp::polygon_property(0x41, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x42] = CGarminTyp::polygon_property(0x42, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x43] = CGarminTyp::polygon_property(0x43, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x44] = CGarminTyp::polygon_property(0x44, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x45] = CGarminTyp::polygon_property(0x45, Qt::NoPen, "#0000ff", Qt::SolidPattern); polygonProperties[0x46] = CGarminTyp::polygon_property(0x46, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x47] = CGarminTyp::polygon_property(0x47, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x48] = CGarminTyp::polygon_property(0x48, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x49] = CGarminTyp::polygon_property(0x49, Qt::NoPen, "#0080ff", Qt::SolidPattern); #ifdef WIN32 polygonProperties[0x4a] = CGarminTyp::polygon_property(0x4a, "#000000", qRgba(255,255,255,0), Qt::SolidPattern); polygonProperties[0x4b] = CGarminTyp::polygon_property(0x4b, "#000000", qRgba(255,255,255,0), Qt::SolidPattern); #else polygonProperties[0x4a] = CGarminTyp::polygon_property(0x4a, "#000000", Qt::transparent, Qt::NoBrush); polygonProperties[0x4b] = CGarminTyp::polygon_property(0x4b, "#000000", Qt::transparent, Qt::NoBrush); #endif polygonProperties[0x4c] = CGarminTyp::polygon_property(0x4c, Qt::NoPen, "#f0e68c", Qt::SolidPattern); polygonProperties[0x4d] = CGarminTyp::polygon_property(0x4d, Qt::NoPen, "#00ffff", Qt::SolidPattern); polygonProperties[0x4e] = CGarminTyp::polygon_property(0x4e, Qt::NoPen, "#d3f5a5", Qt::SolidPattern); polygonProperties[0x4f] = CGarminTyp::polygon_property(0x4f, Qt::NoPen, "#d3f5a5", Qt::SolidPattern); polygonProperties[0x50] = CGarminTyp::polygon_property(0x50, Qt::NoPen, "#b7e999", Qt::SolidPattern); polygonProperties[0x51] = CGarminTyp::polygon_property(0x51, Qt::NoPen, "#0000ff", Qt::DiagCrossPattern); polygonProperties[0x52] = CGarminTyp::polygon_property(0x52, Qt::NoPen, "#4aca4a", Qt::SolidPattern); polygonProperties[0x53] = CGarminTyp::polygon_property(0x53, Qt::NoPen, "#bcedfa", Qt::SolidPattern); polygonProperties[0x54] = CGarminTyp::polygon_property(0x54, Qt::NoPen, "#fde8d5", Qt::SolidPattern); polygonProperties[0x59] = CGarminTyp::polygon_property(0x59, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x69] = CGarminTyp::polygon_property(0x69, Qt::NoPen, "#0080ff", Qt::SolidPattern); polygonProperties[0x01].strings[0x00] = tr("Large urban area (>200K)"); polygonProperties[0x02].strings[0x00] = tr("Small urban area (<200K)"); polygonProperties[0x03].strings[0x00] = tr("Rural housing area"); polygonProperties[0x04].strings[0x00] = tr("Military base"); polygonProperties[0x05].strings[0x00] = tr("Parking lot"); polygonProperties[0x06].strings[0x00] = tr("Parking garage"); polygonProperties[0x07].strings[0x00] = tr("Airport"); polygonProperties[0x08].strings[0x00] = tr("Shopping center"); polygonProperties[0x09].strings[0x00] = tr("Marina"); polygonProperties[0x0a].strings[0x00] = tr("University/College"); polygonProperties[0x0b].strings[0x00] = tr("Hospital"); polygonProperties[0x0c].strings[0x00] = tr("Industrial complex"); polygonProperties[0x0d].strings[0x00] = tr("Reservation"); polygonProperties[0x0e].strings[0x00] = tr("Airport runway"); polygonProperties[0x13].strings[0x00] = tr("Man-made area"); polygonProperties[0x19].strings[0x00] = tr("Sports complex"); polygonProperties[0x18].strings[0x00] = tr("Golf course"); polygonProperties[0x1a].strings[0x00] = tr("Cemetery"); polygonProperties[0x14].strings[0x00] = tr("National park"); polygonProperties[0x15].strings[0x00] = tr("National park"); polygonProperties[0x16].strings[0x00] = tr("National park"); polygonProperties[0x17].strings[0x00] = tr("City park"); polygonProperties[0x1e].strings[0x00] = tr("State park"); polygonProperties[0x1f].strings[0x00] = tr("State park"); polygonProperties[0x20].strings[0x00] = tr("State park"); polygonProperties[0x50].strings[0x00] = tr("Forest"); polygonProperties[0x28].strings[0x00] = tr("Ocean"); polygonProperties[0x29].strings[0x00] = tr("Blue (unknown)"); polygonProperties[0x32].strings[0x00] = tr("Sea"); polygonProperties[0x3b].strings[0x00] = tr("Blue (unknown)"); polygonProperties[0x3c].strings[0x00] = tr("Large lake"); polygonProperties[0x3d].strings[0x00] = tr("Large lake"); polygonProperties[0x3e].strings[0x00] = tr("Medium lake"); polygonProperties[0x3f].strings[0x00] = tr("Medium lake"); polygonProperties[0x40].strings[0x00] = tr("Small lake"); polygonProperties[0x41].strings[0x00] = tr("Small lake"); polygonProperties[0x42].strings[0x00] = tr("Major lake"); polygonProperties[0x43].strings[0x00] = tr("Major lake"); polygonProperties[0x44].strings[0x00] = tr("Large lake"); polygonProperties[0x46].strings[0x00] = tr("Blue (unknown)"); polygonProperties[0x46].strings[0x00] = tr("Major River"); polygonProperties[0x47].strings[0x00] = tr("Large River"); polygonProperties[0x48].strings[0x00] = tr("Medium River"); polygonProperties[0x49].strings[0x00] = tr("Small River"); // polygonProperties[0x4a].strings[0x00] = tr("Definition area"); // polygonProperties[0x4b].strings[0x00] = tr("Background"); polygonProperties[0x4c].strings[0x00] = tr("Intermittent water"); polygonProperties[0x51].strings[0x00] = tr("Wetland/Swamp"); polygonProperties[0x4d].strings[0x00] = tr("Glacier"); polygonProperties[0x4e].strings[0x00] = tr("Orchard/Plantation"); polygonProperties[0x4f].strings[0x00] = tr("Scrub"); polygonProperties[0x52].strings[0x00] = tr("Tundra"); polygonProperties[0x53].strings[0x00] = tr("Flat"); polygonProperties[0x54].strings[0x00] = tr("???"); polygonDrawOrder.clear(); for(int i = 0; i < 0x80; i++) { polygonDrawOrder << (0x7F - i); } pointProperties.clear(); QMap::iterator subfile = subfiles.begin(); while(subfile != subfiles.end()) { if(!(*subfile).parts.contains("TYP")) { ++subfile; continue; } CFileExt file(filename); file.open(QIODevice::ReadOnly); QByteArray array; readFile(file, (*subfile).parts["TYP"].offset, (*subfile).parts["TYP"].size, array); CGarminTyp typ; typ.decode(array, polygonProperties, polylineProperties, polygonDrawOrder, pointProperties); file.close(); break; } } void CMapIMG::readFile(CFileExt& file, quint32 offset, quint32 size, QByteArray& data) { if(offset + size > file.size()) { throw exce_t(eErrOpen, tr("Failed to read: ") + filename); } data = QByteArray::fromRawData(file.data(offset, size), size); // wenn mask == 0 ist kein xor noetig if(mask == 0) { return; } #ifdef HOST_IS_64_BIT quint64 * p64 = (quint64*)data.data(); for(quint32 i = 0; i < size/8; ++i) { *p64++ ^= mask64; } quint32 rest = size % 8; quint8 * p = (quint8*)p64; #else quint32 * p32 = (quint32*)data.data(); for(quint32 i = 0; i < size/4; ++i) { *p32++ ^= mask32; } quint32 rest = size % 4; quint8 * p = (quint8*)p32; #endif for(quint32 i = 0; i < rest; ++i) { *p++ ^= mask; } } void CMapIMG::readBasics() { char tmpstr[64]; qint64 fsize = QFileInfo(filename).size(); CFileExt file(filename); if(!file.open(QIODevice::ReadOnly)) { throw exce_t(eErrOpen, tr("Failed to open: ") + filename); } mask = (quint8)*file.data(0,1); mask32 = mask; mask32 <<= 8; mask32 |= mask; mask32 <<= 8; mask32 |= mask; mask32 <<= 8; mask32 |= mask; mask64 = mask32; mask64 <<= 32; mask64 |= mask32; // read hdr_img_t QByteArray imghdr; readFile(file, 0, sizeof(hdr_img_t), imghdr); hdr_img_t * pImgHdr = (hdr_img_t*)imghdr.data(); if(strncmp(pImgHdr->signature,"DSKIMG",7) != 0) { throw exce_t(errFormat,tr("Bad file format: ") + filename); } if(strncmp(pImgHdr->identifier,"GARMIN",7) != 0) { throw exce_t(errFormat,tr("Bad file format: ") + filename); } mapdesc = QByteArray((const char*)pImgHdr->desc1,20); mapdesc += pImgHdr->desc2; qDebug() << mapdesc; size_t blocksize = pImgHdr->blocksize(); // 1st read FAT QByteArray FATblock; readFile(file, sizeof(hdr_img_t), sizeof(FATblock_t), FATblock); const FATblock_t * pFATBlock = (const FATblock_t * )FATblock.data(); size_t dataoffset = sizeof(hdr_img_t); // skip dummy blocks at the beginning while(dataoffset < (size_t)fsize) { if(pFATBlock->flag != 0x00) { break; } dataoffset += sizeof(FATblock_t); readFile(file, quint32(dataoffset), quint32(sizeof(FATblock_t)), FATblock); pFATBlock = (const FATblock_t * )FATblock.data(); } // start of new subfile part /* It is taken for granted that the single subfile parts are not fragmented within the file. Thus it is not really necessary to store and handle all block sequence numbers. Just the first one will give us the offset. This also implies that it is not necessary to care about FAT blocks with a non-zero part number. 2007-03-31: Garmin's world base map seems to be coded different. The part field seems to be rather a bit field than a part number. As the total subfile size is given for the first part only (for all others it's zero) I use it to identify the 1st part of a subfile 2007-05-26: Gmapsupp images by Sendmap code quite some bull shit, too. The size is stored in every part and they do have a part number. I introduced a set of subfile names storing the subfile's name and type. The first part with a size info and it's name / type not stored in the set is used to get the location information. */ QSet subfileNames; while(dataoffset < (size_t)fsize) { if(pFATBlock->flag != 0x01) { break; } memcpy(tmpstr,pFATBlock->name,sizeof(pFATBlock->name) + sizeof(pFATBlock->type)); tmpstr[sizeof(pFATBlock->name) + sizeof(pFATBlock->type)] = 0; if(gar_load(uint32_t, pFATBlock->size) != 0 && !subfileNames.contains(tmpstr) && tmpstr[0] != 0x20) { subfileNames << tmpstr; memcpy(tmpstr,pFATBlock->name,sizeof(pFATBlock->name)); tmpstr[sizeof(pFATBlock->name)] = 0; // skip MAPSORC.MPS section if(strcmp(tmpstr,"MAPSOURC") && strcmp(tmpstr,"SENDMAP2")) { subfile_desc_t& subfile = subfiles[tmpstr]; subfile.name = tmpstr; memcpy(tmpstr,pFATBlock->type,sizeof(pFATBlock->type)); tmpstr[sizeof(pFATBlock->type)] = 0; subfile_part_t& part = subfile.parts[tmpstr]; part.size = gar_load(uint32_t, pFATBlock->size); part.offset = quint32(gar_load(uint16_t, pFATBlock->blocks[0]) * blocksize); } } dataoffset += sizeof(FATblock_t); readFile(file, quint32(dataoffset), quint32(sizeof(FATblock_t)), FATblock); pFATBlock = (const FATblock_t * )FATblock.data(); } if((dataoffset == sizeof(hdr_img_t)) || (dataoffset >= (size_t)fsize)) { throw exce_t(errFormat,tr("Failed to read file structure: ") + filename); } // gmapsupp.img files do not have a data offset field if(gar_load(uint32_t, pImgHdr->dataoffset) == 0) { pImgHdr->dataoffset = gar_load(uint32_t, dataoffset); } // sometimes there are dummy blocks at the end of the FAT if(gar_load(uint32_t, pImgHdr->dataoffset) != dataoffset) { dataoffset = gar_load(uint32_t, pImgHdr->dataoffset); } #ifdef DEBUG_SHOW_SECT_DESC { QMap::const_iterator subfile = subfiles.begin(); while(subfile != subfiles.end()) { qDebug() << "--- subfile" << subfile->name << "---"; QMap::const_iterator part = subfile->parts.begin(); while(part != subfile->parts.end()) { qDebug() << part.key() << hex << part->offset << part->size; ++part; } ++subfile; } } #endif //DEBUG_SHOW_SECT_DESC int cnt = 1; int tot = subfiles.count(); PROGRESS_SETUP(tr("Loading %1").arg(QFileInfo(filename).fileName()), 0, tot, CMainWindow::getBestWidgetForParent()); maparea = QRectF(); QMap::iterator subfile = subfiles.begin(); while(subfile != subfiles.end()) { PROGRESS(cnt++, throw exce_t(errAbort,tr("User abort: ") + filename)); if((*subfile).parts.contains("GMP")) { throw exce_t(errFormat,tr("File is NT format. QMapShack is unable to read map files with NT format: ") + filename); } readSubfileBasics(*subfile, file); ++subfile; } // combine copyright sections copyright.clear(); foreach(const QString &str, copyrights) { if(!copyright.isEmpty()) { copyright += "\n"; } copyright += str; } qDebug() << "dimensions:\t" << "N" << (maparea.bottom()*RAD_TO_DEG) << "E" << (maparea.right()*RAD_TO_DEG) << "S" << (maparea.top()*RAD_TO_DEG) << "W" << (maparea.left()*RAD_TO_DEG); } void CMapIMG::readSubfileBasics(subfile_desc_t& subfile, CFileExt &file) { quint32 i; // test for mandatory subfile parts if(!(subfile.parts.contains("TRE") && subfile.parts.contains("RGN"))) { return; } QByteArray trehdr; readFile(file, subfile.parts["TRE"].offset, sizeof(hdr_tre_t), trehdr); hdr_tre_t * pTreHdr = (hdr_tre_t * )trehdr.data(); subfile.isTransparent = pTreHdr->POI_flags & 0x02; transparent = subfile.isTransparent ? true : transparent; #ifdef DEBUG_SHOW_TRE_DATA qDebug() << "+++" << subfile.name << "+++"; qDebug() << "TRE header length :" << gar_load(uint16_t, pTreHdr->length); qDebug() << "TRE1 offset :" << hex << gar_load(uint32_t, pTreHdr->tre1_offset); qDebug() << "TRE1 size :" << dec << gar_load(uint32_t, pTreHdr->tre1_size); qDebug() << "TRE2 offset :" << hex << gar_load(uint32_t, pTreHdr->tre2_offset); qDebug() << "TRE2 size :" << dec << gar_load(uint32_t, pTreHdr->tre2_size); #endif // DEBUG_SHOW_TRE_DATA copyrights << QString(file.data(subfile.parts["TRE"].offset + gar_load(uint16_t, pTreHdr->length),0x7FFF)); // read map boundaries from header qint32 i32; i32 = gar_ptr_load(int24_t, &pTreHdr->northbound); subfile.north = GARMIN_RAD(i32); i32 = gar_ptr_load(int24_t, &pTreHdr->eastbound); subfile.east = GARMIN_RAD(i32); i32 = gar_ptr_load(int24_t, &pTreHdr->southbound); subfile.south = GARMIN_RAD(i32); i32 = gar_ptr_load(int24_t, &pTreHdr->westbound); subfile.west = GARMIN_RAD(i32); if(subfile.east == subfile.west) { subfile.east = -subfile.east; } if(subfile.west > 0 && subfile.east < 0) { subfile.east = -subfile.east; } subfile.area = QRectF(QPointF(subfile.west, subfile.north), QPointF(subfile.east, subfile.south)); if(maparea.isNull()) { maparea = subfile.area; } else { maparea = maparea.united(subfile.area); } #ifdef DEBUG_SHOW_TRE_DATA qDebug() << "bounding area (\260)" << (subfile.north*RAD_TO_DEG) << (subfile.east*RAD_TO_DEG) << (subfile.south*RAD_TO_DEG) << (subfile.west*RAD_TO_DEG); qDebug() << "bounding area (rad)" << subfile.area; #endif // DEBUG_SHOW_TRE_DATA QByteArray maplevel; readFile(file, subfile.parts["TRE"].offset + gar_load(uint32_t, pTreHdr->tre1_offset), gar_load(uint32_t, pTreHdr->tre1_size), maplevel); const tre_map_level_t * pMapLevel = (const tre_map_level_t * )maplevel.data(); if(pTreHdr->flag & 0x80) { throw exce_t(errLock,tr("File contains locked / encypted data. Garmin does not " "want you to use this file with any other software than " "the one supplied by Garmin.")); } quint32 nlevels = gar_load(uint32_t, pTreHdr->tre1_size) / sizeof(tre_map_level_t); quint32 nsubdivs = 0; quint32 nsubdivs_last = 0; // count subsections for(i=0; ibits; subfile.maplevels << ml; nsubdivs += gar_load(uint16_t, pMapLevel->nsubdiv); nsubdivs_last = gar_load(uint16_t, pMapLevel->nsubdiv); #ifdef DEBUG_SHOW_MAPLEVEL_DATA qDebug() << "level" << TRE_MAP_LEVEL(pMapLevel) << "inherited" << TRE_MAP_INHER(pMapLevel) << "bits" << pMapLevel->bits << "#subdivs" << gar_load(uint16_t, pMapLevel->nsubdiv); #endif // DEBUG_SHOW_MAPLEVEL_DATA ++pMapLevel; } quint32 nsubdivs_next = nsubdivs - nsubdivs_last; ////////////////////////////////// // read subdivision information ////////////////////////////////// // point to first map level definition pMapLevel = (const tre_map_level_t * )maplevel.data(); // number of subdivisions per map level quint32 nsubdiv = gar_load(uint16_t, pMapLevel->nsubdiv); // point to first 16 byte subdivision definition entry QByteArray subdiv_n; readFile(file, subfile.parts["TRE"].offset + gar_load(uint32_t, pTreHdr->tre2_offset), gar_load(uint32_t, pTreHdr->tre2_size), subdiv_n); tre_subdiv_next_t * pSubDivN = (tre_subdiv_next_t*)subdiv_n.data(); QVector subdivs; subdivs.resize(nsubdivs); QVector::iterator subdiv = subdivs.begin(); QVector::iterator subdiv_prev = subdivs.end(); // absolute offset of RGN data QByteArray rgnhdr; readFile(file, subfile.parts["RGN"].offset, sizeof(hdr_rgn_t), rgnhdr); hdr_rgn_t * pRgnHdr = (hdr_rgn_t*)rgnhdr.data(); quint32 rgnoff = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->offset); quint32 rgnOffPolyg2 = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->offset_polyg2); quint32 rgnOffPolyl2 = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->offset_polyl2); quint32 rgnOffPoint2 = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->offset_point2); quint32 rgnLenPolyg2 = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->length_polyg2); quint32 rgnLenPolyl2 = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->length_polyl2); quint32 rgnLenPoint2 = /*subfile.parts["RGN"].offset +*/ gar_load(uint32_t, pRgnHdr->length_point2); // qDebug() << "***" << hex << subfile.parts["RGN"].offset << (subfile.parts["RGN"].offset + subfile.parts["RGN"].size); // qDebug() << "+++" << hex << rgnOffPolyg2 << (rgnOffPolyg2 + rgnLenPolyg2); // qDebug() << "+++" << hex << rgnOffPolyl2 << (rgnOffPolyl2 + rgnLenPolyl2); // qDebug() << "+++" << hex << rgnOffPoint2 << (rgnOffPoint2 + rgnLenPoint2); // parse all 16 byte subdivision entries for(i=0; in = i; subdiv->next = gar_load(uint16_t, pSubDivN->next); subdiv->terminate = TRE_SUBDIV_TERM(pSubDivN); subdiv->rgn_start = gar_ptr_load(uint24_t, &pSubDivN->rgn_offset); subdiv->rgn_start += rgnoff; // skip if this is the first entry if(subdiv_prev != subdivs.end()) { subdiv_prev->rgn_end = subdiv->rgn_start; } subdiv->hasPoints = pSubDivN->elements & 0x10; subdiv->hasIdxPoints = pSubDivN->elements & 0x20; subdiv->hasPolylines = pSubDivN->elements & 0x40; subdiv->hasPolygons = pSubDivN->elements & 0x80; // if all subdivisions of this level have been parsed, switch to the next one if(nsubdiv == 0) { ++pMapLevel; nsubdiv = gar_load(uint16_t, pMapLevel->nsubdiv); } subdiv->level = TRE_MAP_LEVEL(pMapLevel); subdiv->shift = 24 - pMapLevel->bits; cx = gar_ptr_load(uint24_t, &pSubDivN->center_lng); subdiv->iCenterLng = cx; cy = gar_ptr_load(uint24_t, &pSubDivN->center_lat); subdiv->iCenterLat = cy; width = TRE_SUBDIV_WIDTH(pSubDivN) << subdiv->shift; height = gar_load(uint16_t, pSubDivN->height) << subdiv->shift; subdiv->north = GARMIN_RAD(cy + height + 1); subdiv->south = GARMIN_RAD(cy - height); subdiv->east = GARMIN_RAD(cx + width + 1); subdiv->west = GARMIN_RAD(cx - width); subdiv->area = QRectF(QPointF(subdiv->west, subdiv->north), QPointF(subdiv->east, subdiv->south)); subdiv->offsetPoints2 = 0; subdiv->lengthPoints2 = 0; subdiv->offsetPolylines2 = 0; subdiv->lengthPolylines2 = 0; subdiv->offsetPolygons2 = 0; subdiv->lengthPolygons2 = 0; subdiv_prev = subdiv; ++pSubDivN; ++subdiv; } // switch to last map level ++pMapLevel; // witch pointer to 14 byte subdivision sections tre_subdiv_t* pSubDivL = pSubDivN; // parse all 14 byte subdivision entries of last map level for(; in = i; subdiv->next = 0; subdiv->terminate = TRE_SUBDIV_TERM(pSubDivL); subdiv->rgn_start = gar_ptr_load(uint24_t, &pSubDivL->rgn_offset); subdiv->rgn_start += rgnoff; subdiv_prev->rgn_end = subdiv->rgn_start; subdiv->hasPoints = pSubDivL->elements & 0x10; subdiv->hasIdxPoints = pSubDivL->elements & 0x20; subdiv->hasPolylines = pSubDivL->elements & 0x40; subdiv->hasPolygons = pSubDivL->elements & 0x80; subdiv->level = TRE_MAP_LEVEL(pMapLevel); subdiv->shift = 24 - pMapLevel->bits; cx = gar_ptr_load(uint24_t, &pSubDivL->center_lng); subdiv->iCenterLng = cx; cy = gar_ptr_load(uint24_t, &pSubDivL->center_lat); subdiv->iCenterLat = cy; width = TRE_SUBDIV_WIDTH(pSubDivL) << subdiv->shift; height = gar_load(uint16_t, pSubDivL->height) << subdiv->shift; subdiv->north = GARMIN_RAD(cy + height + 1); subdiv->south = GARMIN_RAD(cy - height); subdiv->east = GARMIN_RAD(cx + width + 1); subdiv->west = GARMIN_RAD(cx - width); subdiv->area = QRectF(QPointF(subdiv->west, subdiv->north), QPointF(subdiv->east, subdiv->south)); subdiv->offsetPoints2 = 0; subdiv->lengthPoints2 = 0; subdiv->offsetPolylines2 = 0; subdiv->lengthPolylines2 = 0; subdiv->offsetPolygons2 = 0; subdiv->lengthPolygons2 = 0; subdiv_prev = subdiv; ++pSubDivL; ++subdiv; } subdivs.last().rgn_end = gar_load(uint32_t, pRgnHdr->hdr_rgn_t::offset) + gar_load(uint32_t, pRgnHdr->hdr_rgn_t::length); // read extended NT elements if((gar_load(uint16_t, pTreHdr->hdr_subfile_part_t::length) >= 0x9A) && pTreHdr->tre7_size && (gar_load(uint16_t, pTreHdr->tre7_rec_size) >= sizeof(tre_subdiv2_t))) { rgnoff = subfile.parts["RGN"].offset; // qDebug() << subdivs.count() << (pTreHdr->tre7_size / pTreHdr->tre7_rec_size) << pTreHdr->tre7_rec_size; QByteArray subdiv2; readFile(file, subfile.parts["TRE"].offset + gar_load(uint32_t, pTreHdr->tre7_offset), gar_load(uint32_t, pTreHdr->tre7_size), subdiv2); tre_subdiv2_t * pSubDiv2 = (tre_subdiv2_t*)subdiv2.data(); // const quint32 entries1 = gar_load(uint32_t, pTreHdr->tre7_size) / gar_load(uint32_t, pTreHdr->tre7_rec_size); // const quint32 entries2 = subdivs.size(); bool skipPois = true; if(gar_load(uint16_t, pTreHdr->tre7_rec_size) == sizeof(tre_subdiv2_t)) { skipPois = false; } // for(int i = 0; i < pTreHdr->tre7_rec_size; ++i){ // if(i%4 == 0) fprintf(stderr,"\n"); // fprintf(stderr,"%02X ", ((quint8*)pSubDiv2)[i]); // } // fprintf(stderr,"\n"); subdiv = subdivs.begin(); subdiv_prev = subdivs.begin(); subdiv->offsetPolygons2 = gar_load(uint32_t, pSubDiv2->offsetPolygons) + rgnOffPolyg2; subdiv->offsetPolylines2 = gar_load(uint32_t, pSubDiv2->offsetPolyline) + rgnOffPolyl2; subdiv->offsetPoints2 = skipPois ? 0 : gar_load(uint32_t, pSubDiv2->offsetPoints) + rgnOffPoint2; ++subdiv; pSubDiv2 = reinterpret_cast((quint8*)pSubDiv2 + gar_endian(uint16_t, pTreHdr->tre7_rec_size)); while(subdiv != subdivs.end()) { // for(int i = 0; i < pTreHdr->tre7_rec_size; ++i){ // if(i%4 == 0) fprintf(stderr,"\n"); // fprintf(stderr,"%02X ", ((quint8*)pSubDiv2)[i]); // } // fprintf(stderr,"\n"); subdiv->offsetPolygons2 = gar_load(uint32_t, pSubDiv2->offsetPolygons) + rgnOffPolyg2; subdiv->offsetPolylines2 = gar_load(uint32_t, pSubDiv2->offsetPolyline) + rgnOffPolyl2; subdiv->offsetPoints2 = skipPois ? 0 : gar_load(uint32_t, pSubDiv2->offsetPoints) + rgnOffPoint2; subdiv_prev->lengthPolygons2 = subdiv->offsetPolygons2 - subdiv_prev->offsetPolygons2; subdiv_prev->lengthPolylines2 = subdiv->offsetPolylines2 - subdiv_prev->offsetPolylines2; subdiv_prev->lengthPoints2 = skipPois ? 0 : subdiv->offsetPoints2 - subdiv_prev->offsetPoints2; subdiv_prev = subdiv; ++subdiv; pSubDiv2 = reinterpret_cast((quint8*)pSubDiv2 + gar_endian(uint16_t, pTreHdr->tre7_rec_size)); } subdiv_prev->lengthPolygons2 = rgnOffPolyg2 + rgnLenPolyg2 - subdiv_prev->offsetPolygons2; subdiv_prev->lengthPolylines2 = rgnOffPolyl2 + rgnLenPolyl2 - subdiv_prev->offsetPolylines2; subdiv_prev->lengthPoints2 = skipPois ? 0 : rgnOffPoint2 + rgnLenPoint2 - subdiv_prev->offsetPoints2; } subfile.subdivs = subdivs; #ifdef DEBUG_SHOW_SUBDIV_DATA { QVector::iterator subdiv = subfile.subdivs.begin(); while(subdiv != subfile.subdivs.end()) { qDebug() << "--- subdiv" << subdiv->n << "---"; qDebug() << "RGN start " << hex << subdiv->rgn_start; qDebug() << "RGN end " << hex << subdiv->rgn_end; qDebug() << "center lng " << GARMIN_DEG(subdiv->iCenterLng); qDebug() << "center lat " << GARMIN_DEG(subdiv->iCenterLat); qDebug() << "has points " << subdiv->hasPoints; qDebug() << "has indexed points " << subdiv->hasIdxPoints; qDebug() << "has polylines " << subdiv->hasPolylines; qDebug() << "has polygons " << subdiv->hasPolygons; qDebug() << "bounding area (m) " << subdiv->area.topLeft() << subdiv->area.bottomRight(); qDebug() << "map level " << subdiv->level; qDebug() << "left shifts " << subdiv->shift; qDebug() << "polyg off. " << hex << subdiv->offsetPolygons2; qDebug() << "polyg len. " << hex << subdiv->lengthPolygons2; qDebug() << "polyl off. " << hex << subdiv->offsetPolylines2; qDebug() << "polyl len. " << hex << subdiv->lengthPolylines2; qDebug() << "point off. " << hex << subdiv->offsetPoints2; qDebug() << "point len. " << hex << subdiv->lengthPoints2; ++subdiv; } } #endif // DEBUG_SHOW_SUBDIV_DATA // qDebug() << "***" << hex << subfile.parts["RGN"].offset << (subfile.parts["RGN"].offset + subfile.parts["RGN"].size); // qDebug() << "+++" << hex << rgnOffPolyg2 << (rgnOffPolyg2 + pRgnHdr->length_polyg2); // qDebug() << "+++" << hex << rgnOffPolyl2 << (rgnOffPolyl2 + pRgnHdr->length_polyl2); // qDebug() << "+++" << hex << rgnOffPoint2 << (rgnOffPoint2 + pRgnHdr->length_point2); if(subfile.parts.contains("LBL")) { QByteArray lblhdr; readFile(file, subfile.parts["LBL"].offset, sizeof(hdr_lbl_t), lblhdr); hdr_lbl_t * pLblHdr = (hdr_lbl_t*)lblhdr.data(); quint32 offsetLbl1 = subfile.parts["LBL"].offset + gar_load(uint32_t, pLblHdr->lbl1_offset); quint32 offsetLbl6 = subfile.parts["LBL"].offset + gar_load(uint32_t, pLblHdr->lbl6_offset); QByteArray nethdr; quint32 offsetNet1 = 0; hdr_net_t * pNetHdr = 0; if(subfile.parts.contains("NET")) { readFile(file, subfile.parts["NET"].offset, sizeof(hdr_net_t), nethdr); pNetHdr = (hdr_net_t*)nethdr.data(); offsetNet1 = subfile.parts["NET"].offset + gar_load(uint32_t, pNetHdr->net1_offset); } quint16 codepage = 0; if(gar_load(uint16_t, pLblHdr->length) > 0xAA) { codepage = gar_load(uint16_t, pLblHdr->codepage); } // qDebug() << file.fileName() << hex << offsetLbl1 << offsetLbl6 << offsetNet1; switch(pLblHdr->coding) { case 0x06: subfile.strtbl = new CGarminStrTbl6(codepage, mask, this); subfile.strtbl->registerLBL1(offsetLbl1, gar_load(uint32_t, pLblHdr->lbl1_length), pLblHdr->addr_shift); subfile.strtbl->registerLBL6(offsetLbl6, gar_load(uint32_t, pLblHdr->lbl6_length)); if(pNetHdr) { subfile.strtbl->registerNET1(offsetNet1, gar_load(uint32_t, pNetHdr->net1_length), pNetHdr->net1_addr_shift); } break; case 0x09: subfile.strtbl = new CGarminStrTbl8(codepage, mask, this); subfile.strtbl->registerLBL1(offsetLbl1, gar_load(uint32_t, pLblHdr->lbl1_length), pLblHdr->addr_shift); subfile.strtbl->registerLBL6(offsetLbl6, gar_load(uint32_t, pLblHdr->lbl6_length)); if(pNetHdr) { subfile.strtbl->registerNET1(offsetNet1, gar_load(uint32_t, pNetHdr->net1_length), pNetHdr->net1_addr_shift); } break; case 0x0A: subfile.strtbl = new CGarminStrTblUtf8(codepage, mask, this); subfile.strtbl->registerLBL1(offsetLbl1, gar_load(uint32_t, pLblHdr->lbl1_length), pLblHdr->addr_shift); subfile.strtbl->registerLBL6(offsetLbl6, gar_load(uint32_t, pLblHdr->lbl6_length)); if(pNetHdr) { subfile.strtbl->registerNET1(offsetNet1, gar_load(uint32_t, pNetHdr->net1_length), pNetHdr->net1_addr_shift); } break; default: ; qWarning() << "Unknown label coding" << hex << pLblHdr->coding; } } } void CMapIMG::processPrimaryMapData() { QMap::const_iterator subfile = subfiles.begin(); /* * Query all subfiles for possible maplevels. * Exclude basemap to avoid pollution. */ while (subfile != subfiles.end()) { QVector::const_iterator maplevel = subfile->maplevels.begin(); while (maplevel != subfile->maplevels.end()) { if (!maplevel->inherited) { map_level_t ml; ml.bits = maplevel->bits; ml.level = maplevel->level; ml.useBaseMap = false; maplevels << ml; } ++maplevel; } ++subfile; } /* Sort all entries, note that stable sort should insure that basemap is preferred when available. */ qStableSort(maplevels.begin(), maplevels.end(), map_level_t::GreaterThan); /* Delete any duplicates for obvious performance reasons. */ QVector::iterator where; where = std::unique(maplevels.begin(), maplevels.end()); maplevels.erase(where, maplevels.end()); #ifdef DEBUG_SHOW_MAPLEVELS for(int i=0; i < maplevels.count(); ++i) { map_level_t& ml = maplevels[i]; qDebug() << ml.bits << ml.level << ml.useBaseMap; } #endif } quint8 CMapIMG::scale2bits(const QPointF& scale) { if(scale.x() >= 70000.0) { return 2; } if(scale.x() >= 50000.0) { return 3; } if(scale.x() >= 30000.0) { return 4; } if(scale.x() >= 20000.0) { return 5; } if(scale.x() >= 15000.0) { return 6; } if(scale.x() >= 10000.0) { return 7; } if(scale.x() >= 7000.0) { return 8; } if(scale.x() >= 5000.0) { return 9; } if(scale.x() >= 3000.0) { return 10; } if(scale.x() >= 2000.0) { return 11; } if(scale.x() >= 1500.0) { return 12; } if(scale.x() >= 1000.0) { return 13; } if(scale.x() >= 700.0) { return 14; } if(scale.x() >= 500.0) { return 15; } if(scale.x() >= 300.0) { return 16; } if(scale.x() >= 200.0) { return 17; } if(scale.x() >= 100.0) { return 18; } if(scale.x() >= 70.0) { return 19; } if(scale.x() >= 30.0) { return 20; } if(scale.x() >= 15.0) { return 21; } if(scale.x() >= 7.0) { return 22; } if(scale.x() >= 3.0) { return 23; } return 24; } void CMapIMG::draw(IDrawContext::buffer_t& buf) { if(map->needsRedraw()) { return; } QPointF bufferScale = buf.scale * buf.zoomFactor; if(isOutOfScale(bufferScale)) { return; } QPainter p(&buf.image); p.setOpacity(getOpacity()/100.0); USE_ANTI_ALIASING(p,true); QFont f = CMainWindow::self().getMapFont(); fm = QFontMetrics(f); p.setFont(f); p.setPen(Qt::black); p.setBrush(Qt::NoBrush); quint8 bits = scale2bits(bufferScale); // if((zoomidx >= 25) && (detailsFineTune < 0)) // { // bits += detailsFineTune + (zoomidx - 25); // } // else // { // bits += detailsFineTune; // } QVector::const_iterator maplevel = maplevels.end(); do { --maplevel; if(bits >= maplevel->bits) { break; } } while(maplevel != maplevels.begin()); QPointF pt1 = buf.ref1; QPointF pt2 = buf.ref2; QPointF pt3 = buf.ref3; QPointF pt4 = buf.ref4; qreal u1 = pt1.x() < pt4.x() ? pt1.x() : pt4.x(); qreal u2 = pt2.x() > pt3.x() ? pt2.x() : pt3.x(); qreal v1 = pt1.y() > pt2.y() ? pt1.y() : pt2.y(); qreal v2 = pt4.y() < pt3.y() ? pt4.y() : pt3.y(); QRectF viewport(u1,v1, u2 - u1, v2 - v1); QVector rectPois; polygons.clear(); polylines.clear(); pois.clear(); points.clear(); labels.clear(); /** convertRad2Px() converts positions into screen coordinates. However the painter devices paints into the buffer which is a little bit larger than the screen. Thus we need the offset of the buffer's top left corner to the top left corner of the screen to adjust all drawings. */ QPointF pp = buf.ref1; map->convertRad2Px(pp); p.save(); p.translate(-pp); if(map->needsRedraw()) { p.restore(); return; } loadVisibleData(false, polygons, polylines, points, pois, maplevel->level, viewport, p); if(map->needsRedraw()) { p.restore(); return; } drawPolygons(p, polygons); if(map->needsRedraw()) { p.restore(); return; } drawPolylines(p, polylines, bufferScale); if(map->needsRedraw()) { p.restore(); return; } drawPoints(p, points, rectPois); if(map->needsRedraw()) { p.restore(); return; } drawPois(p, pois, rectPois); if(map->needsRedraw()) { p.restore(); return; } drawText(p); if(map->needsRedraw()) { p.restore(); return; } drawLabels(p, labels); p.restore(); } void CMapIMG::loadVisibleData(bool fast, polytype_t& polygons, polytype_t& polylines, pointtype_t& points, pointtype_t& pois, unsigned level, const QRectF& viewport, QPainter& p) { #ifndef Q_OS_WIN32 CFileExt file(filename); if(!file.open(QIODevice::ReadOnly)) { return; } #endif QMap::const_iterator subfile = subfiles.constBegin(); while(subfile != subfiles.constEnd()) { // qDebug() << "-------"; // qDebug() << (viewport.topLeft() * RAD_TO_DEG) << (viewport.bottomRight() * RAD_TO_DEG); // qDebug() << (subfile->area.topLeft() * RAD_TO_DEG) << (subfile->area.bottomRight() * RAD_TO_DEG); // qDebug() << subfile->area.intersects(viewport); if(!subfile->area.intersects(viewport)) { ++subfile; continue; } if(map->needsRedraw()) { break; } #ifdef Q_OS_WIN32 CFileExt file(filename); if(!file.open(QIODevice::ReadOnly)) { return; } #endif QByteArray rgndata; readFile(file, subfile->parts["RGN"].offset, subfile->parts["RGN"].size, rgndata); // qDebug() << "rgn range" << hex << subfile->parts["RGN"].offset << (subfile->parts["RGN"].offset + subfile->parts["RGN"].size); const QVector& subdivs = subfile->subdivs; // collect polylines QVector::const_iterator subdiv = subdivs.constBegin(); while(subdiv != subdivs.constEnd()) { // if(subdiv->level == level) qDebug() << "subdiv:" << subdiv->level << level << subdiv->area << viewport << subdiv->area.intersects(viewport); if(subdiv->level != level || !subdiv->area.intersects(viewport)) { ++subdiv; continue; } if(map->needsRedraw()) { break; } loadSubDiv(file, *subdiv, subfile->strtbl, rgndata, fast, viewport, polylines, polygons, points, pois); #ifdef DEBUG_SHOW_SECTION_BORDERS const QRectF& a = subdiv->area; qreal u[2] = {a.left(), a.right()}; qreal v[2] = {a.top(), a.bottom()}; QPolygonF poly; poly << a.bottomLeft() << a.bottomRight() << a.topRight() << a.topLeft(); map->convertRad2Px(poly); p.setPen(QPen(Qt::magenta,2)); p.setBrush(Qt::NoBrush); p.drawPolygon(poly); #endif // DEBUG_SHOW_SECTION_BORDERS ++subdiv; } #ifdef DEBUG_SHOW_SUBDIV_BORDERS QPointF p1 = subfile->area.bottomLeft(); QPointF p2 = subfile->area.bottomRight(); QPointF p3 = subfile->area.topRight(); QPointF p4 = subfile->area.topLeft(); map->convertRad2Px(p1); map->convertRad2Px(p2); map->convertRad2Px(p3); map->convertRad2Px(p4); QPolygonF poly; poly << p1 << p2 << p3 << p4; p.setPen(Qt::black); p.drawPolygon(poly); #endif // DEBUG_SHOW_SUBDIV_BORDERS ++subfile; #ifdef Q_OS_WIN32 file.close(); #else file.free(); #endif } #ifndef Q_OS_WIN32 file.close(); #endif } void CMapIMG::loadSubDiv(CFileExt &file, const subdiv_desc_t& subdiv, IGarminStrTbl * strtbl, const QByteArray& rgndata, bool fast, const QRectF& viewport, polytype_t& polylines, polytype_t& polygons, pointtype_t& points, pointtype_t& pois) { if(subdiv.rgn_start == subdiv.rgn_end && !subdiv.lengthPolygons2 && !subdiv.lengthPolylines2 && !subdiv.lengthPoints2) { return; } //fprintf(stderr, "loadSubDiv\n"); // qDebug() << "---------" << file.fileName() << "---------"; const quint8 * pRawData = (quint8*)rgndata.data(); quint32 opnt = 0, oidx = 0, opline = 0, opgon = 0; quint32 objCnt = subdiv.hasIdxPoints + subdiv.hasPoints + subdiv.hasPolylines + subdiv.hasPolygons; quint16 * pOffset = (quint16*)(pRawData + subdiv.rgn_start); // test for points if(subdiv.hasPoints) { opnt = (objCnt - 1) * sizeof(quint16) + subdiv.rgn_start; } // test for indexed points if(subdiv.hasIdxPoints) { if(opnt) { oidx = gar_load(uint16_t, *pOffset); oidx += subdiv.rgn_start; ++pOffset; } else { oidx = (objCnt - 1) * sizeof(quint16) + subdiv.rgn_start; } } // test for polylines if(subdiv.hasPolylines) { if(opnt || oidx) { opline = gar_load(uint16_t, *pOffset); opline += subdiv.rgn_start; ++pOffset; } else { opline = (objCnt - 1) * sizeof(quint16) + subdiv.rgn_start; } } // test for polygons if(subdiv.hasPolygons) { if(opnt || oidx || opline) { opgon = gar_load(uint16_t, *pOffset); opgon += subdiv.rgn_start; ++pOffset; } else { opgon = (objCnt - 1) * sizeof(quint16) + subdiv.rgn_start; } } #ifdef DEBUG_SHOW_POLY_DATA qDebug() << "--- Subdivision" << subdiv.n << "---"; qDebug() << "adress:" << hex << subdiv.rgn_start << "- " << subdiv.rgn_end; qDebug() << "points: " << hex << opnt; qDebug() << "indexed points: " << hex << oidx; qDebug() << "polylines: " << hex << opline; qDebug() << "polygons: " << hex << opgon; #endif // DEBUG_SHOW_POLY_DATA const quint8 * pData; const quint8 * pEnd; CGarminPolygon p; // decode points if(subdiv.hasPoints && !fast && getShowPOIs()) { pData = pRawData + opnt; pEnd = pRawData + (oidx ? oidx : opline ? opline : opgon ? opgon : subdiv.rgn_end); while(pData < pEnd) { CGarminPoint p; pData += p.decode(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, pData); // skip points outside our current viewport if(!viewport.contains(p.pos)) { continue; } if(strtbl) { p.isLbl6 ? strtbl->get(file, p.lbl_ptr, IGarminStrTbl::poi, p.labels) : strtbl->get(file, p.lbl_ptr, IGarminStrTbl::norm, p.labels); } points.push_back(p); } } // decode indexed points if(subdiv.hasIdxPoints && !fast && getShowPOIs()) { pData = pRawData + oidx; pEnd = pRawData + (opline ? opline : opgon ? opgon : subdiv.rgn_end); while(pData < pEnd) { CGarminPoint p; pData += p.decode(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, pData); // skip points outside our current viewport if(!viewport.contains(p.pos)) { continue; } if(strtbl) { p.isLbl6 ? strtbl->get(file, p.lbl_ptr, IGarminStrTbl::poi, p.labels) : strtbl->get(file, p.lbl_ptr, IGarminStrTbl::norm, p.labels); } pois.push_back(p); } } // decode polylines if(subdiv.hasPolylines && !fast && getShowPolylines()) { CGarminPolygon::cnt = 0; pData = pRawData + opline; pEnd = pRawData + (opgon ? opgon : subdiv.rgn_end); while(pData < pEnd) { pData += p.decode(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, true, pData, pEnd); // skip points outside our current viewport if(isCompletlyOutside(p.pixel, viewport)) { continue; } if(strtbl && !p.lbl_in_NET && p.lbl_info) { strtbl->get(file, p.lbl_info,IGarminStrTbl::norm, p.labels); } else if(strtbl && p.lbl_in_NET && p.lbl_info) { strtbl->get(file, p.lbl_info,IGarminStrTbl::net, p.labels); } polylines.push_back(p); } } // decode polygons if(subdiv.hasPolygons && getShowPolygons()) { CGarminPolygon::cnt = 0; pData = pRawData + opgon; pEnd = pRawData + subdiv.rgn_end; while(pData < pEnd) { pData += p.decode(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, false, pData, pEnd); // skip points outside our current viewport if(isCompletlyOutside(p.pixel, viewport)) { continue; } if(strtbl && !p.lbl_in_NET && p.lbl_info && !fast) { strtbl->get(file, p.lbl_info,IGarminStrTbl::norm, p.labels); } else if(strtbl && p.lbl_in_NET && p.lbl_info && !fast) { strtbl->get(file, p.lbl_info,IGarminStrTbl::net, p.labels); } polygons.push_back(p); } } // qDebug() << "--- Subdivision" << subdiv.n << "---"; // qDebug() << "adress:" << hex << subdiv.rgn_start << "- " << subdiv.rgn_end; // qDebug() << "polyg off: " << hex << subdiv.offsetPolygons2; // qDebug() << "polyg len: " << hex << subdiv.lengthPolygons2 << dec << subdiv.lengthPolygons2; // qDebug() << "polyg end: " << hex << subdiv.lengthPolygons2 + subdiv.offsetPolygons2; // qDebug() << "polyl off: " << hex << subdiv.offsetPolylines2; // qDebug() << "polyl len: " << hex << subdiv.lengthPolylines2 << dec << subdiv.lengthPolylines2; // qDebug() << "polyl end: " << hex << subdiv.lengthPolylines2 + subdiv.offsetPolylines2; // qDebug() << "point off: " << hex << subdiv.offsetPoints2; // qDebug() << "point len: " << hex << subdiv.lengthPoints2 << dec << subdiv.lengthPoints2; // qDebug() << "point end: " << hex << subdiv.lengthPoints2 + subdiv.offsetPoints2; if(subdiv.lengthPolygons2 && getShowPolygons()) { pData = pRawData + subdiv.offsetPolygons2; pEnd = pData + subdiv.lengthPolygons2; while(pData < pEnd) { // qDebug() << "rgn offset:" << hex << (rgnoff + (pData - pRawData)); pData += p.decode2(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, false, pData, pEnd); // skip points outside our current viewport if(isCompletlyOutside(p.pixel, viewport)) { continue; } if(strtbl && !p.lbl_in_NET && p.lbl_info && !fast) { strtbl->get(file, p.lbl_info,IGarminStrTbl::norm, p.labels); } polygons.push_back(p); } } if(subdiv.lengthPolylines2 && !fast && getShowPolylines()) { pData = pRawData + subdiv.offsetPolylines2; pEnd = pData + subdiv.lengthPolylines2; while(pData < pEnd) { // qDebug() << "rgn offset:" << hex << (rgnoff + (pData - pRawData)); pData += p.decode2(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, true, pData, pEnd); // skip points outside our current viewport if(isCompletlyOutside(p.pixel, viewport)) { continue; } if(strtbl && !p.lbl_in_NET && p.lbl_info) { strtbl->get(file, p.lbl_info,IGarminStrTbl::norm, p.labels); } polylines.push_back(p); } } if(subdiv.lengthPoints2 && !fast && getShowPOIs()) { pData = pRawData + subdiv.offsetPoints2; pEnd = pData + subdiv.lengthPoints2; while(pData < pEnd) { CGarminPoint p; // qDebug() << "rgn offset:" << hex << (rgnoff + (pData - pRawData)); pData += p.decode2(subdiv.iCenterLng, subdiv.iCenterLat, subdiv.shift, pData, pEnd); // skip points outside our current viewport if(!viewport.contains(p.pos)) { continue; } if(strtbl) { p.isLbl6 ? strtbl->get(file, p.lbl_ptr, IGarminStrTbl::poi, p.labels) : strtbl->get(file, p.lbl_ptr, IGarminStrTbl::norm, p.labels); } pois.push_back(p); } } } void CMapIMG::drawPolygons(QPainter& p, polytype_t& lines) { quint32 type; int n; const int N = polygonDrawOrder.size(); for(n = 0; n < N; ++n) { type = polygonDrawOrder[(N-1) - n]; p.setPen(polygonProperties[type].pen); p.setBrush(CMainWindow::self().isNight() ? polygonProperties[type].brushNight : polygonProperties[type].brushDay); polytype_t::iterator item = lines.begin(); while (item != lines.end()) { if(item->type != type) { ++item; continue; } QPolygonF& poly = item->pixel; map->convertRad2Px(poly); // simplifyPolyline(line); p.drawPolygon(poly); if(!polygonProperties[type].known) { qDebug() << "unknown polygon" << hex << type; } ++item; } } } void CMapIMG::drawPolylines(QPainter& p, polytype_t& lines, const QPointF& scale) { textpaths.clear(); QFont font = CMainWindow::self().getMapFont(); font.setPixelSize(12); font.setBold(false); QFontMetricsF metrics(font); QVector lengths; lengths.reserve(100); int pixmapCount = 0; int borderCount = 0; int normalCount = 0; int imageCount = 0; // int deletedCount = 0; QHash > dict; for(int i = 0; i < lines.count(); ++i) { dict[lines[i].type].push_back(i); } QMap::iterator props = polylineProperties.begin(); QMap::iterator end = polylineProperties.end(); for(; props != end; ++props) { const quint32 &type = props.key(); const CGarminTyp::polyline_property& property = props.value(); if(dict[type].count() == 0) { continue; } if(property.hasPixmap) { const QImage &pixmap = CMainWindow::self().isNight() ? property.imgNight : property.imgDay; const qreal h = pixmap.height(); QList::const_iterator it = dict[type].constBegin(); for(; it != dict[type].constEnd(); ++it) { CGarminPolygon &item = lines[*it]; { pixmapCount++; QPolygonF& poly = item.pixel; int size = poly.size(); if(size < 2) { continue; } map->convertRad2Px(poly); int i; lengths.resize(0); // deletedCount += line.size(); // simplifyPolyline(line); // deletedCount -= line.size(); // size = line.size(); lengths.reserve(size); qreal u1, u2, v1, v2; QPainterPath path; qreal segLength, totalLength = 0; u1 = poly[0].x(); v1 = poly[0].y(); for(i = 1; i < size; ++i) { u2 = poly[i].x(); v2 = poly[i].y(); segLength = qSqrt((u2 - u1) * (u2 - u1) + (v2 - v1) * (v2 - v1)); totalLength += segLength; lengths << segLength; u1 = u2; v1 = v2; } if (scale.x() < STREETNAME_THRESHOLD && property.labelType != CGarminTyp::eNone) { collectText(item, poly, font, metrics, h); } path.addPolygon(poly); const int nLength = lengths.count(); qreal curLength = 0; QPointF p2 = path.pointAtPercent(curLength / totalLength); for(int i = 0; i < nLength; ++i) { segLength = lengths.at(i); // qDebug() << curLength << totalLength << curLength / totalLength; QPointF p1 = p2; p2 = path.pointAtPercent((curLength + segLength) / totalLength); qreal angle = qAtan((p2.y() - p1.y()) / (p2.x() - p1.x())) * 180 / M_PI; if(p2.x() - p1.x() < 0) { angle += 180; } p.save(); p.translate(p1); p.rotate(angle); p.drawImage(0,-h/2, img2line(pixmap, segLength)); imageCount++; p.restore(); curLength += segLength; } } } } else { if(property.hasBorder) { // draw background line 1st p.setPen(CMainWindow::self().isNight() ? property.penBorderNight : property.penBorderDay); QList::const_iterator it = dict[type].constBegin(); for(; it != dict[type].constEnd(); ++it) { borderCount++; drawLine(p, lines[*it], property, metrics, font, scale); } // draw foreground line in a second run for nicer borders } else { p.setPen(CMainWindow::self().isNight() ? property.penLineNight : property.penLineDay); QList::const_iterator it = dict[type].constBegin(); for(; it != dict[type].constEnd(); ++it) { normalCount++; drawLine(p, lines[*it], property, metrics, font, scale); } } } } // 2nd run to draw foreground lines. props = polylineProperties.begin(); for(; props != end; ++props) { const quint32 &type = props.key(); const CGarminTyp::polyline_property& property = props.value(); if(dict[type].count() == 0) { continue; } if(property.hasBorder && !property.hasPixmap) { // draw foreground line 2nd p.setPen(CMainWindow::self().isNight() ? property.penLineNight : property.penLineDay); QList::const_iterator it = dict[type].constBegin(); for(; it != dict[type].constEnd(); ++it) { drawLine(p, lines[*it]); } } } // qDebug() << "pixmapCount:" << pixmapCount // << "borderCount:" << borderCount // << "normalCount:" << normalCount // << "imageCount:" << imageCount // << "deletedCount:" << deletedCount; } void CMapIMG::drawLine(QPainter& p, CGarminPolygon& l, const CGarminTyp::polyline_property& property, const QFontMetricsF& metrics, const QFont& font, const QPointF& scale) { QPolygonF& poly = l.pixel; const int size = poly.size(); const int lineWidth = p.pen().width(); if(size < 2) { return; } map->convertRad2Px(poly); // simplifyPolyline(line); if (scale.x() < STREETNAME_THRESHOLD && property.labelType != CGarminTyp::eNone) { collectText(l, poly, font, metrics, lineWidth); } p.drawPolyline(poly); } void CMapIMG::drawLine(QPainter& p, const CGarminPolygon& l) { const QPolygonF& poly = l.pixel; const int size = poly.size(); if(size < 2) { return; } // simplifyPolyline(poly); p.drawPolyline(poly); } void CMapIMG::collectText(const CGarminPolygon& item, const QPolygonF& line, const QFont& font, const QFontMetricsF& metrics, qint32 lineWidth) { QString str; if (!item.labels.isEmpty()) { switch(item.type) { case 0x23: case 0x20: case 0x24: case 0x21: case 0x25: case 0x22: { QString unit; QString val = item.labels[0]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2").arg(val).arg(unit); } break; default: str = item.labels.join(" ").simplified(); } } if(str.isEmpty()) { return; } textpath_t tp; tp.polyline = line; tp.font = font; tp.text = str; tp.lineWidth = lineWidth; const int size = line.size(); for(int i = 1; i < size; ++i) { const QPointF &p1 = line[i-1]; const QPointF &p2 = line[i]; qreal dx = p2.x() - p1.x(); qreal dy = p2.y() - p1.y(); tp.lengths << qSqrt(dx * dx + dy * dy); } textpaths << tp; } void CMapIMG::drawPoints(QPainter& p, pointtype_t& pts, QVector& rectPois) { pointtype_t::iterator pt = pts.begin(); while(pt != pts.end()) { // if((pt->type > 0x1600) && (zoomFactor > CResources::self().getZoomLevelThresholdPois())) // { // ++pt; // continue; // }; map->convertRad2Px(pt->pos); const QImage& icon = CMainWindow::self().isNight() ? pointProperties[pt->type].imgNight : pointProperties[pt->type].imgDay; const QSizeF& size = icon.size(); if(isCluttered(rectPois, QRectF(pt->pos, size))) { if(size.width() <= 8 && size.height() <= 8) { p.drawImage(pt->pos.x() - (size.width()/2), pt->pos.y() - (size.height()/2), icon); } else { p.drawPixmap(pt->pos.x() - 4, pt->pos.y() - 4, QPixmap(":/icons/8x8/bullet_blue.png")); } ++pt; continue; } bool showLabel = true; if(pointProperties.contains(pt->type)) { p.drawImage(pt->pos.x() - (size.width()/2), pt->pos.y() - (size.height()/2), icon); showLabel = pointProperties[pt->type].labelType != CGarminTyp::eNone; } else { p.drawPixmap(pt->pos.x() - 4, pt->pos.y() - 4, QPixmap(":/icons/8x8/bullet_blue.png")); } if(CMainWindow::self().isPOIText() && showLabel) { // calculate bounding rectangle with a border of 2 px QRect rect = fm.boundingRect(pt->labels.join(" ")); rect.adjust(0,0,4,4); rect.moveCenter(pt->pos.toPoint()); // test rectangle for intersection with existing labels QVector::const_iterator label = labels.constBegin(); while(label != labels.constEnd()) { if(label->rect.intersects(rect)) { break; } ++label; } // if no intersection was found, add label to list if(label == labels.constEnd()) { QString str; if(!pt->labels.isEmpty()) { if((pt->type == 0x6200)||(pt->type == 0x6300)) { QString unit; QString val = pt->labels[0]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2").arg(val).arg(unit); } else if(pt->type == 0x6616) //669 DAV { if(pt->labels.size()>1) { QString unit; QString val = pt->labels[1]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2 %3").arg(pt->labels[0]).arg(val).arg(unit); } else { str = pt->labels[0]; } } else { str = pt->labels.join(" "); } } labels.push_back(strlbl_t()); strlbl_t& strlbl = labels.last(); strlbl.pt = pt->pos.toPoint(); strlbl.str = str; strlbl.rect = rect; } } ++pt; } } void CMapIMG::drawPois(QPainter& p, pointtype_t& pts, QVector &rectPois) { CGarminTyp::label_type_e labelType = CGarminTyp::eStandard; QPixmap blueBullet(":/icons/8x8/bullet_blue.png"); QPixmap redBullet(":/icons/8x8/bullet_red.png"); pointtype_t::iterator pt = pts.begin(); while(pt != pts.end()) { map->convertRad2Px(pt->pos); const QImage& icon = CMainWindow::self().isNight() ? pointProperties[pt->type].imgNight : pointProperties[pt->type].imgDay; const QSizeF& size = icon.size(); if(isCluttered(rectPois, QRectF(pt->pos, size))) { if(size.width() <= 8 && size.height() <= 8) { p.drawImage(pt->pos.x() - (size.width()/2), pt->pos.y() - (size.height()/2), icon); } else { p.drawPixmap(pt->pos.x() - 4, pt->pos.y() - 4, QPixmap(":/icons/8x8/bullet_blue.png")); } ++pt; continue; } labelType = CGarminTyp::eStandard; if(pointProperties.contains(pt->type)) { p.drawImage(pt->pos.x() - (size.width()/2), pt->pos.y() - (size.height()/2), icon); labelType = pointProperties[pt->type].labelType; } else { p.drawPixmap(pt->pos.x() - 4, pt->pos.y() - 4, redBullet); } if(CMainWindow::self().isPOIText()) { // calculate bounding rectangle with a border of 2 px QRect rect = fm.boundingRect(pt->labels.join(" ")); rect.adjust(0,0,4,4); rect.moveCenter(pt->pos.toPoint()); // test rectangle for intersection with existing labels QVector::const_iterator label = labels.begin(); while(label != labels.end()) { if(label->rect.intersects(rect)) { break; } ++label; } // if no intersection was found, add label to list if(label == labels.end()) { QString str; if(!pt->labels.isEmpty()) { if((pt->type == 0x6200)||(pt->type == 0x6300)) { QString unit; QString val = pt->labels[0]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2").arg(val).arg(unit); } else if(pt->type == 0x6616) //669 DAV { if(pt->labels.size()>1) { QString unit; QString val = pt->labels[1]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2 %3").arg(pt->labels[0]).arg(val).arg(unit); } else { str = pt->labels[0]; } } else { str = pt->labels.join(" "); } } labels.push_back(strlbl_t()); strlbl_t& strlbl = labels.last(); strlbl.pt = pt->pos.toPoint(); strlbl.str = str; strlbl.rect = rect; strlbl.type = labelType; } } ++pt; } } void CMapIMG::drawLabels(QPainter& p, const QVector &lbls) { QFont f = CMainWindow::self().getMapFont(); QVector fonts(8, f); fonts[CGarminTyp::eSmall].setPointSize(f.pointSize() - 2); fonts[CGarminTyp::eLarge].setPointSize(f.pointSize() + 2); QVector::const_iterator lbl = lbls.begin(); while(lbl != lbls.end()) { CDraw::text(lbl->str, p, lbl->pt, Qt::black, fonts[lbl->type]); ++lbl; } } #define D 80 void CMapIMG::drawText(QPainter& p) { p.setPen(Qt::black); QVector::const_iterator textpath = textpaths.constBegin(); QVector::const_iterator end = textpaths.constEnd(); while(textpath != end) { QPainterPath path; QFont font = textpath->font; QFontMetricsF fm(font); path.addPolygon(textpath->polyline); // get path length and string length qreal length = qAbs(path.length()); qreal width = fm.width(textpath->text); // adjust font size until string fits into polyline while(width > (length * 0.7)) { font.setPixelSize(font.pixelSize() - 1); fm = QFontMetricsF(font); width = fm.width(textpath->text); if((font.pixelSize() < 8)) { break; } } // no way to draw a readable string - skip if((font.pixelSize() < 8)) { ++textpath; continue; } fm = QFontMetricsF(font); p.setFont(font); // adjust exact offset to first half of segment const QVector& lengths = textpath->lengths; const qreal ref = (length - width) / 2; qreal offset = 0; for(int i = 0; i < lengths.size(); ++i) { const qreal d = lengths[i]; if((offset + d/2) >= ref) { offset = ref; break; } if((offset + d) >= ref) { offset += d/2; break; } offset += d; } // get starting angle of first two letters const QString& text = textpath->text; qreal percent1 = offset / length; qreal percent2 = (offset + fm.width(text.left(2))) / length; QPointF point1 = path.pointAtPercent(percent1); QPointF point2 = path.pointAtPercent(percent2); qreal angle = qAtan((point2.y() - point1.y()) / (point2.x() - point1.x())) * 180 / M_PI; // flip path if string start is E->W direction // this helps, sometimes, in 50 % of the cases :) if(point2.x() - point1.x() < 0) { path = path.toReversed(); } // draw string letter by letter and adjust angle const int size = text.size(); percent2 = offset / length; point2 = path.pointAtPercent(percent2); for(int i = 0; i < size; ++i) { percent1 = percent2; percent2 = (offset + fm.width(text[i])) / length; point1 = point2; point2 = path.pointAtPercent(percent2); angle = qAtan((point2.y() - point1.y()) / (point2.x() - point1.x())) * 180 / M_PI; if(point2.x() - point1.x() < 0) { angle += 180; } p.save(); p.translate(point1); p.rotate(angle); p.translate(0, -(textpath->lineWidth + 2)); QString str = text.mid(i,1); p.setPen(Qt::white); p.drawText(-1,-1,str); p.drawText( 0,-1,str); p.drawText(+1,-1,str); p.drawText(-1, 0,str); p.drawText(+1, 0,str); p.drawText(-1,+1,str); p.drawText( 0,+1,str); p.drawText(+1,+1,str); p.setPen(Qt::black); p.drawText( 0, 0,str); p.restore(); offset += fm.width(text[i]); } ++textpath; } } void CMapIMG::getInfo(const QPoint& px, QString& str) { } void CMapIMG::getToolTip(const QPoint& px, QString& infotext) { bool first = true; QString str; QMultiMap dict; getInfoPoints(px, dict); getInfoPois(px, dict); getInfoPolylines(px, dict); QList values = dict.values(); foreach(const QString &value, values) { if(value == "-") { continue; } if(first) { first = false; } else { str += "\n"; } str += value; } if(str.isEmpty()) { dict.clear(); getInfoPolygons(px, dict); QList values = dict.values(); foreach(const QString &value, values) { if(value == "-") { continue; } if(first) { first = false; } else { str += "\n"; } str += value; } } if(!infotext.isEmpty() && !str.isEmpty()) { infotext += "\n" + str; } else { infotext += str; } } void CMapIMG::getInfoPoints(const QPoint& pt, QMultiMap& dict) { pointtype_t::const_iterator point = points.begin(); while(point != points.end()) { QPoint x = pt - QPoint(point->pos.x(), point->pos.y()); if(x.manhattanLength() < 10) { if(point->labels.size()) { QString str; if((point->type == 0x6200)||(point->type == 0x6300)) { QString unit; QString val = point->labels[0]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2").arg(val).arg(unit); } else if(point->type == 0x6616) //669 DAV { if(point->labels.size()>1) { QString unit; QString val = point->labels[1]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2 %3").arg(point->labels[0]).arg(val).arg(unit); } else { str = point->labels[0]; } } else { str = point->labels.join(", "); } dict.insert(tr("Point of Interest"),str); } else { if(pointProperties.contains(point->type)) { if(selectedLanguage != NOIDX) { dict.insert(tr("Point of Interest"),pointProperties[point->type].strings[selectedLanguage]); } else { dict.insert(tr("Point of Interest"),pointProperties[point->type].strings[0]); } } else { dict.insert(tr("Point of Interest"), QString(" (%1)").arg(point->type,2,16,QChar('0'))); } } } ++point; } } void CMapIMG::getInfoPois(const QPoint& pt, QMultiMap& dict) { pointtype_t::const_iterator point = pois.begin(); while(point != pois.end()) { QPoint x = pt - QPoint(point->pos.x(), point->pos.y()); if(x.manhattanLength() < 10) { if(point->labels.size()) { QString str; if((point->type == 0x6200)||(point->type == 0x6300)) { QString unit; QString val = point->labels[0]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2").arg(val).arg(unit); } else if(point->type == 0x6616) //669 DAV { if(point->labels.size()>1) { QString unit; QString val = point->labels[1]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); str = QString("%1 %2 %3").arg(point->labels[0]).arg(val).arg(unit); } else { str = point->labels[0]; } } else { str = point->labels.join(", "); } dict.insert(tr("Point of Interest"),str); } else { if(pointProperties.contains(point->type)) { if(selectedLanguage != NOIDX) { dict.insert(tr("Point of Interest"),pointProperties[point->type].strings[selectedLanguage]); } else { dict.insert(tr("Point of Interest"),pointProperties[point->type].strings[0]); } } else { dict.insert(tr("Point of Interest"), QString(" (%1)").arg(point->type,2,16,QChar('0'))); } } } ++point; } } void CMapIMG::getInfoPolylines(const QPoint &pt, QMultiMap& dict) { int i = 0; // index into poly line int len; // number of points in line projXY p1, p2; // the two points of the polyline close to pt qreal dx,dy; // delta x and y defined by p1 and p2 qreal d_p1_p2; // distance between p1 and p2 qreal u; // ratio u the tangent point will divide d_p1_p2 qreal x,y; // coord. (x,y) of the point on line defined by [p1,p2] close to pt qreal distance; // the distance to the polyline qreal shortest; // shortest distance sofar QPointF resPt = pt; QString key, value; quint32 type = 0; shortest = 20; bool found = false; polytype_t::const_iterator line = polylines.begin(); while(line != polylines.end()) { len = line->pixel.size(); // need at least 2 points if(len < 2) { ++line; continue; } // see http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ for(i=1; ipixel[i-1].x(); p1.v = line->pixel[i-1].y(); p2.u = line->pixel[i].x(); p2.v = line->pixel[i].y(); dx = p2.u - p1.u; dy = p2.v - p1.v; d_p1_p2 = qSqrt(dx * dx + dy * dy); u = ((pt.x() - p1.u) * dx + (pt.y() - p1.v) * dy) / (d_p1_p2 * d_p1_p2); if(u < 0.0 || u > 1.0) { continue; } x = p1.u + u * dx; y = p1.v + u * dy; distance = qSqrt((x - pt.x())*(x - pt.x()) + (y - pt.y())*(y - pt.y())); if(distance < shortest) { type = line->type; if(!line->labels.isEmpty()) { switch(type) { // "Minor depth contour" case 0x23: // "Minor land contour" case 0x20: // "Intermediate depth contour", case 0x24: // "Intermediate land contour", case 0x21: // "Major depth contour", case 0x25: // "Major land contour", case 0x22: { QString unit; QString val = line->labels[0]; IUnit::self().meter2elevation(val.toFloat() / 3.28084f, val, unit); value = QString("%1 %2").arg(val).arg(unit); } break; default: value = line->labels.join(" ").simplified(); } } else { value = "-"; } resPt.setX(x); resPt.setY(y); shortest = distance; found = true; } } ++line; } if(!found) { return; } if(selectedLanguage != NOIDX) { key = polylineProperties[type].strings[selectedLanguage]; } if(!key.isEmpty()) { dict.insert(key + QString("(%1)").arg(type,2,16,QChar('0')),value); } else { if(polylineProperties[type].strings.isEmpty()) { dict.insert(tr("Unknown") + QString("(%1)").arg(type,2,16,QChar('0')),value); } else { dict.insert(polylineProperties[type].strings[0] + QString("(%1)").arg(type,2,16,QChar('0')),value); } } // pt = resPt.toPoint(); } void CMapIMG::getInfoPolygons(const QPoint& pt, QMultiMap& dict) { int npol; int i = 0, j = 0,c = 0; projXY p1, p2; // the two points of the polyline close to pt qreal x = pt.x(); qreal y = pt.y(); QString value; polytype_t::const_iterator line = polygons.begin(); while(line != polygons.end()) { npol = line->pixel.size(); if(npol > 2) { c = 0; // see http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ for (i = 0, j = npol-1; i < npol; j = i++) { p1.u = line->pixel[j].x(); p1.v = line->pixel[j].y(); p2.u = line->pixel[i].x(); p2.v = line->pixel[i].y(); if ((((p2.v <= y) && (y < p1.v)) || ((p1.v <= y) && (y < p2.v))) && (x < (p1.u - p2.u) * (y - p2.v) / (p1.v - p2.v) + p2.u)) { c = !c; } } if(c) { if(line->labels.size()) { dict.insert(tr("Area"), line->labels.join(" ").simplified()); } else { if(selectedLanguage != NOIDX) { if(polygonProperties[line->type].strings[selectedLanguage].size()) { dict.insert(tr("Area"), polygonProperties[line->type].strings[selectedLanguage]); } } else { if(polygonProperties[line->type].strings[0].size()) { dict.insert(tr("Area"), polygonProperties[line->type].strings[0]); } } } } } ++line; } } static qreal getDistance(const QPolygonF& line, const QPointF& pt, qreal threshold) { int i = 0; // index into poly line int len; // number of points in line QPointF p1, p2; // the two points of the polyline close to pt qreal dx,dy; // delta x and y defined by p1 and p2 qreal d_p1_p2; // distance between p1 and p2 qreal u; // ratio u the tangent point will divide d_p1_p2 qreal x,y; // coord. (x,y) of the point on line defined by [p1,p2] close to pt qreal distance; // the distance to the polyline qreal d = threshold + 1; len = line.size(); // see http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ for(i=1; i 1.0) { continue; } x = p1.x() + u * dx; y = p1.y() + u * dy; distance = qSqrt((x - pt.x())*(x - pt.x()) + (y - pt.y())*(y - pt.y())); if(distance < threshold) { d = threshold = distance; } } return d; } bool CMapIMG::findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline) { foreach(const CGarminPolygon &line, polylines) { if(line.pixel.size() < 2) { continue; } if(0x20 <= line.type && line.type <= 0x25) { continue; } qreal dist1 = ::getDistance(line.pixel, pt1, threshold); qreal dist2 = ::getDistance(line.pixel, pt2, threshold); if(dist1 < threshold && dist2 < threshold) { polyline = line.coords; threshold = qMin(dist1, dist2); } } return !polyline.isEmpty(); } qmapshack-1.5.1/src/map/CMapDraw.h000644 001750 000144 00000012737 12622435723 017652 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPDRAW_H #define CMAPDRAW_H #include "canvas/IDrawContext.h" #include class QPainter; class CCanvas; class CMapList; class QSettings; class CMapItem; class CMapDraw : public IDrawContext { Q_OBJECT public: CMapDraw(CCanvas * parent); virtual ~CMapDraw(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); /** @brief This is called most likely from the item itself to call it's loadConfig() method. As the setup of a map is stored in the context of the view the correct groups have to be set prior to call the item's loadConfig() method. However the item does not know all that stuff. That is why it has to ask it's CMapDraw object to prepare the QSettings object and to call loadConfig(); @param item the item to call it's loadConfig() method */ void loadConfigForMapItem(CMapItem * item); /** @brief Get a full detailed info text about objects close to the given point This method will call getInfo() of all items in mapList. @param px the point on the screen in pixel @param str a string object to receive all information */ void getInfo(const QPoint& px, QString& str); /** @brief Get an info text fit for a tool tip This method will call getToolTip() of all items in mapList. @param px the point on the screen in pixel @param str a string object to receive all information */ void getToolTip(const QPoint& px, QString& str); /** @brief Set projection of this draw context @param proj a proj4 string */ void setProjection(const QString& proj); static void setupMapPath(); static void setupMapPath(const QString &path); static void setupMapPath(const QStringList &paths); static void saveMapPath(QSettings &cfg); static void loadMapPath(QSettings &cfg); static const QStringList& getSupportedFormats() { return supportedFormats; } static const QString& getCacheRoot() { return cachePath; } /** @brief Forward messages to CCanvas::reportStatus() Messages from various sources will be collected in a list and displayed in the top left corner of the widget. @note The object reporting has to take care to remove the message by reporting an empty string. @param key the key to identify the reporting object @param msg the message to report */ void reportStatusToCanvas(const QString& key, const QString& msg); /** @brief Find a matching street polyline The polyline must be close enough in terms of pixel to point 1 and 2. "Close enough" is defined by the threshold. The returned polyline uses lon/lat as coordinates. @param pt1 first point in [rad] @param pt2 second point in [rad] @param threshold the "close enough" threshold in [pixel] @param polyline the resulting polyline, if any, in [rad] @return Return true if a line has been found. */ bool findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline); protected: void drawt(buffer_t& currentBuffer); private: /** @brief Search in paths found in mapPaths for files with supported extensions and add them to mapList. */ void buildMapList(); /** @brief Save list of active maps to configuration file The group context will be appended by the map's key @param keys the stored map's MD5 keys will be written to keys @param cfg configuration file with correct group context set. */ void saveActiveMapsList(QStringList &keys, QSettings &cfg); /** @brief Open configuration before saving list @param keys the stored map's MD5 keys will be written to keys */ void saveActiveMapsList(QStringList &keys); /** @brief Restore list of active maps from configuration file @param keys MD5 hash keys to identify the maps */ void restoreActiveMapsList(const QStringList &keys); void restoreActiveMapsList(const QStringList& keys, QSettings& cfg); /// the treewidget holding all active and inactive map items CMapList * mapList; /// the group label used in QSettings QString cfgGroup; /// the list of paths to search maps static QStringList mapPaths; static QString cachePath; /// all existing CMapDraw instances static QList maps; /// a list of supported map formats static QStringList supportedFormats; }; #endif //CMAPDRAW_H qmapshack-1.5.1/src/map/CMapTMS.cpp000644 001750 000144 00000036017 12622435717 017753 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CDraw.h" #include "map/CMapDraw.h" #include "map/CMapTMS.h" #include "map/cache/CDiskCache.h" #include "units/IUnit.h" #include #include #include #include #include #include inline int lon2tile(double lon, int z) { return (int)(qRound(256*(lon + 180.0) / 360.0 * qPow(2.0, z))); } inline int lat2tile(double lat, int z) { return (int)(qRound(256*(1.0 - log( qTan(lat * M_PI/180.0) + 1.0 / qCos(lat * M_PI/180.0)) / M_PI) / 2.0 * qPow(2.0, z))); } inline double tile2lon(int x, int z) { return x / qPow(2.0, z) * 360.0 - 180; } inline double tile2lat(int y, int z) { double n = M_PI - 2.0 * M_PI * y / qPow(2.0, z); return 180.0 / M_PI * qAtan(0.5 * (exp(n) - exp(-n))); } CMapTMS::CMapTMS(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility|eFeatTileCache, parent) { qDebug() << "------------------------------"; qDebug() << "TMS: try to open" << filename; pjsrc = pj_init_plus("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"); qDebug() << "tms:" << "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"; QFile file(filename); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to open %1").arg(filename), QMessageBox::Abort, QMessageBox::Abort); return; } QString msg; int line, column; QDomDocument dom; if(!dom.setContent(&file, true, &msg, &line, &column)) { file.close(); QMessageBox::critical(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read: %1\nline %2, column %3:\n %4").arg(filename).arg(line).arg(column).arg(msg), QMessageBox::Abort, QMessageBox::Abort); return; } file.close(); const QDomElement& xmlTms = dom.firstChildElement("TMS"); name = xmlTms.firstChildElement("Title").text(); copyright = xmlTms.firstChildElement("Copyright").text(); if(xmlTms.firstChildElement("MaxZoomLevel").isElement()) { maxZoomLevel = xmlTms.firstChildElement("MaxZoomLevel").text().toInt(); } if(xmlTms.firstChildElement("MinZoomLevel").isElement()) { minZoomLevel = xmlTms.firstChildElement("MinZoomLevel").text().toInt(); } const QDomNodeList& xmlLayers = xmlTms.elementsByTagName("Layer"); qint32 N = xmlLayers.count(); layers.resize(N); for(qint32 n = 0; n < N; ++n) { const QDomNode& xmlLayer = xmlLayers.item(n); int idx = xmlLayer.attributes().namedItem("idx").nodeValue().toInt(); layers[idx].strUrl = xmlLayer.namedItem("ServerUrl").toElement().text(); layers[idx].script = xmlLayer.namedItem("Script").toElement().text(); layers[idx].minZoomLevel = minZoomLevel; layers[idx].maxZoomLevel = maxZoomLevel; if(xmlLayer.namedItem("Title").isElement()) { layers[idx].title = xmlLayer.namedItem("Title").toElement().text(); } else { layers[idx].title = tr("Layer %1").arg(idx + 1); } if(xmlLayer.firstChildElement("MinZoomLevel").isElement()) { layers[idx].minZoomLevel = xmlLayer.firstChildElement("MinZoomLevel").text().toInt(); } if(xmlLayer.firstChildElement("MaxZoomLevel").isElement()) { layers[idx].maxZoomLevel = xmlLayer.firstChildElement("MaxZoomLevel").text().toInt(); } if(layers[idx].strUrl.toLower().startsWith("https") && !QSslSocket::supportsSsl()) { QString msg = tr( "This map requires OpenSSL support. However due to legal restrictions in some countries " "OpenSSL is not packaged with QMapShack. You can have a look at the " "OpenSSL Homepage " "for binaries. You have to copy libeay32.dll and ssleay32.dll into the QMapShack program directory." ); QMessageBox::critical(CMainWindow::getBestWidgetForParent(),tr("Error..."),msg,QMessageBox::Abort); return; } } const QDomElement& xmlRawHeader = xmlTms.firstChildElement("RawHeader"); const QDomNodeList& xmlValues = xmlRawHeader.elementsByTagName("Value"); N = xmlValues.count(); for(qint32 n = 0; n < N; ++n) { rawHeaderItem_t item; const QDomNode& xmlValue = xmlValues.item(n); item.name = xmlValue.attributes().namedItem("name").nodeValue(); item.value = xmlValue.toElement().text(); rawHeaderItems << item; } // if there is more than one layer the layer list in the properties widget has to be enabled. if(layers.size() > 1) { flagsFeature |= eFeatLayers; } // create default cache path from filename QFileInfo fi(filename); slotSetCachePath(QDir(CMapDraw::getCacheRoot()).absoluteFilePath(fi.baseName())); accessManager = new QNetworkAccessManager(parent->thread()); connect(this, SIGNAL(sigQueueChanged()), this, SLOT(slotQueueChanged())); connect(accessManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(slotRequestFinished(QNetworkReply*))); name = fi.baseName().replace("_", " "); isActivated = true; } CMapTMS::~CMapTMS() { // map->reportStatusToCanvas(name, ""); } void CMapTMS::getLayers(QListWidget& list) { QMutexLocker lock(&mutex); list.clear(); if(layers.size() < 2) { return; } int i = 0; foreach(const layer_t &layer, layers) { QListWidgetItem * item = new QListWidgetItem(layer.title, &list); item->setCheckState(layer.enabled ? Qt::Checked : Qt::Unchecked); item->setData(Qt::UserRole, i++); } connect(&list, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(slotLayersChanged(QListWidgetItem*))); } void CMapTMS::saveConfig(QSettings& cfg) { QMutexLocker lock(&mutex); IMap::saveConfig(cfg); if(layers.size() < 2) { return; } // save indices of enabled layers QStringList enabled; for(int i = 0; i< layers.size(); i++) { if(layers[i].enabled) { enabled << QString::number(i); } } cfg.setValue("enabledLayers", enabled); } void CMapTMS::loadConfig(QSettings& cfg) { QMutexLocker lock(&mutex); IMap::loadConfig(cfg); if(layers.size() < 2) { return; } QStringList enabled; // set all layers to disabled first for(int i = 0; i< layers.size(); i++) { layers[i].enabled = false; enabled << QString::number(i); } // enable layers stored in configuration enabled = cfg.value("enabledLayers", enabled).toStringList(); foreach(const QString &str, enabled) { int idx = str.toInt(); if(idx < layers.size()) { layers[idx].enabled = true; } } } void CMapTMS::configureCache() { QMutexLocker lock(&mutex); delete diskCache; diskCache = new CDiskCache(getCachePath(), getCacheSize(), getCacheExpiration(), this); } void CMapTMS::slotQueueChanged() { QMutexLocker lock(&mutex); if(!urlQueue.isEmpty() && urlPending.size() < 6) { // request up to 6 pending request for(int i = 0; i < (6 - urlPending.size()); i++) { QString url = urlQueue.dequeue(); lastRequest = urlQueue.isEmpty(); QNetworkRequest request; request.setUrl(url); foreach(const rawHeaderItem_t &item, rawHeaderItems) { request.setRawHeader(item.name.toLatin1(), item.value.toLatin1()); } accessManager->get(request); urlPending << url; if(lastRequest) { break; } } } else if(lastRequest && urlPending.isEmpty()) { lastRequest = false; // if all tiles are received the map layer can be redrawn with all tiles from cache map->emitSigCanvasUpdate(); } if(timeLastUpdate.elapsed() > 2000) { timeLastUpdate.start(); map->emitSigCanvasUpdate(); } // report status of pending tiles int pending = urlQueue.size() + urlPending.size(); if(pending) { map->reportStatusToCanvas(name, tr("%1: %2 tiles pending
").arg(name).arg(pending)); } else { map->reportStatusToCanvas(name, ""); } } void CMapTMS::slotRequestFinished(QNetworkReply* reply) { QMutexLocker lock(&mutex); QString url = reply->url().toString(); if(urlPending.contains(url)) { QImage img; // only take good responses if(!reply->error()) { // read image data img.loadFromData(reply->readAll()); } // always store image to cache, the cache will take care of NULL images diskCache->store(url, img); urlPending.removeAll(url); } // debug output any error if(reply->error()) { qDebug() << reply->errorString(); } // delete reply object reply->deleteLater(); // check for more items to be queued slotQueueChanged(); } void CMapTMS::slotLayersChanged(QListWidgetItem * item) { QMutexLocker lock(&mutex); bool isChecked = (item->checkState() == Qt::Checked); int idx = item->data(Qt::UserRole).toInt(); if(idx < 0) { QListWidget * list = item->listWidget(); list->blockSignals(true); for(int i = 0; i < layers.size(); i++) { list->item(i + 1)->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked); layers[i].enabled = isChecked; } list->blockSignals(false); } else { layers[idx].enabled = isChecked; } map->emitSigCanvasUpdate(); } QString CMapTMS::createUrl(const layer_t& layer, int x, int y, int z) { QMutexLocker lock(&mutex); if(layer.strUrl.startsWith("script")) { QString filename = layer.strUrl.mid(9); QFile scriptFile(filename); if (!scriptFile.open(QIODevice::ReadOnly)) { return ""; } QTextStream stream(&scriptFile); QString contents = stream.readAll(); scriptFile.close(); QScriptEngine engine; QScriptValue fun = engine.evaluate(contents, filename); if(engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "uncaught exception at line" << line << ":" << fun.toString(); } QScriptValueList args; args << z << x << y; QScriptValue res = fun.call(QScriptValue(), args); return res.toString(); } else if(!layer.script.isEmpty()) { QScriptEngine engine; QScriptValue fun = engine.evaluate(layer.script); QScriptValueList args; args << z << x << y; QScriptValue res = fun.call(QScriptValue(), args); return res.toString(); } return layer.strUrl.arg(z).arg(x).arg(y); } void CMapTMS::draw(IDrawContext::buffer_t& buf) { QMutexLocker lock(&mutex); timeLastUpdate.start(); urlQueue.clear(); if(map->needsRedraw()) { return; } QPointF bufferScale = buf.scale * buf.zoomFactor; if(isOutOfScale(bufferScale)) { return; } // get pixel offset of top left buffer corner QPointF pp = buf.ref1; map->convertRad2Px(pp); // start to draw the map QPainter p(&buf.image); USE_ANTI_ALIASING(p,true); p.setOpacity(getOpacity()/100.0); p.translate(-pp); // calculate maximum viewport qreal x1 = buf.ref1.x() < buf.ref4.x() ? buf.ref1.x() : buf.ref4.x(); qreal y1 = buf.ref1.y() > buf.ref2.y() ? buf.ref1.y() : buf.ref2.y(); qreal x2 = buf.ref2.x() > buf.ref3.x() ? buf.ref2.x() : buf.ref3.x(); qreal y2 = buf.ref3.y() < buf.ref4.y() ? buf.ref3.y() : buf.ref4.y(); if(x1 < -180.0*DEG_TO_RAD) { x1 = -180*DEG_TO_RAD; } if(x2 > 180.0*DEG_TO_RAD) { x2 = 180*DEG_TO_RAD; } // draw layers foreach(const layer_t &layer, layers) { if(!layer.enabled) { continue; } qint32 z = 20; QPointF s1 = buf.scale * buf.zoomFactor; qreal d = NOFLOAT; for(qint32 i = layer.minZoomLevel; i < 21; i++) { qreal s2 = 0.055 * (1< layer.maxZoomLevel) { continue; } z = 21 - z; qint32 row1, row2, col1, col2; col1 = lon2tile(x1 * RAD_TO_DEG, z) / 256; col2 = lon2tile(x2 * RAD_TO_DEG, z) / 256; row1 = lat2tile(y1 * RAD_TO_DEG, z) / 256; row2 = lat2tile(y2 * RAD_TO_DEG, z) / 256; // qDebug() << col1 << col2 << row1 << row2 << (col2 - col1) << (row2 - row1) << ((col2 - col1) * (row2 - row1)); // start to request tiles. draw tiles in cache, queue urls of tile yet to be requested for(qint32 row = row1; row <= row2; row++) { for(qint32 col = col1; col <= col2; col++) { QString url = createUrl(layer, col, row, z); // qDebug() << url; if(diskCache->contains(url)) { QImage img; diskCache->restore(url, img); QPolygonF l; qreal xx1 = tile2lon(col, z) * DEG_TO_RAD; qreal yy1 = tile2lat(row, z) * DEG_TO_RAD; qreal xx2 = tile2lon(col + 1, z) * DEG_TO_RAD; qreal yy2 = tile2lat(row + 1, z) * DEG_TO_RAD; l << QPointF(xx1, yy1) << QPointF(xx2, yy1) << QPointF(xx2, yy2) << QPointF(xx1, yy2); drawTile(img, l, p); } else { urlQueue << url; } } } emit sigQueueChanged(); } } qmapshack-1.5.1/src/map/IMap.h000644 001750 000144 00000014224 12622435723 017033 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IMAP_H #define IMAP_H #include "canvas/IDrawContext.h" #include "canvas/IDrawObject.h" #include #include #include #include class CMapDraw; class IMapProp; class IMap : public IDrawObject { Q_OBJECT public: IMap(quint32 features, CMapDraw * parent); virtual ~IMap(); void saveConfig(QSettings& cfg); void loadConfig(QSettings& cfg); enum features_e { eFeatVisibility = 0x00000001 ,eFeatVectorItems = 0x00000002 ,eFeatTileCache = 0x00000004 ,eFeatLayers = 0x00000008 }; virtual void draw(IDrawContext::buffer_t& buf) = 0; /** @brief Test if map has been loaded successfully @return Return false if map is not loaded */ bool activated() { return isActivated; } /** @brief Get the map's setup widget. As default an instance of CMapPropSetup is used. For other setups you have to override this method. @return A pointer to the widget. Use a smart pointer to store as the widget can be destroyed at any time */ virtual IMapProp *getSetup(); /** @brief getInfo @param px @param str */ virtual void getInfo(const QPoint& px, QString& str) { Q_UNUSED(px); Q_UNUSED(str); } /** @brief getToolTip @param px @param str */ virtual void getToolTip(const QPoint& px, QString& str) { Q_UNUSED(px); Q_UNUSED(str); } /** @brief Return copyright notice if any @return If no copyright notice has been decoded the string will be empty */ const QString& getCopyright() { return copyright; } bool hasFeatureVisibility() { return flagsFeature & eFeatVisibility; } bool hasFeatureVectorItems() { return flagsFeature & eFeatVectorItems; } bool hasFeatureTileCache() { return flagsFeature & eFeatTileCache; } bool hasFeatureLayers() { return flagsFeature & eFeatLayers; } bool getShowPolygons() { return showPolygons; } bool getShowPolylines() { return showPolylines; } bool getShowPOIs() { return showPOIs; } const QString& getCachePath() { return cachePath; } qint32 getCacheSize() { return cacheSizeMB; } qint32 getCacheExpiration() { return cacheExpiration; } /** @brief Find a matching street polyline The polyline must be close enough in terms of pixel to point 1 and 2. "Close enough" is defined by the threshold. The returned polyline uses lon/lat as coordinates. @param pt1 first point in [rad] @param pt2 second point in [rad] @param threshold the "close enough" threshold in [pixel] @param polyline the resulting polyline, if any, in [rad] @return Return true if a line has been found. */ virtual bool findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline); public slots: void slotSetShowPolygons(bool yes) { showPolygons = yes; } void slotSetShowPolylines(bool yes) { showPolylines = yes; } void slotSetShowPOIs(bool yes) { showPOIs = yes; } void slotSetCachePath(const QString& path) { cachePath = path; configureCache(); } void slotSetCacheSize(qint32 size) { cacheSizeMB = size; configureCache(); } void slotSetCacheExpiration(qint32 days) { cacheExpiration = days; configureCache(); } protected: void convertRad2M(QPointF &p); void convertM2Rad(QPointF &p); /** @brief Reproject (translate, rotate, scale) tile before drawing it. @param img the tile as QImage @param l a 4 point polygon to fit the tile in @param p the QPainter used to paint the tile */ void drawTile(QImage& img, QPolygonF& l, QPainter& p); /// the drawcontext this map belongs to CMapDraw * map; /// source projection of the current map file /** Has to be set by subclass. Destruction has to be handled by subclass. */ projPJ pjsrc = 0; /// target projection /** Is set by IMap() to WGS84. Will be freed by ~IMap() */ projPJ pjtar = 0; /** @brief True if map was loaded successfully */ bool isActivated = false; /// the setup dialog. Use getSetup() for access QPointer setup; /// flag field for features defined in features_e quint32 flagsFeature; /// vector maps only: hide/show polygons bool showPolygons = true; /// vector maps only: hide/show polylines bool showPolylines = true; /// vector maps only: hide/show point of interest bool showPOIs = true; /// streaming map only: path to cached tiles QString cachePath; /// streaming map only: maximum size of all tiles in cache [MByte] qint32 cacheSizeMB = 100; /// streaming map only: maximum age of tiles in cache [days] qint32 cacheExpiration = 8; /// a copyright string to be displayed as tool tip QString copyright; }; #endif //IMAP_H qmapshack-1.5.1/src/map/CMapRMAP.cpp000644 001750 000144 00000035101 12622435717 020040 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "helpers/CDraw.h" #include "map/CMapDraw.h" #include "map/CMapRMAP.h" #include "units/IUnit.h" #include #include CMapRMAP::CMapRMAP(const QString &filename, CMapDraw *parent) : IMap(eFeatVisibility,parent) , filename(filename) { qDebug() << "------------------------------"; qDebug() << "RMAP: try to open" << filename; QFile file(filename); file.open(QIODevice::ReadOnly); // qDebug() << file.errorString(); QDataStream stream(&file); stream.setByteOrder(QDataStream::LittleEndian); QByteArray charbuf(20,0); stream.readRawData(charbuf.data(), 19); if("CompeGPSRasterImage" != QString(charbuf)) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("This is not a TwoNav RMAP file."), QMessageBox::Abort, QMessageBox::Abort); return; } quint32 tag1, tag2, tmp32; stream >> tag1 >> tag2 >> tmp32; if(tag1 != 10 || tag2 != 7) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Unknown sub-format."), QMessageBox::Abort, QMessageBox::Abort); return; } stream >> xsize_px >> ysize_px; stream >> tmp32 >> tmp32; stream >> tileSizeX >> tileSizeY; ysize_px = -ysize_px; quint64 mapDataOffset; stream >> mapDataOffset; stream >> tmp32; qint32 nZoomLevels; stream >> nZoomLevels; for(int i=0; i < nZoomLevels; i++) { level_t level; stream >> level.offsetLevel; levels << level; } for(int i=0; i> level.width; stream >> level.height; stream >> level.xTiles; stream >> level.yTiles; for(int j=0; j<(level.xTiles * level.yTiles); j++) { quint64 offset; stream >> offset; level.offsetJpegs << offset; } } file.seek(mapDataOffset); stream >> tmp32 >> tmp32; charbuf.resize(tmp32 + 1); charbuf.fill(0); stream.readRawData(charbuf.data(), tmp32); QPoint p0; QPoint p1; QPoint p2; QPoint p3; projXY c0; projXY c1; projXY c2; projXY c3; bool pointsAreLongLat = true; QString projection; QString datum; QStringList lines = QString(charbuf).split("\r\n"); foreach(const QString &line, lines) { // qDebug() << line; if(line.startsWith("Version=")) { if(line.split("=")[1] != "2") { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Unknown version."), QMessageBox::Abort, QMessageBox::Abort); return; } } else if(line.startsWith("Projection=")) { projection = line.split("=")[1]; } else if(line.startsWith("Datum=")) { datum = line.split("=")[1]; } else if(line.startsWith("P0=")) { QStringList vals = line.split("=")[1].split(","); if(vals.size() < 5) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read reference point."), QMessageBox::Abort, QMessageBox::Abort); return; } p0 = QPoint(vals[0].toInt(), vals[1].toInt()); if(vals[2] == "A") { c0.u = vals[3].toDouble() * DEG_TO_RAD; c0.v = vals[4].toDouble() * DEG_TO_RAD; } else { c0.u = vals[3].toDouble(); c0.v = vals[4].toDouble(); } } else if(line.startsWith("P1=")) { QStringList vals = line.split("=")[1].split(","); if(vals.size() < 5) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read reference point."), QMessageBox::Abort, QMessageBox::Abort); return; } p1 = QPoint(vals[0].toInt(), vals[1].toInt()); if(vals[2] == "A") { c1.u = vals[3].toDouble() * DEG_TO_RAD; c1.v = vals[4].toDouble() * DEG_TO_RAD; } else { pointsAreLongLat = false; c1.u = vals[3].toDouble(); c1.v = vals[4].toDouble(); } } else if(line.startsWith("P2=")) { QStringList vals = line.split("=")[1].split(","); if(vals.size() < 5) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read reference point."), QMessageBox::Abort, QMessageBox::Abort); return; } p2 = QPoint(vals[0].toInt(), vals[1].toInt()); if(vals[2] == "A") { c2.u = vals[3].toDouble() * DEG_TO_RAD; c2.v = vals[4].toDouble() * DEG_TO_RAD; } else { pointsAreLongLat = false; c2.u = vals[3].toDouble(); c2.v = vals[4].toDouble(); } } else if(line.startsWith("P3=")) { QStringList vals = line.split("=")[1].split(","); if(vals.size() < 5) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Failed to read reference point."), QMessageBox::Abort, QMessageBox::Abort); return; } p3 = QPoint(vals[0].toInt(), vals[1].toInt()); if(vals[2] == "A") { c3.u = vals[3].toDouble() * DEG_TO_RAD; c3.v = vals[4].toDouble() * DEG_TO_RAD; } else { pointsAreLongLat = false; c3.u = vals[3].toDouble(); c3.v = vals[4].toDouble(); } } else { // qDebug() << line; } //qDebug() << line; } if(!projection.isEmpty() && !datum.isEmpty()) { if(!setProjection(projection, datum)) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("Unknown projection and datum (%1%2).").arg(projection).arg(datum), QMessageBox::Abort, QMessageBox::Abort); return; } } if(!pj_is_latlong(pjsrc)) { if(pointsAreLongLat) { pj_transform(pjtar, pjsrc, 1, 0, &c0.u, &c0.v, 0); pj_transform(pjtar, pjsrc, 1, 0, &c1.u, &c1.v, 0); pj_transform(pjtar, pjsrc, 1, 0, &c2.u, &c2.v, 0); pj_transform(pjtar, pjsrc, 1, 0, &c3.u, &c3.v, 0); } // qDebug() << c0.u << c0.v; // qDebug() << c1.u << c1.v; // qDebug() << c2.u << c2.v; // qDebug() << c3.u << c3.v; xref1 = NOFLOAT; yref1 = -NOFLOAT; xref2 = -NOFLOAT; yref2 = NOFLOAT; } else { xref1 = 180 * DEG_TO_RAD; yref1 = -90 * DEG_TO_RAD; xref2 = -180 * DEG_TO_RAD; yref2 = 90 * DEG_TO_RAD; } if(c0.u < xref1) { xref1 = c0.u; } if(c0.u > xref2) { xref2 = c0.u; } if(c1.u < xref1) { xref1 = c1.u; } if(c1.u > xref2) { xref2 = c1.u; } if(c2.u < xref1) { xref1 = c2.u; } if(c2.u > xref2) { xref2 = c2.u; } if(c0.v > yref1) { yref1 = c0.v; } if(c0.v < yref2) { yref2 = c0.v; } if(c1.v > yref1) { yref1 = c1.v; } if(c1.v < yref2) { yref2 = c1.v; } if(c2.v > yref1) { yref1 = c2.v; } if(c2.v < yref2) { yref2 = c2.v; } scale.rx() = (xref2 - xref1) / xsize_px; scale.ry() = (yref2 - yref1) / ysize_px; qreal widthL0 = levels[0].width; qreal heightL0 = levels[0].height; for(int i=0; i levels[i].xscale) { return levels[i]; } int j = 0; qreal dsx = NOFLOAT; for(; j < levels.size(); j++) { level_t& level = levels[j]; if(qAbs(level.xscale - s.x()) < dsx) { i = j; dsx = qAbs(level.xscale - s.x()); } } return levels[i]; } void CMapRMAP::draw(IDrawContext::buffer_t& buf) { if(map->needsRedraw()) { return; } // convert top left buffer corner // into buffer's coordinate system QPointF pp = buf.ref1; map->convertRad2Px(pp); // find best level for buffer's scale factor derived from base scale and zoom factor QPointF bufferScale = buf.scale * buf.zoomFactor; if(isOutOfScale(bufferScale)) { return; } level_t& level = findBestLevel(bufferScale); // convert top left and bottom right point of buffer to local coord. system QPointF p1 = buf.ref1; QPointF p2 = buf.ref3; convertRad2M(p1); convertRad2M(p2); // find indices into tile buffer int idxx1 = qFloor((p1.x() - xref1) / (level.xscale * tileSizeX)); int idxy1 = qFloor((p1.y() - yref1) / (level.yscale * tileSizeY)); int idxx2 = qCeil((p2.x() - xref1) / (level.xscale * tileSizeX)); int idxy2 = qCeil((p2.y() - yref1) / (level.yscale * tileSizeY)); if(idxx1 < 0) { idxx1 = 0; } if(idxx1 >= level.xTiles) { idxx1 = level.xTiles; } if(idxx2 < 0) { idxx2 = 0; } if(idxx2 >= level.xTiles) { idxx2 = level.xTiles; } if(idxy1 < 0) { idxy1 = 0; } if(idxy1 >= level.yTiles) { idxy1 = level.yTiles; } if(idxy2 < 0) { idxy2 = 0; } if(idxy2 >= level.yTiles) { idxy2 = level.yTiles; } // ----- start drawing ----- QPainter p(&buf.image); USE_ANTI_ALIASING(p,true); p.setOpacity(getOpacity()/100.0); p.translate(-pp); QFile file(filename); file.open(QIODevice::ReadOnly); QDataStream stream(&file); stream.setByteOrder(QDataStream::LittleEndian); for(int idxy = idxy1; idxy < idxy2; idxy++) { if(map->needsRedraw()) { break; } for(int idxx = idxx1; idxx < idxx2; idxx++) { if(map->needsRedraw()) { break; } quint32 tag; quint32 len; quint64 offset = level.getOffsetJpeg(idxx, idxy); file.seek(offset); stream >> tag >> len; QImage img; img.load(&file,"JPG"); if(img.isNull()) { continue; } qreal imgw = img.width(); qreal imgh = img.height(); // derive tile's corner coordinate QPolygonF l(4); l[0].rx() = xref1 + idxx * tileSizeX * level.xscale; l[0].ry() = yref1 + idxy * tileSizeY * level.yscale; l[1].rx() = xref1 + (idxx * tileSizeX + imgw) * level.xscale; l[1].ry() = yref1 + idxy * tileSizeY * level.yscale; l[2].rx() = xref1 + (idxx * tileSizeX + imgw) * level.xscale; l[2].ry() = yref1 + (idxy * tileSizeY + imgh) * level.yscale; l[3].rx() = xref1 + idxx * tileSizeX * level.xscale; l[3].ry() = yref1 + (idxy * tileSizeY + imgh) * level.yscale; pj_transform(pjsrc,pjtar, 1, 0, &l[0].rx(), &l[0].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[1].rx(), &l[1].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[2].rx(), &l[2].ry(), 0); pj_transform(pjsrc,pjtar, 1, 0, &l[3].rx(), &l[3].ry(), 0); drawTile(img, l, p); } } } qmapshack-1.5.1/src/map/CMapMAP.h000644 001750 000144 00000005254 12622435723 017366 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CMAPMAP_H #define CMAPMAP_H #include "map/IMap.h" #include "map/mapsforge/types.h" #include class CMapDraw; class CMapMAP : public IMap { public: CMapMAP(const QString& filename, CMapDraw *parent); virtual ~CMapMAP(); void draw(IDrawContext::buffer_t& buf); private: enum exce_e {eErrOpen, eErrAccess, errFormat, errAbort}; struct exce_t { exce_t(exce_e err, const QString& msg) : err(err), msg(msg) { } exce_e err; QString msg; }; struct layer_t { quint8 baseZoom; quint8 minZoom; quint8 maxZoom; quint64 offsetSubFile; quint64 sizeSubFile; }; enum header_flags_e { eHeaderFlagDebugInfo = 0x80 ,eHeaderFlagStartPosition = 0x40 ,eHeaderFlagStartZoomLevel = 0x20 ,eHeaderFlagLanguage = 0x10 ,eHeaderFlagComment = 0x08 ,eHeaderFlagCreator = 0x04 }; struct header_t { header_t() : latStart(0), lonStart(0), zoomStart(0) { } char signature[20]; quint32 sizeHeader; quint32 version; quint64 sizeFile; quint64 timestamp; qint32 minLat; qint32 minLon; qint32 maxLat; qint32 maxLon; quint16 sizeTile; utf8 projection; quint8 flags; // optional fields qint32 latStart; qint32 lonStart; quint8 zoomStart; utf8 language; utf8 comment; utf8 creator; QStringList tagsPOIs; QStringList tagsWays; }; QList layers; void readBasics(); QString filename; header_t header; /// top left point of the map QPointF ref1; /// bottom right point of the map QPointF ref2; }; #endif //CMAPMAP_H qmapshack-1.5.1/src/map/IMapPropSetup.h000644 001750 000144 00000002414 12622435723 020713 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IMAPPROPSETUP_H #define IMAPPROPSETUP_H #include class IMap; class CMapDraw; class IMapPropSetup : public QWidget { Q_OBJECT public: IMapPropSetup(IMap * mapfile, CMapDraw * map); virtual ~IMapPropSetup(); protected slots: virtual void slotPropertiesChanged()= 0; protected: IMap * mapfile; CMapDraw * map; }; #endif //IMAPPROPSETUP_H qmapshack-1.5.1/src/map/OSM_Topo.tms000644 001750 000144 00000001315 12527654570 020224 0ustar00oeichlerusers000000 000000 OSM D-Land TK 50 3 18 Open Topo Map http://a.tile.opentopomap.org/%1/%2/%3.png 3 11 Trails http://tile.waymarkedtrails.org/hiking/%1/%2/%3.png 3 9 Whatever Map data: (c) OpenStreetMap contributors, ODbL | Rendering: (c) OpenTopoMap, CC-BY-SA | Trails by tile.waymarkedtrails.org qmapshack-1.5.1/src/map/cache/CDiskCache.h000644 001750 000144 00000003344 12622435723 021172 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDISKCACHE_H #define CDISKCACHE_H #include "map/cache/IDiskCache.h" #include #include #include class QTimer; class CDiskCache : public IDiskCache { Q_OBJECT public: CDiskCache(const QString& path, qint32 size, qint32 days, QObject *parent); virtual ~CDiskCache(); virtual void store(const QString& key, QImage& img); virtual void restore(const QString& key, QImage& img); virtual bool contains(const QString& key); private slots: void slotCleanup(); private: QDir dir; qint32 size; qint32 expiration; /// hash table to cache images as files on disc QHash table; /// hash table to cache loaded images in memory QHash cache; QTimer * timer; QImage dummy {256,256, QImage::Format_ARGB32}; }; #endif //CDISKCACHE_H qmapshack-1.5.1/src/map/cache/CDiskCache.cpp000644 001750 000144 00000010417 12622435717 021527 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CDiskCache.h" #include CDiskCache::CDiskCache(const QString &path, qint32 size, qint32 days, QObject * parent) : IDiskCache(parent) , dir(path) , size(size) , expiration(days) { dummy.fill(Qt::transparent); dir.mkpath(dir.path()); QFileInfoList files = dir.entryInfoList(QStringList("*.png"), QDir::Files); foreach(const QFileInfo &fileinfo, files) { QString hash = fileinfo.baseName(); table[hash] = fileinfo.fileName(); } timer = new QTimer(this); timer->setSingleShot(false); timer->start(20000); connect(timer, SIGNAL(timeout()), this, SLOT(slotCleanup())); } CDiskCache::~CDiskCache() { } void CDiskCache::store(const QString& key, QImage& img) { QMutexLocker lock(&mutex); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(key.toLatin1()); QString hash = md5.result().toHex(); QString filename = QString("%1.png").arg(hash); if(!img.isNull()) { img.save(dir.absoluteFilePath(filename)); table[hash] = filename; cache[hash] = img; } else { cache[hash] = dummy; } } void CDiskCache::restore(const QString& key, QImage& img) { QMutexLocker lock(&mutex); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(key.toLatin1()); QString hash = md5.result().toHex(); if(cache.contains(hash)) { img = cache[hash]; } else if(table.contains(hash)) { img.load(dir.absoluteFilePath(table[hash])); if(!cache.contains(hash)) { cache[hash] = img; } } else { img = QImage(); } } bool CDiskCache::contains(const QString& key) { QMutexLocker lock(&mutex); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(key.toLatin1()); QString hash = md5.result().toHex(); return table.contains(hash) || cache.contains(hash); } void CDiskCache::slotCleanup() { QMutexLocker lock(&mutex); QFileInfoList files = dir.entryInfoList(QStringList("*.png"), QDir::Files); QDateTime now = QDateTime::currentDateTime(); int days = expiration; qint32 maxSize = size * 1024 * 1024; qint32 tmpSize = 0; // expire old files and calculate cache size foreach(const QFileInfo &fileinfo, files) { if(fileinfo.lastModified().daysTo(now) > days) { QString hash = fileinfo.baseName(); table.remove(hash); cache.remove(hash); QFile::remove(fileinfo.absoluteFilePath()); qDebug() << "remove old tile" << fileinfo.lastModified() << fileinfo.absoluteFilePath(); } else { tmpSize += fileinfo.size(); } } if(tmpSize > maxSize) { files = dir.entryInfoList(QStringList("*.png"), QDir::Files, QDir::Time|QDir::Reversed); // if cache is still too large remove oldest files foreach(const QFileInfo &fileinfo, files) { QString hash = fileinfo.baseName(); table.remove(hash); cache.remove(hash); QFile::remove(fileinfo.absoluteFilePath()); qDebug() << "remove" << fileinfo.lastModified() << fileinfo.absoluteFilePath(); tmpSize -= fileinfo.size(); if(tmpSize < maxSize) { break; } } } } qmapshack-1.5.1/src/map/cache/IDiskCache.h000644 001750 000144 00000002456 12622435723 021203 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDISKCACHE_H #define IDISKCACHE_H #include #include class IDiskCache : public QObject { public: IDiskCache(QObject *parent); virtual ~IDiskCache(); virtual void store(const QString& key, QImage& img) = 0; virtual void restore(const QString& key, QImage& img) = 0; virtual bool contains(const QString& key) = 0; protected: QMutex mutex; }; #endif //IDISKCACHE_H qmapshack-1.5.1/src/map/cache/IDiskCache.cpp000644 001750 000144 00000002006 12622435717 021530 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "IDiskCache.h" IDiskCache::IDiskCache(QObject *parent) : QObject(parent) { } IDiskCache::~IDiskCache() { } qmapshack-1.5.1/src/map/CMapList.cpp000644 001750 000144 00000011223 12622435717 020213 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "map/CMapDraw.h" #include "map/CMapItem.h" #include "map/CMapList.h" #include void CMapTreeWidget::dragEnterEvent(QDragEnterEvent * e) { collapseAll(); QTreeWidget::dragEnterEvent(e); } void CMapTreeWidget::dragMoveEvent(QDragMoveEvent * e) { CMapItem * item = dynamic_cast(itemAt(e->pos())); if(item && item->isActivated()) { e->setDropAction(Qt::MoveAction); QTreeWidget::dragMoveEvent(e); } else { e->setDropAction(Qt::IgnoreAction); } } void CMapTreeWidget::dropEvent (QDropEvent * e) { CMapItem * item = dynamic_cast(currentItem()); if(item) { item->showChildren(false); } QTreeWidget::dropEvent(e); if(item) { item->showChildren(true); } emit sigChanged(); } CMapList::CMapList(QWidget *parent) : QWidget(parent) { setupUi(this); connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint))); connect(treeWidget, SIGNAL(sigChanged()), SIGNAL(sigChanged())); connect(actionActivate, SIGNAL(triggered()), this, SLOT(slotActivate())); connect(pushMapHonk, SIGNAL(clicked()), this, SLOT(slotMapHonk())); menu = new QMenu(this); menu->addAction(actionActivate); } CMapList::~CMapList() { } void CMapList::clear() { treeWidget->clear(); } int CMapList::count() { return treeWidget->topLevelItemCount(); } CMapItem * CMapList::item(int i) { return dynamic_cast(treeWidget->topLevelItem(i)); } void CMapList::updateHelpText() { if(treeWidget->topLevelItemCount() == 0) { labelIcon->show(); pushMapHonk->show(); labelHelpFillMapList->show(); labelHelpActivateMap->hide(); } else { pushMapHonk->hide(); labelHelpFillMapList->hide(); CMapItem * item = dynamic_cast(treeWidget->topLevelItem(0)); if(item && item->isActivated()) { labelIcon->hide(); labelHelpActivateMap->hide(); } else { labelIcon->show(); labelHelpActivateMap->show(); } } } void CMapList::slotActivate() { CMapItem * item = dynamic_cast(treeWidget->currentItem()); if(item == 0) { return; } bool activated = item->toggleActivate(); if(!activated) { treeWidget->setCurrentItem(0); } updateHelpText(); } void CMapList::slotContextMenu(const QPoint& point) { CMapItem * item = dynamic_cast(treeWidget->currentItem()); if(item == 0) { return; } bool activated = item->isActivated(); actionActivate->setChecked(activated); actionActivate->setText(activated ? tr("Deactivate") : tr("Activate")); QPoint p = treeWidget->mapToGlobal(point); menu->exec(p); } void saveResource(const QString& name, QDir& dir) { QFile resource1(QString("://map/%1").arg(name)); resource1.open(QIODevice::ReadOnly); QFile file(dir.absoluteFilePath(name)); file.open(QIODevice::WriteOnly); file.write(resource1.readAll()); file.close(); } void CMapList::slotMapHonk() { QString path = QFileDialog::getExistingDirectory(CMainWindow::getBestWidgetForParent(), tr("Where do you want to store maps?"), QDir::homePath()); if(path.isEmpty()) { return; } QDir dir(path); saveResource("WorldSat.wmts", dir); saveResource("WorldTopo.wmts", dir); saveResource("OpenStreetMap.tms", dir); saveResource("OSM_Topo.tms", dir); saveResource("OpenCycleMap.tms", dir); CMapDraw::setupMapPath(path); CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->setScales(CCanvas::eScalesSquare); } } qmapshack-1.5.1/src/map/IMapPropSetup.ui000644 001750 000144 00000021054 12527654570 021111 0ustar00oeichlerusers000000 000000 IMapPropSetup 0 0 403 226 Form 0 3 3 3 3 <html><head/><body><p>Change opacity of map</p></body></html> Qt::Horizontal 3 <html><head/><body><p>Click to use current scale as minimum scale to display the map.</p></body></html> ... :/icons/8x8/bullet_green.png :/icons/8x8/bullet_red.png:/icons/8x8/bullet_green.png 8 8 true <html><head/><body><p>Control the range of scale the map is displayed. Use the two buttons left and right to define the actual scale as either minimum or maximum scale.</p></body></html> true <html><head/><body><p>Click to use current scale as maximum scale to display the map.</p></body></html> ... :/icons/8x8/bullet_green.png :/icons/8x8/bullet_red.png:/icons/8x8/bullet_green.png 8 8 true QFrame::NoFrame QFrame::Plain 3 0 0 0 0 Areas Lines Points QFrame::NoFrame QFrame::Raised 3 0 0 0 0 QFormLayout::AllNonFixedFieldsGrow 3 3 Cache Size (MB) 100 1000 100 Expiration (Days) 1 14 - Cache Path QFrame::NoFrame QFrame::Raised 3 0 0 0 0 0 0 QFrame::StyledPanel qmapshack-1.5.1/src/map/garmin/CGarminStrTblUtf8.cpp000644 001750 000144 00000004645 12622435717 023250 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CGarminStrTblUtf8.h" #include CGarminStrTblUtf8::CGarminStrTblUtf8(const quint16 codepage, const quint8 mask, QObject * parent) : IGarminStrTbl(codepage, mask, parent) { } CGarminStrTblUtf8::~CGarminStrTblUtf8() { } void CGarminStrTblUtf8::get(CFileExt& file, quint32 offset, type_e t, QStringList& labels) { labels.clear(); offset = calcOffset(file, offset, t); if(offset == 0xFFFFFFFF) { return; } if(offset > (quint32)sizeLBL1) { //qWarning() << "Index into string table to large" << hex << offset << dataLBL.size() << hdrLbl->addr_shift << hdrNet->net1_addr_shift; return; } QByteArray data; quint32 size = (sizeLBL1 - offset) < 200 ? (sizeLBL1 - offset) : 200; readFile(file, offsetLBL1 + offset, size, data); char * lbl = data.data(); char * pBuffer = buffer; *pBuffer = 0; while(*lbl != 0) { if((unsigned)*lbl >= 0x1B && (unsigned)*lbl <= 0x1F) { *pBuffer = 0; if(strlen(buffer)) { labels << codec->toUnicode(buffer); pBuffer = buffer; *pBuffer = 0; } ++lbl; continue; } else if((unsigned)*lbl < 0x07) { ++lbl; continue; } else { *pBuffer++ = *lbl++; } } *pBuffer = 0; if(strlen(buffer)) { // qDebug() << QString(buffer); labels << codec->toUnicode(buffer); } } qmapshack-1.5.1/src/map/garmin/CGarminStrTbl6.cpp000644 001750 000144 00000010205 12622435717 022554 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CGarminStrTbl6.h" #include const char CGarminStrTbl6::str6tbl1[] = { ' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' ,0,0,0,0,0 ,'0','1','2','3','4','5','6','7','8','9' ,0,0,0,0,0,0 }; const char CGarminStrTbl6::str6tbl2[] = { //@ ! " # $ % & ' ( ) * + , - . / '@','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/' ,0,0,0,0,0,0,0,0,0,0 //: ; < = > ? ,':',';','<','=','>','?' ,0,0,0,0,0,0,0,0,0,0,0 //[ \ ] ^ _ ,'[','\\',']','^','_' }; const char CGarminStrTbl6::str6tbl3[] = { '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' }; CGarminStrTbl6::CGarminStrTbl6(const quint16 codepage, const quint8 mask, QObject * parent) : IGarminStrTbl(codepage, mask, parent) { } CGarminStrTbl6::~CGarminStrTbl6() { } void CGarminStrTbl6::fill() { quint32 tmp; if(bits < 6) { tmp = *p++; reg |= tmp << (24 - bits); bits += 8; } } void CGarminStrTbl6::get(CFileExt& file, quint32 offset, type_e t, QStringList& labels) { labels.clear(); offset = calcOffset(file, offset,t); if(offset == 0xFFFFFFFF) { return; } if(offset > (quint32)sizeLBL1) { // qWarning() << "Index into string table to large" << hex << offset << dataLBL.size() << hdrLbl->addr_shift << hdrNet->net1_addr_shift; return; } quint8 c1 = 0; quint8 c2 = 0; quint32 idx = 0; reg = 0; bits = 0; QByteArray data; quint32 size = (sizeLBL1 - offset) < 200 ? (sizeLBL1 - offset) : 200; readFile(file, offsetLBL1 + offset, size, data); p = (quint8*)data.data(); fill(); while(idx < sizeof(buffer)) { c1 = reg >> 26; reg <<= 6; bits -= 6; fill(); //terminator if(c1 > 0x2F) { break; } c2 = str6tbl1[c1]; if(c2 == 0) { if(c1 == 0x1C) { c1 = reg >> 26; reg <<= 6; bits -= 6; fill(); buffer[idx++] = str6tbl2[c1]; } else if(c1 == 0x1B) { c1 = reg >> 26; reg <<= 6; bits -= 6; fill(); buffer[idx++] = str6tbl3[c1]; } else if(c1 > 0x1C && c1 < 0x20) { buffer[idx] = 0; if(strlen(buffer)) { if (codepage != 0) { labels << codec->toUnicode(buffer); } else { labels << buffer; } } idx = 0; buffer[0] = 0; } } else { buffer[idx++] = str6tbl1[c1]; } } buffer[idx] = 0; if(strlen(buffer)) { if (codepage != 0) { labels << codec->toUnicode(buffer); } else { labels << buffer; } } } qmapshack-1.5.1/src/map/garmin/CGarminPolygon.cpp000644 001750 000144 00000031647 12622435717 022720 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2006, 2007 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Garmin and MapSource are registered trademarks or trademarks of Garmin Ltd. or one of its subsidiaries. This source is based on John Mechalas documentation "Garmin IMG File Format" found at sourceforge. The missing bits and error were rectified by the source code of Konstantin Galichsky (kg@geopainting.com), http://www.geopainting.com **********************************************************************************************/ #include "CGarminPolygon.h" #include "Garmin.h" #include "helpers/Platform.h" #include #undef DEBUG_SHOW_POLY_DATA #undef DEBUG_SHOW_POLY2_DATA #undef DEBUG_SHOW_POLY_PTS struct sign_info_t { quint32 sign_info_bits = 2; bool x_has_sign = true; bool nx = false; bool y_has_sign = true; bool ny = false; }; quint32 CGarminPolygon::cnt = 0; qint32 CGarminPolygon::maxVecSize = 0; quint32 CGarminPolygon::decode(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, bool line, const quint8 * pData, const quint8 * pEnd) { quint32 bytes_total = 10; // bitstream has a two byte length bool two_byte_len; // coordinates use extra bit - ??? have never seen it bool extra_bit; // bitstream length quint16 bs_len = 0; // base bit size info for coordinates quint8 bs_info; // bits per x coord. quint32 bx; // bits per y coord. quint32 by; const quint8 * const pStart = pData; labels.clear(); coords.resize(0); coords.reserve(maxVecSize); /* poly_type for polylines: bit 0..5 type bit 6 direction for polygons: bit 0..6 type bit 7 bitstream_len is two bytes (true) */ type = *pData++; two_byte_len = type & 0x80; if(line) { direction = (type & 0x40); type &= 0x3F; } else { type &= 0x7F; } /* label info bit 0..21 off set into LBL section bit 22 use extra bit for coordinates bit 23 use label data of NET section */ lbl_info = gar_ptr_load(uint24_t, pData); lbl_in_NET = lbl_info & 0x800000; extra_bit = lbl_info & 0x400000; lbl_info = lbl_info & 0x3FFFFF; pData += 3; // qDebug() << hex << lbl_in_NET << extra_bit << lbl_info; // delta longitude and latitude dLng = gar_ptr_load(uint16_t, pData); pData += 2; dLat = gar_ptr_load(uint16_t, pData); pData += 2; // bitstream length if(two_byte_len) { bs_len = gar_ptr_load(uint16_t, pData); pData += 2; bytes_total += bs_len + 1; } else { #if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) (quint8&)bs_len = *pData++; #else bs_len = *pData++; #endif bytes_total += bs_len; } if(pEnd && ((pStart + bytes_total) > pEnd)) { return bytes_total; } /* bitstream info bit 0..3 base bits longitude bit 4..7 base bits latitude */ bs_info = *pData++; ; //if(extra_bit) qWarning("extrabit"); #ifdef DEBUG_SHOW_POLY_DATA qDebug() << "type: " << type << hex << tmpType; qDebug() << "two byte: " << two_byte_len; qDebug() << "extra bit: " << extra_bit; qDebug() << "dLng: " << dLng; qDebug() << "dLat: " << dLat; qDebug() << "len: " << bs_len; qDebug() << "info: " << hex << bs_info; qDebug() << "1st byte: " << hex << *pData; qDebug() << "bytes total" << bytes_total; #endif // DEBUG_SHOW_POLY_DATA sign_info_t signinfo; bits_per_coord(bs_info,*pData,bx,by,signinfo, false); CShiftReg sr(pData,bs_len,bx,by,extra_bit,signinfo); qint32 x1,y1,x = 0,y = 0; bool isNegative = (iCenterLon >= 0x800000); // first point x1 = ((qint32)dLng << shift) + iCenterLon; y1 = ((qint32)dLat << shift) + iCenterLat; if(x1 >= 0x800000 && !isNegative) { x1 = 0x7fffff; } coords << QPointF(GARMIN_RAD(x1), GARMIN_RAD(y1)); // next points while(sr.get(x,y)) { x1 += (x << shift); y1 += (y << shift); if(x1 >= 0x800000 && !isNegative) { x1 = 0x7fffff; } coords << QPointF(GARMIN_RAD(x1), GARMIN_RAD(y1)); } id = cnt++; // qDebug() << "<<<" << id; if(maxVecSize < coords.size()) { maxVecSize = coords.size(); } if(coords.size() * 1.2 < maxVecSize) { coords.squeeze(); } pixel = coords; return bytes_total; } quint32 CGarminPolygon::decode2(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, bool line, const quint8 * pData, const quint8 * pEnd) { quint32 bytes_total = 6; // bitstream length quint16 bs_len = 0; // type and subtype quint32 subtype; // base bit size info for coordinates quint8 bs_info; // bits per x coord. quint32 bx; // bits per y coord. quint32 by; const quint8 * const pStart = pData; labels.clear(); coords.resize(0); coords.reserve(maxVecSize); type = *pData++; subtype = *pData++; ; type = 0x10000 + (quint16(type) << 8) + (subtype & 0x1f); hasV2Label = subtype & 0x20; // delta longitude and latitude dLng = gar_ptr_load(uint16_t, pData); pData += 2; dLat = gar_ptr_load(uint16_t, pData); pData += 2; if((*pData & 0x1) == 0) { bs_len = gar_ptr_load(uint16_t, pData); bs_len = (bs_len >> 2) - 1; pData += 2; bytes_total += 2; } else { bs_len = ((*pData) >> 1) - 1; pData += 1; bytes_total += 1; } bs_info = *pData++; bytes_total += bs_len + 1; #ifdef DEBUG_SHOW_POLY2_DATA qDebug() << "type: " << type << hex << type; qDebug() << "dLng: " << dLng; qDebug() << "dLat: " << dLat; qDebug() << "len: " << bs_len; qDebug() << "info: " << hex << bs_info; qDebug() << "1st byte: " << hex << *pData; qDebug() << "bytes total" << bytes_total; #endif // DEBUG_SHOW_POLY_DATA sign_info_t signinfo; bits_per_coord(bs_info,*pData,bx,by,signinfo, true); // qDebug() << ">>" << bs_len << bytes_total << (pEnd - pStart); // assert((pEnd - pStart) >= bytes_total); if(((quint32)(pEnd - pStart)) < bytes_total) { return pEnd - pStart; } CShiftReg sr(pData,bs_len,bx,by,false,signinfo); qint32 x1,y1,x = 0,y = 0; bool isNegative = (iCenterLon >= 0x800000); // first point x1 = ((qint32)dLng << shift) + iCenterLon; y1 = ((qint32)dLat << shift) + iCenterLat; if(x1 >= 0x800000 && !isNegative) { x1 = 0x7fffff; } coords << QPointF(GARMIN_RAD(x1), GARMIN_RAD(y1)); // next points while(sr.get(x,y)) { x1 += (x << shift); y1 += (y << shift); if(x1 >= 0x800000 && !isNegative) { x1 = 0x7fffff; } // xy.u = GARMIN_RAD(x1); // xy.v = GARMIN_RAD(y1); // if(qAbs(xy.v) > 2*M_PI || qAbs(xy.u) > 2*M_PI) // { // qDebug() << "bam"; // qDebug() << xy.u << xy.v << pStart << pEnd << (pEnd - pStart) << (cnt + 1) << line; // //assert(0); // } //#ifdef DEBUG_SHOW_POLY_PTS // qDebug() << xy.u << xy.v << (RAD_TO_DEG * xy.u) << (RAD_TO_DEG * xy.v); //#endif coords << QPointF(GARMIN_RAD(x1), GARMIN_RAD(y1)); } if(hasV2Label) { quint32 offset = gar_ptr_load(uint24_t, pData + bs_len); bytes_total += 3; lbl_info = offset & 0x3FFFFF; } else { lbl_info = 0; } id = cnt++; // qDebug() << "<<<" << id; if(maxVecSize < coords.size()) { maxVecSize = coords.size(); } if(coords.size() * 1.2 < maxVecSize) { coords.squeeze(); } pixel = coords; return bytes_total; } void CGarminPolygon::bits_per_coord(quint8 base, quint8 bfirst, quint32& bx, quint32& by, sign_info_t& signinfo, bool isVer2) { bool x_sign_same, y_sign_same; quint8 mask = 0x1; // x_sign_same = bfirst & 0x1; x_sign_same = bfirst & mask; mask <<= 1; if(x_sign_same) { signinfo.x_has_sign = false; // signinfo.nx = bfirst & 0x2; signinfo.nx = bfirst & mask; mask <<= 1; ++signinfo.sign_info_bits; } else { signinfo.x_has_sign = true; } bx = bits_per_coord(base & 0x0F, signinfo.x_has_sign); // y_sign_same = x_sign_same ? (bfirst & 0x04) : (bfirst & 0x02); y_sign_same = bfirst & mask; mask <<= 1; if(y_sign_same) { signinfo.y_has_sign = false; // signinfo.ny = x_sign_same ? bfirst & 0x08 : bfirst & 0x04; signinfo.ny = bfirst & mask; mask <<= 1; ++signinfo.sign_info_bits; } else { signinfo.y_has_sign = true; } by = bits_per_coord((base>>4) & 0x0F, signinfo.y_has_sign); // Determine extra bits. if(isVer2) { ++signinfo.sign_info_bits; if(bfirst & mask) { // qDebug() << "V2"; ++bx; ++by; } } } // extract bits per coordinate int CGarminPolygon::bits_per_coord(quint8 base, bool is_signed) { int n = 2; if ( base <= 9 ) { n+= base; } else { n+= (2*base-9); } if ( is_signed ) { ++n; } return n; } CShiftReg::CShiftReg(const quint8* pData, quint32 n, quint32 bx, quint32 by, bool extra_bit, sign_info_t& si) : reg(0) , pData(pData) , bytes(n) , xmask(0xFFFFFFFF) , ymask(0xFFFFFFFF) , xsign(1) , ysign(1) , xsign2(2) , ysign2(2) , bits(0) , bits_per_x(bx) , bits_per_y(by) , bits_per_coord(bx + by + (extra_bit ? 1 : 0)) , sinfo(si) , extraBit(extra_bit) { // create bit masks xmask = (xmask << (32-bx)) >> (32-bx); ymask = (ymask << (32-by)) >> (32-by); xsign <<= (bits_per_x - 1); ysign <<= (bits_per_y - 1); xsign2 = xsign<<1; ysign2 = ysign<<1; // add sufficient bytes for the first coord. pair fill(bits_per_coord + si.sign_info_bits); // get rid of sign setup bytes reg >>= si.sign_info_bits; bits -= si.sign_info_bits; } bool CShiftReg::get(qint32& x, qint32& y) { x = y = 0; if(bits < (bits_per_coord)) { return false; } // don't know what to do with it -> skip extra bit if(extraBit) { reg >>= 1; bits -= 1; } if(sinfo.x_has_sign) { qint32 tmp = 0; while(1) { tmp = reg & xmask; if(tmp != xsign) { break; } x += tmp - 1; reg >>= bits_per_x; bits -= bits_per_x; fill(bits_per_y + bits_per_x); } if(tmp < xsign) { x += tmp; } else { x = tmp - (xsign2) - x; } } else { x = reg & xmask; if(sinfo.nx) { x = -x; } } reg >>= bits_per_x; bits -= bits_per_x; // take y coord., add sign if necessary, shift register by bits per y coord. if(sinfo.y_has_sign) { qint32 tmp = 0; while(1) { tmp = reg & ymask; if(tmp != ysign) { break; } y += tmp - 1; reg >>= bits_per_y; bits -= bits_per_y; fill(bits_per_y); } if(tmp < ysign) { y += tmp; } else { y = tmp - (ysign2) - y; } } else { y = reg & ymask; if(sinfo.ny) { y = -y; } } reg >>= bits_per_y; bits -= bits_per_y; // fill register until it has enough bits for one coord. pair again fill(bits_per_coord); return true; } void CShiftReg::fill(quint32 b) { quint64 tmp = 0; while((bits < b) && bytes) { #if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) (quint8&)tmp = *pData++; #else tmp = *pData++; #endif --bytes; reg |= tmp << bits; bits += 8; } } qmapshack-1.5.1/src/map/garmin/CGarminTyp.h000644 001750 000144 00000016156 12622435723 021505 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2009 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGARMINTYP_H #define CGARMINTYP_H #include class CGarminTyp { public: CGarminTyp() = default; virtual ~CGarminTyp() = default; enum label_type_e { eStandard = 0 ,eNone = 1 ,eSmall = 2 ,eNormal = 3 ,eLarge = 4 }; struct polyline_property { polyline_property() : type(0) , penLineDay(Qt::magenta,3) , penLineNight(Qt::magenta,3) , hasBorder(false) , penBorderDay(Qt::NoPen) , penBorderNight(Qt::NoPen) , hasPixmap(false) , labelType(eStandard) , colorLabelDay(Qt::black) , colorLabelNight(Qt::black) , known(false) { } polyline_property(quint16 type, const QPen& penLineDay, const QPen& penLineNight, const QPen& penBorderDay, const QPen& penBorderNight) : type(type) , penLineDay(penLineDay) , penLineNight(penLineNight) , hasBorder(true) , penBorderDay(penBorderDay) , penBorderNight(penBorderNight) , hasPixmap(false) , labelType(eStandard) , colorLabelDay(Qt::black) , colorLabelNight(Qt::black) , known(true) { } polyline_property(quint16 type, const QColor& color, int width, Qt::PenStyle style) : type(type) , penLineDay(QPen(color, width, style)) , penLineNight(penLineDay) , hasBorder(false) , penBorderDay(Qt::NoPen) , penBorderNight(Qt::NoPen) , hasPixmap(false) , labelType(eStandard) , colorLabelDay(Qt::black) , colorLabelNight(Qt::black) , known(true) { } quint16 type; QPen penLineDay; QPen penLineNight; bool hasBorder; QPen penBorderDay; QPen penBorderNight; bool hasPixmap; QImage imgDay; QImage imgNight; QMap strings; label_type_e labelType; QColor colorLabelDay; QColor colorLabelNight; bool known; }; struct polygon_property { polygon_property() : type(0) , pen(Qt::magenta) , brushDay(Qt::magenta, Qt::BDiagPattern) , brushNight(Qt::magenta, Qt::BDiagPattern) , labelType(eStandard) , colorLabelDay(Qt::black) , colorLabelNight(Qt::black) , known(false) { } polygon_property(quint16 type, const Qt::PenStyle pensty, const QColor& brushColor, Qt::BrushStyle pattern) : type(type) , pen(pensty) , brushDay(brushColor, pattern) , brushNight(brushColor.darker(150), pattern) , labelType(eStandard) , colorLabelDay(Qt::black) , colorLabelNight(Qt::black) , known(true) { pen.setWidth(1); } polygon_property(quint16 type, const QColor& penColor, const QColor& brushColor, Qt::BrushStyle pattern) : type(type) , pen(penColor,1) , brushDay(brushColor, pattern) , brushNight(brushColor.darker(150), pattern) , labelType(eStandard) , colorLabelDay(Qt::black) , colorLabelNight(Qt::black) , known(true) { } quint16 type; QPen pen; QBrush brushDay; QBrush brushNight; QMap strings; label_type_e labelType; QColor colorLabelDay; QColor colorLabelNight; bool known; }; struct point_property { point_property() : labelType(eStandard) { } QImage imgDay; QImage imgNight; QMap strings; label_type_e labelType; QColor colorLabelDay; QColor colorLabelNight; }; /// decode typ file /** This pure virtual function has to be implemented in every subclass. It should be the only public function needed. The typ file is read and it's content is stored in the passed map/list objects. @param in input data stream @param polygons reference to polygon properties map @param polylines reference to polyline properties map @param drawOrder reference to list of polygon draw orders @param points reference to point properties map */ bool decode(const QByteArray& array, QMap& polygons, QMap& polylines, QList& drawOrder, QMap& points); QSet getLanguages() { return languages; } quint16 getFid() { return fid; } quint16 getPid() { return pid; } protected: virtual bool parseHeader(QDataStream& in); virtual bool parseDrawOrder(QDataStream& in, QList& drawOrder); virtual bool parsePolygon(QDataStream& in, QMap& polygons); virtual bool parsePolyline(QDataStream& in, QMap& polylines); virtual bool parsePoint(QDataStream& in, QMap& points); QTextCodec * getCodec(quint16 codepage); void decodeBitmap(QDataStream &in, QImage &img, int w, int h, int bpp); bool decodeBppAndBytes(int ncolors, int w, int flags, int& bpp, int& bytes); bool decodeColorTable(QDataStream& in, QImage& img, int ncolors, int maxcolor, bool hasAlpha); struct typ_section_t { typ_section_t() : dataOffset(0), dataLength(0), arrayOffset(0), arrayModulo(0), arraySize(0) { } quint32 dataOffset; quint32 dataLength; quint32 arrayOffset; quint16 arrayModulo; quint32 arraySize; }; quint16 version; quint16 codepage; quint16 year; quint8 month; quint8 day; quint8 hour; quint8 minutes; quint8 seconds; quint16 fid; quint16 pid; typ_section_t sectPoints; typ_section_t sectPolylines; typ_section_t sectPolygons; typ_section_t sectOrder; QSet languages; }; #endif //CGARMINTYP_H qmapshack-1.5.1/src/map/garmin/CGarminPoint.h000644 001750 000144 00000003620 12622435723 022012 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2006, 2007 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Garmin and MapSource are registered trademarks or trademarks of Garmin Ltd. or one of its subsidiaries. This source is based on John Mechalas documentation "Garmin IMG File Format" found at sourceforge. The missing bits and error were rectified by the source code of Konstantin Galichsky (kg@geopainting.com), http://www.geopainting.com **********************************************************************************************/ #ifndef CGARMINPOINT_H #define CGARMINPOINT_H #include #include #include class CGarminTile; class CGarminPoint { public: CGarminPoint() = default; virtual ~CGarminPoint() = default; quint32 decode(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, const quint8 * pData); quint32 decode2(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, const quint8 * pData, const quint8 * pEnd); quint32 type = 0; bool isLbl6 = false; bool hasSubType = false; //QString label; QPointF pos; QStringList labels; quint32 lbl_ptr = 0xFFFFFFFF; }; #endif //CGARMINPOINT_H qmapshack-1.5.1/src/map/garmin/CGarminStrTbl6.h000644 001750 000144 00000003023 12622435723 022216 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGARMINSTRTBL6_H #define CGARMINSTRTBL6_H #include "IGarminStrTbl.h" class CGarminStrTbl6 : public IGarminStrTbl { public: CGarminStrTbl6(const quint16 codepage, const quint8 mask, QObject * parent); virtual ~CGarminStrTbl6(); void get(CFileExt& file, quint32 offset, type_e t, QStringList& info); private: static const char str6tbl1[]; static const char str6tbl2[]; static const char str6tbl3[]; void fill(); /// temp shift reg buffer quint32 reg; /// bits in buffer quint32 bits; /// pointer to current data; const quint8 * p; }; #endif //CGARMINSTRTBL6_H qmapshack-1.5.1/src/map/garmin/IGarminStrTbl.cpp000644 001750 000144 00000007454 12622435717 022510 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "IGarminStrTbl.h" #include "helpers/CFileExt.h" #include "helpers/Platform.h" #include IGarminStrTbl::IGarminStrTbl(const quint16 codepage, const quint8 mask, QObject * parent) : QObject(parent) , codepage(codepage) , mask(mask) { if(codepage != 0) { if(1250 <= codepage && codepage <= 1258) { char strcp[64]; sprintf(strcp,"Windows-%i",codepage); codec = QTextCodec::codecForName(strcp); } else if(codepage == 950) { codec = QTextCodec::codecForName("Big5"); } else if(codepage == 850) { codec = QTextCodec::codecForName("IBM 850"); } else if(codepage == 65001) { codec = QTextCodec::codecForName("UTF-8"); } else { qDebug() << "unknown codepage:" << codepage << "0x" << hex << codepage; codec = QTextCodec::codecForName("Latin1"); } } mask32 = mask; mask32 <<= 8; mask32 |= mask; mask32 <<= 8; mask32 |= mask; mask32 <<= 8; mask32 |= mask; mask64 = mask32; mask64 <<= 32; mask64 |= mask32; } IGarminStrTbl::~IGarminStrTbl() { } void IGarminStrTbl::readFile(CFileExt &file, quint32 offset, quint32 size, QByteArray& data) { if(offset + size > file.size()) { // throw exce_t(eErrOpen, tr("Failed to read: ") + file.filename()); return; } data = QByteArray::fromRawData(file.data(offset,size), size); // wenn mask == 0 ist kein xor noetig if(mask == 0) { return; } #ifdef HOST_IS_64_BIT quint64 * p64 = (quint64*)data.data(); for(quint32 i = 0; i < size/8; ++i) { *p64++ ^= mask64; } quint32 rest = size % 8; quint8 * p = (quint8*)p64; #else quint32 * p32 = (quint32*)data.data(); for(quint32 i = 0; i < size/4; ++i) { *p32++ ^= mask32; } quint32 rest = size % 4; quint8 * p = (quint8*)p32; #endif for(quint32 i = 0; i < rest; ++i) { *p++ ^= mask; } } quint32 IGarminStrTbl::calcOffset(CFileExt& file, const quint32 offset, type_e t) { quint32 newOffset = offset; if(t == poi) { QByteArray buffer; readFile(file, offsetLBL6 + offset, sizeof(uint32_t), buffer); newOffset = gar_ptr_load(uint32_t, buffer.data()); newOffset = (newOffset & 0x003FFFFF); } else if(t == net) { if(offsetNET1 == 0) { return 0xFFFFFFFF; } QByteArray data; readFile(file, offsetNET1 + (offset << addrshift2), sizeof(uint32_t), data); newOffset = gar_ptr_load(uint32_t, data.data()); if(newOffset & 0x00400000) { return 0xFFFFFFFF; } newOffset = (newOffset & 0x003FFFFF); } newOffset <<= addrshift1; // qDebug() << hex << newOffset; return newOffset; } qmapshack-1.5.1/src/map/garmin/CGarminPoint.cpp000644 001750 000144 00000006036 12622435717 022354 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2006, 2007 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Garmin and MapSource are registered trademarks or trademarks of Garmin Ltd. or one of its subsidiaries. This source is based on John Mechalas documentation "Garmin IMG File Format" found at sourceforge. The missing bits and error were rectified by the source code of Konstantin Galichsky (kg@geopainting.com), http://www.geopainting.com **********************************************************************************************/ #include "CGarminPoint.h" #include "Garmin.h" #include "helpers/Platform.h" #include quint32 CGarminPoint::decode(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, const quint8 * pData) { qint16 dLng, dLat; type = (quint16)(*pData) << 8; ++pData; lbl_ptr = gar_ptr_load(uint24_t, pData); hasSubType = lbl_ptr & 0x00800000; isLbl6 = lbl_ptr & 0x00400000; lbl_ptr = lbl_ptr & 0x003FFFFF; pData += 3; dLng = gar_ptr_load(int16_t, pData); pData += 2; dLat = gar_ptr_load(int16_t, pData); pData += 2; qint32 x1,y1; x1 = ((qint32)dLng << shift) + iCenterLon; y1 = ((qint32)dLat << shift) + iCenterLat; pos = QPointF(GARMIN_RAD(x1),GARMIN_RAD(y1)); #ifdef DEBUG_SHOW_POINTS qDebug() << x1 << y1 << point.u << point.v; #endif if(hasSubType) { type |= *pData; return 9; } return 8; } quint32 CGarminPoint::decode2(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, const quint8 * pData, const quint8 * pEnd) { qint16 dLng, dLat; quint32 byte_size = 6; quint8 subtype; type = (quint16)(*pData) << 8; ++pData; subtype = (quint16)(*pData); ++pData; type = 0x10000 + type + (subtype & 0x1F); if(subtype & 0x80) { byte_size += 1; } dLng = gar_ptr_load(int16_t, pData); pData += 2; dLat = gar_ptr_load(int16_t, pData); pData += 2; qint32 x1,y1; x1 = ((qint32)dLng << shift) + iCenterLon; y1 = ((qint32)dLat << shift) + iCenterLat; pos = QPointF(GARMIN_RAD(x1),GARMIN_RAD(y1)); if(subtype & 0x20) { byte_size += 3; lbl_ptr = gar_ptr_load(uint24_t, pData); isLbl6 = lbl_ptr & 0x00400000; lbl_ptr &= 0x003FFFFF; } return byte_size; } qmapshack-1.5.1/src/map/garmin/CGarminTyp.cpp000644 001750 000144 00000113523 12624063017 022027 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2009 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . NOTE: this code is based on the opensource typ file generator at http://ati.land.cz/gps/typdecomp/editor.cgi **********************************************************************************************/ #include "CMainWindow.h" #include "map/garmin/CGarminTyp.h" #include "units/IUnit.h" #include #include #include #include #undef DBG bool CGarminTyp::decode(const QByteArray& array, QMap& polygons, QMap& polylines, QList& drawOrder, QMap& points) { QDataStream in(array); in.setVersion(QDataStream::Qt_4_5); in.setByteOrder( QDataStream::LittleEndian); /* Read typ file descriptor */ quint16 descriptor; in >> descriptor; qDebug() << "descriptor" << hex << descriptor; if(!parseHeader(in)) { return false; } if(!parseDrawOrder(in, drawOrder)) { return false; } if(!parsePolygon(in, polygons)) { return false; } if(!parsePolyline(in, polylines)) { return false; } if(!parsePoint(in, points)) { return false; } return true; } QTextCodec * CGarminTyp::getCodec(quint16 codepage) { QTextCodec * codec = QTextCodec::codecForName(QString("CP%1").arg(codepage).toLatin1()); if(codepage == 65001) { codec = QTextCodec::codecForName("UTF-8"); } return codec; } bool CGarminTyp::parseHeader(QDataStream& in) { int i; QString garmintyp; quint8 byte; for(i = 0; i < 10; ++i) { in >> byte; garmintyp.append(byte); } garmintyp.append(0); if(garmintyp != "GARMIN TYP") { qDebug() << "CMapTDB::readTYP() not a known typ file"; return false; } /* reading typ creation date string */ in.device()->seek(0x0c); in >> version >> year >> month >> day >> hour >> minutes >> seconds >> codepage; month -= 1; /* Month are like Microsoft starting 0 ? */ year += 1900; /* Reading points / lines / polygons struct */ in >> sectPoints.dataOffset >> sectPoints.dataLength; in >> sectPolylines.dataOffset >> sectPolylines.dataLength; in >> sectPolygons.dataOffset >> sectPolygons.dataLength; in >> pid >> fid; /* Read Array datas */ in >> sectPoints.arrayOffset >> sectPoints.arrayModulo >> sectPoints.arraySize; in >> sectPolylines.arrayOffset >> sectPolylines.arrayModulo >> sectPolylines.arraySize; in >> sectPolygons.arrayOffset >> sectPolygons.arrayModulo >> sectPolygons.arraySize; in >> sectOrder.arrayOffset >> sectOrder.arrayModulo >> sectOrder.arraySize; #ifdef DBG qDebug() << "Version:" << version << "Codepage:" << codepage; qDebug() << "PID" << hex << pid << "FID" << hex << fid; qDebug() << "Points doff/dlen/aoff/amod/asize:" << hex << "\t" << sectPoints.dataOffset << "\t" << sectPoints.dataLength << "\t" << sectPoints.arrayOffset << "\t" << sectPoints.arrayModulo << "\t" << sectPoints.arrayOffset; qDebug() << "Polylines doff/dlen/aoff/amod/asize:" << hex << "\t" << sectPolylines.dataOffset << "\t" << sectPolylines.dataLength << "\t" << sectPolylines.arrayOffset << "\t" << sectPolylines.arrayModulo << "\t" << sectPolylines.arrayOffset; qDebug() << "Polygons doff/dlen/aoff/amod/asize:" << hex << "\t" << sectPolygons.dataOffset << "\t" << sectPolygons.dataLength << "\t" << sectPolygons.arrayOffset << "\t" << sectPolygons.arrayModulo << "\t" << sectPolygons.arrayOffset; qDebug() << "Order doff/dlen/aoff/amod/asize:" << hex << "\t" << sectOrder.dataOffset << "\t" << sectOrder.dataLength << "\t" << sectOrder.arrayOffset << "\t" << sectOrder.arrayModulo << "\t" << sectOrder.arrayOffset; #endif return true; } bool CGarminTyp::parseDrawOrder(QDataStream& in, QList& drawOrder) { if(sectOrder.arrayModulo != 5) { return false; } if((sectOrder.arraySize % sectOrder.arrayModulo) != 0) { return true; } in.device()->seek(sectOrder.arrayOffset); int i,n; quint8 typ; quint32 subtyp; int count=1; const int N = sectOrder.arraySize / sectOrder.arrayModulo; for (i = 0; i < N; i++) { in >> typ >> subtyp; // qDebug() << hex << typ << subtyp; if (typ == 0) { count++; } else if(subtyp == 0) { #ifdef DBG qDebug() << QString("Type 0x%1 is priority %2").arg(typ,0,16).arg(count); #endif int idx = drawOrder.indexOf(typ); if(idx != NOIDX) { drawOrder.move(idx,0); } } else { quint32 exttyp = 0x010000 | (typ << 8); quint32 mask = 0x1; for(n=0; n < 0x20; ++n) { if(subtyp & mask) { drawOrder.push_front(exttyp|n); #ifdef DBG qDebug() << QString("Type 0x%1 is priority %2").arg(exttyp|n,0,16).arg(count); #endif } mask = mask << 1; } } } #ifdef DBG for(unsigned i = 0; i < drawOrder.size(); ++i) { if(i && i%16 == 0) { printf(" \n"); } printf("%06X ", drawOrder[i]); } printf(" \n"); #endif return true; } bool CGarminTyp::parsePolygon(QDataStream& in, QMap& polygons) { bool tainted = false; if(!sectPolygons.arrayModulo || ((sectPolygons.arraySize % sectPolygons.arrayModulo) != 0)) { return true; } QTextCodec * codec = getCodec(codepage); const int N = sectPolygons.arraySize / sectPolygons.arrayModulo; for (int element = 0; element < N; element++) { quint16 t16_1 = 0, t16_2, subtyp; quint8 t8; quint32 typ, offset=0; bool hasLocalization = false; bool hasTextColor = false; quint8 ctyp; QImage xpmDay(32,32, QImage::Format_Indexed8); QImage xpmNight(32,32, QImage::Format_Indexed8); quint8 r,g,b; quint8 langcode; in.device()->seek( sectPolygons.arrayOffset + (sectPolygons.arrayModulo * element ) ); if (sectPolygons.arrayModulo == 5) { in >> t16_1 >> t16_2 >> t8; offset = t16_2|(t8<<16); } else if (sectPolygons.arrayModulo == 4) { in >> t16_1 >> t16_2; offset = t16_2; } else if (sectPolygons.arrayModulo == 3) { in >> t16_1 >> t8; offset = t8; } t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11); typ = t16_2 & 0x7F; subtyp = t16_1 & 0x1F; if(t16_1 & 0x2000) { typ = 0x10000|(typ << 8)|subtyp; } in.device()->seek(sectPolygons.dataOffset + offset); in >> t8; hasLocalization = t8 & 0x10; hasTextColor = t8 & 0x20; ctyp = t8 & 0x0F; #ifdef DBG qDebug() << "Polygon typ:" << hex << typ << "ctype:" << ctyp << "offset:" << (sectPolygons.dataOffset + offset) << "orig data:" << t16_1; #endif polygon_property& property = polygons[typ]; switch(ctyp) { case 0x01: { // day & night single color in >> b >> g >> r; property.brushDay = QBrush(qRgb(r,g,b)); in >> b >> g >> r; property.brushNight = QBrush(qRgb(r,g,b)); // night and day color for line? in >> b >> g >> r; property.pen = QPen(QBrush(qRgb(r,g,b)),2); in >> b >> g >> r; property.known = true; break; } case 0x06: { // day & night single color in >> b >> g >> r; property.brushDay = QBrush(qRgb(r,g,b)); property.brushNight = QBrush(qRgb(r,g,b)); property.pen = Qt::NoPen; property.known = true; break; } case 0x07: { // day single color & night single color in >> b >> g >> r; property.brushDay = QBrush(qRgb(r,g,b)); in >> b >> g >> r; property.brushNight = QBrush(qRgb(r,g,b)); property.pen = Qt::NoPen; property.known = true; break; } case 0x08: { // day & night two color xpmDay.setColorCount(2); in >> b >> g >> r; xpmDay.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpmDay.setColor(0, qRgb(r,g,b) ); decodeBitmap(in, xpmDay, 32, 32, 1); property.brushDay.setTextureImage(xpmDay); property.brushNight.setTextureImage(xpmDay); property.pen = Qt::NoPen; property.known = true; break; } case 0x09: { //day two color & night two color xpmDay.setColorCount(2); xpmNight.setColorCount(2); in >> b >> g >> r; xpmDay.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpmDay.setColor(0, qRgb(r,g,b) ); in >> b >> g >> r; xpmNight.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpmNight.setColor(0, qRgb(r,g,b) ); decodeBitmap(in, xpmDay, 32, 32, 1); memcpy(xpmNight.bits(), xpmDay.bits(), (32*32)); property.brushDay.setTextureImage(xpmDay); property.brushNight.setTextureImage(xpmNight); property.pen = Qt::NoPen; property.known = true; break; } case 0x0B: { // day one color, transparent & night two color xpmDay.setColorCount(2); xpmNight.setColorCount(2); in >> b >> g >> r; xpmDay.setColor(1, qRgb(r,g,b) ); xpmDay.setColor(0, qRgba(255,255,255,0) ); in >> b >> g >> r; xpmNight.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpmNight.setColor(0, qRgb(r,g,b) ); decodeBitmap(in, xpmDay, 32, 32, 1); memcpy(xpmNight.bits(), xpmDay.bits(), (32*32)); property.brushDay.setTextureImage(xpmDay); property.brushNight.setTextureImage(xpmNight); property.pen = Qt::NoPen; property.known = true; break; } case 0x0D: { // day two color & night one color, transparent xpmDay.setColorCount(2); xpmNight.setColorCount(2); in >> b >> g >> r; xpmDay.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpmDay.setColor(0, qRgb(r,g,b) ); in >> b >> g >> r; xpmNight.setColor(1, qRgb(r,g,b) ); xpmNight.setColor(0, qRgba(255,255,255,0) ); decodeBitmap(in, xpmDay, 32, 32, 1); memcpy(xpmNight.bits(), xpmDay.bits(), (32*32)); property.brushDay.setTextureImage(xpmDay); property.brushNight.setTextureImage(xpmNight); property.pen = Qt::NoPen; property.known = true; break; } case 0x0E: { // day & night one color, transparent xpmDay.setColorCount(2); in >> b >> g >> r; xpmDay.setColor(1, qRgb(r,g,b) ); xpmDay.setColor(0, qRgba(255,255,255,0) ); decodeBitmap(in, xpmDay, 32, 32, 1); property.brushDay.setTextureImage(xpmDay); property.brushNight.setTextureImage(xpmDay); property.pen = Qt::NoPen; property.known = true; break; } case 0x0F: { // day one color, transparent & night one color, transparent xpmDay.setColorCount(2); xpmNight.setColorCount(2); in >> b >> g >> r; xpmDay.setColor(1, qRgb(r,g,b) ); xpmDay.setColor(0, qRgba(255,255,255,0) ); in >> b >> g >> r; xpmNight.setColor(1, qRgb(r,g,b) ); xpmNight.setColor(0, qRgba(255,255,255,0) ); decodeBitmap(in, xpmDay, 32, 32, 1); memcpy(xpmNight.bits(), xpmDay.bits(), (32*32)); property.brushDay.setTextureImage(xpmDay); property.brushNight.setTextureImage(xpmNight); property.pen = Qt::NoPen; property.known = true; break; } default: if(!tainted) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Warning..."), QObject::tr("This is a typ file with unknown polygon encoding. Please report!"), QMessageBox::Abort, QMessageBox::Abort); tainted = true; } qDebug() << "Failed polygon:" << typ << subtyp << hex << typ << subtyp << ctyp; } if(hasLocalization) { qint16 len; quint8 n = 1; in >> t8; len = t8; if(!(t8 & 0x01)) { n = 2; in >> t8; len |= t8 << 8; } len -= n; while(len > 0) { QByteArray str; in >> langcode; languages << langcode; len -= 2*n; while(len > 0) { in >> t8; len -= 2*n; if(t8 == 0) { break; } str += t8; } property.strings[langcode] = codec->toUnicode(str); #ifdef DBG qDebug() << len << langcode << property.strings[langcode]; #endif } } if(hasTextColor) { in >> t8; property.labelType = (label_type_e)(t8 & 0x07); if(t8 & 0x08) { in >> r >> g >> b; property.colorLabelDay = qRgb(r,g,b); } if(t8 & 0x10) { in >> r >> g >> b; property.colorLabelNight = qRgb(r,g,b); } #ifdef DBG qDebug() << "ext. label: type" << property.labelType << "day" << property.colorLabelDay << "night" << property.colorLabelNight; #endif } } return true; } bool CGarminTyp::parsePolyline(QDataStream& in, QMap& polylines) { bool tainted = false; if(!sectPolylines.arrayModulo || ((sectPolylines.arraySize % sectPolylines.arrayModulo) != 0)) { return true; } QTextCodec * codec = getCodec(codepage); const int N = sectPolylines.arraySize / sectPolylines.arrayModulo; for (int element = 0; element < N; element++) { quint16 t16_1 = 0, t16_2, subtyp; quint8 t8_1, t8_2; quint32 typ, offset=0; bool hasLocalization = false; bool hasTextColor = false; //bool renderMode = false; quint8 ctyp, rows; quint8 r,g,b; quint8 langcode; in.device()->seek( sectPolylines.arrayOffset + (sectPolylines.arrayModulo * element ) ); if (sectPolylines.arrayModulo == 5) { in >> t16_1 >> t16_2 >> t8_1; offset = t16_2|(t8_1<<16); } else if (sectPolylines.arrayModulo == 4) { in >> t16_1 >> t16_2; offset = t16_2; } else if (sectPolylines.arrayModulo == 3) { in >> t16_1 >> t8_1; offset = t8_1; } t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11); typ = t16_2 & 0x7F; subtyp = t16_1 & 0x1F; if(t16_1 & 0x2000) { typ = 0x10000|(typ << 8)|subtyp; } in.device()->seek(sectPolylines.dataOffset + offset); in >> t8_1 >> t8_2; ctyp = t8_1 & 0x07; rows = t8_1 >> 3; hasLocalization = t8_2 & 0x01; //renderMode = t8_2 & 0x02; hasTextColor = t8_2 & 0x04; #ifdef DBG qDebug() << "Polyline typ:" << hex << typ << "ctyp:" << ctyp << "offset:" << (sectPolylines.dataOffset + offset) << "orig data:" << t16_1; #endif polyline_property& property = polylines[typ]; #ifdef DBG qDebug() << "rows" << rows << "t8_2" << hex << t8_2; #endif switch(ctyp) { case 0x00: { if(rows) { QImage xpm(32, rows, QImage::Format_Indexed8 ); in >> b >> g >> r; xpm.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpm.setColor(0, qRgb(r,g,b) ); decodeBitmap(in, xpm, 32, rows, 1); property.imgDay = xpm; property.imgNight = xpm; property.hasPixmap = true; property.known = true; } else { quint8 w1, w2; in >> b >> g >> r; property.penLineDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penLineNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penBorderDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> w1 >> w2; property.penLineDay.setWidth(w1); property.penLineNight.setWidth(w1); property.penBorderDay.setWidth(w2); property.penBorderNight.setWidth(w2); property.hasBorder = w2 > w1; property.hasPixmap = false; property.known = true; } break; } case 0x01: { if(rows) { QImage xpm1(32, rows, QImage::Format_Indexed8 ); QImage xpm2(32, rows, QImage::Format_Indexed8 ); in >> b >> g >> r; xpm1.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpm1.setColor(0, qRgb(r,g,b) ); in >> b >> g >> r; xpm2.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpm2.setColor(0, qRgb(r,g,b) ); decodeBitmap(in, xpm1, 32, rows, 1); memcpy(xpm2.bits(), xpm1.bits(), (32*rows)); property.imgDay = xpm1; property.imgNight = xpm2; property.hasPixmap = true; property.known = true; } else { quint8 w1, w2; in >> b >> g >> r; property.penLineDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penBorderDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penLineNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penBorderNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> w1 >> w2; property.penLineDay.setWidth(w1); property.penLineNight.setWidth(w1); property.penBorderDay.setWidth(w2); property.penBorderNight.setWidth(w2); property.hasBorder = w2 > w1; property.hasPixmap = false; property.known = true; } break; } case 0x03: { if(rows) { QImage xpm1(32, rows, QImage::Format_Indexed8 ); QImage xpm2(32, rows, QImage::Format_Indexed8 ); in >> b >> g >> r; xpm1.setColor(1, qRgb(r,g,b) ); xpm1.setColor(0, qRgba(255,255,255,0) ); in >> b >> g >> r; xpm2.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpm2.setColor(0, qRgb(r,g,b) ); decodeBitmap(in, xpm1, 32, rows, 1); memcpy(xpm2.bits(), xpm1.bits(), (32*rows)); property.imgDay = xpm1; property.imgNight = xpm2; property.hasPixmap = true; property.known = true; } else { quint8 w1, w2; in >> b >> g >> r; property.penLineDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderDay = QPen(Qt::NoPen); in >> b >> g >> r; property.penLineNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penBorderNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> w1 >> w2; property.penLineDay.setWidth(w1); property.penLineNight.setWidth(w1); property.penBorderDay.setWidth(w2); property.penBorderNight.setWidth(w2); property.hasBorder = w2 > w1; property.hasPixmap = false; property.known = true; } break; } case 0x05: { if(rows) { QImage xpm1(32, rows, QImage::Format_Indexed8 ); QImage xpm2(32, rows, QImage::Format_Indexed8 ); in >> b >> g >> r; xpm1.setColor(1, qRgb(r,g,b) ); in >> b >> g >> r; xpm1.setColor(0, qRgb(r,g,b) ); in >> b >> g >> r; xpm2.setColor(1, qRgb(r,g,b) ); xpm2.setColor(0, qRgba(255,255,255,0) ); decodeBitmap(in, xpm1, 32, rows, 1); memcpy(xpm2.bits(), xpm1.bits(), (32*rows)); property.imgDay = xpm1; property.imgNight = xpm2; property.hasPixmap = true; property.known = true; } else { quint8 w1; in >> b >> g >> r; property.penLineDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penBorderDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); in >> b >> g >> r; property.penLineNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderNight = QPen(Qt::NoPen); in >> w1; property.penLineDay.setWidth(w1); property.penLineNight.setWidth(w1); property.hasBorder = false; property.hasPixmap = false; property.known = true; } break; } case 0x06: { if(rows) { QImage xpm(32, rows, QImage::Format_Indexed8 ); in >> b >> g >> r; xpm.setColor(1, qRgb(r,g,b) ); xpm.setColor(0, qRgba(255,255,255,0) ); decodeBitmap(in, xpm, 32, rows, 1); property.imgDay = xpm; property.imgNight = xpm; property.hasPixmap = true; property.known = true; } else { quint8 w1; in >> b >> g >> r; property.penLineDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderDay = QPen(Qt::NoPen); property.penLineNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderNight = QPen(Qt::NoPen); in >> w1; property.penLineDay.setWidth(w1); property.penLineNight.setWidth(w1); property.hasBorder = false; property.hasPixmap = false; property.known = true; } break; } case 0x07: { if(rows) { QImage xpm1(32, rows, QImage::Format_Indexed8 ); QImage xpm2(32, rows, QImage::Format_Indexed8 ); in >> b >> g >> r; xpm1.setColor(1, qRgb(r,g,b) ); xpm1.setColor(0, qRgba(255,255,255,0) ); in >> b >> g >> r; xpm2.setColor(1, qRgb(r,g,b) ); xpm2.setColor(0, qRgba(255,255,255,0) ); decodeBitmap(in, xpm1, 32, rows, 1); memcpy(xpm2.bits(), xpm1.bits(), (32*rows)); property.imgDay = xpm1; property.imgNight = xpm2; property.hasPixmap = true; property.known = true; } else { quint8 w1; in >> b >> g >> r; property.penLineDay = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderDay = QPen(Qt::NoPen); in >> b >> g >> r; property.penLineNight = QPen(QBrush(qRgb(r,g,b)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); property.penBorderNight = QPen(Qt::NoPen); in >> w1; property.penLineDay.setWidth(w1); property.penLineNight.setWidth(w1); property.hasBorder = false; property.hasPixmap = false; property.known = true; } break; } default: if(!tainted) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Warning..."), QObject::tr("This is a typ file with unknown polyline encoding. Please report!"), QMessageBox::Abort, QMessageBox::Abort); tainted = true; } qDebug() << "Failed polyline" << hex << ":" << typ << ctyp << rows; continue; } property.imgDay = property.imgDay.convertToFormat(QImage::Format_ARGB32_Premultiplied); property.imgNight = property.imgNight.convertToFormat(QImage::Format_ARGB32_Premultiplied); if(hasLocalization) { qint16 len; quint8 n = 1; in >> t8_1; len = t8_1; if(!(t8_1 & 0x01)) { n = 2; in >> t8_1; len |= t8_1 << 8; } len -= n; while(len > 0) { QByteArray str; in >> langcode; languages << langcode; len -= 2*n; while(len > 0) { in >> t8_1; len -= 2*n; if(t8_1 == 0) { break; } str += t8_1; } property.strings[langcode] = codec->toUnicode(str); #ifdef DBG qDebug() << len << langcode << property.strings[langcode]; #endif } } if(hasTextColor) { in >> t8_1; property.labelType = (label_type_e)(t8_1 & 0x07); if(t8_1 & 0x08) { in >> r >> g >> b; property.colorLabelDay = qRgb(r,g,b); } if(t8_1 & 0x10) { in >> r >> g >> b; property.colorLabelNight = qRgb(r,g,b); } #ifdef DBG qDebug() << "ext. label: type" << property.labelType << "day" << property.colorLabelDay << "night" << property.colorLabelNight; #endif } if(property.hasPixmap) { property.imgDay = property.imgDay.mirrored(false,true); property.imgNight = property.imgNight.mirrored(false,true); } } return true; } bool CGarminTyp::decodeBppAndBytes(int ncolors, int w, int flags, int& bpp, int& bytes) { switch(flags) { case 0x00: { if(ncolors < 3) { bpp = ncolors; } else if(ncolors == 3) { bpp = 2; } else if(ncolors < 16) { bpp = 4; } else if(ncolors < 256) { bpp = 8; } else { return false; } break; } case 0x10: { if(ncolors == 0) { bpp = 1; } else if(ncolors < 3) { bpp = 2; } else if(ncolors < 15) { bpp = 4; } else if(ncolors < 256) { bpp = 8; } else { return false; } break; } case 0x20: { if(ncolors == 0) { bpp = 16; } else if(ncolors < 3) { bpp = ncolors; } else if(ncolors < 4) { bpp = 2; } else if(ncolors < 16) { bpp = 4; } else if(ncolors < 256) { bpp = 8; } else { return false; } break; } default: return false; } bytes = (w * bpp) / 8; if( (w * bpp) & 0x07 ) { ++bytes; } return true; } bool CGarminTyp::decodeColorTable(QDataStream& in, QImage& img, int ncolors, int maxcolor, bool hasAlpha) { img.setColorCount(ncolors); if(hasAlpha) { int i; quint8 byte; quint32 bits = 0; quint32 reg = 0; quint32 mask = 0x000000FF; for (i = 0; i < ncolors; i++) { while(bits < 28) { in >> byte; mask = 0x000000FF << bits; reg = reg & (~mask); reg = reg | (byte << bits); bits += 8; } img.setColor(i, qRgba((reg >> 16) & 0x0FF, (reg >> 8) & 0x0FF, reg & 0x0FF, ~((reg >> 24) & 0x0F) << 4)); reg = reg >> 28; bits -= 28; } for(; i < maxcolor; ++i) { img.setColor(i,qRgba(0,0,0,0)); } } else { int i; quint8 r,g,b; for(i = 0; i < ncolors; ++i) { in >> b >> g >> r; img.setColor(i, qRgb(r,g,b)); } for(; i < maxcolor; ++i) { img.setColor(i,qRgba(0,0,0,0)); } } return true; } void CGarminTyp::decodeBitmap(QDataStream &in, QImage &img, int w, int h, int bpp) { int x = 0,j = 0; quint8 color; if(bpp == 0) { return; } for (int y = 0; y < h; y++) { while ( x < w ) { in >> color; for ( int i = 0; (i < (8 / bpp)) && (x < w); i++ ) { int value; if ( i > 0 ) { value = (color >>= bpp); } else { value = color; } if ( bpp == 4) { value = value & 0xf; } if ( bpp == 2) { value = value & 0x3; } if ( bpp == 1) { value = value & 0x1; } img.setPixel(x,y,value); // qDebug() << QString("value(%4) pixel at (%1,%2) is 0x%3 j is %5").arg(x).arg(y).arg(value,0,16).arg(color).arg(j); x += 1; } j += 1; } x = 0; } } bool CGarminTyp::parsePoint(QDataStream& in, QMap& points) { // bool tainted = false; if(!sectPoints.arrayModulo || ((sectPoints.arraySize % sectPoints.arrayModulo) != 0)) { return true; } QTextCodec * codec = getCodec(codepage); const int N = sectPoints.arraySize / sectPoints.arrayModulo; for (int element=0; element < N; element++) { quint16 t16_1 = 0, t16_2, subtyp; quint8 t8_1; quint32 typ, offset=0; bool hasLocalization = false; bool hasTextColor = false; quint8 langcode; quint8 r,g,b; in.device()->seek( sectPoints.arrayOffset + (sectPoints.arrayModulo * element ) ); if (sectPoints.arrayModulo == 5) { in >> t16_1 >> t16_2 >> t8_1; offset = t16_2|(t8_1<<16); } else if (sectPoints.arrayModulo == 4) { in >> t16_1 >> t16_2; offset = t16_2; } else if (sectPoints.arrayModulo == 3) { in >> t16_1 >> t8_1; offset = t8_1; } t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11); typ = t16_2 & 0x7FF; subtyp = t16_1 & 0x01F; if(t16_1 & 0x2000) { typ = 0x10000|(typ << 8)|subtyp; } else { typ = (typ << 8) + subtyp; } in.device()->seek( sectPoints.dataOffset + offset ); int bpp = 0, wbytes = 0; quint8 w, h, ncolors, ctyp; in >> t8_1 >> w >> h >> ncolors >> ctyp; hasLocalization = t8_1 & 0x04; hasTextColor = t8_1 & 0x08; t8_1 = t8_1 & 0x03; #ifdef DBG qDebug() << "Point typ:" << hex << typ << "ctyp:" << ctyp <<"offset:" << (sectPoints.dataOffset + offset) << "orig data:" << t16_1; #endif if(!decodeBppAndBytes(ncolors, w, ctyp, bpp, wbytes)) { continue; } #ifdef DBG qDebug() << " " << dec << "w" << w << "h" << h << "ncolors" << ncolors << "bpp" << bpp << "wbytes" << wbytes; #endif if(ctyp == 0x20 || ctyp == 0x00) { if((ncolors == 0) && (bpp >= 16)) { ncolors = w*h; } } point_property& property = points[typ]; QImage imgDay(w,h, QImage::Format_Indexed8 ); QImage imgNight(w,h, QImage::Format_Indexed8 ); if(!decodeColorTable(in, imgDay, ncolors, 1 << bpp, ctyp == 0x20)) { continue; } if(bpp >= 16) { continue; } else { decodeBitmap(in, imgDay, w, h, bpp); property.imgDay = imgDay; } if(t8_1 == 0x03) { in >> ncolors >> ctyp; if(!decodeBppAndBytes(ncolors, w, ctyp, bpp, wbytes)) { continue; } if(!decodeColorTable(in, imgNight, ncolors, 1 << bpp, ctyp == 0x20)) { continue; } decodeBitmap(in, imgNight, w, h, bpp); points[typ].imgNight = imgNight; } else if(t8_1 == 0x02) { in >> ncolors >> ctyp; if(!decodeBppAndBytes(ncolors, w, ctyp, bpp, wbytes)) { continue; } if(!decodeColorTable(in, imgDay, ncolors, 1 << bpp, ctyp == 0x20)) { continue; } property.imgNight = imgDay; } else { property.imgNight = imgDay; } if(hasLocalization) { qint16 len; quint8 n = 1; in >> t8_1; len = t8_1; if(!(t8_1 & 0x01)) { n = 2; in >> t8_1; len |= t8_1 << 8; } len -= n; while(len > 0) { QByteArray str; in >> langcode; languages << langcode; len -= 2*n; while(len > 0) { in >> t8_1; len -= 2*n; if(t8_1 == 0) { break; } str += t8_1; } property.strings[langcode] = codec->toUnicode(str); #ifdef DBG qDebug() << len << langcode << property.strings[langcode]; #endif } } if(hasTextColor) { in >> t8_1; property.labelType = (label_type_e)(t8_1 & 0x07); if(t8_1 & 0x08) { in >> r >> g >> b; property.colorLabelDay = qRgb(r,g,b); } if(t8_1 & 0x10) { in >> r >> g >> b; property.colorLabelNight = qRgb(r,g,b); } #ifdef DBG qDebug() << "ext. label: type" << property.labelType << "day" << property.colorLabelDay << "night" << property.colorLabelNight; #endif } } return true; } qmapshack-1.5.1/src/map/garmin/CGarminStrTblUtf8.h000644 001750 000144 00000002424 12622435723 022703 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGARMINSTRTBLUTF8_H #define CGARMINSTRTBLUTF8_H #include "IGarminStrTbl.h" class CGarminStrTblUtf8 : public IGarminStrTbl { public: CGarminStrTblUtf8(const quint16 codepage, const quint8 mask, QObject * parent); virtual ~CGarminStrTblUtf8(); void get(CFileExt& file, quint32 offset, type_e t, QStringList& info); }; #endif //CGARMINSTRTBLUTF8_H qmapshack-1.5.1/src/map/garmin/CGarminStrTbl8.h000644 001750 000144 00000002403 12622435723 022221 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGARMINSTRTBL8_H #define CGARMINSTRTBL8_H #include "IGarminStrTbl.h" class CGarminStrTbl8 : public IGarminStrTbl { public: CGarminStrTbl8(const quint16 codepage, const quint8 mask, QObject * parent); virtual ~CGarminStrTbl8(); void get(CFileExt& file, quint32 offset, type_e t, QStringList& info); }; #endif //CGARMINSTRTBL8_H qmapshack-1.5.1/src/map/garmin/CGarminPolygon.h000644 001750 000144 00000007316 12624061373 022354 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2006, 2007 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Garmin and MapSource are registered trademarks or trademarks of Garmin Ltd. or one of its subsidiaries. This source is based on John Mechalas documentation "Garmin IMG File Format" found at sourceforge. The missing bits and error were rectified by the source code of Konstantin Galichsky (kg@geopainting.com), http://www.geopainting.com **********************************************************************************************/ #ifndef CGARMINPOLYGON_H #define CGARMINPOLYGON_H #include #ifdef __MINGW32__ #undef LP #endif #include #include struct subdiv_desc_t; struct sign_info_t; class CGarminPolygon { public: CGarminPolygon() = default; virtual ~CGarminPolygon() = default; quint32 decode(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, bool line, const quint8 * pData, const quint8 * pEnd); quint32 decode2(qint32 iCenterLon, qint32 iCenterLat, quint32 shift, bool line, const quint8 * pData, const quint8 * pEnd); quint32 type = 0; /// direction of line (polyline, only) bool direction = false; /// the label offset quint32 lbl_info = 0; /// true if label offset has to be used in NET section bool lbl_in_NET = false; /// bool hasV2Label = false; /// delta longitude from subdivision center qint16 dLng = 0; /// delta latitude from subdivision center qint16 dLat = 0; /** @brief the actual polyline points as [pixel] @note After decode() or decode2() the content will be the same as coords. It is up to the render object to convert it into pixel coordinates */ QPolygonF pixel; /// the actual polyline points as longitude / latitude [rad] QPolygonF coords; quint32 id = 0; QStringList labels; static quint32 cnt; static qint32 maxVecSize; private: void bits_per_coord(quint8 base, quint8 bfirst, quint32& bx, quint32& by, sign_info_t& signinfo, bool isVer2); int bits_per_coord(quint8 base, bool is_signed); }; class CShiftReg { public: CShiftReg(const quint8* pData, quint32 n, quint32 bx, quint32 by, bool extra_bit, sign_info_t& si); bool get(qint32& x, qint32& y); private: void fill(quint32 bits); /// the register to work on quint64 reg; /// the data stream to get data from const quint8 * pData; /// bytes left in stream quint32 bytes; /// bitmask x coord. quint32 xmask; /// bitmask y coord. quint32 ymask; /// sign bit for x value qint32 xsign; /// sign bit for y value qint32 ysign; /// sign bit * 2 for x value qint32 xsign2; /// sign bit * 2 for y value qint32 ysign2; /// total bits in register quint8 bits; /// bits per x coord. quint8 bits_per_x; /// bits per y coord. quint8 bits_per_y; /// bits per coord. quint8 bits_per_coord; sign_info_t& sinfo; bool extraBit; }; #endif //CGARMINPOLYGON_H qmapshack-1.5.1/src/map/garmin/IGarminStrTbl.h000644 001750 000144 00000004540 12622435723 022143 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IGARMINSTRTBL_H #define IGARMINSTRTBL_H #include class CFileExt; class QByteArray; class QStringList; class IGarminStrTbl : public QObject { public: IGarminStrTbl(const quint16 codepage, const quint8 mask, QObject * parent); virtual ~IGarminStrTbl(); enum type_e {norm,poi,net}; void registerLBL1(const quint32 offset, const quint32 size, const quint8 shift) { offsetLBL1 = offset; sizeLBL1 = size; addrshift1 = shift; } void registerLBL6(const quint32 offset, const quint32 size) { offsetLBL6 = offset; sizeLBL6 = size; } void registerNET1(const quint32 offset, const quint32 size, const quint8 shift) { offsetNET1 = offset; sizeNET1 = size; addrshift2 = shift; } virtual void get(CFileExt& file, quint32 offset, type_e t, QStringList& info) = 0; protected: void readFile(CFileExt &file, quint32 offset, quint32 size, QByteArray& data); quint32 calcOffset(CFileExt& file, const quint32 offset, type_e t); quint32 offsetLBL1 = 0; quint32 sizeLBL1 = 0; quint32 offsetLBL6 = 0; quint32 sizeLBL6 = 0; quint32 offsetNET1 = 0; quint32 sizeNET1 = 0; quint8 addrshift1 = 0; quint8 addrshift2 = 0; // conversion of strings quint16 codepage; QTextCodec * codec = 0; const quint8 mask; quint32 mask32; quint64 mask64; char buffer[1024]; }; #endif //IGARMINSTRTBL_H qmapshack-1.5.1/src/map/garmin/Garmin.h000644 001750 000144 00000002450 12622435723 020675 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef GARMIN_H #define GARMIN_H #include #ifdef __MINGW32__ #undef LP #endif #define GARMIN_DEG(x) ((x) < 0x800000 ? (qreal)(x) * 360.0 / 16777216.0 : (qreal)((x) - 0x1000000) * 360.0 / 16777216.0) #define GARMIN_RAD(x) ((x) < 0x800000 ? (qreal)(x) * (2*M_PI) / 16777216.0 : (qreal)((x) - 0x1000000) * (2*M_PI) / 16777216.0) typedef quint8 quint24[3]; #endif //GARMIN_H qmapshack-1.5.1/src/map/garmin/CGarminStrTbl8.cpp000644 001750 000144 00000005164 12622435717 022566 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2008 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CGarminStrTbl8.h" #include CGarminStrTbl8::CGarminStrTbl8(const quint16 codepage, const quint8 mask, QObject * parent) : IGarminStrTbl(codepage, mask, parent) { } CGarminStrTbl8::~CGarminStrTbl8() { } void CGarminStrTbl8::get(CFileExt& file, quint32 offset, type_e t, QStringList& info) { info.clear(); offset = calcOffset(file, offset, t); if(offset == 0xFFFFFFFF) { return; } if(offset > (quint32)sizeLBL1) { //qWarning() << "Index into string table to large" << hex << offset << dataLBL.size() << hdrLbl->addr_shift << hdrNet->net1_addr_shift; return; } QByteArray data; quint32 size = (sizeLBL1 - offset) < 200 ? (sizeLBL1 - offset) : 200; readFile(file, offsetLBL1 + offset, size, data); char * lbl = data.data(); char * pBuffer = buffer; *pBuffer = 0; while(*lbl != 0) { if((unsigned)*lbl >= 0x1B && (unsigned)*lbl <= 0x1F) { *pBuffer = 0; if(strlen(buffer)) { if (codepage != 0) { info << codec->toUnicode(buffer); } else { info << buffer; } pBuffer = buffer; *pBuffer = 0; } ++lbl; continue; } else if((unsigned)*lbl < 0x07) { ++lbl; continue; } else { *pBuffer++ = *lbl++; } } *pBuffer = 0; if(strlen(buffer)) { if (codepage != 0) { info << codec->toUnicode(buffer); } else { info << buffer; } } } qmapshack-1.5.1/src/map/IMapPathSetup.ui000644 001750 000144 00000013772 12535614175 021071 0ustar00oeichlerusers000000 000000 IMapPathSetup 0 0 450 277 Setup map paths Root path of tile cache for online maps: - ... :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png 22 22 Qt::Horizontal QAbstractItemView::ExtendedSelection ... :/icons/32x32/Add.png:/icons/32x32/Add.png 22 22 false ... :/icons/32x32/DeleteMultiple.png:/icons/32x32/DeleteMultiple.png 22 22 Qt::Vertical 20 40 64 16777215 :/icons/48x48/Help.png Qt::AlignCenter 0 0 - Qt::AlignJustify|Qt::AlignVCenter true 0 0 Qt::Vertical QDialogButtonBox::Cancel|QDialogButtonBox::Ok Help! I want maps! I don't want to read the documentation! buttonBox accepted() IMapPathSetup accept() 248 254 157 274 buttonBox rejected() IMapPathSetup reject() 316 260 286 274 qmapshack-1.5.1/src/map/IMapPropSetup.cpp000644 001750 000144 00000002265 12622435717 021255 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMapDraw.h" #include "IMap.h" #include "IMapPropSetup.h" IMapPropSetup::IMapPropSetup(IMap *mapfile, CMapDraw *map) : mapfile(mapfile) , map(map) { connect(mapfile, SIGNAL(sigPropertiesChanged()), this, SLOT(slotPropertiesChanged())); } IMapPropSetup::~IMapPropSetup() { } qmapshack-1.5.1/src/resources.qrc000644 001750 000144 00000046514 12623413746 020011 0ustar00oeichlerusers000000 000000 icons/8x8/bullet_black.png icons/8x8/bullet_blue.png icons/8x8/bullet_brown.png icons/8x8/bullet_cyan.png icons/8x8/bullet_dark_blue.png icons/8x8/bullet_dark_cyan.png icons/8x8/bullet_dark_gray.png icons/8x8/bullet_dark_green.png icons/8x8/bullet_dark_magenta.png icons/8x8/bullet_dark_red.png icons/8x8/bullet_dark_yellow.png icons/8x8/bullet_gray.png icons/8x8/bullet_green.png icons/8x8/bullet_magenta.png icons/8x8/bullet_orange.png icons/8x8/bullet_red.png icons/8x8/bullet_white.png icons/8x8/bullet_yellow.png icons/16x16/ActNone.png icons/16x16/ActFoot.png icons/16x16/ActCycle.png icons/16x16/ActBike.png icons/16x16/ActCar.png icons/16x16/ActCable.png icons/16x16/ActShip.png icons/16x16/ActSwim.png icons/16x16/ActAero.png icons/32x32/2DFix.png icons/32x32/3DFix.png icons/32x32/Add.png icons/32x32/AddMapWorkspace.png icons/32x32/CloneMapWorkspace.png icons/32x32/Cancel.png icons/32x32/Check.png icons/32x32/DeleteMultiple.png icons/32x32/DeleteOne.png icons/32x32/Down.png icons/32x32/FolderMap.png icons/32x32/FolderDEM.png icons/32x32/Font.png icons/32x32/FromMap.png icons/32x32/Grid.png icons/32x32/GridSetup.png icons/32x32/GridWizzard.png icons/32x32/Help.png icons/32x32/Info.png icons/32x32/Error.png icons/32x32/Map.png icons/32x32/QMapShack.png icons/32x32/MimeIMG.png icons/32x32/MimeJNX.png icons/32x32/MimeMAP.png icons/32x32/MimeRMAP.png icons/32x32/MimeVRT.png icons/32x32/MimeWMTS.png icons/32x32/MimeTMS.png icons/32x32/MimeDemVRT.png icons/32x32/MouseWheel.png icons/32x32/NightDay.png icons/32x32/NoFix.png icons/32x32/Off.png icons/32x32/POIText.png icons/32x32/Reset.png icons/32x32/Right.png icons/32x32/Left.png icons/32x32/Scale.png icons/32x32/SelectColor.png icons/32x32/ToBottom.png icons/32x32/ToTop.png icons/32x32/ToolTip.png icons/32x32/Up.png icons/32x32/SetupMapWorkspace.png icons/32x32/SetupCoordFormat.png icons/32x32/SaveGIS.png icons/32x32/SaveGISAs.png icons/32x32/SaveAllGIS.png icons/32x32/LoadGIS.png icons/32x32/GpxProject.png icons/32x32/QmsProject.png icons/32x32/DBProject.png icons/32x32/Route.png icons/32x32/RouteSetup.png icons/32x32/Close.png icons/32x32/ShowAll.png icons/32x32/ShowNone.png icons/32x32/Track.png icons/32x32/EditDetails.png icons/32x32/EditText.png icons/32x32/TimeZoneSetup.png icons/32x32/UnitSetup.png icons/32x32/Undo.png icons/32x32/Redo.png icons/32x32/Cut.png icons/32x32/CutHistory.png icons/32x32/Copy.png icons/32x32/Move.png icons/32x32/Paste.png icons/32x32/TextLeft.png icons/32x32/TextRight.png icons/32x32/TextCenter.png icons/32x32/TextJustified.png icons/32x32/TextBold.png icons/32x32/TextUnderlined.png icons/32x32/TextItalic.png icons/32x32/Lock.png icons/32x32/UnLock.png icons/32x32/WptMove.png icons/32x32/WptProj.png icons/32x32/WptProx.png icons/32x32/TrkCut.png icons/32x32/TrkProfile.png icons/32x32/Tainted.png icons/32x32/AddWpt.png icons/32x32/AddTrk.png icons/32x32/AddRte.png icons/32x32/AddArea.png icons/32x32/AddProject.png icons/32x32/LineMove.png icons/32x32/PointMove.png icons/32x32/PointHide.png icons/32x32/PointShow.png icons/32x32/AreaMove.png icons/32x32/SelectRange.png icons/32x32/Area.png icons/32x32/Reverse.png icons/32x32/Combine.png icons/32x32/SearchGoogle.png icons/32x32/Start.png icons/32x32/SetEle.png icons/32x32/Pattern.png icons/32x32/Opacity.png icons/32x32/DatabaseSetup.png icons/32x32/DatabaseConvert.png icons/32x32/PathBlue.png icons/32x32/PathOrange.png icons/32x32/PathGreen.png icons/32x32/Link.png icons/32x32/Database.png icons/32x32/Empty.png icons/32x32/Apply.png icons/32x32/Time.png icons/32x32/Print.png icons/32x32/AddImage.png icons/32x32/DelImage.png icons/32x32/ReloadImage.png icons/32x32/Image.png icons/32x32/Device.png icons/32x32/2NavProject.png icons/32x32/VrtBuilder.png icons/32x32/SaveView.png icons/32x32/LoadView.png icons/32x32/Save.png icons/32x32/ProfileToWindow.png icons/32x32/NoGo.png icons/32x32/Bubble.png icons/32x32/MoveArrow.png icons/32x32/SizeArrow.png icons/32x32/A.png icons/32x32/V.png icons/32x32/O.png icons/32x32/RteInstr.png icons/32x32/Zoom.png icons/32x32/PrintSave.png icons/32x32/ActNone.png icons/32x32/ActFoot.png icons/32x32/ActCycle.png icons/32x32/ActBike.png icons/32x32/ActCar.png icons/32x32/ActCable.png icons/32x32/ActShip.png icons/32x32/ActSwim.png icons/32x32/ActAero.png icons/32x32/Activity.png icons/32x32/CSrcSolid.png icons/32x32/CSrcHR.png icons/32x32/CSrcCAD.png icons/32x32/CSrcATemp.png icons/32x32/CSrcWTemp.png icons/32x32/CSrcDepth.png icons/32x32/CSrcSlope.png icons/32x32/CSrcSpeed.png icons/32x32/CSrcElevation.png icons/32x32/CSrcUnknown.png icons/32x32/Progress.png icons/48x48/2DFix.png icons/48x48/3DFix.png icons/48x48/Add.png icons/48x48/AddMapWorkspace.png icons/48x48/CloneMapWorkspace.png icons/48x48/Cancel.png icons/48x48/Check.png icons/48x48/DeleteMultiple.png icons/48x48/DeleteOne.png icons/48x48/Down.png icons/48x48/FolderMap.png icons/48x48/FolderDEM.png icons/48x48/Font.png icons/48x48/FromMap.png icons/48x48/Grid.png icons/48x48/GridSetup.png icons/48x48/GridWizzard.png icons/48x48/Help.png icons/48x48/Info.png icons/48x48/Error.png icons/48x48/Map.png icons/48x48/QMapShack.png icons/48x48/MimeIMG.png icons/48x48/MimeJNX.png icons/48x48/MimeMAP.png icons/48x48/MimeRMAP.png icons/48x48/MimeVRT.png icons/48x48/MimeWMTS.png icons/48x48/MimeTMS.png icons/48x48/MimeDemVRT.png icons/48x48/MouseWheel.png icons/48x48/NightDay.png icons/48x48/NoFix.png icons/48x48/Off.png icons/48x48/POIText.png icons/48x48/Reset.png icons/48x48/Right.png icons/48x48/Left.png icons/48x48/Scale.png icons/48x48/SelectColor.png icons/48x48/ToBottom.png icons/48x48/ToTop.png icons/48x48/ToolTip.png icons/48x48/Up.png icons/48x48/SetupMapWorkspace.png icons/48x48/SetupCoordFormat.png icons/48x48/SaveGIS.png icons/48x48/SaveGISAs.png icons/48x48/SaveAllGIS.png icons/48x48/LoadGIS.png icons/48x48/GpxProject.png icons/48x48/QmsProject.png icons/48x48/DBProject.png icons/48x48/Route.png icons/48x48/RouteSetup.png icons/48x48/Close.png icons/48x48/ShowAll.png icons/48x48/ShowNone.png icons/48x48/Track.png icons/48x48/EditDetails.png icons/48x48/EditText.png icons/48x48/TimeZoneSetup.png icons/48x48/UnitSetup.png icons/48x48/Undo.png icons/48x48/Redo.png icons/48x48/Cut.png icons/48x48/CutHistory.png icons/48x48/Copy.png icons/48x48/Move.png icons/48x48/Paste.png icons/48x48/TextLeft.png icons/48x48/TextRight.png icons/48x48/TextCenter.png icons/48x48/TextJustified.png icons/48x48/TextBold.png icons/48x48/TextUnderlined.png icons/48x48/TextItalic.png icons/48x48/Lock.png icons/48x48/UnLock.png icons/48x48/WptMove.png icons/48x48/WptProj.png icons/48x48/WptProx.png icons/48x48/TrkCut.png icons/48x48/TrkProfile.png icons/48x48/Tainted.png icons/48x48/AddWpt.png icons/48x48/AddTrk.png icons/48x48/AddRte.png icons/48x48/AddArea.png icons/48x48/AddProject.png icons/48x48/LineMove.png icons/48x48/PointMove.png icons/48x48/PointHide.png icons/48x48/PointShow.png icons/48x48/AreaMove.png icons/48x48/SelectRange.png icons/48x48/Area.png icons/48x48/Reverse.png icons/48x48/Combine.png icons/48x48/SearchGoogle.png icons/48x48/Start.png icons/48x48/SetEle.png icons/48x48/Pattern.png icons/48x48/Opacity.png icons/48x48/DatabaseSetup.png icons/48x48/DatabaseConvert.png icons/48x48/PathBlue.png icons/48x48/PathOrange.png icons/48x48/PathGreen.png icons/48x48/Link.png icons/48x48/Database.png icons/48x48/Empty.png icons/48x48/Apply.png icons/48x48/Time.png icons/48x48/Print.png icons/48x48/AddImage.png icons/48x48/DelImage.png icons/48x48/ReloadImage.png icons/48x48/Image.png icons/48x48/Device.png icons/48x48/2NavProject.png icons/48x48/VrtBuilder.png icons/48x48/SaveView.png icons/48x48/LoadView.png icons/48x48/Save.png icons/48x48/ProfileToWindow.png icons/48x48/NoGo.png icons/48x48/Bubble.png icons/48x48/MoveArrow.png icons/48x48/SizeArrow.png icons/48x48/A.png icons/48x48/V.png icons/48x48/O.png icons/48x48/RteInstr.png icons/48x48/Zoom.png icons/48x48/PrintSave.png icons/48x48/ActNone.png icons/48x48/ActFoot.png icons/48x48/ActCycle.png icons/48x48/ActBike.png icons/48x48/ActCar.png icons/48x48/ActCable.png icons/48x48/ActShip.png icons/48x48/ActSwim.png icons/48x48/ActAero.png icons/48x48/Activity.png icons/48x48/CSrcSolid.png icons/48x48/CSrcHR.png icons/48x48/CSrcCAD.png icons/48x48/CSrcATemp.png icons/48x48/CSrcWTemp.png icons/48x48/CSrcDepth.png icons/48x48/CSrcSlope.png icons/48x48/CSrcSpeed.png icons/48x48/CSrcElevation.png icons/48x48/CSrcUnknown.png icons/48x48/Progress.png icons/cache/32x32/bluepin.png icons/cache/32x32/cito.png icons/cache/32x32/corrected.png icons/cache/32x32/DistIcon.png icons/cache/32x32/dnf.png icons/cache/32x32/down_icon.png icons/cache/32x32/earth.png icons/cache/32x32/event.png icons/cache/32x32/found.png icons/cache/32x32/ftf.png icons/cache/32x32/greenpin.png icons/cache/32x32/halfstar.png icons/cache/32x32/letterbox.png icons/cache/32x32/log.png icons/cache/32x32/maxicon.png icons/cache/32x32/mega.png icons/cache/32x32/minicon.png icons/cache/32x32/multi.png icons/cache/32x32/needs_maintenance.png icons/cache/32x32/OCMLogo.png icons/cache/32x32/OCMLogoSmall.png icons/cache/32x32/other.png icons/cache/32x32/parking.png icons/cache/32x32/pushpin.png icons/cache/32x32/restore.png icons/cache/32x32/SearchIcon.png icons/cache/32x32/star_empty.png icons/cache/32x32/star.png icons/cache/32x32/traditional.png icons/cache/32x32/trailhead.png icons/cache/32x32/treasure.png icons/cache/32x32/unknown.png icons/cache/32x32/up_icon.png icons/cache/32x32/virtual.png icons/cache/32x32/waypoint-flag-red.png icons/cache/32x32/webcam.png icons/cache/32x32/wherigo.png icons/cache/32x32/write_note.png icons/waypoints/32x32/Default.png icons/waypoints/32x32/PinRed.png icons/waypoints/32x32/PinGreen.png icons/waypoints/32x32/PinBlue.png icons/waypoints/32x32/FlagRed.png icons/waypoints/32x32/FlagGreen.png icons/waypoints/32x32/FlagBlue.png icons/waypoints/32x32/BoxRed.png icons/waypoints/32x32/BoxGreen.png icons/waypoints/32x32/BoxBlue.png icons/waypoints/32x32/DiamondRed.png icons/waypoints/32x32/DiamondGreen.png icons/waypoints/32x32/DiamondBlue.png icons/waypoints/32x32/Residence.png icons/waypoints/32x32/Waypoint.png icons/waypoints/32x32/CityCapitol.png icons/waypoints/32x32/CityLarge.png icons/waypoints/32x32/CityMedium.png icons/waypoints/32x32/CitySmall.png cursors/cursorArrow.png cursors/cursorMove.png cursors/cursorMovePoint.png cursors/cursorMoveMap.png cursors/cursorMoveWpt.png cursors/cursorMoveLine.png cursors/cursorMoveArea.png cursors/cursorDelete.png cursors/cursorSelectRange.png cursors/cursorAdd.png cursors/cursorPrint.png cursors/cursorSave.png cursors/wptHighlight.png animation/loader.gif animation/loader2.gif pics/timezones.png pics/splash.png pics/compass.png pics/about.png pics/noMap256x256.png map/WorldTopo.wmts map/WorldSat.wmts map/OpenStreetMap.tms map/OSM_Topo.tms map/OpenCycleMap.tms qmapshack-1.5.1/src/cursors/cursorMoveArea.png000644 001750 000144 00000003411 12527654570 022425 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME  {IDATXý}lUg?9=z&-lK%a+"88^*.M[Tj$b h!Dkt:cXjlXѶFK=ןVIy~o_R01 @1CDf4*v `bbB֬Y+++U@ c&1+q-//X,.ĬDD HE@RǴ f+H.3gN0ulħp]WlۖL&#tZR;wNjjjrhx.읂s@y$IDn`vGdy`p1itww677J$J`vGBNm! é pSNIcccֶHY@ .1kd2D)07ov&''aC aNbVccc1v!ͲuVgttEqځ1 S)ʕ+Rp. m\6J>K>ܫ3,p`RD@hax&϶t:X#r -pjPB|8 Da@C$u؍%gZ| (} h\DD"m "? CGa؇!DV0O~p u Apq˲ }?q ˰"@*ZMz qqp]u<u}0 ,Aq3ɼ`|>Mث^qwsu娨qD \bŊ iWNuUp!?o-a%!\_t\ڀRʫ549|aYA8˗/7b£8F.XR?QaT䢩~ ѕIHR;/"r  pywaxOa/HL%K`Ƃ$<].'{E(<^Gj;Iz7( NuM҅咈^z|4f$I\M 2N hgͪ/MwD䄀}V|RQ]iSsx Ѳ˱cǤ1,k:O@pPy ~WJB4tZi=3/T툆 T7 &\|^)Y,<OޙxKCu-}:hxe`:@˰k|.@e g]EhTKօ:72^y9/^zwM^("$X@~;RkZXPг)̳`&a  p6aà W8Mt,> ¯߀R=OSBJa6 lN6[:N6Îgn2Zh-2NhH` %)X=&3rr2읽}JIENDB`qmapshack-1.5.1/src/cursors/cursorPrint.png000644 001750 000144 00000002704 12616741641 022021 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME   q.QIDATXý]hUwfM棑ŪS[j(RD/C[P4/HD( " VCbZǝލFa`3ws.J)ϲ4 HX @teh@`/.\[GvDê%K @644\ \^,ĒDDrQfsNZcQKZK 9y466,zXcY8/I.Y²eˆN*JH88fN8nٲ7M:ZŹвAlfhh(wl`IG꿀Pr9|GDHd> 0==M. "B>7(~`J\V q8xk@ᔪ /7!ɰgϞMҰ4R9-7q ങu(sqc3OLm:ЁRϞ"~U0 +j+Ç8O>Gv]筷͘tUqL^k=u 8e{\Yėӿֲp 步L&Oڵkt =Ȥv (n/`6Öh .+Nnh l_Dl8tRyo-9l1<W)yժU9rnvv۶;wy{~Vlx]fK1wر󴵵aWBЦ;PJɖV@V礰&46y0s-̡Cرcק]=gNjOE3L'wz* x/eSCŊ9psilƥM\D6AlI*)~d/O.)9V|ffB@1Jʫ+)-m ?+˜I p^\(JH(JX A.QWL"e`*Dmغ6lh:p~b \#R |ٸ?lD\E\`' W7Y"URl&jU-,7WMLTR乿SIW,IENDB`qmapshack-1.5.1/src/cursors/cursorAdd.png000644 001750 000144 00000002252 12527654570 021420 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME  209P7IDATXŖK$WzewHf2BhdMbtgĀ(q'd BY,H\ f%$ D|uUuunu<\49sbF4͏,d0#D ]]]^6 Z< MMM+[@Ьa֒DDR2==477 |n`]n8eyy9EShD]qKeccCZZZ|4w7r1g]AR*X,JPm7WY+ $ Q!QEmmm9|~rص08>NXbfrr~> ˑ' @"""$Ir$I.S P< b@%RTP bϓ$JC*Cppp@X<'Z ""x^)52D/@^ضcY{@6S.wA>D\.D7z\ReD˶mVVVpE=j՗aRqmfff"uӮORKʝP@qW;|EN@FxJ?< "RjDZ~8lnnJ--^NR\TJ |ߧl8>+Q:%0UJ`F800`ed:C(3"5Ht[Pn-GDޝdaaP(yZWe> "cy777wll\}֏*Vw -֐[ڜ4CaeOltG6۫dKZ2#R5/5.0rS+oDgkVw߿\IENDB`qmapshack-1.5.1/src/cursors/cursorSave.png000644 001750 000144 00000002326 12616741641 021623 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME "ccIDATXý=lW|{_v3&@J"Q=i"$@!)M9E rق ([+ !l98~};s1w^O;73s8+@9b{0>@/[[[r̙R#@&=hڵkq` xSDD1RL\nc@#1DZh%"w DJVN@/FC<~X= 9=&qv ,#MS$!MS4Z>}z\.G\|)c̎Vu_zT\|cӑ?FAY'˲wޕ/AL@04S5&q%Ξ=ȲlBnBNxn! CZouoBtKv1\Iu/!+Lkނ$I<}JW\I Ŗk% 4#֕嘟%o=){jٳg\D(% 7D%ȥ!":;͡1͹ydi>mIx:rpԴ MAh_ɽ1%yOs|%w;ؽODQT*!"c0Ɛ$ };ƶINjvv^!ǏG)~R-`x#"wJ]% C*qWmm` XOQݨjۋT*fff2Al76emj||>ZD6r֭G ;rXmL$7kk|U; ([zWA}^"7{'2'][+>_%Ýs2 h.1yDd ծ"՞OfvXbݰm3stis~ѣλ~Sv Dڒ&\ t^bM]s aeeD+Kӌ > Nt;DCIENDB`qmapshack-1.5.1/src/cursors/wptHighlight.png000644 001750 000144 00000006732 12527654570 022143 0ustar00oeichlerusers000000 000000 PNG  IHDR==basBIT|d pHYsItEXtSoftwarewww.inkscape.org< WIDAThϏuWU3CRh ^aȻ2?9K`7" :: >;//\04l ` eDR\Ώݥ.@)=;{oDU9""p+:s韏FZbmMqC唅Si_ı\Qh[!F!%}]?4qBdgE5x'AlN=g:Ƒ0@U3SfsJU%IdsfLJlj ZdҁpYò;L;sӛs"03_u7i|nfZė'pꇈ|.k~os&/!#``·i9uI,6P}˚|ys#Μ:4mZ!k{t@ۥ9"GukKD~YxKߠmOm aIZ6 ?|8#9|0;mZXp]{Ni|Lhv/򗨾x~B;wG® ^ DOQ7luعYo担[Yz8c:@K ׁ61_dž]-kFuyDOzؔL~:˚9&;Ӓ$b'dU9D?~O p.?DͮI7tߝervksle"1 5`Hl_b5OhpL??OU>`? Sb(OJ&sEg>fW77ө=i {je:"۝'O7r{,ik=өgs3=szOJE 2aOڤ*.cl&ww_3|v=71:Ξuy.^4enœ_l<~ITsLwO/aFd4g^Et]m/{] kYzb{Pr<{cS2Ng#2f'}pwD9wO_ ׯcwh:Gz;<``ny1<ƾBѶ ޷ؔ72cp4RHci$e-[:Uvd|.yB3 Lz,`m7c @\/ 8-9Zb42ڶa6k{)[[i潬q]un!ƣ_n/wܑeYg76刳*mے$‚LLLHkkkvl'JXAyB<&❝ݩTjxh0tD+z}0H$pԩSS-%CGEPdYlFD`=A,o}8mHAr+QJ155šCA|c@؀. lvե@Dq״ +35P:.hx$0֏h1 šL&-ضI׹ (z`KF)S=k*IRPtzV(l 81b$۲ @Zes]˲ <ϻL$l~r ¹DEDMlB+."{>YXXqڰ,kKk$.X! lō5i,͜ DJ&itGFaӦM۴tA\pRE'OD@J5@|$uuu eeJ^q`||DT0}֟f`V|&.hv1NZ6- T3 image/svg+xml qmapshack-1.5.1/src/cursors/cursorMoveWpt.png000644 001750 000144 00000002552 12527654570 022334 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME % IDATXýMh\UL#i3ic*6jńhA\XALj!RB)Pi6HdJ nLhA0 "d!bb-I$M&޼"w/I g1{s~Ry0I1@Q @d2 4i0y*d210*FU""ȑ#Gܺ@ HfUA A:m@V, uq) r/qخ*GU+eI>\.' r imm-֎ÕY)!y>lK.%:::0PTk (i>\:6t$aY"BaXzp||\zzzl˲1`)bwQJ155ž}Sa~!,)BT0;;K.[t%`6EqY _`] u]b 4;F-D#V{T^ϲ/tGP@1sX,ؘMYSI^uX~߲/ÈIJSj0Ex\]fu] `hh(}*0CnUv(u }3uK}0 I$LLLx\bw/|k(5RV JH*BD;wuHJg3l}bI(xx}YR^{{ ɹsD"A8C[[L&ڶiwe'f0.ϡe4H4-IVBD^;0<<\mر06 v_DN_KT ( ]P/)(@!!V(%!IٹtfZǰ;s~9sYR04haHS@zzzt:pd`%rqI0FK""qxxs-DQ$B!fe` >6@Ex'jUjF8T*)R*dffFlRmoo6ۉY c0$0$ C<޽{( Щa5+Fhp4M>3͞h r8q'NNNʁ\qƁRAׯ_RD)󖗗k ^%-FWyT|Dz,;f Эi?zA[Dd2:t(p[]+0PZF۲,eqqqgR ue144:sZ>NlgšbSSDJ |߿`E7m8 i`Vq'Po4@xwK(c6.\<ϛ+r"µ /J?`6ѩ'䜆fu8K~p"23,A0-"@{=))\'/&"7 Y~諣c (sۜKS Zv@*?6;[`r٤ (隝iw ]YF{IENDB`qmapshack-1.5.1/src/cursors/cursorMoveLine.png000644 001750 000144 00000002737 12527654570 022456 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME :4=lIDATXý[let@ʭ SK%ĈHD@4 !PAAn& ^)Q@/T7!i &HJc"@--efvoR$'9T0@ P'%@GΝ,((8 T@VqG֭[&ŀ Å5(J?1*u]qGɤ455IQQQ0M1\וx<.XLѨJKKL:5[Ln nfΑAJr H4%#"A0 O7666ʪUxe( $I`b(hmmeѢEN__A|!–@WWh􆤃ADD"AMMvq׀. )|u],ˢ6MaLӾ+ g ktbu<˲Xv?@J) ߾2HK-r{zvbMGG@ |:N*؛E[ہ6Ѥ~sn_9lх<_鲩>Y"{&aa `8X ,f  \+M@~ХNk!k`鋾@ߓңԓGW J1By-J5(|o`qNKSQߒizIkTj."hJ\^`қ .KX]Gp.~x  D2K3ArMg`1V y hxa`k{-`oo-9Ӂ 5%r%"[YC IENDB`qmapshack-1.5.1/src/cursors/COPYRIGHT000644 001750 000144 00000000537 12527654570 020263 0ustar00oeichlerusers000000 000000 All icons in this directory are from http://www.rw-designer.com/cursor-set/proton files are public domain. Quote from source web pages: -------------------------------------------- Released to Public Domain You are free: To use this work for any legal purpose. -------------------------------------------- http://www.rw-designer.com/licenses qmapshack-1.5.1/src/cursors/cursorMoveMap.png000644 001750 000144 00000003306 12527654570 022275 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME /sSIDATXý]h|dQq+ʬpii/VĠKHFmR,jaW7bIv%I5XGq>ϧ}fS }\/=s΃()UU |(2QYY Vˀ``vvVl߾=[ZZ|d4D(!0MS=z*++>~@[(DBẮ~ 'ZBcAE+,il6+Ē%K6J"΢,tZR)L&ܜ"g@ VKP(LOOk.ŋ=Hf(D"A2/Ad2k>z 4O X,50eYŋ},?)m/4!%%%t3YSi( W9-4]Ϟ=&d4S_ ` !u'N8tW+0; Ei4ͼY\|sg!Y`0;@#EQE)qY8xgxx6MsT:O/ N@ ~v^ dY-[˗SRRu)ڰa!̒O؀onnݪ*ԩSMMMu]PVP}>(qF.iW\Q~?b& j0|'*477W|>'}뮮.G42 XU!#aݷoߟ/\p###=BK}555?Ǐ"˥#Ee {uݿ褀ٙIEQlݺTUUݻٳgI$K,cՔfꚻuO"哓q|1J͍7گ_)*TWWb1oO4M;|Z8ii2 K]!"ԵJYhq$BUӵo}}}zHU_/5M\("!T.aeA9nD"7^MLLx<.޽+zzzp8%cyD^J4eK}0155}ڵtqf>7qckfiZymmme[Wo@0 g6miOR?BBo{\+mcIENDB`qmapshack-1.5.1/src/cursors/cursorDelete.png000644 001750 000144 00000002452 12537576062 022134 0ustar00oeichlerusers000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME4T/5IDATXýMh\ULu4UۅVQ\.R"]5%PKAFpэE ~*TЊDKզ%IH&ifޛ>q1wI2ig{o{\,rm~:$,iiHOOyH[XYYr&znS j "#G3sV 8B "DZ 8NW@)8 D)%rY.^(ll1Qf@y+RIŢ\rEo^2wg6֚(Ð(\.xjϞ=tS kڑhU q-q=8&Lg'{L;wT*y"ֺVu.\ c@e[n e1??޽{Za RiC " Ry` pVA@"ĉ)q3L;?]a÷_"BWW =ص[׆h~% dyyyjVzTK |>yQ[eYgZXP)U[A`6###:I`Δ@l۽{ﳳ?~Ǯ]oQ8+[ŲHRLLLJI53Lگfz|ֲWX'" k.bla}ԩo3}d*W r$Mw~|E׭ ˲¾>pӧO۩T8QJkwvv>~X)xoIGh{kih(dhh(L5ڹs'm?X|%ZȈiʿnN<9r9 șJYp ?>*X+ׁz\>V(Vd2h۶4ܸVfnNHTy˜I81~˲>H&>R}`Tj8GϿ-"oUauFuof<ʙm3I1b {%s$u̞[1?L!"?1MB:zs B|o~l"4Ɋf,F0]cpd: A֏IDATxMlEuۀR !z䂔cS)=Q=p@H@HPDV*Ԑu F?g8kNb#H#{wv}o3[8F@ Dz&"HK⽖eWeY9`:4HV*sssR*dvvV,rN4 ,@xXB H*˲(*fB@P(H>qD)%mK24 I щD3f4]DD)%lVemmM._,h4 /G~a[`2aYV.JI.۶%ښ̌Y>@ 2@zܲ|2D"bf F"uพg 6x`ki06R"lV (3i.^(i:z^7[X"JquVP姁>@R$m צinܸ!LF460iDչ-TȢwS"FEfT4V/TfpaM\@=Dg&ohfZ+炚]Ӣl2۵fkD҂hKWZ_-6ZoIENDB`qmapshack-1.5.1/src/device/CDeviceWatcherMac.h000644 001750 000144 00000004111 12622435723 022122 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEVICEWATCHERMAC_H #define CDEVICEWATCHERMAC_H #include #include #include #include #include "device/IDeviceWatcher.h" class CDeviceWorker : public QThread { Q_OBJECT public: CDeviceWorker(); virtual ~CDeviceWorker(); void run() Q_DECL_OVERRIDE; void stop(); void eventDiskAppear(DADiskRef disk); void eventDiskDisappear(DADiskRef disk); signals: void sigDeviceAdded(const QString& dev, const QString& path); void sigDeviceRemoved(const QString& dev, const QString& path); private: void init(); QString getMountPoint(DADiskRef disk); volatile bool mStop; DASessionRef mSession; }; class CDeviceWatcherMac : public IDeviceWatcher { Q_OBJECT public: CDeviceWatcherMac(CGisListWks *parent); virtual ~CDeviceWatcherMac(); CDeviceWorker worker; private slots: void slotUpdate(); void slotDeviceAdded(const QString& dev, const QString& path); void slotDeviceRemoved(const QString& dev, const QString& path); void slotEndListing(); private: void addDevice(QStorageInfo storage); QStringList sDevices; }; #endif //CDEVICEWATCHERMAC_H qmapshack-1.5.1/src/device/CDeviceGarmin.h000644 001750 000144 00000003176 12622435723 021333 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEVICEGARMIN_H #define CDEVICEGARMIN_H #include "device/IDevice.h" class CDeviceGarmin : public IDevice { public: CDeviceGarmin(const QString &path, const QString &key, const QString& model, QTreeWidget * parent); virtual ~CDeviceGarmin(); void insertCopyOfProject(IGisProject * project); void startSavingProject(IGisProject * project); void saveImages(CGisItemWpt& wpt); void loadImages(CGisItemWpt& wpt); void aboutToRemoveProject(IGisProject * project); private: QString id; QString partno; QString description; QString pathGpx = "Garmin/GPX"; QString pathPictures = "Garmin/JPEG"; QString pathSpoilers = "Garmin/GeocachePhotos"; int cntImages = 0; }; #endif //CDEVICEGARMIN_H qmapshack-1.5.1/src/device/CDeviceWatcherWindows.cpp000644 001750 000144 00000004566 12622435717 023430 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "device/CDeviceWatcherWindows.h" #include "gis/CGisListWks.h" #include "CMainWindow.h" #include CDeviceWatcherWindows * CDeviceWatcherWindows::pSelf = 0; CDeviceWatcherWindows::CDeviceWatcherWindows(CGisListWks *parent) : IDeviceWatcher(parent) { pSelf = this; } CDeviceWatcherWindows::~CDeviceWatcherWindows() { } void CDeviceWatcherWindows::slotUpdate() { QApplication::setOverrideCursor(Qt::WaitCursor); foreach(const QStorageInfo &storage, QStorageInfo::mountedVolumes()) { if (storage.isValid() && storage.isReady()) { probeForDevice(storage.rootPath(), storage.rootPath(), storage.name()); } } QApplication::restoreOverrideCursor(); } bool CDeviceWatcherWindows::event(QEvent * e) { if (e->type() == CEventDevice::eEvtDeviceWindows) { CEventDevice * evt = (CEventDevice*)e; qDebug() << "CDeviceWatcherWindows::event()" << evt->path << evt->add; if (evt->add) { QStorageInfo storage(evt->path); if (storage.isValid() && storage.isReady()) { QApplication::setOverrideCursor(Qt::WaitCursor); probeForDevice(evt->path, evt->path, storage.name()); QApplication::restoreOverrideCursor(); } } else { listWks->removeDevice(evt->path); } } return QObject::event(e); }qmapshack-1.5.1/src/device/IDeviceWatcher.h000644 001750 000144 00000004123 12622435723 021512 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDEVICEWATCHER_H #define IDEVICEWATCHER_H #include class CGisListWks; class IDevice; class IDeviceWatcher : public QObject { Q_OBJECT public: IDeviceWatcher(CGisListWks *parent); virtual ~IDeviceWatcher(); signals: void sigChanged(); private slots: /** @brief Find all devices already connected This slot is called only once at startup. All paths to plug-n-play memory have to be passed to probeForDevice(). */ virtual void slotUpdate() = 0; protected: /** @brief Probe a path for a device @param mountPoint This is the mount point in a Linux sense (e.g. "/run/media/GARMIN"). For Windows this will be the drive letter (e.g. "d:\") @param path This is the device path in a Linux sense (e.g. "/org/freedesktop/UDisks2/block_devices/sdc1") For Windows this will be the drive letter, too (e.g. "d:\") @param label A name for the device. In Linux the last part of the mount point is used. In Windows it should be the name of the drive. */ void probeForDevice(const QString &mountPoint, const QString& path, const QString &label); CGisListWks * listWks; }; #endif //IDEVICEWATCHER_H qmapshack-1.5.1/src/device/IDevice.h000644 001750 000144 00000005614 12622435723 020202 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDEVICE_H #define IDEVICE_H #include #include #include "gis/IGisItem.h" class CGisDraw; class CGisItemWpt; class IDevice : public QTreeWidgetItem { public: IDevice(const QString& path, const QString& key, QTreeWidget * parent); virtual ~IDevice(); static void mount(const QString& path); static void umount(const QString &path); static int count() { return cnt; } void mount() { mount(key); } void umount() { umount(key); } const QString& getKey() const { return key; } QString getName() const; void getItemsByPos(const QPointF& pos, QList &items); IGisItem * getItemByKey(const IGisItem::key_t& key); void editItemByKey(const IGisItem::key_t& key); void drawItem(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, CGisDraw * gis); void drawLabel(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis); void drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis); void insertCopyOfProject(IGisProject * project, int& lastResult); void updateProject(IGisProject * project); virtual void startSavingProject(IGisProject * project) { } virtual void saveImages(CGisItemWpt& wpt) { } virtual void loadImages(CGisItemWpt& wpt) { } virtual void aboutToRemoveProject(IGisProject * project) { } IGisProject * getProjectByKey(const QString& key); protected: virtual void insertCopyOfProject(IGisProject * project) = 0; /** @brief Test if a project's filename/path is already used This can happen if there is already a project with the same name but different or no key. @param filename @return If the current operation should be aborted return true. */ bool testForExternalProject(const QString& filename); static int cnt; QDir dir; QString key; }; #endif //IDEVICE_H qmapshack-1.5.1/src/device/CDeviceWatcherWindows.h000644 001750 000144 00000003320 12622435723 023055 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEVICEWATCHERWINDOWS_H #define CDEVICEWATCHERWINDOWS_H #include "device/IDeviceWatcher.h" #include class CEventDevice : public QEvent { public: enum event_types_e { eEvtDeviceWindows = QEvent::User + 200 }; CEventDevice(const QString& path, bool add) : QEvent(QEvent::Type(eEvtDeviceWindows)), add(add), path(path) { } bool add; QString path; }; class CDeviceWatcherWindows : public IDeviceWatcher { Q_OBJECT public: virtual ~CDeviceWatcherWindows(); static CDeviceWatcherWindows * self() { return pSelf; } bool event(QEvent * e); private slots: void slotUpdate(); private: friend class CGisListWks; CDeviceWatcherWindows(CGisListWks *parent); static CDeviceWatcherWindows * pSelf; }; #endif //CDEVICEWATCHERWINDOWS_H qmapshack-1.5.1/src/device/CDeviceWatcherLinux.cpp000644 001750 000144 00000014641 12622435717 023070 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "device/CDeviceWatcherLinux.h" #include "device/IDevice.h" #include "gis/CGisListWks.h" #include #include #include CDeviceWatcherLinux::CDeviceWatcherLinux(CGisListWks *parent) : IDeviceWatcher(parent) { QDBusConnection::systemBus().connect("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2", "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", this, SLOT(slotDeviceAdded(QDBusObjectPath,QVariantMap))); QDBusConnection::systemBus().connect("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2", "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", this, SLOT(slotDeviceRemoved(QDBusObjectPath,QStringList))); } CDeviceWatcherLinux::~CDeviceWatcherLinux() { } void CDeviceWatcherLinux::slotDeviceAdded(const QDBusObjectPath& path, const QVariantMap& map) { // ignore all paths other than a filesystem if(!map.contains("org.freedesktop.UDisks2.Filesystem")) { return; } // create path of to drive the block device belongs to QDBusInterface * blockIface = new QDBusInterface("org.freedesktop.UDisks2", path.path(), "org.freedesktop.UDisks2.Block", QDBusConnection::systemBus(), this); QDBusObjectPath drive_object = blockIface->property("Drive").value(); // read vendor string attached to drive QDBusInterface * driveIface = new QDBusInterface("org.freedesktop.UDisks2", drive_object.path(),"org.freedesktop.UDisks2.Drive", QDBusConnection::systemBus(), this); QString vendor = driveIface->property("Vendor").toString(); QString model = driveIface->property("Model").toString(); delete blockIface; delete driveIface; if(model.isEmpty() || vendor.isEmpty()) { return; } QString strPath = path.path(); IDevice::mount(strPath); QString mountPoint = readMountPoint(strPath); probeForDevice(mountPoint, strPath, QFileInfo(mountPoint).fileName()); IDevice::umount(strPath); } void CDeviceWatcherLinux::slotDeviceRemoved(const QDBusObjectPath& path, const QStringList& list) { // ignore all paths other than a filesystem if(!list.contains("org.freedesktop.UDisks2.Filesystem")) { return; } qDebug() << "slotDeviceRemoved" << path.path() << list; listWks->removeDevice(path.path()); } void CDeviceWatcherLinux::slotUpdate() { QList paths; QDBusMessage call = QDBusMessage::createMethodCall("org.freedesktop.UDisks2","/org/freedesktop/UDisks2/block_devices","org.freedesktop.DBus.Introspectable","Introspect"); QDBusPendingReply reply = QDBusConnection::systemBus().call(call); if (!reply.isValid()) { qWarning("UDisks2Manager: error: %s", qPrintable(reply.error().name())); return; } QDomDocument doc; doc.setContent(reply); const QDomElement& xmlRoot = doc.documentElement(); const QDomNodeList& xmlNodes = xmlRoot.elementsByTagName("node"); const int N = xmlNodes.count(); for(int n = 0; n < N; n++) { const QDomNode& xmlNode = xmlNodes.item(n); const QDomNamedNodeMap& attr = xmlNode.attributes(); QString name = attr.namedItem("name").nodeValue(); if(!name.isEmpty()) { paths << QDBusObjectPath("/org/freedesktop/UDisks2/block_devices/" + name); } } foreach (QDBusObjectPath path, paths) { QDBusMessage call = QDBusMessage::createMethodCall("org.freedesktop.UDisks2", path.path(), "org.freedesktop.DBus.Introspectable","Introspect"); QDBusPendingReply reply = QDBusConnection::systemBus().call(call); if (!reply.isValid()) { qWarning("UDisks2Manager: error: %s", qPrintable(reply.error().name())); continue; } QDomDocument doc; doc.setContent(reply); const QDomElement& xmlRoot = doc.documentElement(); const QDomNodeList& xmlInterfaces = xmlRoot.elementsByTagName("interface"); const int N = xmlInterfaces.count(); for(int n = 0; n < N; n++) { const QDomNode& xmlInterface = xmlInterfaces.item(n); const QDomNamedNodeMap& attr = xmlInterface.attributes(); if(attr.namedItem("name").nodeValue() == "org.freedesktop.UDisks2.Filesystem") { QVariantMap map; map["org.freedesktop.UDisks2.Filesystem"] = QVariant(); slotDeviceAdded(path, map); } } } } QString CDeviceWatcherLinux::readMountPoint(const QString& path) { QStringList points; QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.UDisks2",path,"org.freedesktop.DBus.Properties","Get"); QList args; args << "org.freedesktop.UDisks2.Filesystem" << "MountPoints"; message.setArguments(args); QDBusMessage reply = QDBusConnection::systemBus().call(message); QList list; foreach (QVariant arg, reply.arguments()) { arg.value().variant().value() >> list; } foreach (QByteArray point, list) { points.append(point); } if(!points.isEmpty()) { return points.first(); } return ""; } qmapshack-1.5.1/src/device/CDeviceGarmin.cpp000644 001750 000144 00000022562 12622435717 021671 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "device/CDeviceGarmin.h" #include "gis/CGisListWks.h" #include "gis/gpx/CGpxProject.h" #include "gis/wpt/CGisItemWpt.h" #include #include CDeviceGarmin::CDeviceGarmin(const QString &path, const QString &key, const QString &model, QTreeWidget *parent) : IDevice(path, key, parent) , cntImages(0) { setText(CGisListWks::eColumnName, "Garmin"); QFile file(dir.absoluteFilePath("Garmin/GarminDevice.xml")); file.open(QIODevice::ReadOnly); QDomDocument dom; dom.setContent(&file); file.close(); const QDomElement& xmlDevice = dom.documentElement(); const QDomNode& xmlModel = xmlDevice.namedItem("Model"); id = xmlDevice.namedItem("Id").toElement().text(); description = xmlModel.namedItem("Description").toElement().text(); partno = xmlModel.namedItem("PartNumber").toElement().text(); setText(CGisListWks::eColumnName, QString("%1 (%2)").arg(description).arg(model)); setToolTip(CGisListWks::eColumnName, QString("%1 (%2, %3)").arg(description).arg(partno).arg(model)); const QDomNode& xmlMassStorageMode = xmlDevice.namedItem("MassStorageMode"); const QDomNodeList& xmlDataTypes = xmlMassStorageMode.toElement().elementsByTagName("DataType"); const int N = xmlDataTypes.count(); for(int n = 0; n < N; n++) { const QDomNode& xmlDataType = xmlDataTypes.item(n); const QDomNode& xmlName = xmlDataType.namedItem("Name"); const QDomNode& xmlFile = xmlDataType.namedItem("File"); const QDomNode& xmlLocation = xmlFile.namedItem("Location"); const QDomNode& xmlPath = xmlLocation.namedItem("Path"); QString name = xmlName.toElement().text(); if(name == "GPSData") { pathGpx = xmlPath.toElement().text(); } else if(name == "GeotaggedPhotos") { pathPictures = xmlPath.toElement().text(); } else if(name == "GeocachePhotos") { pathSpoilers = xmlPath.toElement().text(); } } qDebug() << dir.absoluteFilePath(pathGpx); qDebug() << dir.absoluteFilePath(pathPictures); qDebug() << dir.absoluteFilePath(pathSpoilers); if(!dir.exists(pathGpx)) { dir.mkpath(pathGpx); } if(!dir.exists(pathPictures)) { dir.mkpath(pathPictures); } if(!dir.exists(pathSpoilers)) { dir.mkpath(pathSpoilers); } QDir dirGpx(dir.absoluteFilePath(pathGpx)); QStringList entries = dirGpx.entryList(QStringList("*.gpx")); foreach(const QString &entry, entries) { IGisProject * project = new CGpxProject(dirGpx.absoluteFilePath(entry), this); if(!project->isValid()) { delete project; } } QDir dirGpxCurrent(dir.absoluteFilePath(pathGpx + "/Current")); entries = dirGpxCurrent.entryList(QStringList("*.gpx")); foreach(const QString &entry, entries) { IGisProject * project = new CGpxProject(dirGpxCurrent.absoluteFilePath(entry), this); if(!project->isValid()) { delete project; } } QDir dirGpxArchive(dir.absoluteFilePath(pathGpx + "/Archive")); entries = dirGpxArchive.entryList(QStringList("*.gpx")); foreach(const QString &entry, entries) { IGisProject * project = new CGpxProject(dirGpxArchive.absoluteFilePath(entry), this); if(!project->isValid()) { delete project; } } } CDeviceGarmin::~CDeviceGarmin() { } void CDeviceGarmin::insertCopyOfProject(IGisProject * project) { QString name = project->getName(); name = name.remove(QRegExp("[^A-Za-z0-9_]")); QDir dirGpx = dir.absoluteFilePath(pathGpx); QString filename = dirGpx.absoluteFilePath(name + ".gpx"); if(testForExternalProject(filename)) { return; } CGpxProject * gpx = new CGpxProject(filename, project, this); if(!gpx->isValid()) { delete gpx; return; } if(!gpx->save()) { delete gpx; return; } } void CDeviceGarmin::saveImages(CGisItemWpt& wpt) { if(wpt.isGeocache()) { QString name = wpt.getName(); quint32 size = name.size(); QString path = QString("%1/%2/%3").arg(name.at(size-1)).arg(name.at(size -2)).arg(name); const QDir dirSpoilers(dir.absoluteFilePath(pathSpoilers)); const QDir dirCache(dirSpoilers.absoluteFilePath(path)); const QList& images = wpt.getImages(); if(!dirCache.exists()) { dirCache.mkpath(dirCache.absolutePath()); } QString filename; foreach(const CGisItemWpt::image_t& image, images) { filename = image.info; filename = filename.remove(QRegExp("[^A-Za-z0-9_]")); if(!filename.endsWith("jpg")) { filename += ".jpg"; } image.pixmap.save(dirCache.absoluteFilePath(filename)); } } else { const QDir dirImages(dir.absoluteFilePath(pathPictures)); const QString& key = wpt.getKey().project; const QList& images = wpt.getImages(); QList links; QString filename; foreach(const CGisItemWpt::image_t& image, images) { filename = QString("%1.%2.jpg").arg(key).arg(cntImages); image.pixmap.save(dirImages.absoluteFilePath(filename)); IGisItem::link_t link; link.uri = pathPictures + "/" + filename; link.text = QObject::tr("Picture%1").arg(cntImages); link.type = "Garmin"; links << link; cntImages++; } wpt.appendLinks(links); } } void CDeviceGarmin::loadImages(CGisItemWpt& wpt) { if(wpt.isGeocache()) { QString name = wpt.getName(); quint32 size = name.size(); QString path = QString("%1/%2/%3").arg(name.at(size-1)).arg(name.at(size -2)).arg(name); const QDir dirSpoilers(dir.absoluteFilePath(pathSpoilers)); const QDir dirCache(dirSpoilers.absoluteFilePath(path)); QList images; QStringList entries = dirCache.entryList(QStringList("*.jpg"), QDir::Files); foreach(const QString &file, entries) { CGisItemWpt::image_t image; image.pixmap.load(dirCache.absoluteFilePath(file)); if(!image.pixmap.isNull()) { image.fileName = file; image.info = QFileInfo(file).baseName(); images << image; } } if(!images.isEmpty()) { wpt.appendImages(images); } } else { const QList& links = wpt.getLinks(); QList images; foreach(const IGisItem::link_t& link, links) { if(link.type != "Garmin") { continue; } CGisItemWpt::image_t image; image.fileName = link.text; image.pixmap.load(dir.absoluteFilePath(link.uri.toString())); images << image; } if(!images.isEmpty()) { wpt.appendImages(images); wpt.removeLinksByType("Garmin"); } } } void CDeviceGarmin::startSavingProject(IGisProject * project) { cntImages = 0; } void CDeviceGarmin::aboutToRemoveProject(IGisProject * project) { // remove images attached to project const QString& key = project->getKey(); const QDir dirImages(dir.absoluteFilePath(pathPictures)); QStringList entries = dirImages.entryList(QStringList("*.jpg"), QDir::Files); foreach(const QString &entry, entries) { QString filename = dirImages.absoluteFilePath(entry); QFileInfo fi(filename); if(fi.baseName() == key) { QFile::remove(filename); } } // remove geo cache spoiler images const int N = project->childCount(); for(int n = 0; n < N; n++) { CGisItemWpt * wpt = dynamic_cast(project->child(n)); if(wpt && wpt->isGeocache()) { QString name = wpt->getName(); quint32 size = name.size(); QString path = QString("%1/%2/%3").arg(name.at(size-1)).arg(name.at(size -2)).arg(name); QDir dirSpoilers(dir.absoluteFilePath(pathSpoilers)); QDir dirCache(dirSpoilers.absoluteFilePath(path)); dirCache.removeRecursively(); } } } qmapshack-1.5.1/src/device/CDeviceWatcherMac.cpp000644 001750 000144 00000014332 12622435717 022466 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "device/CDeviceWatcherMac.h" #include "device/IDevice.h" #include "gis/CGisListWks.h" #include #include #include #include CDeviceWatcherMac::CDeviceWatcherMac(CGisListWks *parent) : IDeviceWatcher(parent), worker() { connect(&worker, SIGNAL(sigDeviceAdded(const QString &, const QString &)), this, SLOT(slotDeviceAdded(const QString &, const QString &))); connect(&worker, SIGNAL(sigDeviceRemoved(const QString &, const QString &)), this, SLOT(slotDeviceRemoved(const QString &, const QString &))); } CDeviceWatcherMac::~CDeviceWatcherMac() { } void CDeviceWatcherMac::slotEndListing() { worker.stop(); } // Aufruf 1s. nach instanzierung void CDeviceWatcherMac::slotUpdate() { foreach(const QStorageInfo &storage, QStorageInfo::mountedVolumes()) { addDevice(storage); } } void CDeviceWatcherMac::slotDeviceAdded(const QString& dev, const QString& path) { qDebug() << "slotDeviceAdded" << dev << " " << path; QStorageInfo storage(path); addDevice(storage); } void CDeviceWatcherMac::addDevice(QStorageInfo storage) { if (storage.isValid() && storage.isReady()) { QString dev = QString(storage.device()).section('/', -1); if (!sDevices.contains(dev)) { // TODO only add Garmin devices... sDevices.append(dev); //QString label = QFileInfo(path).fileName(); QString path = storage.rootPath(); QString device = storage.device(); QString label = storage.name(); QString name = storage.displayName(); qDebug() << "name: " << storage.name() << " display name: " << name << " root path: " << path << " label (name): " << label << " device: " <removeDevice(dev); } static void onDiskAppear(DADiskRef disk, void *context) { CDeviceWorker *p = static_cast(context); p->eventDiskAppear(disk); } static void onDiskDisappear(DADiskRef disk, void *context) { CDeviceWorker *p = static_cast(context); p->eventDiskDisappear(disk); } void CDeviceWorker::eventDiskAppear(DADiskRef disk) { QString disk_name = DADiskGetBSDName(disk); QString path = getMountPoint(disk); qDebug() << "onDiskAppear " << path << " " << disk_name; if (!QMetaObject::invokeMethod(this, "sigDeviceAdded", Qt::QueuedConnection, Q_ARG(QString, disk_name), Q_ARG(QString, path))) { qWarning("invoke deviceAdded failed"); } } void CDeviceWorker::eventDiskDisappear(DADiskRef disk) { QString disk_name = DADiskGetBSDName(disk); qDebug() << "onDiskDisappear " << disk_name; if (!QMetaObject::invokeMethod(this, "sigDeviceRemoved", Qt::QueuedConnection, Q_ARG(QString, disk_name), Q_ARG(QString, ""))) { qWarning("invoke deviceRemoved failed"); } } QString CDeviceWorker::getMountPoint(DADiskRef disk) { //QString path = getMountPoint(disk); CFDictionaryRef dict = DADiskCopyDescription(disk); QString path; CFURLRef fspath = (CFURLRef) CFDictionaryGetValue(dict, kDADiskDescriptionVolumePathKey); if(fspath) { char buf[512]; CFURLGetFileSystemRepresentation(fspath, false, (UInt8 *)buf, sizeof(buf)); path = buf; } else { // wieso braucht es diesen Workaround für gewisse Devices? // bei manchen Devices ist der Volumen Path Key (fspath) nicht gesetzt. // TODO anderen Key verwenden? QString disk_name = DADiskGetBSDName(disk); foreach(const QStorageInfo &storage, QStorageInfo::mountedVolumes()) { QString dev = QString(storage.device()).section('/', -1); if(dev == disk_name) { path = storage.rootPath(); } } } CFRelease(dict); return path; } CDeviceWorker::CDeviceWorker() : QThread() { init(); QThread::start(); } CDeviceWorker::~CDeviceWorker() { } void CDeviceWorker::stop() { mStop = true; wait(); DAUnregisterCallback(mSession, (void*)onDiskAppear, this); DAUnregisterCallback(mSession, (void*)onDiskDisappear, this); qDebug() << "Thread.stop"; } void CDeviceWorker::run() { qDebug() << "Thread.run"; mStop = false; DASessionScheduleWithRunLoop(mSession, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); SInt32 result; do { result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, true); } while (!mStop && result); DASessionUnscheduleFromRunLoop(mSession, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); } void CDeviceWorker::init() { mSession = DASessionCreate(kCFAllocatorDefault); DARegisterDiskAppearedCallback(mSession, NULL, onDiskAppear, this); DARegisterDiskDisappearedCallback(mSession, NULL, onDiskDisappear, this); } qmapshack-1.5.1/src/device/IDevice.cpp000644 001750 000144 00000015554 12622435717 020544 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "device/IDevice.h" #include "gis/CGisListWks.h" #include "gis/prj/IGisProject.h" #include "helpers/CSelectCopyAction.h" #ifdef Q_OS_LINUX #include #endif int IDevice::cnt = 0; IDevice::IDevice(const QString &path, const QString &key, QTreeWidget *parent) : QTreeWidgetItem(parent) , dir(path) , key(key) { setIcon(CGisListWks::eColumnIcon, QIcon("://icons/32x32/Device.png")); cnt++; } IDevice::~IDevice() { cnt--; } void IDevice::mount(const QString& path) { #ifdef Q_OS_LINUX QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.UDisks2",path,"org.freedesktop.UDisks2.Filesystem","Mount"); QVariantMap args; args.insert("options", "sync"); message << args; QDBusConnection::systemBus().call(message); #endif } void IDevice::umount(const QString &path) { #ifdef Q_OS_LINUX QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.UDisks2",path,"org.freedesktop.UDisks2.Filesystem","Unmount"); QVariantMap args; message << args; QDBusConnection::systemBus().call(message); #endif } QString IDevice::getName() const { return text(CGisListWks::eColumnName); } void IDevice::getItemsByPos(const QPointF& pos, QList &items) { const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { project->getItemsByPos(pos, items); } } } IGisItem * IDevice::getItemByKey(const IGisItem::key_t& key) { IGisItem * item = 0; const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { if(project->getKey() != key.project) { continue; } item = project->getItemByKey(key); if(item != 0) { break; } } } return item; } IGisProject * IDevice::getProjectByKey(const QString& key) { const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { if(project->getKey() != key) { continue; } return project; } } return 0; } void IDevice::editItemByKey(const IGisItem::key_t& key) { const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { project->editItemByKey(key); } } } void IDevice::insertCopyOfProject(IGisProject * project, int& lastResult) { IGisProject * project2 = getProjectByKey(project->getKey()); if(project2) { int result = lastResult; if(lastResult == CSelectCopyAction::eResultNone) { CSelectCopyAction dlg(project, project2, CMainWindow::getBestWidgetForParent()); dlg.exec(); result = dlg.getResult(); if(dlg.allOthersToo()) { lastResult = result; } } if(result == CSelectCopyAction::eResultSkip) { return; } if(result == CSelectCopyAction::eResultNone) { return; } if(project2->remove()) { delete project2; } else { return; } } insertCopyOfProject(project); } void IDevice::updateProject(IGisProject * project) { IGisProject * project2 = getProjectByKey(project->getKey()); if(project2) { if(project2->remove()) { delete project2; } else { return; } } project->blockUpdateItems(true); insertCopyOfProject(project); project->blockUpdateItems(false); } bool IDevice::testForExternalProject(const QString& filename) { if(QDir(filename).exists() || QFile::exists(filename)) { QString msg = QObject::tr("There is another project with the same name. If you press 'ok' it will be removed and replaced."); int res = QMessageBox::warning(CMainWindow::getBestWidgetForParent(), getName(), msg, QMessageBox::Ok|QMessageBox::Abort, QMessageBox::Ok); if(res != QMessageBox::Ok) { return true; } if(QDir(filename).exists()) { QDir(filename).removeRecursively(); } else if(QFile::exists(filename)) { QFile(filename).remove(); } QFileInfo fi(filename); const int N = childCount(); for(int n = 0; n < N; n++) { QTreeWidgetItem * item = child(n); if(item->text(CGisListWks::eColumnName) == fi.fileName()) { delete item; break; } } } return false; } void IDevice::drawItem(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, CGisDraw * gis) { const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { project->drawItem(p, viewport, blockedAreas, gis); } } } void IDevice::drawLabel(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis) { const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { project->drawLabel(p, viewport, blockedAreas, fm, gis); } } } void IDevice::drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis) { const int N = childCount(); for(int n = 0; n < N; n++) { IGisProject * project = dynamic_cast(child(n)); if(project) { project->drawItem(p, viewport, gis); } } } qmapshack-1.5.1/src/device/CDeviceTwoNav.cpp000644 001750 000144 00000010273 12622435717 021666 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "device/CDeviceTwoNav.h" #include "gis/CGisListWks.h" #include "gis/gpx/CGpxProject.h" #include "gis/tnv/CTwoNavProject.h" #include CDeviceTwoNav::CDeviceTwoNav(const QString &path, const QString &key, const QString& model, QTreeWidget *parent) : IDevice(path, key, parent) { setText(CGisListWks::eColumnName, QString("TwoNav (%1)").arg(model)); setToolTip(CGisListWks::eColumnName, QString("TwoNav (%1)").arg(model)); if(QFile::exists(dir.absoluteFilePath("TwoNav/RegInfo.ini"))) { readReginfo(dir.absoluteFilePath("TwoNav/RegInfo.ini")); } else if(QFile::exists(dir.absoluteFilePath("TwoNavData/RegInfo.ini"))) { readReginfo(dir.absoluteFilePath("TwoNavData/RegInfo.ini")); } pathData = "TwoNavData/Data/"; QDir dirData(dir.absoluteFilePath(pathData)); { IGisProject * project = new CTwoNavProject(dirData.absolutePath(), this); if(!project->isValid()) { delete project; } } QStringList entries = dirData.entryList(QStringList("*.gpx")); foreach(const QString &entry, entries) { IGisProject * project = new CGpxProject(dirData.absoluteFilePath(entry), this); if(!project->isValid()) { delete project; } } entries = dirData.entryList(QDir::NoDotAndDotDot|QDir::Dirs); foreach(const QString &entry, entries) { IGisProject * project = new CTwoNavProject(dirData.absoluteFilePath(entry), this); if(!project->isValid()) { delete project; } } // special case: read the gpx files in the track log directory. dirData = dir.absoluteFilePath(pathData + "Tracklog"); entries = dirData.entryList(QStringList("*.gpx")); foreach(const QString &entry, entries) { IGisProject * project = new CGpxProject(dirData.absoluteFilePath(entry), this); if(!project->isValid()) { delete project; } } } CDeviceTwoNav::~CDeviceTwoNav() { } void CDeviceTwoNav::readReginfo(const QString& filename) { QString product, unittype; QRegExp re("(.*)=(.*)"); QFile file(filename); file.open(QIODevice::ReadOnly); while(!file.atEnd()) { QString line = file.readLine().simplified(); if(re.exactMatch(line)) { QString tok = re.cap(1); QString val = re.cap(2); if(tok == "product") { product = val; } else if(tok == "unittype") { unittype = val; } } } if(!product.isEmpty() && !unittype.isEmpty()) { setText(CGisListWks::eColumnName, QString("%1 (%2)").arg(product).arg(unittype)); } } void CDeviceTwoNav::insertCopyOfProject(IGisProject * project) { QString name = project->getName(); name = name.remove(QRegExp("[^A-Za-z0-9_]")); QDir dirData = dir.absoluteFilePath(pathData); QString filename = dirData.absoluteFilePath(name); if(testForExternalProject(filename)) { return; } CTwoNavProject * proj = new CTwoNavProject(filename, project, this); if(!proj->isValid()) { delete proj; return; } if(!proj->save()) { proj->remove(); delete proj; return; } } qmapshack-1.5.1/src/device/CDeviceWatcherLinux.h000644 001750 000144 00000002707 12622435723 022532 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEVICEWATCHERLINUX_H #define CDEVICEWATCHERLINUX_H #include "device/IDeviceWatcher.h" class QDBusObjectPath; class CDeviceWatcherLinux : public IDeviceWatcher { Q_OBJECT public: CDeviceWatcherLinux(CGisListWks *parent); virtual ~CDeviceWatcherLinux(); private slots: void slotDeviceAdded(const QDBusObjectPath& path, const QVariantMap& map); void slotDeviceRemoved(const QDBusObjectPath& path, const QStringList& list); void slotUpdate(); private: QString readMountPoint(const QString &path); }; #endif //CDEVICEWATCHERLINUX_H qmapshack-1.5.1/src/device/CDeviceTwoNav.h000644 001750 000144 00000002462 12622435723 021331 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDEVICETWONAV_H #define CDEVICETWONAV_H #include "device/IDevice.h" class CDeviceTwoNav : public IDevice { public: CDeviceTwoNav(const QString &path, const QString &key, const QString &model, QTreeWidget * parent); virtual ~CDeviceTwoNav(); void insertCopyOfProject(IGisProject * project); private: void readReginfo(const QString& filename); QString pathData; }; #endif //CDEVICETWONAV_H qmapshack-1.5.1/src/device/IDeviceWatcher.cpp000644 001750 000144 00000004135 12622435717 022053 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "device/CDeviceGarmin.h" #include "device/CDeviceTwoNav.h" #include "device/IDeviceWatcher.h" #include "gis/CGisListWks.h" #include #include IDeviceWatcher::IDeviceWatcher(CGisListWks *parent) : QObject(parent) , listWks(parent) { QTimer::singleShot(1000, this, SLOT(slotUpdate())); } IDeviceWatcher::~IDeviceWatcher() { } void IDeviceWatcher::probeForDevice(const QString& mountPoint, const QString& path, const QString& label) { QDir dir(mountPoint); if(!dir.exists()) { return; } qDebug() << "Probe device at" << mountPoint << path << label; QStringList entries = dir.entryList(); CCanvas::setOverrideCursor(Qt::WaitCursor,"probeForDevice"); if(entries.contains("Garmin")) { if(dir.exists("Garmin/GarminDevice.xml")) { new CDeviceGarmin(mountPoint, path, label, listWks); emit sigChanged(); } } else if(entries.contains("TwoNavData")) { new CDeviceTwoNav(mountPoint, path, label, listWks); emit sigChanged(); } else { qDebug() << "Don't know it :("; } CCanvas::restoreOverrideCursor("probeForDevice"); } qmapshack-1.5.1/src/CMakeLists.txt000644 001750 000144 00000036231 12623413746 020023 0ustar00oeichlerusers000000 000000 # Find includes in corresponding build directories set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) # Find the QtWidgets library find_package(Qt5Widgets) find_package(Qt5Core) find_package(Qt5Xml) find_package(Qt5Script) find_package(Qt5Sql) find_package(Qt5WebKitWidgets) find_package(Qt5LinguistTools) find_package(Qt5PrintSupport) if(UNIX) find_package(Qt5DBus) endif(UNIX) find_package(GDAL REQUIRED) find_package(PROJ REQUIRED) find_package(ROUTINO REQUIRED) if(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif(UNIX) if (APPLE) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -framework Foundation -framework DiskArbitration") SET(LINK_FLAGS "${LINK_FLAGS} -framework Foundation -framework DiskArbitration") endif(APPLE) if(APPLE) FIND_LIBRARY(DiskArbitration_LIBRARY DiskArbitration) FIND_LIBRARY(Foundation_LIBRARY Foundation) endif(APPLE) set( SRCS main.cpp GeoMath.cpp CMainWindow.cpp CAbout.cpp widgets/CTinySpinBox.cpp widgets/CDoubleSpinBox.cpp widgets/CFadingIcon.cpp widgets/CTextEditWidget.cpp widgets/CHistoryListWidget.cpp widgets/CPhotoAlbum.cpp widgets/CColorLegend.cpp helpers/CInputDialog.cpp helpers/CPositionDialog.cpp helpers/CWptIconDialog.cpp helpers/CSelectProjectDialog.cpp helpers/CSelectCopyAction.cpp helpers/CElevationDialog.cpp helpers/CLinksDialog.cpp helpers/CPhotoViewer.cpp helpers/CProgressDialog.cpp helpers/CCommandProcessor.cpp helpers/CAppSetup.cpp helpers/CDraw.cpp canvas/CCanvas.cpp canvas/CCanvasSetup.cpp canvas/IDrawContext.cpp canvas/IDrawObject.cpp dem/IDem.cpp map/IMapProp.cpp dem/IDemProp.cpp dem/CDemDraw.cpp dem/CDemVRT.cpp dem/CDemList.cpp dem/CDemItem.cpp dem/CDemPathSetup.cpp dem/CDemPropSetup.cpp map/IMap.cpp map/CMapDraw.cpp map/CMapItem.cpp map/CMapList.cpp map/CMapRMAP.cpp map/CMapJNX.cpp map/CMapIMG.cpp map/CMapMAP.cpp map/CMapVRT.cpp map/CMapWMTS.cpp map/CMapTMS.cpp map/CMapPathSetup.cpp map/CMapPropSetup.cpp map/garmin/IGarminStrTbl.cpp map/garmin/CGarminStrTbl6.cpp map/garmin/CGarminStrTbl8.cpp map/garmin/CGarminStrTblUtf8.cpp map/garmin/CGarminPoint.cpp map/garmin/CGarminPolygon.cpp map/garmin/CGarminTyp.cpp map/cache/CDiskCache.cpp map/cache/IDiskCache.cpp map/mapsforge/types.cpp units/IUnit.cpp units/CUnitImperial.cpp units/CUnitMetric.cpp units/CUnitNautic.cpp units/CTimeZoneSetup.cpp units/CUnitsSetup.cpp units/CCoordFormatSetup.cpp grid/CGrid.cpp grid/CGridSetup.cpp grid/CProjWizard.cpp grid/mitab.cpp mouse/IMouse.cpp mouse/IScrOpt.cpp mouse/CMouseDummy.cpp mouse/CMouseNormal.cpp mouse/CMouseMoveWpt.cpp mouse/CMouseEditTrk.cpp mouse/CMouseEditRte.cpp mouse/CMouseEditArea.cpp mouse/CMouseRangeTrk.cpp mouse/CMouseWptBubble.cpp mouse/CMousePrint.cpp mouse/CScrOptUnclutter.cpp mouse/CScrOptRangeTrk.cpp mouse/line/IMouseEditLine.cpp mouse/line/CScrOptEditLine.cpp mouse/line/CScrOptRangeLine.cpp mouse/line/ILineOp.cpp mouse/line/CLineOpMovePoint.cpp mouse/line/CLineOpAddPoint.cpp mouse/line/CLineOpDeletePoint.cpp mouse/line/CLineOpSelectRange.cpp gis/WptIcons.cpp gis/CGisDraw.cpp gis/IGisItem.cpp gis/IGisLine.cpp gis/CGisWidget.cpp gis/CGisListDB.cpp gis/CGisListWks.cpp gis/CSelDevices.cpp gis/prj/IGisProject.cpp gis/prj/CDetailsPrj.cpp gis/gpx/CGpxProject.cpp gis/gpx/serialization.cpp gis/qms/CQmsProject.cpp gis/qms/serialization.cpp gis/tnv/CTwoNavProject.cpp gis/tnv/serialization.cpp gis/db/IDB.cpp gis/db/CSetupDatabase.cpp gis/db/CSetupWorkspace.cpp gis/db/CSetupFolder.cpp gis/db/IDBFolder.cpp gis/db/CDBFolderLostFound.cpp gis/db/CDBFolderDatabase.cpp gis/db/CDBFolderGroup.cpp gis/db/CDBFolderOther.cpp gis/db/CDBFolderProject.cpp gis/db/CDBItem.cpp gis/db/CDBProject.cpp gis/db/CLostFoundProject.cpp gis/db/CSelectSaveAction.cpp gis/db/CSelectDBFolder.cpp gis/search/CSearchGoogle.cpp gis/wpt/CGisItemWpt.cpp gis/wpt/CScrOptWpt.cpp gis/wpt/CDetailsWpt.cpp gis/wpt/CDetailsGeoCache.cpp gis/wpt/CProjWpt.cpp gis/wpt/CSetupNewWpt.cpp gis/trk/CGisItemTrk.cpp gis/trk/CScrOptTrk.cpp gis/trk/CDetailsTrk.cpp gis/trk/CCombineTrk.cpp gis/trk/CCutTrk.cpp gis/trk/CSelectActivity.cpp gis/trk/CActivityTrk.cpp gis/trk/CPropertyTrk.cpp gis/trk/CKnownExtension.cpp gis/trk/filter/filter.cpp gis/trk/filter/CFilterDouglasPeuker.cpp gis/trk/filter/CFilterInvalid.cpp gis/trk/filter/CFilterReset.cpp gis/trk/filter/CFilterDelete.cpp gis/trk/filter/CFilterMedian.cpp gis/trk/filter/CFilterReplaceElevation.cpp gis/trk/filter/CFilterOffsetElevation.cpp gis/trk/filter/CFilterNewDate.cpp gis/trk/filter/CFilterObscureDate.cpp gis/trk/filter/CFilterSpeed.cpp gis/rte/CGisItemRte.cpp gis/rte/CScrOptRte.cpp gis/rte/CCreateRouteFromWpt.cpp gis/rte/CDetailsRte.cpp gis/rte/router/IRouter.cpp gis/rte/router/CRouterSetup.cpp gis/rte/router/CRouterRoutino.cpp gis/rte/router/CRouterMapQuest.cpp gis/rte/router/CRouterRoutinoPathSetup.cpp gis/ovl/CGisItemOvlArea.cpp gis/ovl/CScrOptOvlArea.cpp gis/ovl/CDetailsOvlArea.cpp plot/IPlot.cpp plot/CPlotAxis.cpp plot/CPlotAxisTime.cpp plot/CPlotData.cpp plot/CPlotProfile.cpp plot/ITrack.cpp plot/CPlotTrack.cpp plot/CPlot.cpp qlgt/converter.cpp qlgt/CQlb.cpp qlgt/IItem.cpp qlgt/CQlgtFolder.cpp qlgt/CQlgtWpt.cpp qlgt/CQlgtTrack.cpp qlgt/CQlgtRoute.cpp qlgt/CQlgtDiary.cpp qlgt/IQlgtOverlay.cpp qlgt/CQlgtDb.cpp qlgt/CQmsDb.cpp device/IDeviceWatcher.cpp device/IDevice.cpp device/CDeviceGarmin.cpp device/CDeviceTwoNav.cpp tool/IToolShell.cpp tool/CMapVrtBuilder.cpp tool/CImportDatabase.cpp tool/CRoutinoDatabaseBuilder.cpp print/CPrintDialog.cpp ) if(UNIX AND NOT APPLE) set( SRCS ${SRCS} device/CDeviceWatcherLinux.cpp ) endif(UNIX AND NOT APPLE) if (APPLE) set( SRCS ${SRCS} device/CDeviceWatcherMac.cpp ) endif(APPLE) if(WIN32) set( SRCS ${SRCS} device/CDeviceWatcherWindows.cpp ) endif(WIN32) set( HDRS version.h GeoMath.h CMainWindow.h CAbout.h widgets/CTinySpinBox.h widgets/CDoubleSpinBox.h widgets/CFadingIcon.h widgets/CHistoryListWidget.h widgets/CTextEditWidget.h widgets/CPhotoAlbum.h widgets/CColorLegend.h helpers/Platform.h helpers/CFileExt.h helpers/CAppOpts.h helpers/CSettings.h helpers/CInputDialog.h helpers/CPositionDialog.h helpers/CSelectCopyAction.h helpers/CWptIconDialog.h helpers/CSelectProjectDialog.h helpers/CElevationDialog.h helpers/CLinksDialog.h helpers/CPhotoViewer.h helpers/CProgressDialog.h helpers/CCommandProcessor.h helpers/CAppSetup.h helpers/CDraw.h canvas/CCanvas.h canvas/CCanvasSetup.h canvas/IDrawContext.h canvas/IDrawObject.h dem/IDem.h dem/IDemProp.h dem/CDemDraw.h dem/CDemVRT.h dem/CDemList.h dem/CDemItem.h dem/CDemPathSetup.h dem/CDemPropSetup.h map/IMap.h map/IMapProp.h map/CMapDraw.h map/CMapItem.h map/CMapList.h map/CMapRMAP.h map/CMapJNX.h map/CMapIMG.h map/CMapMAP.h map/CMapVRT.h map/CMapWMTS.h map/CMapTMS.h map/CMapPathSetup.h map/CMapPropSetup.h map/garmin/Garmin.h map/garmin/IGarminStrTbl.h map/garmin/CGarminStrTbl6.h map/garmin/CGarminStrTbl8.h map/garmin/CGarminStrTblUtf8.h map/garmin/CGarminPoint.h map/garmin/CGarminPolygon.h map/garmin/CGarminTyp.h map/cache/CDiskCache.h map/cache/IDiskCache.h map/mapsforge/types.h units/IUnit.h units/CUnitImperial.h units/CUnitMetric.h units/CUnitNautic.h units/CTimeZoneSetup.h units/CUnitsSetup.h units/CCoordFormatSetup.h grid/CGrid.h grid/CGridSetup.h grid/CProjWizard.h grid/mitab.h mouse/IMouse.h mouse/IScrOpt.h mouse/CMouseDummy.h mouse/CMouseNormal.h mouse/CMouseMoveWpt.h mouse/CMouseEditTrk.h mouse/CMouseEditRte.h mouse/CMouseEditArea.h mouse/CMouseRangeTrk.h mouse/CMouseWptBubble.h mouse/CMousePrint.h mouse/CScrOptUnclutter.h mouse/CScrOptRangeTrk.h mouse/line/CScrOptEditLine.h mouse/line/CScrOptRangeLine.h mouse/line/IMouseEditLine.h mouse/line/ILineOp.h mouse/line/CLineOpMovePoint.h mouse/line/CLineOpAddPoint.h mouse/line/CLineOpDeletePoint.h mouse/line/CLineOpSelectRange.h gis/WptIcons.h gis/CGisDraw.h gis/IGisItem.h gis/IGisLine.h gis/prj/IGisProject.h gis/prj/CDetailsPrj.h gis/CGisWidget.h gis/CGisListDB.h gis/CGisListWks.h gis/CSelDevices.h gis/qms/CQmsProject.h gis/tnv/CTwoNavProject.h gis/db/IDB.cpp gis/db/macros.h gis/db/CSetupDatabase.h gis/db/CSetupWorkspace.h gis/db/CSetupFolder.h gis/db/IDBFolder.h gis/db/CDBFolderLostFound.h gis/db/CDBFolderDatabase.h gis/db/CDBFolderGroup.h gis/db/CDBFolderOther.h gis/db/CDBFolderProject.h gis/db/CDBItem.h gis/db/CDBProject.h gis/db/CLostFoundProject.h gis/db/CSelectSaveAction.h gis/db/CSelectDBFolder.h gis/wpt/CGisItemWpt.h gis/wpt/CScrOptWpt.h gis/wpt/CDetailsWpt.h gis/wpt/CDetailsGeoCache.h gis/wpt/CProjWpt.h gis/wpt/CSetupNewWpt.h gis/trk/CGisItemTrk.h gis/trk/CScrOptTrk.h gis/trk/CDetailsTrk.h gis/trk/CCombineTrk.h gis/trk/CCutTrk.h gis/trk/CSelectActivity.h gis/trk/CActivityTrk.h gis/trk/CPropertyTrk.h gis/trk/CKnownExtension.h gis/trk/filter/CFilterDouglasPeuker.h gis/trk/filter/CFilterInvalid.h gis/trk/filter/CFilterReset.h gis/trk/filter/CFilterDelete.h gis/trk/filter/CFilterMedian.h gis/trk/filter/CFilterReplaceElevation.h gis/trk/filter/CFilterOffsetElevation.h gis/trk/filter/CFilterNewDate.h gis/trk/filter/CFilterObscureDate.h gis/trk/filter/CFilterSpeed.h gis/rte/CGisItemRte.h gis/rte/CScrOptRte.h gis/rte/CCreateRouteFromWpt.h gis/rte/CDetailsRte.h gis/rte/router/IRouter.h gis/rte/router/CRouterSetup.h gis/rte/router/CRouterRoutino.h gis/rte/router/CRouterMapQuest.h gis/rte/router/CRouterRoutinoPathSetup.h gis/ovl/CGisItemOvlArea.h gis/ovl/CScrOptOvlArea.h gis/ovl/CDetailsOvlArea.h gis/gpx/CGpxProject.h gis/search/CSearchGoogle.h plot/IPlot.h plot/CPlotAxis.h plot/CPlotAxisTime.h plot/CPlotData.h plot/CPlotProfile.h plot/ITrack.h plot/CPlotTrack.h plot/CPlot.h qlgt/CQlb.h qlgt/IItem.h qlgt/CQlgtFolder.h qlgt/CQlgtWpt.h qlgt/CQlgtTrack.h qlgt/CQlgtRoute.h qlgt/CQlgtDiary.h qlgt/IQlgtOverlay.h qlgt/CQlgtDb.h qlgt/CQmsDb.h device/IDeviceWatcher.h device/IDevice.h device/CDeviceGarmin.h device/CDeviceTwoNav.h tool/IToolShell.h tool/CMapVrtBuilder.h tool/CImportDatabase.h tool/CRoutinoDatabaseBuilder.h print/CPrintDialog.h ) if(UNIX AND NOT APPLE) set( HDRS ${HDRS} device/CDeviceWatcherLinux.h ) endif(UNIX AND NOT APPLE) if(APPLE) set( HDRS ${HDRS} device/CDeviceWatcherMac.h ) endif(APPLE) if(WIN32) set( HDRS ${HDRS} device/CDeviceWatcherWindows.h ) endif(WIN32) set( UIS IMainWindow.ui IAbout.ui widgets/ITextEditWidget.ui widgets/IPhotoAlbum.ui helpers/IInputDialog.ui helpers/IPositionDialog.ui helpers/IWptIconDialog.ui helpers/ISelectProjectDialog.ui helpers/ISelectCopyAction.ui helpers/IElevationDialog.ui helpers/ILinksDialog.ui helpers/IProgressDialog.ui canvas/ICanvasSetup.ui dem/IDemList.ui dem/IDemPathSetup.ui dem/IDemPropSetup.ui map/IMapList.ui map/IMapPathSetup.ui map/IMapPropSetup.ui grid/IGridSetup.ui grid/IProjWizard.ui mouse/IScrOptRangeTrk.ui mouse/line/IScrOptEditLine.ui mouse/line/IScrOptRangeLine.ui gis/IGisWidget.ui gis/ISelDevices.ui gis/prj/IDetailsPrj.ui gis/db/ISetupDatabase.ui gis/db/ISetupWorkspace.ui gis/db/ISetupFolder.ui gis/db/ISelectSaveAction.ui gis/db/ISelectDBFolder.ui gis/wpt/IScrOptWpt.ui gis/wpt/IDetailsWpt.ui gis/wpt/IDetailsGeoCache.ui gis/wpt/IProjWpt.ui gis/wpt/ISetupNewWpt.ui gis/trk/IScrOptTrk.ui gis/trk/IDetailsTrk.ui gis/trk/ICombineTrk.ui gis/trk/ICutTrk.ui gis/trk/ISelectActivity.ui gis/trk/filter/IFilterDouglasPeuker.ui gis/trk/filter/IFilterInvalid.ui gis/trk/filter/IFilterReset.ui gis/trk/filter/IFilterDelete.ui gis/trk/filter/IFilterMedian.ui gis/trk/filter/IFilterReplaceElevation.ui gis/trk/filter/IFilterOffsetElevation.ui gis/trk/filter/IFilterNewDate.ui gis/trk/filter/IFilterObscureDate.ui gis/trk/filter/IFilterSpeed.ui gis/rte/IScrOptRte.ui gis/rte/ICreateRouteFromWpt.ui gis/rte/IDetailsRte.ui gis/rte/router/IRouterSetup.ui gis/rte/router/IRouterRoutino.ui gis/rte/router/IRouterMapQuest.ui gis/rte/router/IRouterRoutinoPathSetup.ui gis/ovl/IScrOptOvlArea.ui gis/ovl/IDetailsOvlArea.ui units/ITimeZoneSetup.ui units/IUnitsSetup.ui units/ICoordFormatSetup.ui tool/IImportDatabase.ui tool/IMapVrtBuilder.ui tool/IRoutinoDatabaseBuilder.ui print/IPrintDialog.ui ) set( RCS resources.qrc ) qt5_wrap_ui(UI_HDRS ${UIS}) qt5_add_resources(RC_SRCS ${RCS}) if(UNIX) add_definitions(-Wall -Wno-switch -Wno-strict-aliasing) endif(UNIX) if(WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif(WIN32) add_definitions(-DROUTINO_XML_PATH=${ROUTINO_XML_PATH}) file(GLOB TRANSLATIONS_FILES locale/*.ts) qt5_add_translation( ${APPLICATION_NAME}_QM_FILES ${TRANSLATIONS_FILES} ) set(ALLINP ${SRCS} ${HDRS} ${UI_HDRS} ${RC_SRCS} ${${APPLICATION_NAME}_QM_FILES} ) include_directories( ${CMAKE_BINARY_DIR} ${GDAL_INCLUDE_DIRS} ${PROJ_INCLUDE_DIRS} ${ROUTINO_INCLUDE_DIRS} ) if(APPLE) INCLUDE_DIRECTORIES(/System/Library/Frameworks/Foundation.framework) INCLUDE_DIRECTORIES(/System/Library/Frameworks/DiskArbitration.framework) endif(APPLE) add_executable(${APPLICATION_NAME} WIN32 ${ALLINP}) if(UNIX) set(DBUS_LIB Qt5::DBus) else(UNIX) set(DBUS_LIB) endif(UNIX) target_link_libraries(${APPLICATION_NAME} Qt5::Widgets Qt5::Xml Qt5::Script Qt5::Sql Qt5::WebKitWidgets Qt5::PrintSupport ${DBUS_LIB} ${GDAL_LIBRARIES} ${PROJ_LIBRARIES} ${ROUTINO_LIBRARIES} ) if(APPLE) target_link_libraries(${APPLICATION_NAME} ${Foundation_LIBRARY} ${DiskArbitration_LIBRARY} ) endif(APPLE) install( TARGETS ${APPLICATION_NAME} DESTINATION ${BIN_INSTALL_DIR} ) if (UNIX AND NOT WIN32 AND NOT APPLE) install( FILES ${${APPLICATION_NAME}_QM_FILES} DESTINATION ${DATA_INSTALL_PREFIX}/${APPLICATION_NAME}/translations) endif (UNIX AND NOT WIN32 AND NOT APPLE) qmapshack-1.5.1/src/gis/db/ISetupDatabase.ui000644 001750 000144 00000006163 12527654570 021635 0ustar00oeichlerusers000000 000000 ISetupDatabase 0 0 476 112 Add database... File - Name Add new database. ... :/icons/32x32/Add.png:/icons/32x32/Add.png Open existing database. ... :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISetupDatabase accept() 248 254 157 274 buttonBox rejected() ISetupDatabase reject() 316 260 286 274 qmapshack-1.5.1/src/gis/db/macros.h000644 001750 000144 00000002174 12622435723 020064 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef MACROS_H #define MACROS_H #define DB_VERSION 1 #define QUERY_EXEC(cmd) \ if(!query.exec()) \ { \ qDebug() << query.lastQuery(); \ qDebug() << query.lastError(); \ cmd; \ } \ #endif //MACROS_H qmapshack-1.5.1/src/gis/db/CDBProject.h000644 001750 000144 00000004755 12622435723 020526 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBPROJECT_H #define CDBPROJECT_H #include "gis/prj/IGisProject.h" #include class CEvtD2WShowItems; class CEvtD2WHideItems; class CQlgtFolder; class CDBProject : public IGisProject { public: CDBProject(CGisListWks * parent); CDBProject(const QString &dbName, quint64 id, CGisListWks * parent); CDBProject(CQlgtFolder& folder); virtual ~CDBProject(); /** @brief Restore database link after the project has been restored from binary storage. Typically this is done after the project has been restored in the workspace on application's startup. */ void restoreDBLink(); bool save(); bool saveAs(); quint64 getId() { return id; } QString getDBName() { return db.connectionName(); } /** @brief Serialize object out of a QDataStream See CGisSerialization.cpp for implementation @param stream the binary data stream @return The stream object. */ QDataStream& operator<<(QDataStream& stream); /** @brief Serialize object into a QDataStream See CGisSerialization.cpp for implementation @param stream the binary data stream @return The stream object. */ QDataStream& operator>>(QDataStream& stream); void postStatus(); void showItems(CEvtD2WShowItems * evt); void hideItems(CEvtD2WHideItems * evt); protected: void setupName(const QString &defaultName); void updateItem(IGisItem * item, quint64 idItem); quint64 insertItem(IGisItem * item); QSqlDatabase db; quint64 id; }; #endif //CDBPROJECT_H qmapshack-1.5.1/src/gis/db/CSetupWorkspace.h000644 001750 000144 00000002331 12622435723 021655 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSETUPWORKSPACE_H #define CSETUPWORKSPACE_H #include "ui_ISetupWorkspace.h" #include class CSetupWorkspace : public QDialog, private Ui::ISetupWorkspace { Q_OBJECT public: CSetupWorkspace(QWidget * parent); virtual ~CSetupWorkspace(); public slots: void accept(); }; #endif //CSETUPWORKSPACE_H qmapshack-1.5.1/src/gis/db/ISetupWorkspace.ui000644 001750 000144 00000004462 12527654570 022067 0ustar00oeichlerusers000000 000000 ISetupWorkspace 0 0 421 88 Setup workspace... 0 0 save workspace on exit, and every minutes Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISetupWorkspace accept() 227 91 157 111 buttonBox rejected() ISetupWorkspace reject() 295 97 286 111 qmapshack-1.5.1/src/gis/db/CSetupDatabase.h000644 001750 000144 00000002656 12622435723 021435 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSETUPDATABASE_H #define CSETUPDATABASE_H #include "ui_ISetupDatabase.h" #include class CGisListDB; class CSetupDatabase : public QDialog, private Ui::ISetupDatabase { Q_OBJECT public: CSetupDatabase(QString &name, QString &filename, CGisListDB& parent); virtual ~CSetupDatabase(); public slots: void accept(); private slots: void slotNewDB(); void slotOpenDB(); void slotUpdateButtonBox(); private: CGisListDB& list; QString& name; QString& filename; }; #endif //CSETUPDATABASE_H qmapshack-1.5.1/src/gis/db/IDB.h000644 001750 000144 00000002342 12622435723 017173 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDB_H #define IDB_H #include #include class IDB { public: IDB(); virtual ~IDB(); protected: bool setupDB(const QString &filename, const QString &connectionName); bool initDB(); bool migrateDB(int version); QSqlDatabase db; static QMap references; }; #endif //IDB_H qmapshack-1.5.1/src/gis/db/ISetupFolder.ui000644 001750 000144 00000005430 12527654570 021340 0ustar00oeichlerusers000000 000000 ISetupFolder 0 0 400 170 Database Folder... Folder name Group :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png Project :/icons/32x32/PathGreen.png:/icons/32x32/PathGreen.png true Other :/icons/32x32/PathOrange.png:/icons/32x32/PathOrange.png Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISetupFolder accept() 248 254 157 274 buttonBox rejected() ISetupFolder reject() 316 260 286 274 qmapshack-1.5.1/src/gis/db/CDBFolderDatabase.cpp000644 001750 000144 00000003440 12622435717 022304 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/db/CDBFolderDatabase.h" #include "gis/db/CDBFolderLostFound.h" CDBFolderDatabase::CDBFolderDatabase(const QString& filename, const QString& name, QTreeWidget *parent) : IDBFolder(false, IDB::db, eTypeDatabase, 1, parent) , filename(filename) , folderLostFound(0) { setToolTip(CGisListDB::eColumnName, QObject::tr("All your data grouped by folders.")); setIcon(CGisListDB::eColumnCheckbox, QIcon("://icons/32x32/Database.png")); setText(CGisListDB::eColumnName, name); setupDB(filename, name); setupFromDB(); } CDBFolderDatabase::~CDBFolderDatabase() { } void CDBFolderDatabase::expanding() { IDBFolder::expanding(); folderLostFound = new CDBFolderLostFound(IDB::db, 0); insertChild(0, folderLostFound); } void CDBFolderDatabase::updateLostFound() { if(folderLostFound) { folderLostFound->update(); } } qmapshack-1.5.1/src/gis/db/CSelectSaveAction.h000644 001750 000144 00000003022 12622435723 022070 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSELECTSAVEACTION_H #define CSELECTSAVEACTION_H #include "ui_ISelectSaveAction.h" #include class IGisItem; class CSelectSaveAction : public QDialog, private Ui::ISelectSaveAction { Q_OBJECT public: CSelectSaveAction(const IGisItem * src, const IGisItem * tar, QWidget * parent); virtual ~CSelectSaveAction(); enum result_e { eResultNone, eResultSave, eResultSkip, }; result_e getResult() { return result; } bool allOthersToo(); private slots: void slotSelectResult(); private: result_e result = eResultNone; }; #endif //CSELECTSAVEACTION_H qmapshack-1.5.1/src/gis/db/CDBFolderProject.cpp000644 001750 000144 00000002361 12622435717 022207 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/db/CDBFolderProject.h" CDBFolderProject::CDBFolderProject(QSqlDatabase& db, quint64 key, QTreeWidgetItem * parent) : IDBFolder(true, db, eTypeProject, key, parent) { setIcon(CGisListDB::eColumnCheckbox,QIcon("://icons/32x32/PathGreen.png")); setupFromDB(); } CDBFolderProject::~CDBFolderProject() { } qmapshack-1.5.1/src/gis/db/CDBFolderGroup.cpp000644 001750 000144 00000002345 12622435717 021677 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/db/CDBFolderGroup.h" CDBFolderGroup::CDBFolderGroup(QSqlDatabase& db, quint64 key, QTreeWidgetItem * parent) : IDBFolder(false, db, eTypeGroup, key, parent) { setIcon(CGisListDB::eColumnCheckbox,QIcon("://icons/32x32/PathBlue.png")); setupFromDB(); } CDBFolderGroup::~CDBFolderGroup() { } qmapshack-1.5.1/src/gis/db/CDBFolderGroup.h000644 001750 000144 00000002232 12622435723 021334 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBFOLDERGROUP_H #define CDBFOLDERGROUP_H #include "gis/db/IDBFolder.h" class CDBFolderGroup : public IDBFolder { public: CDBFolderGroup(QSqlDatabase &db, quint64 key, QTreeWidgetItem * parent); virtual ~CDBFolderGroup(); }; #endif //CDBFOLDERGROUP_H qmapshack-1.5.1/src/gis/db/CDBFolderLostFound.h000644 001750 000144 00000002532 12622435723 022160 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBFOLDERLOSTFOUND_H #define CDBFOLDERLOSTFOUND_H #include "gis/db/IDBFolder.h" class CDBFolderLostFound : public IDBFolder { public: CDBFolderLostFound(QSqlDatabase &db, QTreeWidgetItem *parent); virtual ~CDBFolderLostFound(); void update(CEvtW2DAckInfo * info); void update(); void expanding() { } void clear(); bool delItem(CDBItem * item); protected: void setupFromDB(); }; #endif //CDBFOLDERLOSTFOUND_H qmapshack-1.5.1/src/gis/db/CDBFolderOther.cpp000644 001750 000144 00000002346 12622435717 021665 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/db/CDBFolderOther.h" CDBFolderOther::CDBFolderOther(QSqlDatabase& db, quint64 key, QTreeWidgetItem * parent) : IDBFolder(true, db, eTypeOther, key, parent) { setIcon(CGisListDB::eColumnCheckbox,QIcon("://icons/32x32/PathOrange.png")); setupFromDB(); } CDBFolderOther::~CDBFolderOther() { } qmapshack-1.5.1/src/gis/db/CDBProject.cpp000644 001750 000144 00000035313 12622435717 021056 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisWidget.h" #include "gis/db/CDBProject.h" #include "gis/db/CSelectSaveAction.h" #include "gis/db/macros.h" #include "gis/gpx/CGpxProject.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/qms/CQmsProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CProgressDialog.h" #include "helpers/CSettings.h" #include #include CDBProject::CDBProject(CGisListWks * parent) : IGisProject(eTypeDb, "", parent) , id(0) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/DBProject.png")); } CDBProject::CDBProject(const QString& dbName, quint64 id, CGisListWks *parent) : IGisProject(eTypeDb, dbName, parent) , id(id) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/DBProject.png")); db = QSqlDatabase::database(dbName); QSqlQuery query(db); query.prepare("SELECT date, name, data FROM folders WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); QString date = query.value(0).toString(); QString name = query.value(1).toString(); QByteArray data = query.value(2).toByteArray(); if(data.isEmpty()) { metadata.name = name; metadata.time = QDateTime::fromString(date,"yyyy-MM-dd hh:mm:ss"); query.prepare("UPDATE folders SET key=:key WHERE id=:id"); query.bindValue(":key", getKey()); query.bindValue(":id", id); QUERY_EXEC(return ); } else { QDataStream in(&data, QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); *this << in; filename = dbName; } setupName(name); setToolTip(CGisListWks::eColumnName, getInfo()); updateItems(); valid = true; } CDBProject::~CDBProject() { CEvtW2DAckInfo * info = new CEvtW2DAckInfo(false, getId(), db.connectionName()); CGisWidget::self().postEventForDb(info); } void CDBProject::restoreDBLink() { db = QSqlDatabase::database(filename); QSqlQuery query(db); query.prepare("SELECT id FROM folders WHERE key=:key"); query.bindValue(":key", getKey()); QUERY_EXEC(return ); if(query.next()) { id = query.value(0).toULongLong(); setupName("----"); valid = true; } } void CDBProject::setupName(const QString &defaultName) { IGisProject::setupName(defaultName); QSqlQuery query(db); query.prepare("SELECT t1.name FROM folders AS t1 WHERE id=(SELECT parent FROM folder2folder WHERE child=:id) AND (t1.type=:type1 OR t1.type=:type2)"); query.bindValue(":id", id); query.bindValue(":type1", IDBFolder::eTypeGroup); query.bindValue(":type2", IDBFolder::eTypeProject); QUERY_EXEC(); if(query.next()) { nameSuffix = query.value(0).toString(); } setText(CGisListWks::eColumnName, getNameEx()); } void CDBProject::postStatus() { CEvtW2DAckInfo * info = new CEvtW2DAckInfo(true, getId(), db.connectionName()); bool changedItems = false; const int N = childCount(); for(int n = 0; n < N; n++) { IGisItem * item = dynamic_cast(child(n)); if(item) { info->keysChildren << item->getKey().item; changedItems |= item->isChanged(); } } updateItems(); if(!changedItems) { setText(CGisListWks::eColumnDecoration,""); } CGisWidget::self().postEventForDb(info); } bool CDBProject::saveAs() { SETTINGS; QString path = cfg.value("Paths/lastGisPath", QDir::homePath()).toString(); QString filter = filedialogFilterQMS; QString fn = QFileDialog::getSaveFileName(CMainWindow::getBestWidgetForParent(), QObject::tr("Save GIS data to..."), path, filedialogSaveFilters, &filter); if(fn.isEmpty()) { return false; } bool res = false; if(filter == filedialogFilterGPX) { res = CGpxProject::saveAs(fn, *this); } else if(filter == filedialogFilterQMS) { res = CQmsProject::saveAs(fn, *this); } else { return false; } path = QFileInfo(fn).absolutePath(); cfg.setValue("Paths/lastGisPath", path); return res; } void CDBProject::updateItem(IGisItem * item, quint64 idItem) { QSqlQuery query(db); // serialize complete history of item QByteArray data; QDataStream in(&data, QIODevice::WriteOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in << item->getHistory(); // prepare icon to be saved QBuffer buffer; buffer.open(QIODevice::ReadWrite); QPixmap pixmap = item->getIcon(); pixmap.save(&buffer, "PNG"); buffer.seek(0); query.prepare("UPDATE items SET type=:type, key=:key, icon=:icon, name=:name, comment=:comment, data=:data WHERE id=:id"); query.bindValue(":type", item->type()); query.bindValue(":key", item->getKey().item); query.bindValue(":icon", buffer.data()); query.bindValue(":name", item->getName()); query.bindValue(":comment", item->getInfo()); query.bindValue(":data", data); query.bindValue(":id", idItem); QUERY_EXEC(throw -1); } quint64 CDBProject::insertItem(IGisItem * item) { QSqlQuery query(db); // serialize complete history of item QByteArray data; QDataStream in(&data, QIODevice::WriteOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in << item->getHistory(); // prepare icon to be saved QBuffer buffer; buffer.open(QIODevice::ReadWrite); QPixmap pixmap = item->getIcon(); pixmap.save(&buffer, "PNG"); buffer.seek(0); query.prepare("INSERT INTO items (type, key, icon, name, comment, data) VALUES (:type, :key, :icon, :name, :comment, :data)"); query.bindValue(":type", item->type()); query.bindValue(":key", item->getKey().item); query.bindValue(":icon", buffer.data()); query.bindValue(":name", item->getName()); query.bindValue(":comment", item->getInfo()); query.bindValue(":data", data); QUERY_EXEC(throw -1); query.prepare("SELECT last_insert_rowid() from items"); QUERY_EXEC(throw -1); query.next(); quint64 idItem = query.value(0).toULongLong(); if(idItem == 0) { qDebug() << "childId equals 0. bad."; throw -1; } return idItem; } bool CDBProject::save() { bool clearProjectChangeFlag = true; int lastResult = CSelectSaveAction::eResultNone; QSqlQuery query(db); int N = childCount(); PROGRESS_SETUP(QObject::tr("Save ..."), 0, N, CMainWindow::getBestWidgetForParent()); CEvtW2DAckInfo * info = new CEvtW2DAckInfo(true, getId(), db.connectionName()); try { for(int i = 0; i < N; i++) { PROGRESS(i, throw 0); IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } // skip unchanged items if(!item->isChanged()) { info->keysChildren << item->getKey().item; continue; } // test if item exists in database quint64 idItem = 0; quint32 typeItem = 0; query.prepare("SELECT id, type FROM items WHERE key=:key"); query.bindValue(":key", item->getKey().item); QUERY_EXEC(throw -1); if(query.next()) { idItem = query.value(0).toULongLong(); typeItem = query.value(1).toUInt(); // check if relation already exists. query.prepare("SELECT id FROM folder2item WHERE parent=:parent AND child=:child"); query.bindValue(":parent", id); query.bindValue(":child", idItem); QUERY_EXEC(); if(!query.next()) { // update dialog int result = lastResult; if(lastResult == CSelectSaveAction::eResultNone) { IGisItem * item1 = 0; // load item from database for a compare switch(typeItem) { case IGisItem::eTypeWpt: item1 = new CGisItemWpt(idItem, db, 0); break; case IGisItem::eTypeTrk: item1 = new CGisItemTrk(idItem, db, 0); break; case IGisItem::eTypeRte: item1 = new CGisItemRte(idItem, db, 0); break; case IGisItem::eTypeOvl: item1 = new CGisItemOvlArea(idItem, db, 0); break; default: ; } if(item1 == 0) { qDebug() << "no item to compare!?."; throw -1; } CSelectSaveAction dlg(item, item1, &progress); dlg.exec(); result = dlg.getResult(); if(dlg.allOthersToo()) { lastResult = result; } } if(result == CSelectSaveAction::eResultNone) { // no decision by user, cancel operation. // this is different to a skip as a skip will // just skip saving the data, but the item to folder // link will be still processed. clearProjectChangeFlag = false; continue; } // create relation query.prepare("INSERT INTO folder2item (parent, child) VALUES (:parent, :child)"); query.bindValue(":parent", id); query.bindValue(":child", idItem); QUERY_EXEC(throw -1); if(result == CSelectSaveAction::eResultSave) { // the item is in the database and has no relation to the folder -> update only if the user confirms. updateItem(item, idItem); } } else { // the item is in the database and has a relation to the folder -> simply update item updateItem(item, idItem); } } else { // the item is not in the database -> insert item and create relation to folder idItem = insertItem(item); // create relation query.prepare("INSERT INTO folder2item (parent, child) VALUES (:parent, :child)"); query.bindValue(":parent", id); query.bindValue(":child", idItem); QUERY_EXEC(throw -1); } info->keysChildren << item->getKey().item; item->updateDecoration(IGisItem::eMarkNone, IGisItem::eMarkChanged); } // serialize metadata of project QByteArray data; QDataStream in(&data, QIODevice::WriteOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); *this >> in; // update folder entry in database query.prepare("UPDATE folders SET name=:name, comment=:comment, data=:data WHERE id=:id"); query.bindValue(":name", getName()); query.bindValue(":comment", getInfo()); query.bindValue(":data", data); query.bindValue(":id", getId()); QUERY_EXEC(throw -1); info->updateLostFound = true; CGisWidget::self().postEventForDb(info); if(clearProjectChangeFlag) { setText(CGisListWks::eColumnDecoration,""); } } catch(int n) { if(n < 0) { delete info; } else { info->updateLostFound = true; CGisWidget::self().postEventForDb(info); } return false; } return true; } void CDBProject::showItems(CEvtD2WShowItems * evt) { foreach(const evt_item_t &item, evt->items) { IGisItem * gisItem = 0; switch(item.type) { case IGisItem::eTypeWpt: gisItem = new CGisItemWpt(item.id, db, this); break; case IGisItem::eTypeTrk: gisItem = new CGisItemTrk(item.id, db, this); break; case IGisItem::eTypeRte: gisItem = new CGisItemRte(item.id, db, this); break; case IGisItem::eTypeOvl: gisItem = new CGisItemOvlArea(item.id, db, this); break; default: ; } /* [Issue #72] Database/Workspace inconsisteny in QMS 1.4.0 When an item with no key is loaded it is "healed". The healing will mark it as changed. To avoid this save all items that are marked as changed right after loading from the database. */ if(gisItem && gisItem->isChanged()) { updateItem(gisItem, item.id); gisItem->updateDecoration(IGisItem::eMarkNone, IGisItem::eMarkChanged); } } postStatus(); setToolTip(CGisListWks::eColumnName, getInfo()); } void CDBProject::hideItems(CEvtD2WHideItems * evt) { IGisItem::key_t key; key.project = getKey(); QMessageBox::StandardButtons last = QMessageBox::YesToAll; foreach(const QString &k, evt->keys) { key.item = k; delItemByKey(key, last); } postStatus(); setToolTip(CGisListWks::eColumnName, getInfo()); } qmapshack-1.5.1/src/gis/db/CDBItem.h000644 001750 000144 00000002570 12622435723 020007 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBITEM_H #define CDBITEM_H #include class IDBFolder; class QSqlDatabase; class CDBItem : public QTreeWidgetItem { public: CDBItem(QSqlDatabase& db, quint64 id, IDBFolder * parent); virtual ~CDBItem(); quint64 getId() { return id; } const QString& getKey() { return key; } void toggle(); void remove(); private: QSqlDatabase& db; quint64 id; int type; QString key; }; #endif //CDBITEM_H qmapshack-1.5.1/src/gis/db/CDBFolderLostFound.cpp000644 001750 000144 00000006553 12622435717 022525 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/CGisWidget.h" #include "gis/db/CDBFolderLostFound.h" #include "gis/db/CDBItem.h" #include "gis/db/macros.h" #include CDBFolderLostFound::CDBFolderLostFound(QSqlDatabase& db, QTreeWidgetItem *parent) : IDBFolder(true, db, eTypeLostFound, 0, parent) { setToolTip(CGisListDB::eColumnName, QObject::tr("All your data grouped by folders.")); setupFromDB(); setCheckState(CGisListDB::eColumnCheckbox, Qt::Unchecked); CEvtD2WReqInfo * evt = new CEvtD2WReqInfo(getId(), getDBName()); CGisWidget::self().postEventForWks(evt); } CDBFolderLostFound::~CDBFolderLostFound() { } void CDBFolderLostFound::setupFromDB() { int cnt = 0; QSqlQuery query(db); qDeleteAll(takeChildren()); query.prepare("SELECT id FROM items AS t1 WHERE NOT EXISTS(SELECT * FROM folder2item WHERE child=t1.id) ORDER BY t1.type, t1.name"); QUERY_EXEC(return ); while(query.next()) { quint64 id = query.value(0).toULongLong(); new CDBItem(db, id, this); cnt++; } if(cnt) { setText(CGisListDB::eColumnName, QObject::tr("Lost & Found (%1)").arg(cnt)); setIcon(CGisListDB::eColumnCheckbox, QIcon("://icons/32x32/DeleteMultiple.png")); } else { setText(CGisListDB::eColumnName, QObject::tr("Lost & Found")); setIcon(CGisListDB::eColumnCheckbox, QIcon("://icons/32x32/Empty.png")); } CEvtD2WUpdateLnF * evt = new CEvtD2WUpdateLnF(getId(), getDBName()); CGisWidget::self().postEventForWks(evt); } void CDBFolderLostFound::update(CEvtW2DAckInfo * info) { if(info->id != 0) { return; } setCheckState(CGisListDB::eColumnCheckbox, info->isLoaded ? Qt::Checked : Qt::Unchecked); } void CDBFolderLostFound::update() { setupFromDB(); } void CDBFolderLostFound::clear() { QSqlQuery query(db); query.prepare("DELETE FROM items WHERE id NOT IN (SELECT child from folder2item)"); QUERY_EXEC(return ); setupFromDB(); } bool CDBFolderLostFound::delItem(CDBItem * item) { QSqlQuery query(db); if(checkState(CGisListDB::eColumnCheckbox) == Qt::Checked) { CEvtD2WHideItems * evt = new CEvtD2WHideItems(getId(), getDBName()); evt->keys << item->getKey(); CGisWidget::self().postEventForWks(evt); } query.prepare("DELETE FROM items WHERE id=:id AND id NOT IN (SELECT child from folder2item)"); query.bindValue(":id", item->getId()); QUERY_EXEC(return false); return true; } qmapshack-1.5.1/src/gis/db/CSetupWorkspace.cpp000644 001750 000144 00000003456 12622435717 022224 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "config.h" #include "gis/db/CSetupWorkspace.h" #include "helpers/CSettings.h" #include CSetupWorkspace::CSetupWorkspace(QWidget *parent) : QDialog(parent) { setupUi(this); SETTINGS; cfg.beginGroup("Database"); checkSaveOnExit->setChecked(cfg.value("saveOnExit", true).toBool()); spinSaveEvery->setValue(cfg.value("saveEvery",5).toInt()); cfg.endGroup(); connect(checkSaveOnExit, SIGNAL(toggled(bool)), spinSaveEvery, SLOT(setEnabled(bool))); } CSetupWorkspace::~CSetupWorkspace() { } void CSetupWorkspace::accept() { SETTINGS; cfg.beginGroup("Database"); cfg.setValue("saveOnExit", checkSaveOnExit->isChecked()); cfg.setValue("saveEvery", spinSaveEvery->value()); cfg.endGroup(); QMessageBox::information(this, tr("Setup database..."), tr("Changes will become active after an application's restart."), QMessageBox::Ok); QDialog::accept(); } qmapshack-1.5.1/src/gis/db/IDB.cpp000644 001750 000144 00000013066 12622435717 017536 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/db/IDB.h" #include "gis/db/macros.h" #include QMap IDB::references; IDB::IDB() { } IDB::~IDB() { references[db.connectionName()]--; if(references[db.connectionName()] == 0 && db.isOpen()) { qDebug() << "close database" << db.connectionName(); db.close(); } } bool IDB::setupDB(const QString& filename, const QString& connectionName) { references[connectionName]++; if(!QSqlDatabase::contains(connectionName)) { db = QSqlDatabase::addDatabase("QSQLITE", connectionName); db.setDatabaseName(filename); if(!db.open()) { qDebug() << "failed to open database" << db.lastError(); } } else { db = QSqlDatabase::database(connectionName); } QSqlQuery query(db); query.prepare("PRAGMA locking_mode=EXCLUSIVE"); QUERY_EXEC(return false); query.prepare("PRAGMA temp_store=MEMORY"); QUERY_EXEC(return false); query.prepare("PRAGMA default_cache_size=50"); QUERY_EXEC(return false); query.prepare("PRAGMA page_size=8192"); QUERY_EXEC(return false); query.prepare("PRAGMA synchronous=OFF"); QUERY_EXEC(return false); if(!query.exec("SELECT version FROM versioninfo")) { return initDB(); } else if(query.next()) { int version = query.value(0).toInt(); if(version != DB_VERSION) { return migrateDB(version); } } else { return initDB(); } query.prepare( "UPDATE folders SET name=:name WHERE id=1"); query.bindValue(":name", connectionName); QUERY_EXEC() return true; } bool IDB::initDB() { QSqlQuery query(db); if(query.exec( "CREATE TABLE versioninfo ( version TEXT, type TEXT )")) { query.prepare( "INSERT INTO versioninfo (version, type) VALUES(:version, 'QMapShack')"); query.bindValue(":version", DB_VERSION); QUERY_EXEC(return false); } if(!query.exec( "CREATE TABLE folders (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER NOT NULL," "key TEXT," "date DATETIME DEFAULT CURRENT_TIMESTAMP," "name TEXT NOT NULL," "comment TEXT," "locked BOOLEAN DEFAULT FALSE," "data BLOB" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return false; } if(!query.exec( "CREATE TABLE items (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER," "key TEXT NOT NULL," "date DATETIME DEFAULT CURRENT_TIMESTAMP," "icon BLOB NOT NULL," "name TEXT NOT NULL," "comment TEXT," "data BLOB NOT NULL" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return false; } query.prepare("INSERT INTO folders (type, name, comment) VALUES (2, :name, '')"); query.bindValue(":name", db.connectionName()); QUERY_EXEC(return false); if(!query.exec( "CREATE TABLE folder2folder (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "parent INTEGER NOT NULL," "child INTEGER NOT NULL," "FOREIGN KEY(parent) REFERENCES folders(id)," "FOREIGN KEY(child) REFERENCES folders(id)" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return false; } if(!query.exec( "CREATE TABLE folder2item (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "parent INTEGER NOT NULL," "child INTEGER NOT NULL," "FOREIGN KEY(parent) REFERENCES folders(id)," "FOREIGN KEY(child) REFERENCES items(id)" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); return false; } return true; } bool IDB::migrateDB(int version) { QSqlQuery query(db); for(version++; version <= DB_VERSION; version++) { // switch(version) // { // default:; // } } query.prepare( "UPDATE versioninfo set version=:version"); query.bindValue(":version", version - 1); QUERY_EXEC(return false); return true; } qmapshack-1.5.1/src/gis/db/CDBFolderDatabase.h000644 001750 000144 00000002774 12622435723 021757 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBFOLDERDATABASE_H #define CDBFOLDERDATABASE_H #include "gis/db/IDBFolder.h" #include class CDBFolderLostFound; class CDBFolderDatabase : public IDBFolder, private IDB { public: CDBFolderDatabase(const QString &filename, const QString &name, QTreeWidget *parent); virtual ~CDBFolderDatabase(); void expanding(); void updateLostFound(); const QString& getFilename() { return filename; } QSqlDatabase& getDb() { return IDB::db; } private: QString filename; CDBFolderLostFound * folderLostFound = 0; }; #endif //CDBFOLDERDATABASE_H qmapshack-1.5.1/src/gis/db/CSelectDBFolder.h000644 001750 000144 00000002544 12622435723 021465 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSELECTDBFOLDER_H #define CSELECTDBFOLDER_H #include "ui_ISelectDBFolder.h" #include class CSelectDBFolder : public QDialog, private Ui::ISelectDBFolder { Q_OBJECT public: CSelectDBFolder(quint64& id, QString& db, QWidget * parent); virtual ~CSelectDBFolder(); private slots: void slotItemExpanded(QTreeWidgetItem * item); void slotItemSelectionChanged(); private: quint64& id; QString& db; }; #endif //CSELECTDBFOLDER_H qmapshack-1.5.1/src/gis/db/CSelectDBFolder.cpp000644 001750 000144 00000004777 12622435717 022035 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/db/CDBFolderDatabase.h" #include "gis/db/CSelectDBFolder.h" #include "helpers/CSettings.h" #include CSelectDBFolder::CSelectDBFolder(quint64 &id, QString &db, QWidget *parent) : QDialog(parent) , id(id) , db(db) { setupUi(this); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); SETTINGS; QStringList names = cfg.value("Database/names").toStringList(); QStringList files = cfg.value("Database/files").toStringList(); const int N = names.count(); for(int i = 0; i < N; i++) { new CDBFolderDatabase(files[i], names[i], treeWidget); } connect(treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*))); connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged())); CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSelectDBFolder"); } CSelectDBFolder::~CSelectDBFolder() { CCanvas::restoreOverrideCursor("~CSelectDBFolder"); } void CSelectDBFolder::slotItemExpanded(QTreeWidgetItem * item) { IDBFolder * folder = dynamic_cast(item); if(folder == 0) { return; } folder->expanding(); } void CSelectDBFolder::slotItemSelectionChanged() { IDBFolder * folder = dynamic_cast(treeWidget->currentItem()); if(folder) { id = folder->getId(); db = folder->getDBName(); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } else { id = -1; db.clear(); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } } qmapshack-1.5.1/src/gis/db/ISelectDBFolder.ui000644 001750 000144 00000003313 12527654570 021663 0ustar00oeichlerusers000000 000000 ISelectDBFolder 0 0 400 300 Select Parent Folder... Name Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISelectDBFolder accept() 248 254 157 274 buttonBox rejected() ISelectDBFolder reject() 316 260 286 274 qmapshack-1.5.1/src/gis/db/ISelectSaveAction.ui000644 001750 000144 00000006201 12527654570 022275 0ustar00oeichlerusers000000 000000 ISelectSaveAction 0 0 471 253 Copy item... QFormLayout::AllNonFixedFieldsGrow Replace existing item Replace with: TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::Horizontal Do not replace item Use item: TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::Horizontal And for all other items, too. qmapshack-1.5.1/src/gis/db/IDBFolder.h000644 001750 000144 00000004565 12622435723 020340 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IDBFOLDER_H #define IDBFOLDER_H #include #include class QSqlDatabase; class CEvtW2DAckInfo; class CDBFolderDatabase; class CDBItem; class IDBFolder : public QTreeWidgetItem { public: enum type_e { eTypeLostFound = 1 ,eTypeDatabase = 2 ,eTypeGroup = 3 ,eTypeProject = 4 ,eTypeOther = 5 }; IDBFolder(bool isLoadable, QSqlDatabase& db, type_e type, quint64 id, QTreeWidgetItem * parent); IDBFolder(bool isLoadable, QSqlDatabase& db, type_e type, quint64 id, QTreeWidget * parent); virtual ~IDBFolder(); quint64 getId() { return id; } QString getDBName(); CDBFolderDatabase * getDBFolder(); IDBFolder * getFolder(quint64 idFolder); virtual quint64 addFolder(type_e type, const QString &name); virtual void expanding(); virtual void update(CEvtW2DAckInfo * info); virtual void toggle(); virtual void remove(); static IDBFolder * createFolderByType(QSqlDatabase &db, int type, quint64 id, QTreeWidgetItem *parent); static quint64 addFolderToDb(type_e type, const QString& name, quint64 idParent, QSqlDatabase& db); bool operator<(const QTreeWidgetItem &other) const; protected: virtual void setupFromDB(); virtual void addChildren(const QSet &activeChildren); virtual void remove(quint64 idParent, quint64 idFolder); QSqlDatabase& db; quint64 id; QString key; bool isLoadable; }; #endif //IDBFOLDER_H qmapshack-1.5.1/src/gis/db/IDBFolder.cpp000644 001750 000144 00000030750 12622435717 020671 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/CGisWidget.h" #include "gis/IGisItem.h" #include "gis/db/CDBFolderDatabase.h" #include "gis/db/CDBFolderGroup.h" #include "gis/db/CDBFolderOther.h" #include "gis/db/CDBFolderProject.h" #include "gis/db/CDBItem.h" #include "gis/db/IDBFolder.h" #include "gis/db/macros.h" #include IDBFolder::IDBFolder(bool isLoadable, QSqlDatabase& db, type_e type, quint64 id, QTreeWidgetItem *parent) : QTreeWidgetItem(parent, type) , db(db) , id(id) , isLoadable(isLoadable) { } IDBFolder::IDBFolder(bool isLoadable, QSqlDatabase& db, type_e type, quint64 id, QTreeWidget * parent) : QTreeWidgetItem(parent, type) , db(db) , id(id) , isLoadable(isLoadable) { } IDBFolder::~IDBFolder() { } bool IDBFolder::operator<(const QTreeWidgetItem &other) const { const IDBFolder * folder = dynamic_cast(&other); if(folder == 0) { return false; } return text(CGisListDB::eColumnName) < folder->text(CGisListDB::eColumnName); } IDBFolder * IDBFolder::createFolderByType(QSqlDatabase& db, int type, quint64 id, QTreeWidgetItem * parent) { switch(type) { case eTypeGroup: return new CDBFolderGroup(db, id, parent); case eTypeProject: return new CDBFolderProject(db, id, parent); case eTypeOther: return new CDBFolderOther(db, id, parent); default: return 0; } } QString IDBFolder::getDBName() { return db.connectionName(); } CDBFolderDatabase * IDBFolder::getDBFolder() { if(type() == eTypeDatabase) { return dynamic_cast(this); } IDBFolder * folder = dynamic_cast(parent()); if(folder != 0) { return folder->getDBFolder(); } return 0; } IDBFolder * IDBFolder::getFolder(quint64 idFolder) { if(id == idFolder) { return this; } const int N = childCount(); for(int n = 0; n < N; n++) { IDBFolder * folder1 = dynamic_cast(child(n)); if(folder1 == 0) { return 0; } IDBFolder * folder2 = folder1->getFolder(idFolder); if(folder2) { return folder2; } } return 0; } quint64 IDBFolder::addFolder(type_e type, const QString& name) { quint64 idChild = IDBFolder::addFolderToDb(type, name, id, db); if(idChild != 0) { IDBFolder::createFolderByType(db, type, idChild, this); expanding(); } return idChild; } quint64 IDBFolder::addFolderToDb(type_e type, const QString& name, quint64 idParent, QSqlDatabase& db) { QSqlQuery query(db); query.prepare("INSERT INTO folders (name, type) VALUES (:name, :type)"); query.bindValue(":name", name); query.bindValue(":type", type); QUERY_EXEC(return 0); query.prepare("SELECT last_insert_rowid() from folders"); QUERY_EXEC(return 0); query.next(); quint64 idChild = query.value(0).toULongLong(); if(idChild == 0) { qDebug() << "CGisListDB::slotAddFolder(): childId equals 0. bad."; return 0; } query.prepare("INSERT INTO folder2folder (parent, child) VALUES (:parent, :child)"); query.bindValue(":parent", idParent); query.bindValue(":child", idChild); QUERY_EXEC(return 0); return idChild; } void IDBFolder::expanding() { qDeleteAll(takeChildren()); addChildren(QSet()); CEvtD2WReqInfo * evt = new CEvtD2WReqInfo(getId(), getDBName()); CGisWidget::self().postEventForWks(evt); } void IDBFolder::update(CEvtW2DAckInfo * info) { if(info->id != id) { // forward call if not for local ID for(int i = 0; i < childCount(); i++) { IDBFolder * folder = dynamic_cast(child(i)); if(folder) { folder->update(info); } } return; } setCheckState(CGisListDB::eColumnCheckbox, info->isLoaded ? Qt::Checked : Qt::Unchecked); QSqlQuery query(db); // update text and tooltip query.prepare("SELECT name, comment FROM folders WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); setText(CGisListDB::eColumnName, query.value(0).toString()); setToolTip(CGisListDB::eColumnName, query.value(1).toString()); // count folders linked to this folder query.prepare("SELECT COUNT() FROM folder2folder WHERE parent=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); qint32 nFolders = query.value(0).toInt(); // count items linked to this folder query.prepare("SELECT COUNT() FROM folder2item WHERE parent=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); qint32 nItems = query.value(0).toInt(); // set indicator according to items if(nFolders || nItems) { setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } else { setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator); } if(isExpanded()) { qDeleteAll(takeChildren()); addChildren(info->keysChildren); } } void IDBFolder::toggle() { if(checkState(CGisListDB::eColumnCheckbox) == Qt::Checked) { CEvtD2WShowFolder * evt1 = new CEvtD2WShowFolder(getId(), getDBName()); CGisWidget::self().postEventForWks(evt1); QSqlQuery query(db); if(getId() == 0) { query.prepare("SELECT id, type FROM items AS t1 WHERE NOT EXISTS(SELECT * FROM folder2item WHERE child=t1.id) ORDER BY t1.type, t1.name"); } else { query.prepare("SELECT t1.child, t2.type FROM folder2item AS t1, items AS t2 WHERE t1.parent = :id AND t2.id = t1.child ORDER BY t2.id"); query.bindValue(":id", getId()); } QUERY_EXEC(return ); CEvtD2WShowItems * evt2 = new CEvtD2WShowItems(getId(), getDBName()); while(query.next()) { evt2->items << evt_item_t(query.value(0).toULongLong(), query.value(1).toUInt()); } CGisWidget::self().postEventForWks(evt2); } else { CEvtD2WHideFolder * evt1 = new CEvtD2WHideFolder(getId(), getDBName()); CGisWidget::self().postEventForWks(evt1); } } void IDBFolder::remove() { IDBFolder * folder = dynamic_cast(parent()); if(folder == 0) { return; } remove(folder->getId(), getId()); CEvtD2WHideFolder * evt1 = new CEvtD2WHideFolder(getId(), getDBName()); CGisWidget::self().postEventForWks(evt1); } void IDBFolder::setupFromDB() { if(id == 0) { return; } QSqlQuery query(db); query.prepare("SELECT key, name, comment FROM folders WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); key = query.value(0).toString(); setText(CGisListDB::eColumnName, query.value(1).toString()); setToolTip(CGisListDB::eColumnName, query.value(2).toString()); query.prepare("SELECT EXISTS(SELECT 1 FROM folder2folder WHERE parent=:id LIMIT 1)"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); if(query.value(0).toInt() == 1) { setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } else { query.prepare("SELECT EXISTS(SELECT 1 FROM folder2item WHERE parent=:id LIMIT 1)"); query.bindValue(":id", id); QUERY_EXEC(return ); query.next(); if(query.value(0).toInt() == 1) { setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } } if(isLoadable) { setCheckState(CGisListDB::eColumnCheckbox, Qt::Unchecked); CEvtD2WReqInfo * evt = new CEvtD2WReqInfo(getId(), getDBName()); CGisWidget::self().postEventForWks(evt); } } void IDBFolder::addChildren(const QSet& activeChildren) { QSqlQuery query(db); // folders 1st query.prepare("SELECT t1.child, t2.type FROM folder2folder AS t1, folders AS t2 WHERE t1.parent = :id AND t2.id = t1.child ORDER BY t2.id"); query.bindValue(":id", id); QUERY_EXEC(return ); while(query.next()) { quint64 idChild = query.value(0).toULongLong(); quint32 typeChild = query.value(1).toInt(); IDBFolder::createFolderByType(db, typeChild, idChild, this); } sortChildren(CGisListDB::eColumnName, Qt::AscendingOrder); // tracks 2nd query.prepare("SELECT t1.child FROM folder2item AS t1, items AS t2 WHERE t1.parent = :id AND t2.id = t1.child AND t2.type=:type ORDER BY t2.id"); query.bindValue(":id", id); query.bindValue(":type", IGisItem::eTypeTrk); QUERY_EXEC(return ); while(query.next()) { quint64 idChild = query.value(0).toULongLong(); CDBItem * item = new CDBItem(db, idChild, this); item->setCheckState(CGisListDB::eColumnCheckbox, activeChildren.contains(item->getKey()) ? Qt::Checked : Qt::Unchecked); } // routes 3rd query.prepare("SELECT t1.child FROM folder2item AS t1, items AS t2 WHERE t1.parent = :id AND t2.id = t1.child AND t2.type=:type ORDER BY t2.id"); query.bindValue(":id", id); query.bindValue(":type", IGisItem::eTypeRte); QUERY_EXEC(return ); while(query.next()) { quint64 idChild = query.value(0).toULongLong(); CDBItem * item = new CDBItem(db, idChild, this); item->setCheckState(CGisListDB::eColumnCheckbox, activeChildren.contains(item->getKey()) ? Qt::Checked : Qt::Unchecked); } //waypoints 4th query.prepare("SELECT t1.child FROM folder2item AS t1, items AS t2 WHERE t1.parent = :id AND t2.id = t1.child AND t2.type=:type ORDER BY t2.id"); query.bindValue(":id", id); query.bindValue(":type", IGisItem::eTypeWpt); QUERY_EXEC(return ); while(query.next()) { quint64 idChild = query.value(0).toULongLong(); CDBItem * item = new CDBItem(db, idChild, this); item->setCheckState(CGisListDB::eColumnCheckbox, activeChildren.contains(item->getKey()) ? Qt::Checked : Qt::Unchecked); } // overlays 5th query.prepare("SELECT t1.child FROM folder2item AS t1, items AS t2 WHERE t1.parent = :id AND t2.id = t1.child AND t2.type=:type ORDER BY t2.id"); query.bindValue(":id", id); query.bindValue(":type", IGisItem::eTypeOvl); QUERY_EXEC(return ); while(query.next()) { quint64 idChild = query.value(0).toULongLong(); CDBItem * item = new CDBItem(db, idChild, this); item->setCheckState(CGisListDB::eColumnCheckbox, activeChildren.contains(item->getKey()) ? Qt::Checked : Qt::Unchecked); } } void IDBFolder::remove(quint64 idParent, quint64 idFolder) { QSqlQuery query(db); // delete this particular relation first query.prepare("DELETE FROM folder2folder WHERE parent=:parent AND child=:child"); query.bindValue(":parent", idParent); query.bindValue(":child", idFolder); QUERY_EXEC(); query.prepare("SELECT EXISTS(SELECT 1 FROM folder2folder WHERE child=:id LIMIT 1)"); query.bindValue(":id", idFolder); QUERY_EXEC(); // if there is no other relation delete the children, too. if(!query.next() || (query.value(0).toInt() == 0)) { query.prepare("SELECT child FROM folder2folder WHERE parent=:id"); query.bindValue(":id", idFolder); QUERY_EXEC(); while(query.next()) { remove(idFolder, query.value(0).toULongLong()); } // remove the child items relations query.prepare("DELETE FROM folder2item WHERE parent=:id"); query.bindValue(":id", idFolder); QUERY_EXEC(); // and remove the folder query.prepare("DELETE FROM folders WHERE id=:id"); query.bindValue(":id", idFolder); QUERY_EXEC() } } qmapshack-1.5.1/src/gis/db/CSelectSaveAction.cpp000644 001750 000144 00000003634 12622435717 022437 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/IGisItem.h" #include "gis/db/CSelectSaveAction.h" CSelectSaveAction::CSelectSaveAction(const IGisItem *src, const IGisItem *tar, QWidget *parent) : QDialog(parent) { setupUi(this); labelIcon1->setPixmap(src->getIcon()); labelInfo1->setText(src->getInfo()); labelIcon2->setPixmap(tar->getIcon()); labelInfo2->setText(tar->getInfo()); adjustSize(); connect(pushSave, SIGNAL(clicked()), this, SLOT(slotSelectResult())); connect(pushSkip, SIGNAL(clicked()), this, SLOT(slotSelectResult())); CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSelectSaveAction"); } CSelectSaveAction::~CSelectSaveAction() { CCanvas::restoreOverrideCursor("~CSelectSaveAction"); } bool CSelectSaveAction::allOthersToo() { return checkAllOtherToo->isChecked(); } void CSelectSaveAction::slotSelectResult() { if(sender() == pushSave) { result = eResultSave; } else if(sender() == pushSkip) { result = eResultSkip; } accept(); } qmapshack-1.5.1/src/gis/db/CDBFolderOther.h000644 001750 000144 00000002231 12622435723 021320 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBFOLDEROTHER_H #define CDBFOLDEROTHER_H #include "gis/db/IDBFolder.h" class CDBFolderOther : public IDBFolder { public: CDBFolderOther(QSqlDatabase &db, quint64 key, QTreeWidgetItem *parent); virtual ~CDBFolderOther(); }; #endif //CDBFOLDEROTHER_H qmapshack-1.5.1/src/gis/db/CSetupDatabase.cpp000644 001750 000144 00000006423 12622435717 021767 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisListDB.h" #include "gis/db/CSetupDatabase.h" #include "helpers/CSettings.h" #include CSetupDatabase::CSetupDatabase(QString& name, QString& filename, CGisListDB &parent) : QDialog(&parent) , list(parent) , name(name) , filename(filename) { setupUi(this); lineName->setText(name); labelFilename->setText(filename); connect(toolNewDB, SIGNAL(clicked()), this, SLOT(slotNewDB())); connect(toolAddDB, SIGNAL(clicked()), this, SLOT(slotOpenDB())); connect(lineName, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateButtonBox())); slotUpdateButtonBox(); } CSetupDatabase::~CSetupDatabase() { } void CSetupDatabase::slotUpdateButtonBox() { bool enable = true; if(lineName->text().isEmpty()) { enable = false; } if(labelFilename->text() == "-") { enable = false; } buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable); } void CSetupDatabase::accept() { name = lineName->text(); if(list.hasDatabase(name)) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), tr("Error..."), tr("There is already a database with name '%1'").arg(name), QMessageBox::Abort); return; } filename = labelFilename->text(); QDialog::accept(); } void CSetupDatabase::slotNewDB() { SETTINGS; QString path = cfg.value("Database/lastDatabasePath", QDir::homePath()).toString(); QString filename = QFileDialog::getSaveFileName(this, tr("New database..."), path, "QMapShack Database (*.db)"); if(filename.isEmpty()) { return; } QFileInfo fi(filename); if(fi.suffix().toLower() != "db") { filename += ".db"; } cfg.setValue("Database/lastDatabasePath", fi.absolutePath()); labelFilename->setText(filename); slotUpdateButtonBox(); } void CSetupDatabase::slotOpenDB() { SETTINGS; QString path = cfg.value("Database/lastDatabasePath", QDir::homePath()).toString(); QString filename = QFileDialog::getOpenFileName(this, tr("Open database..."), path, "QMapShack Database (*.db)"); if(filename.isEmpty()) { return; } QFileInfo fi(filename); if(fi.suffix().toLower() != "db") { filename += ".db"; } cfg.setValue("Database/lastDatabasePath", fi.absolutePath()); labelFilename->setText(filename); slotUpdateButtonBox(); } qmapshack-1.5.1/src/gis/db/CDBFolderProject.h000644 001750 000144 00000002245 12622435723 021652 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDBFOLDERPROJECT_H #define CDBFOLDERPROJECT_H #include "gis/db/IDBFolder.h" class CDBFolderProject : public IDBFolder { public: CDBFolderProject(QSqlDatabase &db, quint64 key, QTreeWidgetItem *parent); virtual ~CDBFolderProject(); }; #endif //CDBFOLDERPROJECT_H qmapshack-1.5.1/src/gis/db/CSetupFolder.h000644 001750 000144 00000002622 12622435723 021135 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSETUPFOLDER_H #define CSETUPFOLDER_H #include "gis/db/IDBFolder.h" #include "ui_ISetupFolder.h" #include class CSetupFolder : public QDialog, private Ui::ISetupFolder { Q_OBJECT public: CSetupFolder(IDBFolder::type_e& type, QString& name, bool groupAllowed, QWidget * parent); virtual ~CSetupFolder(); public slots: void accept(); private slots: void slotNameChanged(const QString& text); private: IDBFolder::type_e& type; QString& name; }; #endif //CSETUPFOLDER_H qmapshack-1.5.1/src/gis/db/CLostFoundProject.h000644 001750 000144 00000002410 12622435723 022140 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CLOSTFOUNDPROJECT_H #define CLOSTFOUNDPROJECT_H #include "gis/db/CDBProject.h" #include class CLostFoundProject : public CDBProject { public: CLostFoundProject(const QString &dbName, CGisListWks * parent); virtual ~CLostFoundProject(); bool save() { return false; } void updateFromDb(); }; #endif //CLOSTFOUNDPROJECT_H qmapshack-1.5.1/src/gis/db/CSetupFolder.cpp000644 001750 000144 00000004416 12622435717 021476 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/db/CSetupFolder.h" #include CSetupFolder::CSetupFolder(IDBFolder::type_e& type, QString &name, bool groupAllowed, QWidget *parent) : QDialog(parent) , type(type) , name(name) { setupUi(this); connect(lineName, SIGNAL(textChanged(QString)), this, SLOT(slotNameChanged(QString))); lineName->setText(name); switch(type) { case IDBFolder::eTypeGroup: radioGroup->setChecked(true); break; case IDBFolder::eTypeProject: radioProject->setChecked(true); break; case IDBFolder::eTypeOther: radioOther->setChecked(true); break; default: ; } radioGroup->setEnabled(groupAllowed); slotNameChanged(name); CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSetupFolder"); } CSetupFolder::~CSetupFolder() { CCanvas::restoreOverrideCursor("~CSetupFolder"); } void CSetupFolder::accept() { name = lineName->text(); if(radioGroup->isChecked()) { type = IDBFolder::eTypeGroup; } else if(radioProject->isChecked()) { type = IDBFolder::eTypeProject; } else if(radioOther->isChecked()) { type = IDBFolder::eTypeOther; } QDialog::accept(); } void CSetupFolder::slotNameChanged(const QString& text) { buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); } qmapshack-1.5.1/src/gis/db/CDBItem.cpp000644 001750 000144 00000006175 12622435717 020352 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListDB.h" #include "gis/CGisWidget.h" #include "gis/db/CDBItem.h" #include "gis/db/IDBFolder.h" #include "gis/db/macros.h" #include CDBItem::CDBItem(QSqlDatabase &db, quint64 id, IDBFolder *parent) : QTreeWidgetItem(parent) , db(db) , id(id) { // qDebug() << "CDBItem::CDBItem()"; QSqlQuery query(db); query.prepare("SELECT type, key, icon, name, comment FROM items WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); if(query.next()) { QPixmap pixmap; type = query.value(0).toInt(); key = query.value(1).toString(); pixmap.loadFromData(query.value(2).toByteArray(), "PNG"); setIcon(CGisListDB::eColumnCheckbox, pixmap); setText(CGisListDB::eColumnName, query.value(3).toString()); setToolTip(CGisListDB::eColumnName, query.value(4).toString()); } } CDBItem::~CDBItem() { // qDebug() << "CDBItem::~CDBItem()"; } void CDBItem::toggle() { IDBFolder * folder = dynamic_cast(parent()); if(folder == 0) { return; } if(checkState(CGisListDB::eColumnCheckbox) == Qt::Checked) { CEvtD2WShowFolder * evt1 = new CEvtD2WShowFolder(folder->getId(), folder->getDBName()); CGisWidget::self().postEventForWks(evt1); CEvtD2WShowItems * evt2 = new CEvtD2WShowItems(folder->getId(), folder->getDBName()); evt2->items << evt_item_t(id, type); CGisWidget::self().postEventForWks(evt2); } else { CEvtD2WHideItems * evt2 = new CEvtD2WHideItems(folder->getId(), folder->getDBName()); evt2->keys << key; CGisWidget::self().postEventForWks(evt2); } } void CDBItem::remove() { IDBFolder * folder = dynamic_cast(parent()); if(folder == 0) { return; } if(checkState(CGisListDB::eColumnCheckbox) == Qt::Checked) { CEvtD2WHideItems * evt = new CEvtD2WHideItems(folder->getId(), folder->getDBName()); evt->keys << key; CGisWidget::self().postEventForWks(evt); } QSqlQuery query(db); query.prepare("DELETE FROM folder2item WHERE parent=:parent AND child=:child"); query.bindValue(":parent", folder->getId()); query.bindValue(":child", id); QUERY_EXEC(); } qmapshack-1.5.1/src/gis/db/CLostFoundProject.cpp000644 001750 000144 00000004755 12622435717 022514 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListWks.h" #include "gis/db/CLostFoundProject.h" #include "gis/db/macros.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include #include CLostFoundProject::CLostFoundProject(const QString &dbName, CGisListWks * parent) : CDBProject(parent) { type = eTypeLostFound; db = QSqlDatabase::database(dbName); setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/DeleteMultiple.png")); filename = dbName; metadata.name = QObject::tr("Lost & Found"); setupName(dbName); valid = true; } CLostFoundProject::~CLostFoundProject() { } void CLostFoundProject::updateFromDb() { qDeleteAll(takeChildren()); QSqlQuery query(db); query.prepare("SELECT id, type FROM items AS t1 WHERE NOT EXISTS(SELECT * FROM folder2item WHERE child=t1.id) ORDER BY t1.type, t1.name"); QUERY_EXEC(return ); while(query.next()) { quint64 id = query.value(0).toULongLong(); quint32 type = query.value(1).toUInt(); switch(type) { case IGisItem::eTypeWpt: new CGisItemWpt(id, db, this); break; case IGisItem::eTypeTrk: new CGisItemTrk(id, db, this); break; case IGisItem::eTypeRte: new CGisItemRte(id, db, this); break; case IGisItem::eTypeOvl: new CGisItemOvlArea(id, db, this); break; default: ; } } setText(CGisListWks::eColumnDecoration,""); } qmapshack-1.5.1/src/gis/qms/CQmsProject.cpp000644 001750 000144 00000010115 12622435720 021527 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisListWks.h" #include "gis/gpx/CGpxProject.h" #include "gis/qms/CQmsProject.h" #include "helpers/CSettings.h" #include CQmsProject::CQmsProject(const QString &filename, CGisListWks *parent) : IGisProject(eTypeQms, filename, parent) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/QmsProject.png")); // cerate file instance QFile file(filename); // if the file does not exist, the filename is assumed to be a name for a new project if(!file.exists()) { IGisProject::filename.clear(); setupName(filename); setToolTip(CGisListWks::eColumnName, getInfo()); valid = true; return; } if(!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Failed to open..."), QObject::tr("Failed to open %1").arg(filename), QMessageBox::Abort); return; } QDataStream in(&file); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); *this << in; file.close(); markAsSaved(); setupName(QFileInfo(filename).baseName().replace("_", " ")); setToolTip(CGisListWks::eColumnName, getInfo()); updateItems(); valid = true; } CQmsProject::~CQmsProject() { } bool CQmsProject::save() { if(filename.isEmpty()) { return saveAs(); } else { if(saveAs(filename, *this)) { markAsSaved(); } } return true; } bool CQmsProject::saveAs() { SETTINGS; QString path = cfg.value("Paths/lastGisPath", QDir::homePath()).toString(); path += "/" + getName() + ".qms"; QString filter = filedialogFilterQMS; QString fn = QFileDialog::getSaveFileName(CMainWindow::getBestWidgetForParent(), QObject::tr("Save GIS data to..."), path, filedialogSaveFilters, &filter); if(fn.isEmpty()) { return false; } bool res = false; if(filter == filedialogFilterGPX) { res = CGpxProject::saveAs(fn, *this); } else if(filter == filedialogFilterQMS) { filename = fn; metadata.name.clear(); setupName(QFileInfo(filename).baseName()); res = saveAs(fn, *this); if(res) { markAsSaved(); } } else { return false; } path = QFileInfo(fn).absolutePath(); cfg.setValue("Paths/lastGisPath", path); return res; } bool CQmsProject::saveAs(const QString& fn, IGisProject& project) { QString _fn_ = fn; QFileInfo fi(_fn_); if(fi.suffix().toLower() != "qms") { _fn_ += ".qms"; } // todo save qms QFile file(_fn_); if(!file.open(QIODevice::WriteOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Failed to open..."), QObject::tr("Failed to open %1").arg(_fn_), QMessageBox::Abort); return false; } QDataStream out(&file); out.setByteOrder(QDataStream::LittleEndian); out.setVersion(QDataStream::Qt_5_2); QString tmp = project.getFilename(); project.setFilename(_fn_); project.IGisProject::operator>>(out); project.setFilename(tmp); file.close(); return true; } qmapshack-1.5.1/src/gis/qms/serialization.cpp000644 001750 000144 00000055434 12624147203 022225 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisListWks.h" #include "gis/db/CDBProject.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include #define VER_TRK quint8(2) #define VER_WPT quint8(2) #define VER_RTE quint8(2) #define VER_AREA quint8(1) #define VER_LINK quint8(1) #define VER_TRKSEG quint8(1) #define VER_TRKPT quint8(2) #define VER_RTEPT quint8(2) #define VER_RTESUBPT quint8(1) #define VER_WPT_T quint8(1) #define VER_GC_T quint8(1) #define VER_GCLOG_T quint8(1) #define VER_IMAGE quint8(1) #define VER_PROJECT quint8(4) #define VER_COPYRIGHT quint8(1) #define VER_PERSON quint8(1) #define VER_HIST quint8(1) #define VER_HIST_EVT quint8(2) #define VER_ITEM quint8(2) #define MAGIC_SIZE 10 #define MAGIC_TRK "QMTrk " #define MAGIC_WPT "QMWpt " #define MAGIC_RTE "QMRte " #define MAGIC_AREA "QMArea " #define MAGIC_PROJ "QMProj " QDataStream& operator<<(QDataStream& stream, const IGisItem::link_t& link) { stream << VER_LINK << link.uri << link.text << link.type; return stream; } QDataStream& operator>>(QDataStream& stream, IGisItem::link_t& link) { quint8 version; stream >> version >> link.uri >> link.text >> link.type; return stream; } QDataStream& operator<<(QDataStream& stream, const IGisItem::wpt_t& wpt) { stream << VER_WPT_T; stream << wpt.lat; stream << wpt.lon; stream << wpt.ele; stream << wpt.time; stream << wpt.magvar; stream << wpt.geoidheight; stream << wpt.name; stream << wpt.cmt; stream << wpt.desc; stream << wpt.src; stream << wpt.links; stream << wpt.sym; stream << wpt.type; stream << wpt.fix; stream << wpt.sat; stream << wpt.hdop; stream << wpt.vdop; stream << wpt.pdop; stream << wpt.ageofdgpsdata; stream << wpt.dgpsid; return stream; } QDataStream& operator>>(QDataStream& stream, IGisItem::wpt_t& wpt) { quint8 version; stream >> version; stream >> wpt.lat; stream >> wpt.lon; stream >> wpt.ele; stream >> wpt.time; stream >> wpt.magvar; stream >> wpt.geoidheight; stream >> wpt.name; stream >> wpt.cmt; stream >> wpt.desc; stream >> wpt.src; stream >> wpt.links; stream >> wpt.sym; stream >> wpt.type; stream >> wpt.fix; stream >> wpt.sat; stream >> wpt.hdop; stream >> wpt.vdop; stream >> wpt.pdop; stream >> wpt.ageofdgpsdata; stream >> wpt.dgpsid; return stream; } QDataStream& operator<<(QDataStream& stream, const IGisItem::history_event_t& e) { stream << VER_HIST_EVT; stream << e.time; stream << e.icon; stream << e.comment; stream << e.data; stream << e.hash; return stream; } QDataStream& operator>>(QDataStream& stream, IGisItem::history_event_t& e) { quint8 version; stream >> version; stream >> e.time; stream >> e.icon; stream >> e.comment; stream >> e.data; if(version > 1) { stream >> e.hash; } return stream; } QDataStream& operator<<(QDataStream& stream, const IGisItem::history_t& h) { stream << VER_HIST; stream << h.histIdxInitial; stream << h.histIdxCurrent; stream << h.events; return stream; } QDataStream& operator>>(QDataStream& stream, IGisItem::history_t& h) { quint8 version; stream >> version; stream >> h.histIdxInitial; stream >> h.histIdxCurrent; stream >> h.events; if(h.histIdxCurrent >= h.events.size()) { h.histIdxCurrent = h.events.size() - 1; } if(h.histIdxInitial >= h.events.size()) { h.histIdxInitial = h.events.size() - 1; } if(h.histIdxCurrent < 0) { h.histIdxInitial = NOIDX; h.histIdxCurrent = NOIDX; h.events.clear(); } if(h.histIdxInitial < 0) { h.histIdxInitial = NOIDX; h.histIdxCurrent = NOIDX; h.events.clear(); } return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemWpt::geocachelog_t& log) { stream << VER_GCLOG_T; stream << log.id; stream << log.date; stream << log.type; stream << log.finderId; stream << log.finder; stream << quint8(log.textIsHtml); stream << log.text; return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemWpt::geocachelog_t& log) { quint8 version, tmp8; stream >> version; stream >> log.id; stream >> log.date; stream >> log.type; stream >> log.finderId; stream >> log.finder; stream >> tmp8; log.textIsHtml = tmp8; stream >> log.text; return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemWpt::geocache_t& geocache) { stream << VER_GC_T; stream << quint8(geocache.hasData); if(geocache.hasData) { stream << geocache.id; stream << quint8(geocache.available); stream << quint8(geocache.archived); stream << geocache.difficulty; stream << geocache.terrain; stream << geocache.status; stream << geocache.name; stream << geocache.owner; stream << geocache.ownerId; stream << geocache.type; stream << geocache.container; stream << quint8(geocache.shortDescIsHtml); stream << geocache.shortDesc; stream << quint8(geocache.longDescIsHtml); stream << geocache.longDesc; stream << geocache.hint; stream << geocache.country; stream << geocache.state; stream << geocache.locale; stream << geocache.logs; } return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemWpt::geocache_t& geocache) { quint8 version, tmp8; stream >> version; stream >> tmp8; geocache.hasData = tmp8; if(geocache.hasData) { stream >> geocache.id; stream >> tmp8; geocache.available = tmp8; stream >> tmp8; geocache.archived = tmp8; stream >> geocache.difficulty; stream >> geocache.terrain; stream >> geocache.status; stream >> geocache.name; stream >> geocache.owner; stream >> geocache.ownerId; stream >> geocache.type; stream >> geocache.container; stream >> tmp8; geocache.shortDescIsHtml = tmp8; stream >> geocache.shortDesc; stream >> tmp8; geocache.longDescIsHtml = tmp8; stream >> geocache.longDesc; stream >> geocache.hint; stream >> geocache.country; stream >> geocache.state; stream >> geocache.locale; stream >> geocache.logs; } return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemWpt::image_t& image) { QBuffer imgBuf; image.pixmap.save(&imgBuf,"JPEG"); stream << VER_IMAGE; stream << imgBuf.data(); stream << image.direction; stream << image.info; stream << image.filePath; stream << image.fileName; return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemWpt::image_t& image) { quint8 version; QBuffer imgBuf; stream >> version; stream >> imgBuf.buffer(); stream >> image.direction; stream >> image.info; stream >> image.filePath; stream >> image.fileName; image.pixmap.load(&imgBuf,"JPEG"); return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemTrk::trkseg_t& seg) { stream << VER_TRKSEG << seg.pts; return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemTrk::trkseg_t& seg) { quint8 version; stream >> version >> seg.pts; return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemTrk::trkpt_t& pt) { stream << VER_TRKPT << pt.flags; stream << (const IGisItem::wpt_t&)pt; stream << pt.extensions; return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemTrk::trkpt_t& pt) { quint8 version; stream >> version >> pt.flags; stream >> (IGisItem::wpt_t&)pt; if(version > 1) { stream >> pt.extensions; } return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemRte::subpt_t& pt) { stream << VER_RTESUBPT; stream << pt.lon; stream << pt.lat; stream << pt.type; stream << pt.turn; stream << pt.bearing; stream << pt.streets; stream << pt.instruction; stream << pt.distance; stream << pt.time; return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemRte::subpt_t& pt) { quint8 version; stream >> version; stream >> pt.lon; stream >> pt.lat; stream >> pt.type; stream >> pt.turn; stream >> pt.bearing; stream >> pt.streets; stream >> pt.instruction; stream >> pt.distance; stream >> pt.time; return stream; } QDataStream& operator<<(QDataStream& stream, const CGisItemRte::rtept_t& pt) { stream << VER_RTEPT << pt.focus << pt.icon; stream << (const IGisItem::wpt_t&)pt; stream << pt.fakeSubpt; stream << pt.subpts; return stream; } QDataStream& operator>>(QDataStream& stream, CGisItemRte::rtept_t& pt) { quint8 version; stream >> version >> pt.focus >> pt.icon; stream >> (IGisItem::wpt_t&)pt; if(version > 1) { stream >> pt.fakeSubpt; stream >> pt.subpts; } return stream; } QDataStream& operator<<(QDataStream& stream, const IGisProject::copyright_t& c) { stream << VER_COPYRIGHT << c.author << c.year << c.license; return stream; } QDataStream& operator>>(QDataStream& stream, IGisProject::copyright_t& c) { quint8 version; stream >> version >> c.author >> c.year >> c.license; return stream; } QDataStream& operator<<(QDataStream& stream, const IGisProject::person_t& p) { stream << VER_PERSON << p.name << p.id << p.domain << p.link; return stream; } QDataStream& operator>>(QDataStream& stream, IGisProject::person_t& p) { quint8 version; stream >> version >> p.name >> p.id >> p.domain >> p.link; return stream; } // ---------------- main objects --------------------------------- QDataStream& CGisItemTrk::operator>>(QDataStream& stream) const { QByteArray buffer; QDataStream out(&buffer, QIODevice::WriteOnly); out.setByteOrder(QDataStream::LittleEndian); out.setVersion(QDataStream::Qt_5_2); out << key.item; out << flags; out << trk.name; out << trk.cmt; out << trk.desc; out << trk.src; out << trk.links; out << trk.number; out << trk.type; out << trk.color; out << colorSource; out << limitLow; out << limitHigh; out << trk.segs; stream.writeRawData(MAGIC_TRK, MAGIC_SIZE); stream << VER_TRK; stream << qCompress(buffer,9); return stream; } QDataStream& CGisItemTrk::operator<<(QDataStream& stream) { quint8 version; QByteArray buffer; QIODevice * dev = stream.device(); qint64 pos = dev->pos(); char magic[10]; stream.readRawData(magic,MAGIC_SIZE); if(strncmp(magic,MAGIC_TRK,MAGIC_SIZE)) { dev->seek(pos); return stream; } stream >> version; stream >> buffer; buffer = qUncompress(buffer); QDataStream in(&buffer, QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in >> key.item; in >> flags; in >> trk.name; in >> trk.cmt; in >> trk.desc; in >> trk.src; in >> trk.links; in >> trk.number; in >> trk.type; in >> trk.color; // versions >= 2 contain colorized tracks if(version >= 2) { QString source; in >> colorSource; in >> limitLow; in >> limitHigh; } trk.segs.clear(); in >> trk.segs; deriveSecondaryData(); setColor(str2color(trk.color)); setText(CGisListWks::eColumnName, trk.name); setToolTip(CGisListWks::eColumnName, getInfo()); notifyChange(); return stream; } QDataStream& CGisItemWpt::operator<<(QDataStream& stream) { quint8 version; QByteArray buffer; QIODevice * dev = stream.device(); qint64 pos = dev->pos(); char magic[10]; stream.readRawData(magic,MAGIC_SIZE); if(strncmp(magic,MAGIC_WPT,MAGIC_SIZE)) { dev->seek(pos); return stream; } stream >> version; stream >> buffer; buffer = qUncompress(buffer); QDataStream in(&buffer, QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in >> key.item; in >> flags; in >> proximity; in >> wpt; in >> geocache; in >> images; if(version > 1) { in >> offsetBubble; in >> widthBubble; } setIcon(); setText(CGisListWks::eColumnName, wpt.name); setToolTip(CGisListWks::eColumnName, getInfo()); return stream; } QDataStream& CGisItemWpt::operator>>(QDataStream& stream) const { QByteArray buffer; QDataStream out(&buffer, QIODevice::WriteOnly); out.setByteOrder(QDataStream::LittleEndian); out.setVersion(QDataStream::Qt_5_2); out << key.item; out << flags; out << proximity; out << wpt; out << geocache; out << images; out << offsetBubble; out << widthBubble; stream.writeRawData(MAGIC_WPT, MAGIC_SIZE); stream << VER_WPT; stream << qCompress(buffer,9); return stream; } QDataStream& CGisItemRte::operator<<(QDataStream& stream) { quint8 version; QByteArray buffer; QIODevice * dev = stream.device(); qint64 pos = dev->pos(); char magic[10]; stream.readRawData(magic,MAGIC_SIZE); if(strncmp(magic,MAGIC_RTE,MAGIC_SIZE)) { dev->seek(pos); return stream; } stream >> version; stream >> buffer; buffer = qUncompress(buffer); QDataStream in(&buffer, QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in >> key.item; in >> flags; in >> rte.name; in >> rte.cmt; in >> rte.desc; in >> rte.src; in >> rte.links; in >> rte.number; in >> rte.type; in >> rte.pts; if(version > 1) { in >> lastRoutedWith; in >> lastRoutedTime; in >> totalDistance; in >> totalTime; } setSymbol(); deriveSecondaryData(); setText(CGisListWks::eColumnName, rte.name); setToolTip(CGisListWks::eColumnName, getInfo()); return stream; } QDataStream& CGisItemRte::operator>>(QDataStream& stream) const { QByteArray buffer; QDataStream out(&buffer, QIODevice::WriteOnly); out.setByteOrder(QDataStream::LittleEndian); out.setVersion(QDataStream::Qt_5_2); out << key.item; out << flags; out << rte.name; out << rte.cmt; out << rte.desc; out << rte.src; out << rte.links; out << rte.number; out << rte.type; out << rte.pts; out << lastRoutedWith; out << lastRoutedTime; out << totalDistance; out << totalTime; stream.writeRawData(MAGIC_RTE, MAGIC_SIZE); stream << VER_RTE; stream << qCompress(buffer,9); return stream; } QDataStream& CGisItemOvlArea::operator<<(QDataStream& stream) { quint8 version, tmp8; QByteArray buffer; QIODevice * dev = stream.device(); qint64 pos = dev->pos(); char magic[10]; stream.readRawData(magic,MAGIC_SIZE); if(strncmp(magic,MAGIC_AREA,MAGIC_SIZE)) { dev->seek(pos); return stream; } stream >> version; stream >> buffer; buffer = qUncompress(buffer); QDataStream in(&buffer, QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in >> key.item; in >> flags; in >> area.name; in >> area.cmt; in >> area.desc; in >> area.src; in >> area.links; in >> area.number; in >> area.type; in >> area.pts; in >> area.color; in >> area.width; in >> area.style; in >> tmp8; area.opacity = tmp8; deriveSecondaryData(); setColor(str2color(area.color)); setText(CGisListWks::eColumnName, area.name); setToolTip(CGisListWks::eColumnName, getInfo()); return stream; } QDataStream& CGisItemOvlArea::operator>>(QDataStream& stream) const { QByteArray buffer; QDataStream out(&buffer, QIODevice::WriteOnly); out.setByteOrder(QDataStream::LittleEndian); out.setVersion(QDataStream::Qt_5_2); out << key.item; out << flags; out << area.name; out << area.cmt; out << area.desc; out << area.src; out << area.links; out << area.number; out << area.type; out << area.pts; out << area.color; out << area.width; out << area.style; out << quint8(area.opacity); stream.writeRawData(MAGIC_AREA, MAGIC_SIZE); stream << VER_AREA; stream << qCompress(buffer,9); return stream; } QDataStream& IGisProject::operator<<(QDataStream& stream) { quint8 version; QIODevice * dev = stream.device(); qint64 pos = dev->pos(); char magic[10]; stream.readRawData(magic,MAGIC_SIZE); if(strncmp(magic,MAGIC_PROJ,MAGIC_SIZE)) { dev->seek(pos); return stream; } blockUpdateItems(true); stream >> version; stream >> filename; stream >> metadata.name; stream >> metadata.desc; stream >> metadata.author; stream >> metadata.copyright; stream >> metadata.links; stream >> metadata.time; stream >> metadata.keywords; stream >> metadata.bounds; if(version > 1) { stream >> key; } if(version > 2) { qint32 tmp; stream >> tmp; sorting = (sorting_e)tmp; } if(version > 3) { qint8 tmp; stream >> tmp; noCorrelation = tmp != 0; } while(!stream.atEnd()) { IGisItem::history_t history; quint8 changed = 0; quint8 version, type; stream >> version; stream >> type; stream >> history; if(version > 1) { stream >> changed; } IGisItem * item = 0; switch(type) { case IGisItem::eTypeWpt: item = new CGisItemWpt(history, this); break; case IGisItem::eTypeTrk: item = new CGisItemTrk(history, this); break; case IGisItem::eTypeRte: item = new CGisItemRte(history, this); break; case IGisItem::eTypeOvl: item = new CGisItemOvlArea(history, this); break; default: ; } if(item && changed) { item->updateDecoration(IGisItem::eMarkChanged, IGisItem::eMarkNone); } } blockUpdateItems(false); return stream; } QDataStream& IGisProject::operator>>(QDataStream& stream) { stream.writeRawData(MAGIC_PROJ, MAGIC_SIZE); stream << VER_PROJECT; stream << filename; stream << metadata.name; stream << metadata.desc; stream << metadata.author; stream << metadata.copyright; stream << metadata.links; stream << metadata.time; stream << metadata.keywords; stream << metadata.bounds; stream << key; stream << qint32(sorting); stream << qint8(noCorrelation); for(int i = 0; i < childCount(); i++) { CGisItemTrk * item = dynamic_cast(child(i)); if(item == 0) { continue; } stream << VER_ITEM; stream << quint8(item->type()); stream << item->getHistory(); stream << quint8(item->data(1,Qt::UserRole).toUInt() & IGisItem::eMarkChanged); } for(int i = 0; i < childCount(); i++) { CGisItemRte * item = dynamic_cast(child(i)); if(item == 0) { continue; } stream << VER_ITEM; stream << quint8(item->type()); stream << item->getHistory(); stream << quint8(item->data(1,Qt::UserRole).toUInt() & IGisItem::eMarkChanged); } for(int i = 0; i < childCount(); i++) { CGisItemWpt * item = dynamic_cast(child(i)); if(item == 0) { continue; } stream << VER_ITEM; stream << quint8(item->type()); stream << item->getHistory(); stream << quint8(item->data(1,Qt::UserRole).toUInt() & IGisItem::eMarkChanged); } for(int i = 0; i < childCount(); i++) { CGisItemOvlArea * item = dynamic_cast(child(i)); if(item == 0) { continue; } stream << VER_ITEM; stream << quint8(item->type()); stream << item->getHistory(); stream << quint8(item->data(1,Qt::UserRole).toUInt() & IGisItem::eMarkChanged); } return stream; } QDataStream& CDBProject::operator<<(QDataStream& stream) { quint8 version; QIODevice * dev = stream.device(); qint64 pos = dev->pos(); char magic[10]; stream.readRawData(magic,MAGIC_SIZE); if(strncmp(magic,MAGIC_PROJ,MAGIC_SIZE)) { dev->seek(pos); return stream; } stream >> version; stream >> filename; stream >> metadata.name; stream >> metadata.desc; stream >> metadata.author; stream >> metadata.copyright; stream >> metadata.links; stream >> metadata.time; stream >> metadata.keywords; stream >> metadata.bounds; if(version > 1) { stream >> key; } if(version > 2) { qint32 tmp; stream >> tmp; sorting = (sorting_e)tmp; } if(version > 3) { qint8 tmp; stream >> tmp; noCorrelation = tmp != 0; } return stream; } QDataStream& CDBProject::operator>>(QDataStream& stream) { stream.writeRawData(MAGIC_PROJ, MAGIC_SIZE); stream << VER_PROJECT; stream << filename; stream << metadata.name; stream << metadata.desc; stream << metadata.author; stream << metadata.copyright; stream << metadata.links; stream << metadata.time; stream << metadata.keywords; stream << metadata.bounds; stream << key; stream << qint32(sorting); stream << qint8(noCorrelation); return stream; } qmapshack-1.5.1/src/gis/qms/CQmsProject.h000644 001750 000144 00000002352 12622435723 021203 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CQMSPROJECT_H #define CQMSPROJECT_H #include "gis/prj/IGisProject.h" class CQmsProject : public IGisProject { public: CQmsProject(const QString& filename, CGisListWks * parent); virtual ~CQmsProject(); bool save(); bool saveAs(); static bool saveAs(const QString& fn, IGisProject& project); }; #endif //CQMSPROJECT_H qmapshack-1.5.1/src/gis/WptIcons.h000644 001750 000144 00000003015 12622435723 017754 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef WPTICONS_H #define WPTICONS_H #include #include struct icon_t { icon_t() : focus(16,16) { } icon_t(const QString& path, int x, int y) : path(path), focus(x,y) { } QString path; QPoint focus; }; extern void initWptIcons(); extern const QMap &getWptIcons(); extern QPixmap getWptIconByName(const QString& name, QPointF &focus, QString * src = 0); extern void setWptIconByName(const QString& name, const QString& filename); extern void setWptIconByName(const QString& name, const QPixmap& icon); extern QPixmap loadIcon(const QString& path); #endif //WPTICONS_H qmapshack-1.5.1/src/gis/wpt/CDetailsGeoCache.cpp000644 001750 000144 00000022546 12622435720 022431 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/wpt/CDetailsGeoCache.h" #include "gis/wpt/CGisItemWpt.h" #include #include #include #define HTTP_ATTR_WHAT QNetworkRequest::Attribute(QNetworkRequest::User + 1) #define HTTP_ATTR_INFO QNetworkRequest::Attribute(QNetworkRequest::User + 2) CDetailsGeoCache::CDetailsGeoCache(CGisItemWpt &wpt, QWidget *parent) : QDialog(parent) , wpt(wpt) { setupUi(this); setWindowTitle(wpt.getName()); QString val, unit; QString strPos; QPointF pos = wpt.getPosition(); IUnit::degToStr(pos.x(), pos.y(), strPos); const CGisItemWpt::geocache_t& geocache = wpt.getGeoCache(); labelName->setText(geocache.name); labelPositon->setText(strPos); qreal d = geocache.difficulty; labelD1->setPixmap(QPixmap(d < 0.5 ? "://icons/cache/32x32/star_empty.png" : d < 1.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelD2->setPixmap(QPixmap(d < 1.5 ? "://icons/cache/32x32/star_empty.png" : d < 2.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelD3->setPixmap(QPixmap(d < 2.5 ? "://icons/cache/32x32/star_empty.png" : d < 3.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelD4->setPixmap(QPixmap(d < 3.5 ? "://icons/cache/32x32/star_empty.png" : d < 4.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelD5->setPixmap(QPixmap(d < 4.5 ? "://icons/cache/32x32/star_empty.png" : d < 5.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); qreal t = geocache.terrain; labelT1->setPixmap(QPixmap(t < 0.5 ? "://icons/cache/32x32/star_empty.png" : t < 1.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelT2->setPixmap(QPixmap(t < 1.5 ? "://icons/cache/32x32/star_empty.png" : t < 2.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelT3->setPixmap(QPixmap(t < 2.5 ? "://icons/cache/32x32/star_empty.png" : t < 3.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelT4->setPixmap(QPixmap(t < 3.5 ? "://icons/cache/32x32/star_empty.png" : t < 4.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); labelT5->setPixmap(QPixmap(t < 4.5 ? "://icons/cache/32x32/star_empty.png" : t < 5.0 ? "://icons/cache/32x32/halfstar.png" : "://icons/cache/32x32/star.png").scaled(16,16,Qt::KeepAspectRatio, Qt::SmoothTransformation)); checkHint->setEnabled(!geocache.hint.isEmpty()); labelHint->setText(geocache.hint.isEmpty() ? tr("none") : tr("???")); toolIcon->setIcon(wpt.getIcon()); labelStatus->hide(); QString desc; if(geocache.shortDescIsHtml) { desc += geocache.shortDesc; } else { QString str = geocache.shortDesc; desc += "

" + str.replace("\n","
") + "

"; } if(geocache.longDescIsHtml) { desc += geocache.longDesc; } else { QString str = geocache.longDesc; desc += "

" + str.replace("\n","
") + "

"; } webDesc->setHtml(desc); webDesc->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); timerDownload = new QTimer(this); timerDownload->setSingleShot(true); connect(timerDownload, SIGNAL(timeout()), this, SLOT(slotDownloadDone())); connect(checkHint, SIGNAL(toggled(bool)), this, SLOT(slotHintChanged(bool))); connect(webDesc, SIGNAL(linkClicked(QUrl)), this, SLOT(slotLinkClicked(QUrl))); connect(toolUpdateSpoiler, SIGNAL(clicked()), this, SLOT(slotCollectSpoiler())); networkManager = new QNetworkAccessManager(this); connect(networkManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(slotRequestFinished(QNetworkReply*))); const QList& images = wpt.getImages(); photoAlbum->reload(images); if(images.isEmpty()) { slotCollectSpoiler(); toolUpdateSpoiler->setEnabled(false); } else { toolUpdateSpoiler->setEnabled(true); } if(wpt.isOnDevice()) { toolUpdateSpoiler->setEnabled(false); } listHistory->setEnabled(false); listHistory->setupHistory(wpt); } CDetailsGeoCache::~CDetailsGeoCache() { } void CDetailsGeoCache::slotHintChanged(bool on) { if(on) { labelHint->setText(wpt.getGeoCache().hint); } else { labelHint->setText(tr("???")); } } void CDetailsGeoCache::slotLinkClicked(const QUrl& url) { QDesktopServices::openUrl(url); } void CDetailsGeoCache::slotCollectSpoiler() { const QList& links = wpt.getLinks(); if(links.isEmpty()) { return; } wpt.loadHistory(0); photoAlbum->reload(wpt.getImages()); listHistory->setupHistory(wpt); QNetworkRequest request; request.setUrl(links.first().uri); networkManager->get(request); timerDownload->start(10000); labelStatus->show(); labelStatus->setText(tr("Searching for images...")); } void CDetailsGeoCache::slotRequestFinished(QNetworkReply * reply) { if(reply->error() != QNetworkReply::NoError) { qDebug() << reply->errorString(); reply->deleteLater(); return; } //qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); //qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute); //qDebug() << reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if(reply->property("whatfor") == "image") { QString info = reply->property("info").toString(); if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 301) { QNetworkRequest request; request.setUrl(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl()); QNetworkReply * reply = networkManager->get(request); reply->setProperty("whatfor", "image"); reply->setProperty("info", info); } else if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) { CGisItemWpt::image_t image; image.info = info; image.pixmap.loadFromData(reply->readAll()); wpt.addImage(image); photoAlbum->reload(wpt.getImages()); listHistory->setupHistory(wpt); cntSpoiler--; if(cntSpoiler == 0) { slotDownloadDone(); } } } else { if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 301) { QNetworkRequest request; request.setUrl(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl()); networkManager->get(request); } } QString asw = reply->readAll(); reply->deleteLater(); if(asw.isEmpty()) { return; } QRegExp re1(".*CachePageImages.*"); QRegExp re2("(http://.*\\.jpg).*>(.*)"); re2.setMinimal(true); bool watchOut = false; QStringList lines = asw.split("\n"); foreach(const QString &line, lines) { if(!watchOut && re1.exactMatch(line)) { watchOut = true; } else if(watchOut) { int pos = 0; while ((pos = re2.indexIn(line, pos)) != NOIDX) { QString url = re2.cap(1); QString info = re2.cap(2); QNetworkRequest request; request.setUrl(url); QNetworkReply * reply = networkManager->get(request); reply->setProperty("whatfor", "image"); reply->setProperty("info", info); cntSpoiler++; pos += re2.matchedLength(); } watchOut = false; } } } void CDetailsGeoCache::slotDownloadDone() { timerDownload->stop(); cntSpoiler = 0; if(wpt.getImages().isEmpty()) { labelStatus->setText(tr("No images found")); } else { labelStatus->hide(); } } qmapshack-1.5.1/src/gis/wpt/CDetailsWpt.cpp000644 001750 000144 00000015565 12622435720 021550 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/wpt/CDetailsWpt.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CElevationDialog.h" #include "helpers/CInputDialog.h" #include "helpers/CLinksDialog.h" #include "helpers/CPositionDialog.h" #include "helpers/CWptIconDialog.h" #include "units/IUnit.h" #include "widgets/CTextEditWidget.h" #include #include CDetailsWpt::CDetailsWpt(CGisItemWpt &wpt, QWidget *parent) : QDialog(parent) , wpt(wpt) { setupUi(this); photoAlbum->hide(); setupGui(); toolLock->setDisabled(wpt.isOnDevice()); connect(labelName, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(labelPosition, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(labelElevation, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(labelProximity, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(textCmtDesc, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotLinkActivated(QUrl))); connect(toolIcon, SIGNAL(clicked()), this, SLOT(slotChangeIcon())); connect(toolLock, SIGNAL(toggled(bool)), this, SLOT(slotChangeReadOnlyMode(bool))); connect(listHistory, SIGNAL(sigChanged()), this, SLOT(setupGui())); connect(toolAddImage, SIGNAL(clicked()), photoAlbum, SLOT(slotAddImage())); connect(toolDelImage, SIGNAL(clicked()), photoAlbum, SLOT(slotDelImage())); connect(photoAlbum, SIGNAL(sigChanged(QList)), this, SLOT(slotChangedImages(QList))); } CDetailsWpt::~CDetailsWpt() { } void CDetailsWpt::setupGui() { if(originator) { return; } originator = true; setWindowTitle(wpt.getName()); QString val, unit; QString strPos; QPointF pos = wpt.getPosition(); IUnit::degToStr(pos.x(), pos.y(), strPos); bool isReadOnly = wpt.isReadOnly(); toolIcon->setEnabled(!isReadOnly); toolIcon->setIcon(wpt.getIcon()); toolIcon->setObjectName(wpt.getIconName()); labelName->setText(IGisItem::toLink(isReadOnly, "name", wpt.getName(), "")); labelPosition->setText(IGisItem::toLink(isReadOnly, "position", strPos, "")); labelTainted->setVisible(wpt.isTainted()); QString elevationStr = "--"; if(wpt.getElevation() != NOINT) { IUnit::self().meter2elevation(wpt.getElevation(), val, unit); elevationStr = QString("%1 %2").arg(val).arg(unit); } labelElevation->setText(IGisItem::toLink(isReadOnly, "elevation", elevationStr, "")); QString proxStr = "--"; if(wpt.getProximity() != NOFLOAT) { IUnit::self().meter2elevation(wpt.getProximity(), val, unit); proxStr = QString("%1 %2").arg(val).arg(unit); } labelProximity->setText(IGisItem::toLink(isReadOnly, "proximity", proxStr, "")); if(wpt.getTime().isValid()) { labelTime->setText(IUnit::datetime2string(wpt.getTime(), false, QPointF(pos.x()*DEG_TO_RAD, pos.y()*DEG_TO_RAD))); } textCmtDesc->document()->clear(); textCmtDesc->append(IGisItem::createText(isReadOnly, wpt.getComment(), wpt.getDescription(), wpt.getLinks())); textCmtDesc->moveCursor (QTextCursor::Start); textCmtDesc->ensureCursorVisible(); toolLock->setChecked(isReadOnly); listHistory->setupHistory(wpt); const QList& images = wpt.getImages(); photoAlbum->reload(images); toolAddImage->setVisible(!isReadOnly); toolDelImage->setVisible(!isReadOnly && !images.isEmpty()); originator = false; } void CDetailsWpt::slotLinkActivated(const QString& link) { if(link == "name") { QString name = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new waypoint name."), QLineEdit::Normal, wpt.getName()); if(name.isEmpty()) { return; } wpt.setName(name); } else if(link == "elevation") { QVariant var(wpt.getElevation()); CElevationDialog dlg(this, var, QVariant(NOINT), wpt.getPosition()); if(dlg.exec() == QDialog::Accepted) { wpt.setElevation(var.toInt()); } } else if(link == "proximity") { QVariant var(wpt.getProximity()); CInputDialog dlg(this, tr("Enter new proximity range."), var, QVariant(NOFLOAT)); if(dlg.exec() == QDialog::Accepted) { wpt.setProximity(var.toDouble()); } } else if(link == "position") { QPointF pos = wpt.getPosition(); CPositionDialog dlg(this, pos); if(dlg.exec() == QDialog::Accepted) { wpt.setPosition(pos); } } setupGui(); } void CDetailsWpt::slotLinkActivated(const QUrl& url) { if(url.toString() == "comment") { CTextEditWidget dlg(this); dlg.setHtml(wpt.getComment()); if(dlg.exec() == QDialog::Accepted) { wpt.setComment(dlg.getHtml()); } setupGui(); } else if(url.toString() == "description") { CTextEditWidget dlg(this); dlg.setHtml(wpt.getDescription()); if(dlg.exec() == QDialog::Accepted) { wpt.setDescription(dlg.getHtml()); } setupGui(); } else if(url.toString() == "links") { QList links = wpt.getLinks(); CLinksDialog dlg(links, this); if(dlg.exec() == QDialog::Accepted) { wpt.setLinks(links); } setupGui(); } else { QDesktopServices::openUrl(url); } } void CDetailsWpt::slotChangeIcon() { if(!wpt.isReadOnly()) { CWptIconDialog dlg(toolIcon); if(dlg.exec() == QDialog::Accepted) { wpt.setIcon(toolIcon->objectName()); setupGui(); } } } void CDetailsWpt::slotChangeReadOnlyMode(bool on) { wpt.setReadOnlyMode(on); setupGui(); } void CDetailsWpt::slotChangedImages(const QList& images) { wpt.setImages(images); setupGui(); } qmapshack-1.5.1/src/gis/wpt/CGisItemWpt.h000644 001750 000144 00000023345 12624147124 021164 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISITEMWPT_H #define CGISITEMWPT_H #include "gis/IGisItem.h" #include "gis/tnv/CTwoNavProject.h" #include class IGisProject; class QDomNode; class CScrOptWpt; class QSqlDatabase; class CQlgtWpt; class QTextEdit; class QDir; class CGisItemWpt : public IGisItem { public: enum geocacheservice_e {eGC, eOC, eTC}; struct geocachelog_t { quint32 id = 0; QDateTime date; QString type; QString finderId; QString finder; bool textIsHtml = false; QString text; }; struct geocache_t { geocacheservice_e service = eOC; bool hasData = false; quint32 id = 0; bool available = true; bool archived = false; qreal difficulty = 0; qreal terrain = 0; QString status; QString name; QString owner; QString ownerId; QString type; QString container; bool shortDescIsHtml = false; QString shortDesc; bool longDescIsHtml = false; QString longDesc; QString hint; QString country; QString state; QString locale; QList logs; }; struct image_t { QImage pixmap; qreal direction = 0; QString info; QString filePath; QString fileName; }; /** @brief Create a completely new waypoint @param pos the waypoint's position [°] @param name the waypoint's name @param icon the waypoint's icon @param project the project the waypoint is added to */ CGisItemWpt(const QPointF& pos, const QString& name, const QString& icon, IGisProject * project); /** @brief Create a copy of an existing waypoint with a new position @param pos the waypoint's new position [°] @param parentWpt the waypoint to copy @param project the project the waypoint is added to */ CGisItemWpt(const QPointF& pos, const CGisItemWpt &parentWpt, IGisProject *project); /** @brief Create a 1:1 copy of an existing waypoint (with new key) @param parentWpt the waypoint to copy @param project the project the waypoint is added to @param idx the index to insert the item. If -1 the item will be appended to it's group */ CGisItemWpt(const CGisItemWpt &parentWpt, IGisProject *project, int idx, bool clone); /** @brief Create item from GPX. @param xml the GPX section containing the item @param project the project to append with item */ CGisItemWpt(const QDomNode& xml, IGisProject * project); /** @brief Create item from list of changes @param hist the change history @param project the project to append with item */ CGisItemWpt(const history_t& hist, IGisProject * project); /** @brief Read item from database by it's database ID @param id the item's ID in the database @param db the database itself @param project the project to append with item */ CGisItemWpt(quint64 id, QSqlDatabase& db, IGisProject * project); /** @brief Read item from text stream with TwoNav encoding @param tnvWpt @param project */ CGisItemWpt(const CTwoNavProject::wpt_t& tnvWpt, IGisProject * project); CGisItemWpt(const CQlgtWpt& wpt1); virtual ~CGisItemWpt(); /** @brief Save waypoint to GPX tree @param gpx The node to append by the waypoint */ void save(QDomNode& gpx); /** @brief Save waypoint to TwoNav waypoint file @param out the text stream to write to */ void saveTwoNav(QTextStream &out, const QDir &dir); /** @brief Read serialized waypoint from a binary data stream @param stream the data stream to read from @return A reference to the stream */ QDataStream& operator<<(QDataStream& stream); /** @brief Serialize waypoint into a binary data stream @param stream the data stream to write to. @return A reference to the stream */ QDataStream& operator>>(QDataStream& stream) const; void setName(const QString& str); void setPosition(const QPointF& pos); void setElevation(qint32 val); void setProximity(qreal val); void setIcon(const QString& name); void setComment(const QString& str); void setDescription(const QString& str); void setLinks(const QList& links); void setImages(const QList& imgs); /** @brief Silently append list of links Devices uses links to reference multimedia content attached to the waypoint. These links have to be added to the list of normal links. See removeLinksByType() on how to remove these links again. @param links list of links. */ void appendLinks(const QList& links) { wpt.links = links + wpt.links; } /** @brief Silently append list of images This is used to restore images from a device. As these images where part of the waypoint object in the first place they have to be added to the waypoint again without creating a new history entry. @param imgs list of images */ void appendImages(const QList& imgs) { images += imgs; } /** @brief Append the list of images by a single image. @param img a single image */ void addImage(const image_t& img); const QString& getName() const { return wpt.name.isEmpty() ? noName : wpt.name; } QString getInfo(bool allowEdit = false) const; QPointF getPosition() const { return QPointF(wpt.lon, wpt.lat); } qint32 getElevation() const { return wpt.ele; } qreal getProximity() const { return proximity; } const QDateTime& getTime() const { return wpt.time; } const QString& getIconName() const { return wpt.sym; } const QString& getComment() const { return wpt.cmt; } const QString& getDescription() const { return wpt.desc; } const geocache_t& getGeoCache() const { return geocache; } const QList& getLinks() const { return wpt.links; } const QList& getImages() const { return images; } IScrOpt * getScreenOptions(const QPoint &origin, IMouse * mouse); QPointF getPointCloseBy(const QPoint& ) { return posScreen; } void drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis); void drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis); void drawLabel(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis); void drawHighlight(QPainter& p); bool isCloseTo(const QPointF& pos); void mouseMove(const QPointF& pos); void mousePress(const QPointF& pos); void mouseRelease(const QPointF& pos); bool isGeocache() { return geocache.hasData; } void gainUserFocus(bool yes); void edit(); /** @brief Remove all links from the waypoint's link list with a given type This is used by devices that use links to attach multimedia items to a waypoint like images. These links only make sense on the device. Therefor the links have to be removed after the waypoint has been loaded from the device. @param type */ void removeLinksByType(const QString& type); void toggleBubble(); bool hasBubble() { return bool(flags & eFlagWptBubble); } static bool getNewWptData(QPointF& pt, QString& icon, QString& name); private: void setIcon(); void setSymbol(); void readGpx(const QDomNode& xml); void readTwoNav(const CTwoNavProject::wpt_t &tnvWpt); void readGcExt(const QDomNode& xmlCache); void writeGcExt(QDomNode& xmlCache); void drawBubble(QPainter& p); QPolygonF makePolyline(const QPointF& anchor, const QRectF& r); bool processMouseOverBubble(const QPoint &pos); static key_t keyUserFocus; // --- start all waypoint data ---- wpt_t wpt; qreal proximity = NOFLOAT; geocache_t geocache; QList images; QPointF focus; QPointF posScreen = NOPOINTF; // additional data, common to all IGisItems, is found in IItem // // --- stop all waypoint data ---- QPointer scrOpt; bool doBubble = false; bool doSpecialCursor = false; bool doBubbleMove = false; bool doBubbleSize = false; bool mouseIsOverBubble = false; QRect rectBubble; QRect rectBubbleMove {0,0,16,16}; QRect rectBubbleEdit {0,0,16,16}; QRect rectBubbleSize {0,0,16,16}; QPoint offsetMouse; QPoint offsetBubble {-320, -150}; quint32 widthBubble = 300; }; #endif //CGISITEMWPT_H qmapshack-1.5.1/src/gis/wpt/CDetailsWpt.h000644 001750 000144 00000003027 12622435723 021206 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDETAILSWPT_H #define CDETAILSWPT_H #include "ui_IDetailsWpt.h" #include #include class CDetailsWpt : public QDialog, private Ui::IDetailsWpt { Q_OBJECT public: CDetailsWpt(CGisItemWpt& wpt, QWidget * parent); virtual ~CDetailsWpt(); private slots: void slotLinkActivated(const QString& link); void slotLinkActivated(const QUrl& url); void slotChangeIcon(); void slotChangeReadOnlyMode(bool on); void slotChangedImages(const QList& images); void setupGui(); private: CGisItemWpt& wpt; bool originator = false; }; #endif //CDETAILSWPT_H qmapshack-1.5.1/src/gis/wpt/IScrOptWpt.ui000644 001750 000144 00000013541 12616742144 021232 0ustar00oeichlerusers000000 000000 IScrOptWpt 0 0 500 157 0 0 Form 3 3 3 3 3 3 true View details and edit. ... :/icons/32x32/EditDetails.png:/icons/32x32/EditDetails.png Copy waypoint into another project. ... :/icons/32x32/Copy.png:/icons/32x32/Copy.png Delete waypoint from project. ... :/icons/32x32/DeleteOne.png:/icons/32x32/DeleteOne.png Qt::Vertical Show content as static bubble. ... :/icons/32x32/Bubble.png:/icons/32x32/Bubble.png true Move waypoint to a new location. ... :/icons/32x32/WptMove.png:/icons/32x32/WptMove.png Clone waypoint and move clone a given distance and angle. ... :/icons/32x32/WptProj.png:/icons/32x32/WptProj.png Qt::Horizontal 40 20 0 0 300 0 500 16777215 TextLabel true Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse 0 100 CPhotoAlbum QWidget
widgets/CPhotoAlbum.h
1
qmapshack-1.5.1/src/gis/wpt/CSetupNewWpt.cpp000644 001750 000144 00000005153 12622435720 021725 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/WptIcons.h" #include "gis/wpt/CSetupNewWpt.h" #include "helpers/CPositionDialog.h" #include "helpers/CWptIconDialog.h" #include "units/IUnit.h" #include CSetupNewWpt::CSetupNewWpt(QPointF &pt, QString &icon, QString &name, QWidget *parent) : QDialog(parent) , pt(pt) , icon(icon) , name(name) { QPointF focus; setupUi(this); toolIcon->setObjectName(icon); toolIcon->setIcon(getWptIconByName(icon, focus)); QString pos; IUnit::degToStr(pt.x(), pt.y(), pos); linePosition->setText(pos); labelWarning->hide(); lineName->setText(name); connect(linePosition, SIGNAL(textEdited(QString)), this, SLOT(slotEditPosition(QString))); connect(lineName, SIGNAL(textEdited(QString)), this, SLOT(slotEditName(QString))); connect(toolIcon, SIGNAL(clicked()), this, SLOT(slotChangeIcon())); checkInput(); } CSetupNewWpt::~CSetupNewWpt() { } void CSetupNewWpt::accept() { if(CPositionDialog::getPosition(pt, linePosition->text())) { icon = toolIcon->objectName(); name = lineName->text(); QDialog::accept(); } } void CSetupNewWpt::reject() { pt = NOPOINTF; name.clear(); icon.clear(); QDialog::reject(); } void CSetupNewWpt::slotEditPosition(const QString& str) { labelWarning->setVisible(!IUnit::isValidCoordString(str)); checkInput(); } void CSetupNewWpt::slotEditName(const QString& str) { checkInput(); } void CSetupNewWpt::slotChangeIcon() { CWptIconDialog dlg(toolIcon); dlg.exec(); } void CSetupNewWpt::checkInput() { bool isEnabled = (labelWarning->isHidden() && !lineName->text().isEmpty()); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isEnabled); } qmapshack-1.5.1/src/gis/wpt/CSetupNewWpt.h000644 001750 000144 00000002734 12622435723 021377 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSETUPNEWWPT_H #define CSETUPNEWWPT_H #include "ui_ISetupNewWpt.h" #include class CSetupNewWpt : public QDialog, private Ui::ISetupNewWpt { Q_OBJECT public: CSetupNewWpt(QPointF& pt, QString& icon, QString& name, QWidget * parent); virtual ~CSetupNewWpt(); public slots: void accept(); void reject(); private slots: void slotEditPosition(const QString& str); void slotEditName(const QString& str); void slotChangeIcon(); private: void checkInput(); QPointF &pt; QString &icon; QString &name; }; #endif //CSETUPNEWWPT_H qmapshack-1.5.1/src/gis/wpt/CProjWpt.cpp000644 001750 000144 00000005476 12622435720 021075 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "gis/prj/IGisProject.h" #include "gis/wpt/CGisItemWpt.h" #include "gis/wpt/CProjWpt.h" #include "helpers/CWptIconDialog.h" #include "units/IUnit.h" #include #include CProjWpt::CProjWpt(CGisItemWpt& wpt, QWidget *parent) : QDialog(parent) , wpt(wpt) { setupUi(this); name = wpt.getName(); toolIcon->setIcon(wpt.getIcon()); toolIcon->setObjectName(wpt.getIconName()); labelName->setText(QString("%2").arg(name)); QString val, unit; IUnit::self().meter2distance(0,val,unit); labelDistUnit->setText(unit); connect(labelName, SIGNAL(linkActivated(QString)), this, SLOT(slotChangeName())); connect(toolIcon, SIGNAL(clicked()), this, SLOT(slotChangeIcon())); } CProjWpt::~CProjWpt() { } void CProjWpt::slotChangeIcon() { CWptIconDialog dlg(toolIcon); dlg.exec(); } void CProjWpt::slotChangeName() { QString n = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new waypoint name."), QLineEdit::Normal, wpt.getName()); if(n.isEmpty()) { return; } name = n; labelName->setText(QString("%2").arg(name)); } void CProjWpt::accept() { qreal dist = lineDist->text().toDouble(); qreal bearing = lineBearing->text().toDouble(); if((dist <= 0) || (bearing > 180) || (bearing < -180)) { return; } IGisProject * project = dynamic_cast(wpt.parent()); if(project == 0) { return; } QPointF pos = wpt.getPosition() * DEG_TO_RAD; pos = GPS_Math_Wpt_Projection(pos, dist, bearing * DEG_TO_RAD) * RAD_TO_DEG; CGisItemWpt * newWpt = new CGisItemWpt(pos, wpt, project); if(name != newWpt->getName()) { newWpt->setName(name); } if(toolIcon->objectName() != newWpt->getIconName()) { newWpt->setIcon(toolIcon->objectName()); } QDialog::accept(); } qmapshack-1.5.1/src/gis/wpt/CScrOptWpt.cpp000644 001750 000144 00000006574 12622435720 021375 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/wpt/CGisItemWpt.h" #include "gis/wpt/CProjWpt.h" #include "gis/wpt/CScrOptWpt.h" #include "helpers/CDraw.h" #include "mouse/IMouse.h" #include CScrOptWpt::CScrOptWpt(CGisItemWpt *wpt, const QPoint& point, IMouse *parent) : IScrOpt(parent) { key = wpt->getKey(); setupUi(this); setOrigin(point); label->setFont(CMainWindow::self().getMapFont()); label->setText(wpt->getInfo()); adjustSize(); toolProj->setDisabled(wpt->isGeocache() || wpt->isOnDevice()); toolMove->setDisabled(wpt->isGeocache() || wpt->isOnDevice()); photoAlbum->reload(wpt->getImages()); toolBubble->setChecked(wpt->hasBubble()); anchor = wpt->getPointCloseBy(point); move(anchor.toPoint() + QPoint(-width()/2,SCR_OPT_OFFSET)); show(); connect(toolDelete, SIGNAL(clicked()), this, SLOT(hide())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(hide())); connect(toolMove, SIGNAL(clicked()), this, SLOT(hide())); connect(toolProj, SIGNAL(clicked()), this, SLOT(hide())); connect(toolBubble, SIGNAL(clicked()), this, SLOT(hide())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelete())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(slotEdit())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(slotCopy())); connect(toolMove, SIGNAL(clicked()), this, SLOT(slotMove())); connect(toolProj, SIGNAL(clicked()), this, SLOT(slotProj())); connect(toolBubble, SIGNAL(clicked()), this, SLOT(slotBubble())); adjustSize(); } CScrOptWpt::~CScrOptWpt() { } void CScrOptWpt::slotDelete() { CGisWidget::self().delItemByKey(key); deleteLater(); } void CScrOptWpt::slotEdit() { CGisWidget::self().editItemByKey(key); deleteLater(); } void CScrOptWpt::slotCopy() { CGisWidget::self().copyItemByKey(key); deleteLater(); } void CScrOptWpt::slotMove() { CGisWidget::self().moveWptByKey(key); deleteLater(); } void CScrOptWpt::slotProj() { CGisWidget::self().projWptByKey(key); deleteLater(); } void CScrOptWpt::slotBubble() { CGisWidget::self().toggleWptBubble(key); deleteLater(); } void CScrOptWpt::draw(QPainter& p) { IGisItem * item = CGisWidget::self().getItemByKey(key); if(item == 0) { deleteLater(); return; } item->drawHighlight(p); CDraw::bubble(p, geometry(), anchor.toPoint()); } qmapshack-1.5.1/src/gis/wpt/CGisItemWpt.cpp000644 001750 000144 00000047171 12624153766 021533 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/CGisListWks.h" #include "gis/WptIcons.h" #include "gis/prj/IGisProject.h" #include "gis/wpt/CDetailsGeoCache.h" #include "gis/wpt/CDetailsWpt.h" #include "gis/wpt/CGisItemWpt.h" #include "gis/wpt/CScrOptWpt.h" #include "gis/wpt/CSetupNewWpt.h" #include "helpers/CDraw.h" #include "helpers/CSettings.h" #include "mouse/IMouse.h" #include "units/IUnit.h" #include #include IGisItem::key_t CGisItemWpt::keyUserFocus; /// used to add a new waypoint CGisItemWpt::CGisItemWpt(const QPointF& pos, const QString& name, const QString &icon, IGisProject *project) : IGisItem(project, eTypeWpt, NOIDX) { wpt.name = name; wpt.sym = icon; wpt.lon = pos.x(); wpt.lat = pos.y(); wpt.time = QDateTime::currentDateTimeUtc(); flags = eFlagCreatedInQms|eFlagWriteAllowed; qreal ele = CMainWindow::self().getEelevationAt(pos * DEG_TO_RAD); wpt.ele = (ele == NOFLOAT) ? NOINT : qRound(ele); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); setupHistory(); updateDecoration(eMarkChanged, eMarkNone); } /// used to move a copy of waypoint CGisItemWpt::CGisItemWpt(const QPointF& pos, const CGisItemWpt& parentWpt, IGisProject *project) : IGisItem(project, eTypeWpt, NOIDX) { *this = parentWpt; wpt.lon = pos.x(); wpt.lat = pos.y(); wpt.time = QDateTime::currentDateTimeUtc(); key.clear(); history.events.clear(); flags = eFlagCreatedInQms|eFlagWriteAllowed; qreal ele = CMainWindow::self().getEelevationAt(pos * DEG_TO_RAD); wpt.ele = (ele == NOFLOAT) ? NOINT : qRound(ele); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); setupHistory(); updateDecoration(eMarkChanged, eMarkNone); } /// used to create a copy of waypoint with new parent CGisItemWpt::CGisItemWpt(const CGisItemWpt &parentWpt, IGisProject *project, int idx, bool clone) : IGisItem(project, eTypeWpt, idx) { history = parentWpt.history; loadHistory(history.histIdxCurrent); if(clone) { wpt.name += QObject::tr("_Clone"); key.clear(); history.events.clear(); setupHistory(); } if(parentWpt.isOnDevice() || !parentWpt.isReadOnly()) { flags |= eFlagWriteAllowed; } else { flags &= ~eFlagWriteAllowed; } updateDecoration(eMarkChanged, eMarkNone); } /// used to create waypoint from GPX file CGisItemWpt::CGisItemWpt(const QDomNode &xml, IGisProject *project) : IGisItem(project, eTypeWpt, project->childCount()) { readGpx(xml); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); genKey(); setupHistory(); updateDecoration(eMarkNone, eMarkNone); } CGisItemWpt::CGisItemWpt(const history_t& hist, IGisProject * project) : IGisItem(project, eTypeWpt, project->childCount()) { history = hist; loadHistory(hist.histIdxCurrent); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); } CGisItemWpt::CGisItemWpt(quint64 id, QSqlDatabase& db, IGisProject * project) : IGisItem(project, eTypeWpt, NOIDX) { loadFromDb(id, db); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); } CGisItemWpt::CGisItemWpt(const CTwoNavProject::wpt_t &tnvWpt, IGisProject * project) : IGisItem(project, eTypeWpt, NOIDX) { readTwoNav(tnvWpt); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); genKey(); setupHistory(); updateDecoration(eMarkNone, eMarkNone); } CGisItemWpt::~CGisItemWpt() { } void CGisItemWpt::setSymbol() { setIcon(); } bool CGisItemWpt::getNewWptData(QPointF& pt, QString& icon, QString& name) { SETTINGS; QString lastName = cfg.value("Waypoint/lastName", "wpt").toString(); QString lastIcon = cfg.value("Waypoint/lastIcon", "Waypoint").toString(); const int s = lastName.size(); if(s != 0) { int idx; for(idx = s; idx > 0; idx--) { if(!lastName[idx - 1].isDigit()) { break; } } if(idx == 0) { lastName = QString::number(lastName.toInt() + 1); } else { lastName = lastName.left(idx) + QString::number(lastName.mid(idx).toInt() + 1); } } name = lastName; icon = lastIcon; CSetupNewWpt dlg(pt, icon, name, CMainWindow::getBestWidgetForParent()); if(dlg.exec() != QDialog::Accepted) { return false; } cfg.setValue("Waypoint/lastName", name); cfg.setValue("Waypoint/lastIcon", icon); return true; } QString CGisItemWpt::getInfo(bool allowEdit) const { QString str = "
" + getName() + "
"; if(geocache.hasData) { str += QString(" %4 (%1, D %2, T %3)").arg(geocache.container).arg(geocache.difficulty, 0,'f',1).arg(geocache.terrain, 0,'f',1).arg(geocache.name); } if(wpt.time.isValid()) { if(!str.isEmpty()) { str += "
\n"; } str += IUnit::datetime2string(wpt.time, false, QPointF(wpt.lon*DEG_TO_RAD, wpt.lat*DEG_TO_RAD)); } if(wpt.ele != NOINT) { if(!str.isEmpty()) { str += "
\n"; } QString val, unit; IUnit::self().meter2elevation(wpt.ele, val, unit); str += QObject::tr("Elevation: %1 %2").arg(val).arg(unit); } if(proximity != NOFLOAT) { if(!str.isEmpty()) { str += "
\n"; } QString val, unit; IUnit::self().meter2distance(proximity, val, unit); str += QObject::tr("Proximity: %1 %2").arg(val).arg(unit); } QString desc = removeHtml(wpt.desc).simplified(); if(desc.count()) { if(!str.isEmpty()) { str += "
\n"; } if(desc.count() < 200) { str += desc; } else { str += desc.left(197) + "..."; } } else { QString cmt = removeHtml(wpt.cmt).simplified(); if(cmt.count()) { if(!str.isEmpty()) { str += "
\n"; } if(cmt.count() < 200) { str += cmt; } else { str += cmt.left(197) + "..."; } } } return str; } IScrOpt * CGisItemWpt::getScreenOptions(const QPoint& origin, IMouse * mouse) { if(scrOpt.isNull()) { scrOpt = new CScrOptWpt(this, origin, mouse); } return scrOpt; } void CGisItemWpt::setIcon() { if(geocache.hasData) { icon = getWptIconByName(geocache.type, focus); } else { icon = getWptIconByName(wpt.sym, focus); } QTreeWidgetItem::setIcon(CGisListWks::eColumnIcon,icon); } void CGisItemWpt::setName(const QString& str) { SETTINGS; cfg.setValue("Waypoint/lastName", str); setText(CGisListWks::eColumnName, str); wpt.name = str; changed(QObject::tr("Changed name"),"://icons/48x48/EditText.png"); } void CGisItemWpt::setPosition(const QPointF& pos) { wpt.lon = pos.x(); wpt.lat = pos.y(); boundingRect = QRectF(QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD,QPointF(wpt.lon,wpt.lat)*DEG_TO_RAD); changed(QObject::tr("Changed position"),"://icons/48x48/WptMove.png"); } void CGisItemWpt::setElevation(qint32 val) { wpt.ele = val; changed(QObject::tr("Changed elevation"),"://icons/48x48/SetEle.png"); } void CGisItemWpt::setProximity(qreal val) { proximity = val; changed(QObject::tr("Changed proximity"),"://icons/48x48/WptProx.png"); } void CGisItemWpt::setIcon(const QString& name) { SETTINGS; cfg.setValue("Waypoint/lastIcon", name); wpt.sym = name; QPointF focus; QString path; getWptIconByName(name, focus, &path); changed(QObject::tr("Changed icon"), path); } void CGisItemWpt::setComment(const QString& str) { wpt.cmt = str; changed(QObject::tr("Changed comment"), "://icons/48x48/EditText.png"); } void CGisItemWpt::setDescription(const QString& str) { wpt.desc = str; changed(QObject::tr("Changed description"), "://icons/48x48/EditText.png"); } void CGisItemWpt::setLinks(const QList& links) { wpt.links = links; changed(QObject::tr("Changed links"), "://icons/48x48/Link.png"); } void CGisItemWpt::setImages(const QList& imgs) { images = imgs; changed(QObject::tr("Changed images"), "://icons/48x48/Image.png"); } void CGisItemWpt::addImage(const image_t& img) { images.append(img); changed(QObject::tr("Add image"), "://icons/48x48/Image.png"); } bool CGisItemWpt::isCloseTo(const QPointF& pos) { if(posScreen == NOPOINTF) { return false; } return (pos - posScreen).manhattanLength() < 22; } void CGisItemWpt::gainUserFocus(bool yes) { keyUserFocus = yes ? key : key_t(); } void CGisItemWpt::edit() { if(geocache.hasData) { CDetailsGeoCache dlg(*this, CMainWindow::getBestWidgetForParent()); dlg.exec(); } else { CDetailsWpt dlg(*this, CMainWindow::getBestWidgetForParent()); dlg.exec(); } } void CGisItemWpt::drawItem(QPainter& p, const QPolygonF& viewport, QList &blockedAreas, CGisDraw *gis) { posScreen = QPointF(wpt.lon * DEG_TO_RAD, wpt.lat * DEG_TO_RAD); if(!isVisible(posScreen, viewport, gis)) { rectBubble = QRect(); posScreen = NOPOINTF; return; } gis->convertRad2Px(posScreen); drawBubble(p); p.drawPixmap(posScreen - focus, icon); if(proximity != NOFLOAT) { QPointF pt1(wpt.lon * DEG_TO_RAD, wpt.lat * DEG_TO_RAD); pt1 = GPS_Math_Wpt_Projection(pt1, proximity, 90 * DEG_TO_RAD); gis->convertRad2Px(pt1); qreal r = pt1.x() - posScreen.x(); p.save(); p.setBrush(Qt::NoBrush); p.setPen(QPen(Qt::white,3)); p.drawEllipse(QRect(posScreen.x() - r - 1, posScreen.y() - r - 1, 2*r + 1, 2*r + 1)); p.setPen(QPen(Qt::red,1)); p.drawEllipse(QRect(posScreen.x() - r - 1, posScreen.y() - r - 1, 2*r + 1, 2*r + 1)); p.restore(); } blockedAreas << QRectF(posScreen - focus, icon.size()); } void CGisItemWpt::drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis) { if(mouseIsOverBubble && !doBubbleMove && !doBubbleSize && rectBubble.isValid() && !isReadOnly()) { QPainterPath clip; clip.addRoundedRect(rectBubble, 5, 5); p.setClipPath(clip); QRect barTop(rectBubble.topLeft(), QSize(rectBubble.width(), 26)); QRect barBottom(barTop); barBottom.moveBottomLeft(rectBubble.bottomLeft()); barBottom.adjust(1,0,-1,-1); barTop.adjust(1,1,-1,0); p.setPen(Qt::NoPen); p.setBrush(QColor(200,200,255,150)); p.drawRect(barTop); p.drawRect(barBottom); p.setBrush(Qt::white); p.drawRoundedRect(rectBubbleMove.adjusted(-2,-2,2,2), 3,3); p.drawRoundedRect(rectBubbleEdit.adjusted(-2,-2,2,2), 3,3); p.drawRoundedRect(rectBubbleSize.adjusted(-2,-2,2,2), 3,3); p.drawPixmap(rectBubbleMove, QPixmap("://icons/32x32/MoveArrow.png")); p.drawPixmap(rectBubbleEdit, QPixmap("://icons/32x32/EditDetails.png")); p.drawPixmap(rectBubbleSize, QPixmap("://icons/32x32/SizeArrow.png")); } } void CGisItemWpt::drawLabel(QPainter& p, const QPolygonF &viewport, QList &blockedAreas, const QFontMetricsF &fm, CGisDraw *gis) { if(posScreen == NOPOINTF) { return; } QPointF pt = posScreen - focus; QRectF rect = fm.boundingRect(wpt.name); rect.adjust(-2,-2,2,2); // place label on top rect.moveCenter(pt + QPointF(icon.width()/2, -fm.height())); if(isBlocked(rect, blockedAreas)) { // place label on bottom rect.moveCenter(pt + QPointF( icon.width()/2, +fm.height() + icon.height())); if(isBlocked(rect, blockedAreas)) { // place label on right rect.moveCenter(pt + QPointF( icon.width() + rect.width()/2, +fm.height())); if(isBlocked(rect, blockedAreas)) { // place label on left rect.moveCenter(pt + QPointF( -rect.width()/2, +fm.height())); if(isBlocked(rect, blockedAreas)) { // failed to place label anywhere return; } } } } CDraw::text(wpt.name,p,rect.toRect(), Qt::darkBlue); blockedAreas << rect; } void CGisItemWpt::drawHighlight(QPainter& p) { if(posScreen == NOPOINTF) { return; } p.drawImage(posScreen - QPointF(31,31), QImage("://cursors/wptHighlight.png")); } void CGisItemWpt::drawBubble(QPainter& p) { if(!(flags & eFlagWptBubble)) { return; } QString str = QString("%1").arg(getName()); if(!removeHtml(wpt.desc).simplified().isEmpty()) { str += QString("

%1

").arg(wpt.desc); } if(!removeHtml(wpt.cmt).simplified().isEmpty()) { str += QString("

%1

").arg(wpt.cmt); } QTextDocument doc; doc.setHtml(str); doc.setTextWidth(widthBubble); rectBubble.setWidth(widthBubble); rectBubble.setHeight(doc.size().height()); QPoint posBubble = posScreen.toPoint() + offsetBubble; rectBubble.moveTopLeft(posBubble); rectBubbleMove.moveTopLeft(rectBubble.topLeft() + QPoint(5,5)); rectBubbleEdit.moveTopLeft(rectBubbleMove.topRight() + QPoint(7,0)); rectBubbleSize.moveBottomRight(rectBubble.bottomRight() - QPoint(5,5)); QPolygonF frame = makePolyline(posScreen, rectBubble); p.setPen(CDraw::penBorderGray); p.setBrush(CDraw::brushBackWhite); p.drawPolygon(frame); p.save(); p.translate(posBubble); p.setPen(Qt::black); doc.drawContents(&p); p.restore(); } QPolygonF CGisItemWpt::makePolyline(const QPointF& anchor, const QRectF& r) { QPolygonF poly1, poly2; poly1 << r.topLeft() << r.topRight() << r.bottomRight() << r.bottomLeft(); if(!r.contains(anchor)) { qreal w = rectBubble.width()>>1; qreal h = rectBubble.height()>>1; if(w > 30) { w = 30; } if(h > 30) { h = 30; } w = h = qMin(w,h); if(anchor.x() < r.left()) { poly2 << anchor << (r.center() + QPoint(0,-h)) << (r.center() + QPoint(0,h)) << anchor; } else if(r.right() < anchor.x()) { poly2 << anchor << (r.center() + QPoint(0,-h)) << (r.center() + QPoint(0,h)) << anchor; } else if(anchor.y() < r.top()) { poly2 << anchor << (r.center() + QPoint(-w,0)) << (r.center() + QPoint(w,0)) << anchor; } else if(r.bottom() < anchor.y()) { poly2 << anchor << (r.center() + QPoint(-w,0)) << (r.center() + QPoint(w,0)) << anchor; } QPainterPath path1; path1.addRoundedRect(r,5,5); QPainterPath path2; path2.addPolygon(poly2); path1 = path1.united(path2); poly1 = path1.toFillPolygon(); } return poly1; } void CGisItemWpt::removeLinksByType(const QString& type) { QList::iterator link = wpt.links.begin(); while(link != wpt.links.end()) { if(link->type == type) { link = wpt.links.erase(link); continue; } link++; } } void CGisItemWpt::mouseMove(const QPointF& pos) { if(!hasBubble() || isReadOnly()) { return; } if(!processMouseOverBubble(pos.toPoint())) { if(mouseIsOverBubble) { if(!rectBubble.contains(pos.toPoint())) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { doBubbleMove = doBubbleSize = false; canvas->resetMouse(); } mouseIsOverBubble = false; } } else { if(rectBubble.contains(pos.toPoint())) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { doBubbleMove = doBubbleSize = false; canvas->setMouseWptBubble(getKey()); } mouseIsOverBubble = true; } } } } void CGisItemWpt::mousePress(const QPointF& pos) { if(!mouseIsOverBubble) { return; } QPoint pos1 = pos.toPoint(); if(rectBubbleMove.contains(pos1)) { offsetMouse = pos1 - rectBubble.topLeft(); doBubbleMove = true; } else if(rectBubbleEdit.contains(pos1)) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { doBubbleMove = doBubbleSize = false; canvas->resetMouse(); } mouseIsOverBubble = false; edit(); } else if(rectBubbleSize.contains(pos1)) { offsetMouse = pos1 - rectBubble.bottomRight(); doBubbleSize = true; } } void CGisItemWpt::mouseRelease(const QPointF& pos) { if(!mouseIsOverBubble) { return; } updateHistory(); doBubbleMove = doBubbleSize = false; } void CGisItemWpt::toggleBubble() { if(flags & eFlagWptBubble) { flags &= ~eFlagWptBubble; } else { flags |= eFlagWptBubble; } updateHistory(); } bool CGisItemWpt::processMouseOverBubble(const QPoint &pos) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(!canvas || !mouseIsOverBubble) { return false; } if(doBubbleMove) { offsetBubble = pos - posScreen.toPoint(); offsetBubble -= offsetMouse; canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); return true; } else if(doBubbleSize) { qDebug() << offsetMouse; int width = pos.x() - rectBubble.left() - offsetMouse.x(); if(width > 50) { widthBubble = width; } canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); return true; } else if(rectBubbleMove.contains(pos) || rectBubbleEdit.contains(pos) || rectBubbleSize.contains(pos)) { if(!doSpecialCursor) { CCanvas::setOverrideCursor(Qt::PointingHandCursor, "processMouseOverBubble"); doSpecialCursor = true; } } else { if(doSpecialCursor) { CCanvas::restoreOverrideCursor("processMouseOverBubble"); doSpecialCursor = false; } } return false; } qmapshack-1.5.1/src/gis/wpt/IDetailsWpt.ui000644 001750 000144 00000030761 12616742144 021410 0ustar00oeichlerusers000000 000000 IDetailsWpt 0 0 550 454 Dialog :/icons/32x32/Map.png:/icons/32x32/Map.png 0 0 0 0 0 0 Info 0 0 0 0 0 3 0 0 Position: - 0 0 Ele. 0 0 - 0 0 Proximity: 0 0 - 0 0 QFrame::NoFrame false 0 150 16777215 150 15 75 true - 0 0 25 25 <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> :/icons/32x32/Tainted.png true Toggle read only mode. You have to open the lock to edit the item. ... :/icons/32x32/UnLock.png :/icons/32x32/Lock.png:/icons/32x32/UnLock.png 22 22 true true 0 0 Date/Time: - Add images. ... :/icons/32x32/AddImage.png:/icons/32x32/AddImage.png 22 22 Delete selected image. ... :/icons/32x32/DelImage.png:/icons/32x32/DelImage.png 22 22 Qt::Vertical 20 40 ... 22 22 true Hist. 0 0 0 0 0 CHistoryListWidget QListWidget
widgets/CHistoryListWidget.h
CPhotoAlbum QWidget
widgets/CPhotoAlbum.h
1
qmapshack-1.5.1/src/gis/wpt/IDetailsGeoCache.ui000644 001750 000144 00000024461 12616742144 022274 0ustar00oeichlerusers000000 000000 IDetailsGeoCache 0 0 800 667 600 400 Dialog :/icons/32x32/Map.png:/icons/32x32/Map.png 0 0 Position: - 0 0 Difficulty 0 0 - 0 0 - 0 0 - 0 0 - 0 0 - 0 0 Terrain 0 0 - 0 0 - 0 0 - 0 0 - 0 0 - Update spoilers ... :/icons/32x32/ReloadImage.png:/icons/32x32/ReloadImage.png 22 22 Qt::Vertical 20 40 - Qt::Horizontal about:blank ... 22 22 true Hint: Qt::Horizontal 40 20 TextLabel 0 150 QWebView QWidget
QtWebKitWidgets/QWebView
CPhotoAlbum QWidget
widgets/CPhotoAlbum.h
1
CHistoryListWidget QListWidget
widgets/CHistoryListWidget.h
qmapshack-1.5.1/src/gis/wpt/IProjWpt.ui000644 001750 000144 00000007142 12527654570 020740 0ustar00oeichlerusers000000 000000 IProjWpt 0 0 240 354 Waypoint Projection ... 22 22 true 15 75 true - Clone waypoint and move by: m ° :/pics/compass.png Qt::AlignCenter Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IProjWpt accept() 248 254 157 274 buttonBox rejected() IProjWpt reject() 316 260 286 274 qmapshack-1.5.1/src/gis/wpt/CScrOptWpt.h000644 001750 000144 00000002762 12622435723 021040 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTWPT_H #define CSCROPTWPT_H #include "gis/IGisItem.h" #include "mouse/IScrOpt.h" #include "ui_IScrOptWpt.h" #include class CGisItemWpt; class IMouse; class CScrOptWpt : public IScrOpt, private Ui::IScrOptWpt { Q_OBJECT public: CScrOptWpt(CGisItemWpt * wpt, const QPoint &point, IMouse *parent); virtual ~CScrOptWpt(); void draw(QPainter& p); private slots: void slotDelete(); void slotEdit(); void slotCopy(); void slotMove(); void slotProj(); void slotBubble(); private: IGisItem::key_t key; QPointF anchor; }; #endif //CSCROPTWPT_H qmapshack-1.5.1/src/gis/wpt/CProjWpt.h000644 001750 000144 00000002476 12622435723 020542 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPROJWPT_H #define CPROJWPT_H #include "ui_IProjWpt.h" #include class CGisItemWpt; class CProjWpt : public QDialog, private Ui::IProjWpt { Q_OBJECT public: CProjWpt(CGisItemWpt &wpt, QWidget * parent); virtual ~CProjWpt(); public slots: void accept(); private slots: void slotChangeIcon(); void slotChangeName(); private: CGisItemWpt& wpt; QString name; }; #endif //CPROJWPT_H qmapshack-1.5.1/src/gis/wpt/ISetupNewWpt.ui000644 001750 000144 00000005432 12527654570 021600 0ustar00oeichlerusers000000 000000 ISetupNewWpt 0 0 400 196 New Waypoint... Symbol ... true Position Name Bad position format. Must be: "[N|S] ddd mm.sss [W|E] ddd mm.sss" or "[N|S] ddd.ddd [W|E] ddd.ddd" Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISetupNewWpt accept() 248 254 157 274 buttonBox rejected() ISetupNewWpt reject() 316 260 286 274 qmapshack-1.5.1/src/gis/wpt/CDetailsGeoCache.h000644 001750 000144 00000003136 12622435723 022073 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDETAILSGEOCACHE_H #define CDETAILSGEOCACHE_H #include "ui_IDetailsGeoCache.h" #include class CGisItemWpt; class QNetworkAccessManager; class QTimer; class CDetailsGeoCache : public QDialog, private Ui::IDetailsGeoCache { Q_OBJECT public: CDetailsGeoCache(CGisItemWpt& wpt, QWidget * parent); virtual ~CDetailsGeoCache(); private slots: void slotHintChanged(bool on); void slotLinkClicked(const QUrl& url); void slotCollectSpoiler(); void slotRequestFinished(QNetworkReply * reply); void slotDownloadDone(); private: CGisItemWpt& wpt; QNetworkAccessManager * networkManager; int cntSpoiler = 0; QTimer * timerDownload; }; #endif //CDETAILSGEOCACHE_H qmapshack-1.5.1/src/gis/CSelDevices.h000644 001750 000144 00000002406 12622435723 020342 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSELDEVICES_H #define CSELDEVICES_H #include "ui_ISelDevices.h" #include class QTreeWidget; class IGisProject; class CSelDevices : public QDialog, private Ui::ISelDevices { Q_OBJECT public: CSelDevices(IGisProject *project, QTreeWidget * wks); virtual ~CSelDevices(); void getSlectedDevices(QSet& keys); }; #endif //CSELDEVICES_H qmapshack-1.5.1/src/gis/IGisLine.cpp000644 001750 000144 00000004020 12622435720 020176 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "dem/CDemDraw.h" #include "gis/CGisDraw.h" #include "gis/IGisLine.h" IGisLine::subpt_t::subpt_t(const QPointF& pt) : coord(pt) { } IGisLine::point_t::point_t(const QPointF& pt) { coord = pt; pixel = pt; } void IGisLine::point_t::resetElevation() { ele = NOINT; for(int i = 0; i < subpts.size(); i++) { subpts[i].ele = NOINT; } } void SGisLine::updateElevation(CDemDraw * dem) { for(int i = 0; i < size(); i++) { IGisLine::point_t& pt = (*this)[i]; pt.ele = dem->getElevationAt(pt.coord); for(int n = 0; n < pt.subpts.size(); n++) { IGisLine::subpt_t& sub = pt.subpts[n]; sub.ele = dem->getElevationAt(sub.coord); } } } void SGisLine::updatePixel(CGisDraw * gis) { for(int i = 0; i < size(); i++) { IGisLine::point_t& pt = (*this)[i]; pt.pixel = pt.coord; gis->convertRad2Px(pt.pixel); for(int n = 0; n < pt.subpts.size(); n++) { IGisLine::subpt_t& sub = pt.subpts[n]; sub.pixel = sub.coord; gis->convertRad2Px(sub.pixel); } } } qmapshack-1.5.1/src/gis/rte/CGisItemRte.cpp000644 001750 000144 00000056046 12624154220 021456 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "gis/CGisDraw.h" #include "gis/CGisListWks.h" #include "gis/WptIcons.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CDetailsRte.h" #include "gis/rte/CGisItemRte.h" #include "gis/rte/CScrOptRte.h" #include "helpers/CDraw.h" #include #include #include const QPen CGisItemRte::penBackground(Qt::white, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); IGisItem::key_t CGisItemRte::keyUserFocus; #define MIN_DIST_FOCUS 200 void CGisItemRte::rtept_t::updateIcon() { if(sym.isEmpty()) { icon = QPixmap(); focus = NOPOINTF; } else { icon = getWptIconByName(sym, focus); } } /// used to create a copy of route with new parent CGisItemRte::CGisItemRte(const CGisItemRte& parentRte, IGisProject * project, int idx, bool clone) : IGisItem(project, eTypeRte, idx) { history = parentRte.history; loadHistory(history.histIdxCurrent); if(clone) { rte.name += QObject::tr("_Clone"); key.clear(); history.events.clear(); } if(parentRte.isOnDevice()) { flags |= eFlagWriteAllowed; } else if(!parentRte.isReadOnly()) { flags |= eFlagWriteAllowed; } else { flags &= ~eFlagWriteAllowed; } setupHistory(); deriveSecondaryData(); updateDecoration(eMarkChanged, eMarkNone); } /// used to create route from GPX file CGisItemRte::CGisItemRte(const QDomNode& xml, IGisProject *parent) : IGisItem(parent, eTypeRte, parent->childCount()) { // --- start read and process data ---- readRte(xml, rte); // --- stop read and process data ---- setupHistory(); deriveSecondaryData(); updateDecoration(eMarkNone, eMarkNone); } CGisItemRte::CGisItemRte(const history_t& hist, IGisProject * project) : IGisItem(project, eTypeRte, project->childCount()) { history = hist; loadHistory(hist.histIdxCurrent); deriveSecondaryData(); } CGisItemRte::CGisItemRte(quint64 id, QSqlDatabase& db, IGisProject * project) : IGisItem(project, eTypeRte, NOIDX) { loadFromDb(id, db); } CGisItemRte::CGisItemRte(const SGisLine &l, const QString &name, IGisProject *project, int idx) : IGisItem(project, eTypeRte, idx) { rte.name = name; readRouteDataFromGisLine(l); flags |= eFlagCreatedInQms|eFlagWriteAllowed; setupHistory(); updateDecoration(eMarkChanged, eMarkNone); } CGisItemRte::~CGisItemRte() { // reset user focus if focused on this track if(key == keyUserFocus) { keyUserFocus.clear(); } } bool CGisItemRte::isCalculated() { bool yes = true; foreach(const rtept_t &pt, rte.pts) { if((pt.fakeSubpt.lat == NOFLOAT) || (pt.fakeSubpt.lon == NOFLOAT)) { yes = false; } } return yes; } void CGisItemRte::deriveSecondaryData() { qreal north = -90; qreal east = -180; qreal south = 90; qreal west = 180; //foreach(const rtept_t &rtept, rte.pts) const int N = rte.pts.count(); for(int i = 0; i < N; i++) { rtept_t &rtept = rte.pts[i]; if(rtept.lon < west) { west = rtept.lon; } if(rtept.lon > east) { east = rtept.lon; } if(rtept.lat < south) { south = rtept.lat; } if(rtept.lat > north) { north = rtept.lat; } foreach(const subpt_t &subpt, rtept.subpts) { if(subpt.lon < west) { west = subpt.lon; } if(subpt.lon > east) { east = subpt.lon; } if(subpt.lat < south) { south = subpt.lat; } if(subpt.lat > north) { north = subpt.lat; } } rtept.updateIcon(); } boundingRect = QRectF(QPointF(west * DEG_TO_RAD, north * DEG_TO_RAD), QPointF(east * DEG_TO_RAD,south * DEG_TO_RAD)); } void CGisItemRte::edit() { CDetailsRte dlg(*this, CMainWindow::getBestWidgetForParent()); dlg.exec(); } void CGisItemRte::setSymbol() { icon = QPixmap("://icons/32x32/Route.png").scaled(22,22, Qt::KeepAspectRatio, Qt::SmoothTransformation); setIcon(CGisListWks::eColumnIcon, icon); } void CGisItemRte::setName(const QString& str) { setText(CGisListWks::eColumnName, str); rte.name = str; changed(QObject::tr("Changed name."), "://icons/48x48/EditText.png"); } void CGisItemRte::setComment(const QString& str) { rte.cmt = str; changed(QObject::tr("Changed comment"), "://icons/48x48/EditText.png"); } void CGisItemRte::setDescription(const QString& str) { rte.desc = str; changed(QObject::tr("Changed description"), "://icons/48x48/EditText.png"); } void CGisItemRte::setLinks(const QList& links) { rte.links = links; changed(QObject::tr("Changed links"), "://icons/48x48/Link.png"); } QString CGisItemRte::getInfo(bool allowEdit) const { QString val1, unit1, val2, unit2; QString str = "
"; if(allowEdit) { str += "" + toLink(isReadOnly(), "name", getName(), "") + ""; } else { str += "
" + getName() + "
"; } str += "
\n"; if(totalDistance != NOFLOAT) { IUnit::self().meter2distance(totalDistance, val1, unit1); str += QObject::tr("Length: %1 %2").arg(val1).arg(unit1); } else { str += QObject::tr("Length: -"); } str += "
\n"; if(totalTime != 0) { IUnit::self().seconds2time(totalTime, val1, unit1); str += QObject::tr("Time: %1 %2").arg(val1).arg(unit1); } else { str += QObject::tr("Time: -"); } if(!lastRoutedWith.isEmpty()) { str += "
\n"; str += QObject::tr("Last time routed:
%1").arg(IUnit::datetime2string(lastRoutedTime, false, boundingRect.center())); str += "
\n"; str += QObject::tr("with %1").arg(lastRoutedWith); } return str; } IScrOpt * CGisItemRte::getScreenOptions(const QPoint& origin, IMouse * mouse) { if(scrOpt.isNull()) { scrOpt = new CScrOptRte(this, origin, mouse); } return scrOpt; } QPointF CGisItemRte::getPointCloseBy(const QPoint& screenPos) { QMutexLocker lock(&mutexItems); qint32 d = NOINT; QPointF pt = NOPOINTF; foreach(const QPointF &point, line) { int tmp = (screenPos - point).manhattanLength(); if(tmp < d) { pt = point; d = tmp; } } return pt; } bool CGisItemRte::isCloseTo(const QPointF& pos) { QMutexLocker lock(&mutexItems); qreal dist = GPS_Math_DistPointPolyline(line, pos); return dist < 20; } void CGisItemRte::gainUserFocus(bool yes) { keyUserFocus = yes ? key : key_t(); } void CGisItemRte::looseUserFocus() { if(keyUserFocus == key) { keyUserFocus.clear(); } } void CGisItemRte::drawItem(QPainter& p, const QPolygonF& viewport, QList &blockedAreas, CGisDraw *gis) { QMutexLocker lock(&mutexItems); line.clear(); if(!isVisible(boundingRect, viewport, gis)) { return; } QPointF p1 = viewport[0]; QPointF p2 = viewport[2]; gis->convertRad2Px(p1); gis->convertRad2Px(p2); QRectF extViewport(p1,p2); QVector points; QVector icons; QVector focus; foreach(const rtept_t &rtept, rte.pts) { QPointF pt(rtept.lon * DEG_TO_RAD, rtept.lat * DEG_TO_RAD); gis->convertRad2Px(pt); line << pt; points << 1; icons << rtept.icon; focus << rtept.focus; blockedAreas << QRectF(pt - rtept.focus, rtept.icon.size()); foreach(const subpt_t &subpt, rtept.subpts) { QPointF pt(subpt.lon * DEG_TO_RAD, subpt.lat * DEG_TO_RAD); gis->convertRad2Px(pt); line << pt; if(subpt.type != subpt_t::eTypeNone) { points << 2; } else { points << 0; } } } p.setPen(penBackground); p.drawPolyline(line); p.setPen(Qt::NoPen); p.setBrush(Qt::white); for(int i = 0; i < line.size(); i++) { switch(points[i]) { case 1: p.drawEllipse(line[i],7,7); break; case 2: p.drawEllipse(line[i],5,5); break; } } p.setPen(hasUserFocus() ? penForegroundFocus : penForeground); p.setBrush(hasUserFocus() ? penForegroundFocus.color() : penForeground.color()); CDraw::arrows(line, extViewport, p, 10, 80); p.drawPolyline(line); p.setPen(Qt::NoPen); for(int i = 0, n = 0; i < line.size(); i++) { switch(points[i]) { case 1: p.setBrush(Qt::red); p.drawEllipse(line[i],5,5); if(focus[n] != NOPOINTF) { p.drawPixmap(line[i] - focus[n], icons[n]); } n++; break; case 2: p.setBrush(Qt::cyan); p.drawEllipse(line[i],3,3); break; } } } void CGisItemRte::drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis) { QMutexLocker lock(&mutexItems); if(rte.pts.isEmpty()) { return; } if(hasUserFocus() && mouseMoveFocus && mouseMoveFocus->lon != NOFLOAT && mouseMoveFocus->lat != NOFLOAT) { QPointF anchor(mouseMoveFocus->lon, mouseMoveFocus->lat); anchor *= DEG_TO_RAD; gis->convertRad2Px(anchor); p.drawEllipse(anchor, 5, 5); QString str, val, unit; IUnit::self().seconds2time(mouseMoveFocus->time, val, unit); str += QObject::tr("Time: %1 %2").arg(val).arg(unit) + " "; IUnit::self().meter2distance(mouseMoveFocus->distance, val, unit); str += QObject::tr("Distance: %1 %2").arg(val).arg(unit); str += "\n" + mouseMoveFocus->instruction; // calculate bounding box of text QFont f = CMainWindow::self().getMapFont(); QFontMetrics fm(f); QRect rectText = fm.boundingRect(QRect(0,0,500,0), Qt::AlignLeft|Qt::AlignTop|Qt::TextWordWrap, str); rectText.adjust(-5, -5, 5, 5); rectText.moveBottomLeft(anchor.toPoint() + QPoint(-50,-50)); p.setFont(f); CDraw::bubble(p, rectText, anchor.toPoint(), 18 /* px */, 21 /* px */); p.save(); p.translate(5,5); p.setPen(Qt::darkBlue); p.drawText(rectText, str); p.restore(); } } void CGisItemRte::drawLabel(QPainter& p, const QPolygonF& viewport, QList &blockedAreas, const QFontMetricsF &fm, CGisDraw *gis) { QMutexLocker lock(&mutexItems); if(!isVisible(boundingRect, viewport, gis)) { return; } foreach(const rtept_t &rtept, rte.pts) { QPointF pt(rtept.lon * DEG_TO_RAD, rtept.lat * DEG_TO_RAD); gis->convertRad2Px(pt); //pt = pt - rtept.focus; //p.drawPixmap(pt, rtept.icon); QRectF rect = fm.boundingRect(rtept.name); rect.adjust(-2,-2,2,2); // place label on top rect.moveCenter(pt + QPointF(rtept.icon.width()/2, -fm.height())); if(isBlocked(rect, blockedAreas)) { // place label on bottom rect.moveCenter(pt + QPointF( rtept.icon.width()/2, +fm.height() + rtept.icon.height())); if(isBlocked(rect, blockedAreas)) { // place label on right rect.moveCenter(pt + QPointF( rtept.icon.width() + rect.width()/2, +fm.height())); if(isBlocked(rect, blockedAreas)) { // place label on left rect.moveCenter(pt + QPointF( -rect.width()/2, +fm.height())); if(isBlocked(rect, blockedAreas)) { // failed to place label anywhere return; } } } } CDraw::text(rtept.name, p, rect.toRect(), Qt::darkBlue); blockedAreas << rect; } } void CGisItemRte::drawHighlight(QPainter& p) { QMutexLocker lock(&mutexItems); if(line.isEmpty() || hasUserFocus()) { return; } p.setPen(QPen(QColor(255,0,0,100),11,Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(line); } void CGisItemRte::readRouteDataFromGisLine(const SGisLine &l) { bool doAutoRouting = !l.first().subpts.isEmpty(); rte.pts.clear(); for(int i = 0; i < l.size(); i++) { rte.pts << rtept_t(); rtept_t& rtept = rte.pts.last(); const point_t& pt = l[i]; rtept.lon = pt.coord.x() * RAD_TO_DEG; rtept.lat = pt.coord.y() * RAD_TO_DEG; rtept.ele = pt.ele; } if(doAutoRouting) { calc(); } deriveSecondaryData(); } void CGisItemRte::setDataFromPolyline(const SGisLine &l) { QMutexLocker lock(&mutexItems); mouseMoveFocus = 0; readRouteDataFromGisLine(l); flags |= eFlagTainted; changed(QObject::tr("Changed route points."), "://icons/48x48/LineMove.png"); } void CGisItemRte::getPolylineFromData(SGisLine& l) { QMutexLocker lock(&mutexItems); l.clear(); foreach(const rtept_t &rtept, rte.pts) { l << point_t(QPointF(rtept.lon * DEG_TO_RAD, rtept.lat * DEG_TO_RAD)); point_t& pt = l.last(); pt.subpts.clear(); foreach(const subpt_t &subpt, rtept.subpts) { pt.subpts << IGisLine::subpt_t(QPointF(subpt.lon * DEG_TO_RAD, subpt.lat * DEG_TO_RAD)); } } } void CGisItemRte::calc() { QMutexLocker lock(&mutexItems); mouseMoveFocus = 0; for(int i = 0; i < rte.pts.size(); i++) { rte.pts[i].subpts.clear(); } CRouterSetup::self().calcRoute(getKey()); } void CGisItemRte::reset() { QMutexLocker lock(&mutexItems); for(int i = 0; i < rte.pts.size(); i++) { rtept_t& pt = rte.pts[i]; pt.subpts.clear(); pt.fakeSubpt = subpt_t(); } mouseMoveFocus = 0; totalDistance = NOFLOAT; totalTime = 0; lastRoutedTime = QDateTime(); lastRoutedWith = ""; deriveSecondaryData(); updateHistory(); if(key == keyUserFocus) { gainUserFocus(false); } } QPointF CGisItemRte::setMouseFocusByPoint(const QPoint& pt, focusmode_e fmode, const QString &owner) { QMutexLocker lock(&mutexItems); const subpt_t * newPointOfFocus = 0; quint32 idx = 0; if(pt != NOPOINT && GPS_Math_DistPointPolyline(line, pt) < MIN_DIST_FOCUS) { quint32 i = 0; qint32 d1 = NOINT; foreach(const QPointF &point, line) { int tmp = (pt - point).manhattanLength(); if(tmp <= d1) { idx = i; d1 = tmp; } i++; } newPointOfFocus = getSubPtByIndex(idx); } if(newPointOfFocus && (newPointOfFocus->type == subpt_t::eTypeNone)) { newPointOfFocus = 0; } mouseMoveFocus = newPointOfFocus; return newPointOfFocus ? ((int)idx < line.size() ? line[idx] : NOPOINTF) : NOPOINTF; } const CGisItemRte::subpt_t * CGisItemRte::getSubPtByIndex(quint32 idx) { quint32 cnt = 0; foreach(const rtept_t &rtept, rte.pts) { if(cnt == idx) { return &rtept.fakeSubpt; } foreach(const subpt_t &subpt, rtept.subpts) { cnt++; if(cnt == idx) { return &subpt; } } cnt++; } return 0; } void CGisItemRte::setResult(Routino_Output * route, const QString& options) { QMutexLocker lock(&mutexItems); qint32 idxRtept = -1; rtept_t * rtept = 0; Routino_Output * next = route; while(next) { if(next->type == ROUTINO_POINT_WAYPOINT) { idxRtept++; rtept = &rte.pts[idxRtept]; rtept->subpts.clear(); rtept->fakeSubpt.lon = next->lon * RAD_TO_DEG; rtept->fakeSubpt.lat = next->lat * RAD_TO_DEG; rtept->fakeSubpt.turn = next->turn; rtept->fakeSubpt.bearing = next->bearing; rtept->fakeSubpt.distance = next->dist * 1000; rtept->fakeSubpt.time = next->time * 60; rtept->fakeSubpt.type = subpt_t::eTypeWpt; rtept->fakeSubpt.instruction = QString(next->desc1) + ".\n" + QString(next->desc2) + "."; } else if(rtept != 0) { rtept->subpts << subpt_t(); subpt_t& subpt = rtept->subpts.last(); subpt.lon = next->lon * RAD_TO_DEG; subpt.lat = next->lat * RAD_TO_DEG; subpt.turn = next->turn; subpt.bearing = next->bearing; subpt.distance = next->dist * 1000; subpt.time = next->time * 60; if(next->name != 0) { subpt.streets << next->name; } if(next->type > ROUTINO_POINT_CHANGE) { subpt.type = subpt_t::eTypeJunct; } else { subpt.type = subpt_t::eTypeNone; } totalDistance = subpt.distance; totalTime = subpt.time; subpt.instruction = QString(next->desc1) + ".\n" + QString(next->desc2) + "."; } next = next->next; } lastRoutedTime = QDateTime::currentDateTimeUtc(); lastRoutedWith = "Routino, " + options; deriveSecondaryData(); updateHistory(); } struct maneuver_t { QStringList streets; QString instruction; quint32 time; qreal dist; qint32 bearing; qint32 turn; }; static const qint32 idx2bearing[] = { NOINT , 0 , -45 , 45 , 180 , 135 , -135 , -90 , 90 }; void CGisItemRte::setResult(const QDomDocument& xml, const QString &options) { QMutexLocker lock(&mutexItems); QDomElement response = xml.firstChildElement("response"); QDomElement route = response.firstChildElement("route"); // get time of travel QDomElement xmlTime = route.firstChildElement("time"); totalTime = xmlTime.text().toUInt(); // build list of maneuvers QDomNodeList xmlLegs = route.firstChildElement("legs").elementsByTagName("leg"); const qint32 L = xmlLegs.size(); QList maneuvers; for(int l = 0; l < L; l++) { QDomNode xmlLeg = xmlLegs.item(l); QDomNodeList xmlManeuvers = xmlLeg.firstChildElement("maneuvers").elementsByTagName("maneuver"); const qint32 M = xmlManeuvers.size(); for(int m = 0; m < M; m++) { maneuvers << maneuver_t(); maneuver_t& maneuver = maneuvers.last(); QDomNode xmlManeuver = xmlManeuvers.item(m); maneuver.instruction = xmlManeuver.firstChildElement("narrative").text(); maneuver.time = xmlManeuver.firstChildElement("time").text().toUInt(); maneuver.dist = xmlManeuver.firstChildElement("distance").text().toFloat(); maneuver.bearing = idx2bearing[xmlManeuver.firstChildElement("direction").text().toUInt()]; maneuver.turn = xmlManeuver.firstChildElement("turnType").text().toInt(); QDomNodeList xmlStreets = xmlManeuver.toElement().elementsByTagName("streets"); const int S = xmlStreets.size(); for(int s = 0; s < S; s++) { QDomNode xmlStreet = xmlStreets.item(s); maneuver.streets << xmlStreet.toElement().text(); } } } QVector shape; // read the shape QDomElement xmlShape = route.firstChildElement("shape"); QDomElement xmlShapePoints = xmlShape.firstChildElement("shapePoints"); QDomNodeList xmlLatLng = xmlShapePoints.elementsByTagName("latLng"); const qint32 N = xmlLatLng.size(); for(int n = 0; n < N; n++) { QDomNode elem = xmlLatLng.item(n); QDomElement lat = elem.firstChildElement("lat"); QDomElement lng = elem.firstChildElement("lng"); shape << subpt_t(); subpt_t& subpt = shape.last(); subpt.lon = lng.text().toFloat(); subpt.lat = lat.text().toFloat(); } QVector idxLegs; QDomElement xmlLegIndexes = xmlShape.firstChildElement("legIndexes"); QDomNodeList xmlIndex = xmlLegIndexes.elementsByTagName("index"); const qint32 I = xmlIndex.size(); for(int i = 0; i < I; i++) { QDomNode elem = xmlIndex.item(i); idxLegs << elem.toElement().text().toUInt(); } quint32 time = 0; quint32 dist = 0; QDomElement xmlManeuverIndexes = xmlShape.firstChildElement("maneuverIndexes"); xmlIndex = xmlManeuverIndexes.elementsByTagName("index"); qint32 M = xmlIndex.size(); for(int m = 0; m < M; m++) { QDomNode elem = xmlIndex.item(m); quint32 idx = elem.toElement().text().toUInt(); subpt_t& subpt = shape[idx]; maneuver_t& maneuver = maneuvers[m]; subpt.type = subpt_t::eTypeJunct; subpt.instruction = maneuver.instruction; subpt.time = time; time += maneuver.time; subpt.distance = dist; dist += maneuver.dist * 1000; subpt.bearing = maneuver.bearing; subpt.turn = maneuver.turn; subpt.streets = maneuver.streets; } for(int i = 0; i < rte.pts.size() - 1; i++ ) { quint32 idx1 = idxLegs[i]; quint32 idx2 = idxLegs[i+1]; rtept_t& rtept = rte.pts[i]; rtept.subpts = shape.mid(idx1, idx2 - idx1 + 1); rtept.fakeSubpt.lon = rtept.lon; rtept.fakeSubpt.lat = rtept.lat; } rtept_t& rtept = rte.pts.last(); rtept.fakeSubpt.lon = rtept.lon; rtept.fakeSubpt.lat = rtept.lat; totalDistance = dist; lastRoutedTime = QDateTime::currentDateTimeUtc(); lastRoutedWith = "MapQuest" + options; deriveSecondaryData(); updateHistory(); } qmapshack-1.5.1/src/gis/rte/router/CRouterMapQuest.h000644 001750 000144 00000003432 12622435723 023366 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CROUTERMAPQUEST_H #define CROUTERMAPQUEST_H #include "gis/rte/router/IRouter.h" #include "ui_IRouterMapQuest.h" class QNetworkAccessManager; class CGisItemRte; class QNetworkReply; class QTimer; class CRouterMapQuest : public IRouter, private Ui::IRouterMapQuest { Q_OBJECT public: CRouterMapQuest(QWidget * parent); virtual ~CRouterMapQuest(); void calcRoute(const IGisItem::key_t& key); int calcRoute(const QPointF& p1, const QPointF& p2, QPolygonF& coords) { return -1; } QString getOptions(); private slots: void slotRequestFinished(QNetworkReply* reply); void slotCloseStatusMsg(); private: void addMapQuestLocations(QDomDocument& xml, QDomElement& locations, CGisItemRte& rte); static const QByteArray keyMapQuest; QNetworkAccessManager * networkAccessManager; QTimer * timerCloseStatusMsg; }; #endif //CROUTERMAPQUEST_H qmapshack-1.5.1/src/gis/rte/router/IRouterRoutinoPathSetup.ui000644 001750 000144 00000010275 12531331712 025313 0ustar00oeichlerusers000000 000000 IRouterRoutinoPathSetup 0 0 450 200 Setup Routino database... ... :/icons/32x32/Add.png:/icons/32x32/Add.png 22 22 false ... :/icons/32x32/DeleteMultiple.png:/icons/32x32/DeleteMultiple.png 22 22 Qt::Vertical 20 40 0 0 :/icons/48x48/Help.png - Qt::AlignJustify|Qt::AlignTop true Qt::Vertical QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() IRouterRoutinoPathSetup accept() 248 254 157 274 buttonBox rejected() IRouterRoutinoPathSetup reject() 316 260 286 274 qmapshack-1.5.1/src/gis/rte/router/CRouterMapQuest.cpp000644 001750 000144 00000027351 12622435720 023724 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/rte/CGisItemRte.h" #include "gis/rte/router/CRouterMapQuest.h" #include "helpers/CSettings.h" #include #include #include const QByteArray CRouterMapQuest::keyMapQuest = "Fmjtd%7Cluu2n16t2h%2Crw%3Do5-haya0"; CRouterMapQuest::CRouterMapQuest(QWidget *parent) : IRouter(false, parent) { setupUi(this); comboMQPreference->addItem(tr("Fastest"), "fastest"); comboMQPreference->addItem(tr("Shortest"), "shortest"); comboMQPreference->addItem(tr("Bicycle"), "bicycle"); comboMQPreference->addItem(tr("Pedestrian"), "pedestrian"); comboMQLanguage->addItem(tr("US English"), "en_US"); comboMQLanguage->addItem(tr("British English"), "en_GB"); comboMQLanguage->addItem(tr("Danish"), "da_DK"); comboMQLanguage->addItem(tr("Dutch"), "nl_NL"); comboMQLanguage->addItem(tr("French"), "fr_FR"); comboMQLanguage->addItem(tr("German"), "de_DE"); comboMQLanguage->addItem(tr("Italian"), "it_IT"); comboMQLanguage->addItem(tr("Norwegian"), "no_NO"); comboMQLanguage->addItem(tr("Spanish"), "es_ES"); comboMQLanguage->addItem(tr("Swedish"), "sv_SE"); SETTINGS; int langIdx; QString locale = QLocale::system().name(); cfg.beginGroup("Route/mapquest"); langIdx = comboMQLanguage->findData(locale); comboMQPreference->setCurrentIndex(cfg.value("preference", 0).toInt()); checkMQAvoidLimAccess->setChecked(cfg.value("avoidLimAccess", false).toBool()); checkMQAvoidTollRoads->setChecked(cfg.value("avoidTollRoads", false).toBool()); checkMQAvoidSeasonal->setChecked(cfg.value("avoidSeasonal", false).toBool()); checkMQAvoidUnpaved->setChecked(cfg.value("avoidUnpaved", false).toBool()); checkMQAvoidFerry->setChecked(cfg.value("avoidFerry", false).toBool()); checkMQAvoidCountryBorder->setChecked(cfg.value("avoidCountryBorder", false).toBool()); comboMQLanguage->setCurrentIndex(cfg.value("language", langIdx).toInt()); cfg.endGroup(); networkAccessManager = new QNetworkAccessManager(this); connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotRequestFinished(QNetworkReply*))); timerCloseStatusMsg = new QTimer(this); timerCloseStatusMsg->setSingleShot(true); timerCloseStatusMsg->setInterval(5000); connect(timerCloseStatusMsg, SIGNAL(timeout()), this, SLOT(slotCloseStatusMsg())); } CRouterMapQuest::~CRouterMapQuest() { SETTINGS; cfg.beginGroup("Route/mapquest"); cfg.setValue("preference", comboMQPreference->currentIndex()); cfg.setValue("avoidLimAccess", checkMQAvoidLimAccess->isChecked()); cfg.setValue("avoidTollRoads", checkMQAvoidTollRoads->isChecked()); cfg.setValue("avoidSeasonal", checkMQAvoidSeasonal->isChecked()); cfg.setValue("avoidUnpaved", checkMQAvoidUnpaved->isChecked()); cfg.setValue("avoidFerry", checkMQAvoidFerry->isChecked()); cfg.setValue("avoidCountryBorder", checkMQAvoidCountryBorder->isChecked()); cfg.setValue("language", comboMQLanguage->currentIndex()); cfg.endGroup(); } void CRouterMapQuest::slotCloseStatusMsg() { timerCloseStatusMsg->stop(); CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); canvas->reportStatus("MapQuest", ""); } } QString CRouterMapQuest::getOptions() { QString str; int times = 1; str += ", " + tr("mode \"%1\"").arg(comboMQPreference->currentText()); if(checkMQAvoidLimAccess->isChecked()) { str += ", " + tr("no highways"); } if(str.size() > 40 * times) { str += "
"; times++; } if(checkMQAvoidTollRoads->isChecked()) { str += ", " + tr("no toll roads"); } if(str.size() > 40 * times) { str += "
"; times++; } if(checkMQAvoidSeasonal->isChecked()) { str += ", " + tr("no seasonal"); } if(str.size() > 40 * times) { str += "
"; times++; } if(checkMQAvoidUnpaved->isChecked()) { str += ", " + tr("no unpaved"); } if(str.size() > 40 * times) { str += "
"; times++; } if(checkMQAvoidFerry->isChecked()) { str += ", " + tr("no ferry"); } if(str.size() > 40 * times) { str += "
"; times++; } if(checkMQAvoidCountryBorder->isChecked()) { str += ", " + tr("no crossing of country borders"); } return str; } void CRouterMapQuest::addMapQuestLocations(QDomDocument& xml, QDomElement& locations, CGisItemRte &rte) { SGisLine line; rte.getPolylineFromData(line); foreach(const IGisLine::point_t &pt, line) { QDomElement location = xml.createElement("location"); location.appendChild(xml.createTextNode(QString("%1,%2").arg(pt.coord.y()*RAD_TO_DEG).arg(pt.coord.x()*RAD_TO_DEG))); locations.appendChild(location); } } void CRouterMapQuest::calcRoute(const IGisItem::key_t& key) { CGisItemRte * rte = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(rte == 0) { return; } rte->reset(); slotCloseStatusMsg(); QDomDocument xml; QDomElement route = xml.createElement("route"); xml.appendChild(route); QDomElement locations = xml.createElement("locations"); route.appendChild(locations); addMapQuestLocations(xml, locations, *rte); QDomElement options = xml.createElement("options"); route.appendChild(options); QDomElement shapeFormat = xml.createElement("shapeFormat"); shapeFormat.appendChild(xml.createTextNode("raw")); options.appendChild(shapeFormat); QDomElement generalize = xml.createElement("generalize"); generalize.appendChild(xml.createTextNode("0")); options.appendChild(generalize); QDomElement unit = xml.createElement("unit"); unit.appendChild(xml.createTextNode("k")); options.appendChild(unit); QDomElement routeType = xml.createElement("routeType"); routeType.appendChild(xml.createTextNode(comboMQPreference->itemData(comboMQPreference->currentIndex()).toString())); options.appendChild(routeType); QDomElement locale = xml.createElement("locale"); locale.appendChild(xml.createTextNode(comboMQLanguage->itemData(comboMQLanguage->currentIndex()).toString())); options.appendChild(locale); QDomElement avoids = xml.createElement("avoids"); if(checkMQAvoidLimAccess->isChecked()) { QDomElement avoid = xml.createElement("avoid"); avoid.appendChild(xml.createTextNode("Limited Access")); avoids.appendChild(avoid); } if(checkMQAvoidTollRoads->isChecked()) { QDomElement avoid = xml.createElement("avoid"); avoid.appendChild(xml.createTextNode("Toll road")); avoids.appendChild(avoid); } if(checkMQAvoidSeasonal->isChecked()) { QDomElement avoid = xml.createElement("avoid"); avoid.appendChild(xml.createTextNode("Approximate Seasonal Closure")); avoids.appendChild(avoid); } if(checkMQAvoidUnpaved->isChecked()) { QDomElement avoid = xml.createElement("avoid"); avoid.appendChild(xml.createTextNode("Unpaved")); avoids.appendChild(avoid); } if(checkMQAvoidFerry->isChecked()) { QDomElement avoid = xml.createElement("avoid"); avoid.appendChild(xml.createTextNode("Ferry")); avoids.appendChild(avoid); } if(checkMQAvoidCountryBorder->isChecked()) { QDomElement avoid = xml.createElement("avoid"); avoid.appendChild(xml.createTextNode("Country border crossing")); avoids.appendChild(avoid); } options.appendChild(avoids); QString xmlstr = xml.toString(0); qDebug() << xmlstr; xmlstr = xmlstr.replace("\n",""); QUrl url("http://open.mapquestapi.com"); url.setPath("/directions/v2/route"); QUrlQuery urlQuery; urlQuery.addQueryItem("key", keyMapQuest); urlQuery.addQueryItem("ambiguities", "ignore"); urlQuery.addQueryItem("inFormat", "xml"); urlQuery.addQueryItem("outFormat", "xml"); urlQuery.addQueryItem("xml", QUrl::toPercentEncoding(xmlstr)); url.setQuery(urlQuery); QNetworkRequest request; request.setUrl(url); QNetworkReply* reply = networkAccessManager->get(request); reply->setProperty("key.item", key.item); reply->setProperty("key.project", key.project); reply->setProperty("key.device", key.device); reply->setProperty("options", getOptions()); reply->setProperty("time", QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()); CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); canvas->reportStatus("MapQuest", tr("MapQuest
Routing request sent to server. Please wait...")); } } void CRouterMapQuest::slotRequestFinished(QNetworkReply* reply) { if(reply->error() != QNetworkReply::NoError) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->reportStatus("MapQuest", tr("MapQuest
Bad response from server:
%1").arg(reply->errorString())); timerCloseStatusMsg->start(); } reply->deleteLater(); return; } QByteArray res = reply->readAll(); reply->deleteLater(); if(res.isEmpty()) { return; } QDomDocument xml; xml.setContent(res); QFile f("test.xml"); f.open(QIODevice::WriteOnly); f.write(xml.toString().toUtf8()); f.close(); QDomElement response = xml.firstChildElement("response"); QDomElement info = response.firstChildElement("info"); QDomElement statusCode = info.firstChildElement("statusCode"); if(statusCode.isNull() || statusCode.text().toInt() != 0) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { QDomElement messages = info.firstChildElement("messages"); QDomElement message = messages.firstChildElement("message"); canvas->reportStatus("MapQuest", tr("MapQuest
Bad response from server:
%1").arg(message.text())); timerCloseStatusMsg->start(); } return; } IGisItem::key_t key; key.item = reply->property("key.item").toString(); key.project = reply->property("key.project").toString(); key.device = reply->property("key.device").toString(); qint64 time = reply->property("time").toLongLong(); time = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch() - time; CGisItemRte * rte = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(rte != 0) { rte->setResult(xml, reply->property("options").toString() + tr("
Calculation time: %1s").arg(time/1000.0, 0,'f',2)); } slotCloseStatusMsg(); } qmapshack-1.5.1/src/gis/rte/router/CRouterSetup.cpp000644 001750 000144 00000005012 12622435720 023253 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisWidget.h" #include "gis/rte/CGisItemRte.h" #include "gis/rte/router/CRouterMapQuest.h" #include "gis/rte/router/CRouterRoutino.h" #include "gis/rte/router/CRouterSetup.h" #include "helpers/CSettings.h" #include CRouterSetup * CRouterSetup::pSelf = 0; CRouterSetup::CRouterSetup(QWidget * parent) : QWidget(parent) { setupUi(this); pSelf = this; comboRouter->addItem(tr("Routino (offline)")); comboRouter->addItem(tr("MapQuest (online)")); stackedWidget->addWidget(new CRouterRoutino(this)); stackedWidget->addWidget(new CRouterMapQuest(this)); connect(comboRouter, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSelectRouter(int))); SETTINGS; comboRouter->setCurrentIndex(cfg.value("Route/current",0).toInt()); } CRouterSetup::~CRouterSetup() { SETTINGS; cfg.setValue("Route/current", comboRouter->currentIndex()); } bool CRouterSetup::hasFastRouting() { IRouter * router = dynamic_cast(stackedWidget->currentWidget()); if(router) { return router->hasFastRouting(); } return false; } void CRouterSetup::slotSelectRouter(int i) { stackedWidget->setCurrentIndex(i); } void CRouterSetup::calcRoute(const IGisItem::key_t& key) { IRouter * router = dynamic_cast(stackedWidget->currentWidget()); if(router) { router->calcRoute(key); } } int CRouterSetup::calcRoute(const QPointF& p1, const QPointF& p2, QPolygonF& coords) { IRouter * router = dynamic_cast(stackedWidget->currentWidget()); if(router) { return router->calcRoute(p1, p2, coords); } return false; } qmapshack-1.5.1/src/gis/rte/router/IRouter.cpp000644 001750 000144 00000002067 12622435720 022247 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/rte/router/IRouter.h" IRouter::IRouter(bool fastRouting, QWidget *parent) : QWidget(parent) , fastRouting(fastRouting) { } IRouter::~IRouter() { } qmapshack-1.5.1/src/gis/rte/router/CRouterRoutino.cpp000644 001750 000144 00000034321 12622435720 023617 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/rte/CGisItemRte.h" #include "gis/rte/router/CRouterRoutino.h" #include "gis/rte/router/CRouterRoutinoPathSetup.h" #include "helpers/CAppSetup.h" #include "helpers/CProgressDialog.h" #include "helpers/CSettings.h" #include #include #include QPointer CRouterRoutino::progress; int ProgressFunc(double complete) { if(CRouterRoutino::progress.isNull()) { return true; } CRouterRoutino::progress->setValue(complete*100); return !CRouterRoutino::progress->wasCanceled(); } CRouterRoutino::CRouterRoutino(QWidget *parent) : IRouter(true, parent) { setupUi(this); if(Routino_CheckAPIVersion() != ROUTINO_ERROR_NONE) { QMessageBox::warning(this, tr("Warning..."), tr("Found Routino with a wrong version. Expected %1 found %2").arg(ROUTINO_API_VERSION).arg(Routino_APIVersion), QMessageBox::Ok); return; } comboMode->addItem(tr("Shortest")); comboMode->addItem(tr("Quickest")); int res = 0; CAppSetup *setup = CAppSetup::getPlattformInstance(); res = Routino_ParseXMLProfiles(setup->routinoPath("profiles.xml").toUtf8()); if(res) { QMessageBox::critical(this, "Routino...", xlateRoutinoError(Routino_errno), QMessageBox::Abort); return; } res = Routino_ParseXMLTranslations(setup->routinoPath("translations.xml").toUtf8()); if(res) { QMessageBox::critical(this, "Routino...", xlateRoutinoError(Routino_errno), QMessageBox::Abort); return; } comboProfile->addItem(tr("Foot"), "foot"); comboProfile->addItem(tr("Horse"), "horse"); comboProfile->addItem(tr("Wheelchair"), "wheelchair"); comboProfile->addItem(tr("Bicycle"), "bicycle"); comboProfile->addItem(tr("Moped"), "moped"); comboProfile->addItem(tr("Motorcycle"), "motorcycle"); comboProfile->addItem(tr("Motorcar"), "motorcar"); comboProfile->addItem(tr("Goods"), "goods"); comboLanguage->addItem(tr("English"), "en"); comboLanguage->addItem(tr("German"), "de"); comboLanguage->addItem(tr("French"), "fr"); comboLanguage->addItem(tr("Hungarian"), "hu"); comboLanguage->addItem(tr("Dutch"), "nl"); comboLanguage->addItem(tr("Russian"), "ru"); comboLanguage->addItem(tr("Polish"), "pl"); connect(toolSetupPaths, SIGNAL(clicked()), this, SLOT(slotSetupPaths())); SETTINGS; dbPaths = cfg.value("Route/routino/paths", dbPaths).toStringList(); buildDatabaseList(); comboProfile->setCurrentIndex(cfg.value("Route/routino/profile",0).toInt()); comboLanguage->setCurrentIndex(cfg.value("Route/routino/language",0).toInt()); comboMode->setCurrentIndex(cfg.value("Route/routino/mode",0).toInt()); comboDatabase->setCurrentIndex(cfg.value("Route/routino/database",0).toInt()); updateHelpText(); } CRouterRoutino::~CRouterRoutino() { SETTINGS; cfg.setValue("Route/routino/paths", dbPaths); cfg.setValue("Route/routino/profile", comboProfile->currentIndex()); cfg.setValue("Route/routino/language", comboLanguage->currentIndex()); cfg.setValue("Route/routino/mode", comboMode->currentIndex()); cfg.setValue("Route/routino/database", comboDatabase->currentIndex()); freeDatabaseList(); Routino_FreeXMLProfiles(); Routino_FreeXMLTranslations(); } QString CRouterRoutino::xlateRoutinoError(int err) { switch(err) { case ROUTINO_ERROR_NO_DATABASE: return tr("A function was called without the database variable set."); case ROUTINO_ERROR_NO_PROFILE: return tr("A function was called without the profile variable set."); case ROUTINO_ERROR_NO_TRANSLATION: return tr("A function was called without the translation variable set."); case ROUTINO_ERROR_NO_DATABASE_FILES: return tr("The specified database to load did not exist."); case ROUTINO_ERROR_BAD_DATABASE_FILES: return tr("The specified database could not be loaded."); case ROUTINO_ERROR_NO_PROFILES_XML: return tr("The specified profiles XML file did not exist."); case ROUTINO_ERROR_BAD_PROFILES_XML: return tr("The specified profiles XML file could not be loaded."); case ROUTINO_ERROR_NO_TRANSLATIONS_XML: return tr("The specified translations XML file did not exist."); case ROUTINO_ERROR_BAD_TRANSLATIONS_XML: return tr("The specified translations XML file could not be loaded."); case ROUTINO_ERROR_NO_SUCH_PROFILE: return tr("The requested profile name does not exist in the loaded XML file."); case ROUTINO_ERROR_NO_SUCH_TRANSLATION: return tr("The requested translation language does not exist in the loaded XML file."); case ROUTINO_ERROR_NO_NEARBY_HIGHWAY: return tr("There is no highway near the coordinates to place a waypoint."); case ROUTINO_ERROR_PROFILE_DATABASE_ERR: return tr("The profile and database do not work together."); case ROUTINO_ERROR_NOTVALID_PROFILE: return tr("The profile being used has not been validated."); case ROUTINO_ERROR_BAD_USER_PROFILE: return tr("The user specified profile contained invalid data."); case ROUTINO_ERROR_BAD_OPTIONS: return tr("The routing options specified are not consistent with each other."); case ROUTINO_ERROR_WRONG_API_VERSION: return tr("There is a mismatch between the library and caller API version."); case ROUTINO_ERROR_PROGRESS_ABORTED: return tr("Route calculation was aborted by user."); } if(ROUTINO_ERROR_NO_ROUTE_1 <= err) { int n = err - 1000; return tr("A route could not be found to waypoint %1.").arg(n); } return tr("Unknown error: %1").arg(err); } bool CRouterRoutino::hasFastRouting() { return IRouter::hasFastRouting() && (comboDatabase->count() != 0); } QString CRouterRoutino::getOptions() { QString str; str = tr("profile \"%1\"").arg(comboProfile->currentText()); str += tr(", mode \"%1\"").arg(comboMode->currentText()); return str; } void CRouterRoutino::slotSetupPaths() { CRouterRoutinoPathSetup dlg(dbPaths); dlg.exec(); buildDatabaseList(); updateHelpText(); } void CRouterRoutino::buildDatabaseList() { QRegExp re("(.*)-segments.mem"); freeDatabaseList(); foreach(const QString &path, dbPaths) { QDir dir(path); foreach(const QString &filename, dir.entryList(QStringList("*segments.mem"), QDir::Files|QDir::Readable, QDir::Name)) { QString prefix; if(re.exactMatch(filename)) { prefix = re.cap(1); } else { continue; } #ifdef OS_WIN QFileInfo fi(dir.absoluteFilePath(filename)); if(fi.size() > 0x0FFFFFFFFLL) { QMessageBox::warning(this, tr("Warning..."), tr("%1: Due to limitations in the Windows POSIX API Routino can't handle files larger than 4GB.").arg(prefix), QMessageBox::Ok); continue; } #endif Routino_Database * data = Routino_LoadDatabase(dir.absolutePath().toUtf8(), prefix.toUtf8()); if(data) { comboDatabase->addItem(prefix.replace("_", " "), quint64(data)); } else { QMessageBox::critical(this, "Routino...", xlateRoutinoError(Routino_errno), QMessageBox::Abort); } } } } void CRouterRoutino::freeDatabaseList() { for(int i = 0; i < comboDatabase->count(); i++) { Routino_Database * data = (Routino_Database*)comboDatabase->itemData(i, Qt::UserRole).toULongLong(); Routino_UnloadDatabase(data); } comboDatabase->clear(); } void CRouterRoutino::updateHelpText() { if(comboDatabase->count() != 0) { frameHelp->hide(); comboDatabase->setEnabled(true); } else { frameHelp->show(); comboDatabase->setEnabled(false); } } void CRouterRoutino::calcRoute(const IGisItem::key_t& key) { if(!mutex.tryLock()) { return; } try { QTime time; time.start(); CGisItemRte * rte = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(rte == 0) { throw QString(); } Routino_Database * data = (Routino_Database*)comboDatabase->currentData(Qt::UserRole).toULongLong(); if(data == 0) { throw QString(); } rte->reset(); QString strProfile = comboProfile->currentData(Qt::UserRole).toString(); QString strLanguage = comboLanguage->currentData(Qt::UserRole).toString(); Routino_Profile *profile = Routino_GetProfile(strProfile.toUtf8()); Routino_Translation *translation = Routino_GetTranslation(strLanguage.toUtf8()); int res = Routino_ValidateProfile(data,profile); if(res != 0) { throw xlateRoutinoError(Routino_errno); } int options = ROUTINO_ROUTE_LIST_HTML_ALL; if(comboMode->currentIndex() == 0) { options |= ROUTINO_ROUTE_SHORTEST; } if(comboMode->currentIndex() == 1) { options |= ROUTINO_ROUTE_QUICKEST; } SGisLine line; rte->getPolylineFromData(line); int idx = 0; QVector waypoints(line.size(), 0); foreach(const IGisLine::point_t &pt, line) { waypoints[idx] = Routino_FindWaypoint(data, profile, pt.coord.y()*RAD_TO_DEG, pt.coord.x()*RAD_TO_DEG); if(waypoints[idx] == NULL) { throw xlateRoutinoError(Routino_errno); } idx++; } progress = new CProgressDialog(tr("Calculate route with %1").arg(getOptions()), 0, NOINT, this); Routino_Output * route = Routino_CalculateRoute(data,profile,translation,waypoints.data(),waypoints.size(),options, ProgressFunc); delete progress; if(route != NULL) { rte->setResult(route, getOptions() + tr("
Calculation time: %1s").arg(time.elapsed()/1000.0, 0,'f',2)); Routino_DeleteRoute(route); } else { if(Routino_errno != ROUTINO_ERROR_PROGRESS_ABORTED) { throw xlateRoutinoError(Routino_errno); } } } catch(const QString& msg) { if(!msg.isEmpty()) { QMessageBox::critical(this, "Routino...", msg, QMessageBox::Abort); } } mutex.unlock(); CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } } int CRouterRoutino::calcRoute(const QPointF& p1, const QPointF& p2, QPolygonF& coords) { if(!mutex.tryLock()) { return -1; } try { Routino_Database * data = (Routino_Database*)comboDatabase->currentData(Qt::UserRole).toULongLong(); if(data == 0) { throw QString(); } QString strProfile = comboProfile->currentData(Qt::UserRole).toString(); QString strLanguage = comboLanguage->currentData(Qt::UserRole).toString(); Routino_Profile *profile = Routino_GetProfile(strProfile.toUtf8()); Routino_Translation *translation = Routino_GetTranslation(strLanguage.toUtf8()); int res = Routino_ValidateProfile(data,profile); if(res != 0) { throw xlateRoutinoError(Routino_errno); } int options = ROUTINO_ROUTE_LIST_HTML_ALL; if(comboMode->currentIndex() == 0) { options |= ROUTINO_ROUTE_SHORTEST; } if(comboMode->currentIndex() == 1) { options |= ROUTINO_ROUTE_QUICKEST; } Routino_Waypoint* waypoints[2] = {0}; waypoints[0] = Routino_FindWaypoint(data, profile, p1.y()*RAD_TO_DEG, p1.x()*RAD_TO_DEG); if(waypoints[0] == NULL) { throw xlateRoutinoError(Routino_errno); } waypoints[1] = Routino_FindWaypoint(data, profile, p2.y()*RAD_TO_DEG, p2.x()*RAD_TO_DEG); if(waypoints[1] == NULL) { throw xlateRoutinoError(Routino_errno); } progress = new CProgressDialog(tr("Calculate route with %1").arg(getOptions()), 0, NOINT, this); Routino_Output * route = Routino_CalculateRoute(data,profile,translation,waypoints,2,options, ProgressFunc); delete progress; if(route != NULL) { Routino_Output * next = route; while(next) { if(next->type != ROUTINO_POINT_WAYPOINT) { coords << QPointF(next->lon, next->lat); } next = next->next; } Routino_DeleteRoute(route); } else { if(Routino_errno != ROUTINO_ERROR_PROGRESS_ABORTED) { throw xlateRoutinoError(Routino_errno); } else { throw QString(); } } } catch(const QString& msg) { if(!msg.isEmpty()) { QMessageBox::critical(this, "Routino...", msg, QMessageBox::Abort); } coords.clear(); } mutex.unlock(); return coords.size(); } qmapshack-1.5.1/src/gis/rte/router/IRouter.h000644 001750 000144 00000002633 12622435723 021716 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IROUTER_H #define IROUTER_H #include "gis/IGisItem.h" #include class IRouter : public QWidget { Q_OBJECT public: IRouter(bool fastRouting, QWidget * parent); virtual ~IRouter(); virtual void calcRoute(const IGisItem::key_t& key) = 0; virtual int calcRoute(const QPointF& p1, const QPointF& p2, QPolygonF& coords) = 0; virtual bool hasFastRouting() { return fastRouting; } virtual QString getOptions() = 0; private: bool fastRouting; }; #endif //IROUTER_H qmapshack-1.5.1/src/gis/rte/router/IRouterSetup.ui000644 001750 000144 00000002131 12570062516 023114 0ustar00oeichlerusers000000 000000 IRouterSetup 0 0 302 382 Form 3 3 3 3 3 Qt::Horizontal qmapshack-1.5.1/src/gis/rte/router/CRouterSetup.h000644 001750 000144 00000003023 12622435723 022723 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CROUTERSETUP_H #define CROUTERSETUP_H #include "gis/IGisItem.h" #include "ui_IRouterSetup.h" #include class CRouterSetup : public QWidget, private Ui::IRouterSetup { Q_OBJECT public: static CRouterSetup& self() { return *pSelf; } virtual ~CRouterSetup(); void calcRoute(const IGisItem::key_t &key); int calcRoute(const QPointF& p1, const QPointF& p2, QPolygonF& coords); bool hasFastRouting(); private slots: void slotSelectRouter(int i); private: friend class Ui_IMainWindow; CRouterSetup(QWidget * parent); static CRouterSetup * pSelf; }; #endif //CROUTERSETUP_H qmapshack-1.5.1/src/gis/rte/router/CRouterRoutinoPathSetup.cpp000644 001750 000144 00000005060 12622435720 025453 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/rte/router/CRouterRoutinoPathSetup.h" #include CRouterRoutinoPathSetup::CRouterRoutinoPathSetup(QStringList &paths) : QDialog(CMainWindow::getBestWidgetForParent()) , paths(paths) { setupUi(this); connect(toolAdd, SIGNAL(clicked()), this, SLOT(slotAddPath())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelPath())); connect(listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged())); foreach(const QString &path, paths) { QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(path); } labelHelp->setText(tr("Add or remove paths containing Routino data. There can be multiple databases in a path but no sub-path is parsed.")); } CRouterRoutinoPathSetup::~CRouterRoutinoPathSetup() { } void CRouterRoutinoPathSetup::slotItemSelectionChanged() { QList items = listWidget->selectedItems(); toolDelete->setEnabled(!items.isEmpty()); } void CRouterRoutinoPathSetup::slotAddPath() { QString path = QFileDialog::getExistingDirectory(this, tr("Select routing data file path..."), QDir::homePath(), 0); if(!path.isEmpty()) { QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(path); } } void CRouterRoutinoPathSetup::slotDelPath() { QList items = listWidget->selectedItems(); qDeleteAll(items); } void CRouterRoutinoPathSetup::accept() { paths.clear(); for(int i = 0; i < listWidget->count(); i++) { QListWidgetItem *item = listWidget->item(i); paths << item->text(); } QDialog::accept(); } qmapshack-1.5.1/src/gis/rte/router/IRouterMapQuest.ui000644 001750 000144 00000006217 12570062516 023564 0ustar00oeichlerusers000000 000000 IRouterMapQuest 0 0 400 300 Form Highways Seasonal Language Country Border Profile Avoid: Ferry Toll Road Unpaved Qt::Vertical 20 123 <p>Directions Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> </p> true qmapshack-1.5.1/src/gis/rte/router/CRouterRoutino.h000644 001750 000144 00000003332 12622435723 023265 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CROUTERROUTINO_H #define CROUTERROUTINO_H #include "gis/rte/router/IRouter.h" #include "ui_IRouterRoutino.h" #include #include class CProgressDialog; class CRouterRoutino : public IRouter, private Ui::IRouterRoutino { Q_OBJECT public: CRouterRoutino(QWidget * parent); virtual ~CRouterRoutino(); void calcRoute(const IGisItem::key_t& key); int calcRoute(const QPointF& p1, const QPointF& p2, QPolygonF& coords); bool hasFastRouting(); QString getOptions(); static QPointer progress; private slots: void slotSetupPaths(); private: void buildDatabaseList(); void freeDatabaseList(); void updateHelpText(); QString xlateRoutinoError(int err); QStringList dbPaths; QMutex mutex {QMutex::NonRecursive}; }; #endif //CROUTERROUTINO_H qmapshack-1.5.1/src/gis/rte/router/IRouterRoutino.ui000644 001750 000144 00000010365 12570062516 023463 0ustar00oeichlerusers000000 000000 IRouterRoutino 0 0 400 300 Form Profile Mode Database Add paths with Routino database. ... :/icons/32x32/PathBlue.png:/icons/32x32/PathBlue.png Language Qt::Vertical 20 40 Qt::Horizontal 40 20 QFrame::NoFrame QFrame::Plain 0 0 :/icons/48x48/Help.png To use offline routing you need to define paths to local routing data. Use the setup tool button to register a path. Qt::AlignJustify|Qt::AlignTop true qmapshack-1.5.1/src/gis/rte/router/CRouterRoutinoPathSetup.h000644 001750 000144 00000002647 12622435723 025133 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CROUTERROUTINOPATHSETUP_H #define CROUTERROUTINOPATHSETUP_H #include "ui_IRouterRoutinoPathSetup.h" #include class CRouterRoutinoPathSetup : public QDialog, private Ui::IRouterRoutinoPathSetup { Q_OBJECT public: CRouterRoutinoPathSetup(QStringList& paths); virtual ~CRouterRoutinoPathSetup(); public slots: void accept(); private slots: void slotAddPath(); void slotDelPath(); void slotItemSelectionChanged(); private: QStringList& paths; }; #endif //CROUTERROUTINOPATHSETUP_H qmapshack-1.5.1/src/gis/rte/CCreateRouteFromWpt.h000644 001750 000144 00000002626 12622435723 022653 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCREATEROUTEFROMWPT_H #define CCREATEROUTEFROMWPT_H #include #include "ui_ICreateRouteFromWpt.h" #include class CCreateRouteFromWpt : public QDialog, private Ui::ICreateRouteFromWpt { Q_OBJECT public: CCreateRouteFromWpt(const QList& keys, QWidget * parent); virtual ~CCreateRouteFromWpt(); public slots: void accept(); private slots: void slotSelectionChanged(); void slotUp(); void slotDown(); }; #endif //CCREATEROUTEFROMWPT_H qmapshack-1.5.1/src/gis/rte/CGisItemRte.h000644 001750 000144 00000013463 12624147203 021122 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISITEMRTE_H #define CGISITEMRTE_H #include "gis/IGisItem.h" #include "gis/IGisLine.h" #include #include #include class QDomNode; class IGisProject; class CQlgtRoute; class CScrOptRte; class CGisItemRte : public IGisItem, public IGisLine { public: enum focusmode_e { eFocusMouseMove ,eFocusMouseClick }; struct subpt_t { enum type_e { eTypeNone , eTypeWpt , eTypeJunct }; qreal lon = NOFLOAT; qreal lat = NOFLOAT; quint8 type = eTypeNone; qreal turn = NOFLOAT; qreal bearing = NOFLOAT; QStringList streets; QString instruction; qreal distance = 0; quint32 time = 0; }; struct rtept_t : public wpt_t { rtept_t() { fakeSubpt.type = subpt_t::eTypeWpt; } void updateIcon(); QPixmap icon; QPointF focus; subpt_t fakeSubpt; QVector subpts; }; struct rte_t { // -- all gpx tags - start QString name; QString cmt; QString desc; QString src; QList links; quint64 number = 0; QString type; QVector pts; // -- all gpx tags - stop }; CGisItemRte(const QDomNode &xml, IGisProject *parent); CGisItemRte(const CGisItemRte& parentRte, IGisProject *project, int idx, bool clone); CGisItemRte(const history_t& hist, IGisProject * project); CGisItemRte(quint64 id, QSqlDatabase& db, IGisProject * project); CGisItemRte(const CQlgtRoute& rte1); CGisItemRte(const SGisLine& l, const QString &name, IGisProject *project, int idx); virtual ~CGisItemRte(); QDataStream& operator<<(QDataStream& stream); QDataStream& operator>>(QDataStream& stream) const; const QString& getName() const { return rte.name.isEmpty() ? noName : rte.name; } QString getInfo(bool allowEdit = false) const; IScrOpt * getScreenOptions(const QPoint &origin, IMouse * mouse); QPointF getPointCloseBy(const QPoint& screenPos); void drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis); void drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis); void drawLabel(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis); void drawHighlight(QPainter& p); void save(QDomNode& gpx); bool isCloseTo(const QPointF& pos); /** @brief Switch user focus on and off. If the focus is switched on any other route having the focus will loose it. @param yes set true to gain focus. */ void gainUserFocus(bool yes); /** @brief Make sure the route has lost focus. If the route has the focus, keyUserFocus will be reset. In all other cases nothing will be done. */ void looseUserFocus(); /** @brief Check for user focus @return True if the route has user focus */ bool hasUserFocus() const { return key == keyUserFocus; } /** @brief Get the key of the current track with user focus @return If no route has the focus an empty string is returned */ static const key_t& getKeyUserFocus() { return keyUserFocus; } void setDataFromPolyline(const SGisLine& l); void getPolylineFromData(SGisLine &l); const QString& getComment() const { return rte.cmt; } const QString& getDescription() const { return rte.desc; } const QList& getLinks() const { return rte.links; } const rte_t& getRoute() const { return rte; } void setName(const QString& str); void setComment(const QString& str); void setDescription(const QString& str); void setLinks(const QList& links); void calc(); void reset(); void edit(); QPointF setMouseFocusByPoint(const QPoint& pt, focusmode_e fmode, const QString &owner); void setResult(Routino_Output * route, const QString &options); void setResult(const QDomDocument& xml, const QString &options); bool isCalculated(); private: void deriveSecondaryData(); void setSymbol(); void readRte(const QDomNode& xml, rte_t& rte); void readRouteDataFromGisLine(const SGisLine &l); const subpt_t * getSubPtByIndex(quint32 idx); static key_t keyUserFocus; static const QPen penBackground; QPen penForeground {Qt::darkBlue, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; QPen penForegroundFocus {Qt::magenta, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; rte_t rte; QPolygonF line; QString lastRoutedWith; QDateTime lastRoutedTime; qreal totalDistance = NOFLOAT; quint32 totalTime = 0; const subpt_t * mouseMoveFocus = 0; QPointer scrOpt; }; #endif //CGISITEMRTE_H qmapshack-1.5.1/src/gis/rte/CScrOptRte.h000644 001750 000144 00000003033 12622435723 020770 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTRTE_H #define CSCROPTRTE_H #include "gis/IGisItem.h" #include "mouse/IScrOpt.h" #include "ui_IScrOptRte.h" #include class CGisItemRte; class IMouse; class CScrOptRte : public IScrOpt, private Ui::IScrOptRte { Q_OBJECT public: CScrOptRte(CGisItemRte * rte, const QPoint &point, IMouse *parent); virtual ~CScrOptRte(); void draw(QPainter& p); private slots: void slotEditDetails(); void slotDelete(); void slotCopy(); void slotCalc(); void slotReset(); void slotEdit(); void slotInstruction(bool on); private: IGisItem::key_t key; QPointF anchor; }; #endif //CSCROPTRTE_H qmapshack-1.5.1/src/gis/rte/IDetailsRte.ui000644 001750 000144 00000015237 12623413746 021352 0ustar00oeichlerusers000000 000000 IDetailsRte 0 0 400 400 3 3 3 3 3 0 Info 3 3 3 3 3 0 0 - Qt::AlignJustify|Qt::AlignTop 0 0 0 0 25 25 <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> :/icons/32x32/Tainted.png true Toggle read only mode. You have to open the lock to edit the item. ... :/icons/32x32/UnLock.png :/icons/32x32/Lock.png:/icons/32x32/UnLock.png 22 22 true true Qt::Vertical QSizePolicy::Minimum 20 40 0 0 false Hist. 0 0 0 0 0 CHistoryListWidget QListWidget
widgets/CHistoryListWidget.h
qmapshack-1.5.1/src/gis/rte/CDetailsRte.cpp000644 001750 000144 00000010654 12622435720 021502 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/rte/CDetailsRte.h" #include "gis/rte/CGisItemRte.h" #include "helpers/CLinksDialog.h" #include "units/IUnit.h" #include "widgets/CTextEditWidget.h" #include CDetailsRte::CDetailsRte(CGisItemRte& rte, QWidget *parent) : QDialog(parent) , rte(rte) { setupUi(this); setupGui(); if(rte.isOnDevice()) { toolLock->setDisabled(true); } connect(toolLock, SIGNAL(toggled(bool)), this, SLOT(slotChangeReadOnlyMode(bool))); connect(textCmtDesc, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotLinkActivated(QUrl))); connect(labelInfo, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(listHistory, SIGNAL(sigChanged()), this, SLOT(setupGui())); } CDetailsRte::~CDetailsRte() { } void CDetailsRte::setupGui() { if(originator) { return; } originator = true; bool isReadOnly = rte.isReadOnly(); setWindowTitle(rte.getName()); if(rte.isTainted()) { labelTainted->show(); } else { labelTainted->hide(); } labelInfo->setText(rte.getInfo(true)); textCmtDesc->document()->clear(); textCmtDesc->append(IGisItem::createText(isReadOnly, rte.getComment(), rte.getDescription(), rte.getLinks())); textCmtDesc->moveCursor (QTextCursor::Start); textCmtDesc->ensureCursorVisible(); // treeWidget->clear(); // QString val, unit; // foreach(const CGisItemRte::rtept_t& rtept, rte.getRoute().pts) // { // QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget); // item->setText(0, tr("Route waypoint")); // foreach(const CGisItemRte::subpt_t& subpt, rtept.subpts) // { // if(subpt.type != CGisItemRte::subpt_t::eTypeJunct) // { // continue; // } // QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget); // IUnit::self().meter2distance(subpt.distance, val, unit); // QString str = QString("Time: %1 Dist.: %2 %3").arg(subpt.time.toString()).arg(val).arg(unit); // str += "\n" + subpt.instruction; // item->setText(0,str); // } // } toolLock->setChecked(isReadOnly); listHistory->setupHistory(rte); originator = false; } void CDetailsRte::slotChangeReadOnlyMode(bool on) { rte.setReadOnlyMode(on); setupGui(); } void CDetailsRte::slotLinkActivated(const QString& link) { if(link == "name") { QString name = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new route name."), QLineEdit::Normal, rte.getName()); if(name.isEmpty()) { return; } rte.setName(name); } setupGui(); } void CDetailsRte::slotLinkActivated(const QUrl& url) { if(url.toString() == "comment") { CTextEditWidget dlg(0); dlg.setHtml(rte.getComment()); if(dlg.exec() == QDialog::Accepted) { rte.setComment(dlg.getHtml()); } setupGui(); } else if(url.toString() == "description") { CTextEditWidget dlg(0); dlg.setHtml(rte.getDescription()); if(dlg.exec() == QDialog::Accepted) { rte.setDescription(dlg.getHtml()); } setupGui(); } else if(url.toString() == "links") { QList links = rte.getLinks(); CLinksDialog dlg(links, this); if(dlg.exec() == QDialog::Accepted) { rte.setLinks(links); } setupGui(); } else { QDesktopServices::openUrl(url); } } qmapshack-1.5.1/src/gis/rte/CScrOptRte.cpp000644 001750 000144 00000007212 12622435720 021323 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisWidget.h" #include "gis/rte/CGisItemRte.h" #include "gis/rte/CScrOptRte.h" #include "helpers/CDraw.h" #include "mouse/IMouse.h" CScrOptRte::CScrOptRte(CGisItemRte *rte, const QPoint& point, IMouse *parent) : IScrOpt(parent) { key = rte->getKey(); setupUi(this); setOrigin(point); label->setFont(CMainWindow::self().getMapFont()); label->setText(rte->getInfo()); adjustSize(); toolInstruction->setEnabled(rte->isCalculated()); toolInstruction->setChecked(rte->hasUserFocus()); bool isOnDevice = rte->isOnDevice(); toolEdit->setDisabled(isOnDevice); anchor = rte->getPointCloseBy(point); if((anchor - point).manhattanLength() > 50) { anchor = point; } move(anchor.toPoint() + QPoint(-width()/2,SCR_OPT_OFFSET)); show(); connect(toolEditDetails, SIGNAL(clicked()), this, SLOT(hide())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCalc, SIGNAL(clicked()), this, SLOT(hide())); connect(toolReset, SIGNAL(clicked()), this, SLOT(hide())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(hide())); connect(toolInstruction, SIGNAL(toggled(bool)), this, SLOT(hide())); connect(toolEditDetails, SIGNAL(clicked()), this, SLOT(slotEditDetails())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelete())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(slotCopy())); connect(toolCalc, SIGNAL(clicked()), this, SLOT(slotCalc())); connect(toolReset, SIGNAL(clicked()), this, SLOT(slotReset())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(slotEdit())); connect(toolInstruction, SIGNAL(toggled(bool)), this, SLOT(slotInstruction(bool))); } CScrOptRte::~CScrOptRte() { } void CScrOptRte::slotEditDetails() { CGisWidget::self().editItemByKey(key); deleteLater(); } void CScrOptRte::slotDelete() { CGisWidget::self().delItemByKey(key); deleteLater(); } void CScrOptRte::slotCopy() { CGisWidget::self().copyItemByKey(key); deleteLater(); } void CScrOptRte::slotCalc() { CGisWidget::self().calcRteByKey(key); deleteLater(); } void CScrOptRte::slotReset() { CGisWidget::self().resetRteByKey(key); deleteLater(); } void CScrOptRte::slotEdit() { CGisWidget::self().editRteByKey(key); deleteLater(); } void CScrOptRte::slotInstruction(bool on) { CGisWidget::self().focusRteByKey(on, key); deleteLater(); } void CScrOptRte::draw(QPainter& p) { IGisItem * item = CGisWidget::self().getItemByKey(key); if(item == 0) { QWidget::deleteLater(); return; } item->drawHighlight(p); CDraw::bubble(p, geometry(), anchor.toPoint()); } qmapshack-1.5.1/src/gis/rte/IScrOptRte.ui000644 001750 000144 00000011772 12623413746 021177 0ustar00oeichlerusers000000 000000 IScrOptRte 0 0 400 69 Form 3 3 3 3 3 3 View details and edit. ... :/icons/32x32/EditDetails.png:/icons/32x32/EditDetails.png Copy route into another project. ... :/icons/32x32/Copy.png:/icons/32x32/Copy.png Delete route from project. ... :/icons/32x32/DeleteOne.png:/icons/32x32/DeleteOne.png Qt::Vertical ... :/icons/32x32/RteInstr.png:/icons/32x32/RteInstr.png true Calculate route. ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png Reset route calculation. ... :/icons/32x32/Reset.png:/icons/32x32/Reset.png Move route points. ... :/icons/32x32/LineMove.png:/icons/32x32/LineMove.png Qt::Horizontal 40 20 TextLabel Qt::AutoText Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse qmapshack-1.5.1/src/gis/rte/ICreateRouteFromWpt.ui000644 001750 000144 00000006452 12542601324 023041 0ustar00oeichlerusers000000 000000 ICreateRouteFromWpt 0 0 400 300 Create Route from Waypoints false ... :/icons/32x32/Up.png:/icons/32x32/Up.png 22 22 false ... :/icons/32x32/Down.png:/icons/32x32/Down.png 22 22 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ICreateRouteFromWpt accept() 248 254 157 274 buttonBox rejected() ICreateRouteFromWpt reject() 316 260 286 274 qmapshack-1.5.1/src/gis/rte/CDetailsRte.h000644 001750 000144 00000002654 12622435723 021153 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDETAILSRTE_H #define CDETAILSRTE_H #include "ui_IDetailsRte.h" #include class CGisItemRte; class CDetailsRte : public QDialog, private Ui::IDetailsRte { Q_OBJECT public: CDetailsRte(CGisItemRte& rte, QWidget * parent); virtual ~CDetailsRte(); private slots: void slotChangeReadOnlyMode(bool on); void slotLinkActivated(const QUrl& url); void slotLinkActivated(const QString& link); void setupGui(); private: CGisItemRte& rte; bool originator = false; }; #endif //CDETAILSRTE_H qmapshack-1.5.1/src/gis/rte/CCreateRouteFromWpt.cpp000644 001750 000144 00000007535 12622435720 023207 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisWidget.h" #include "gis/CGisWidget.h" #include "gis/IGisLine.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CCreateRouteFromWpt.h" #include "gis/rte/CGisItemRte.h" #include "gis/wpt/CGisItemWpt.h" #include #include CCreateRouteFromWpt::CCreateRouteFromWpt(const QList &keys, QWidget *parent) : QDialog(parent) { setupUi(this); foreach(const IGisItem::key_t& key, keys) { CGisItemWpt * wpt = dynamic_cast(CGisWidget::self().getItemByKey(key)); if(wpt == 0) { continue; } QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(wpt->getName()); item->setIcon(wpt->getIcon()); item->setToolTip(wpt->getInfo()); item->setData(Qt::UserRole + 0, QPointF(wpt->getPosition()*DEG_TO_RAD)); } connect(listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); connect(toolUp, SIGNAL(clicked()), this, SLOT(slotUp())); connect(toolDown, SIGNAL(clicked()), this, SLOT(slotDown())); } CCreateRouteFromWpt::~CCreateRouteFromWpt() { } void CCreateRouteFromWpt::accept() { QDialog::accept(); QString name = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new route name."), QLineEdit::Normal, ""); if(name.isEmpty()) { return; } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } SGisLine points; for(int i = 0; i < listWidget->count(); i++) { QListWidgetItem * item = listWidget->item(i); points << IGisLine::point_t(item->data(Qt::UserRole + 0).toPointF()); } CGisItemRte* rte = new CGisItemRte(points,name, project, NOIDX); rte->calc(); } void CCreateRouteFromWpt::slotSelectionChanged() { QListWidgetItem * item = listWidget->currentItem(); if(item != 0) { int row = listWidget->row(item); toolUp->setEnabled(row != 0); toolDown->setEnabled(row != (listWidget->count() - 1)); } else { toolUp->setEnabled(false); toolDown->setEnabled(false); } } void CCreateRouteFromWpt::slotUp() { QListWidgetItem * item = listWidget->currentItem(); if(item) { int row = listWidget->row(item); if(row == 0) { return; } listWidget->takeItem(row); row = row - 1; listWidget->insertItem(row,item); listWidget->setCurrentItem(item); } } void CCreateRouteFromWpt::slotDown() { QListWidgetItem * item = listWidget->currentItem(); if(item) { int row = listWidget->row(item); if(row == (listWidget->count() - 1)) { return; } listWidget->takeItem(row); row = row + 1; listWidget->insertItem(row,item); listWidget->setCurrentItem(item); } } qmapshack-1.5.1/src/gis/IGisLine.h000644 001750 000144 00000003360 12622435723 017654 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IGISLINE_H #define IGISLINE_H #include #include class QPolygonF; class CGisDraw; class CDemDraw; struct SGisLine; class IGisLine { public: IGisLine() = default; virtual ~IGisLine() = default; struct subpt_t { subpt_t() = default; subpt_t(const QPointF& pt); QPointF coord; QPointF pixel; qint32 ele = NOINT; }; struct point_t : public subpt_t { point_t() = default; point_t(const QPointF &pt); void resetElevation(); QVector subpts; }; virtual void setDataFromPolyline(const SGisLine& line) = 0; virtual void getPolylineFromData(SGisLine& line) = 0; }; struct SGisLine : public QVector { void updateElevation(CDemDraw * dem); void updatePixel(CGisDraw * gis); }; #endif //IGISLINE_H qmapshack-1.5.1/src/gis/CSelDevices.cpp000644 001750 000144 00000004501 12622435720 020670 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "device/IDevice.h" #include "gis/CGisListWks.h" #include "gis/CSelDevices.h" #include "gis/prj/IGisProject.h" #include CSelDevices::CSelDevices(IGisProject * project, QTreeWidget *wks) : QDialog(wks) { setupUi(this); const int N = wks->topLevelItemCount(); for(int n = 0; n < N; n++) { IDevice * device = dynamic_cast(wks->topLevelItem(n)); if(device == 0) { continue; } QListWidgetItem * item = new QListWidgetItem(listWidget); item->setText(device->getName()); item->setData(Qt::UserRole, device->getKey()); item->setIcon(device->icon(CGisListWks::eColumnIcon)); IGisProject * proj = device->getProjectByKey(project->getKey()); if(proj) { item->setCheckState(Qt::Checked); } else { item->setCheckState(Qt::Unchecked); } } CCanvas::setOverrideCursor(Qt::ArrowCursor, "CSelDevices"); } CSelDevices::~CSelDevices() { CCanvas::restoreOverrideCursor("~CSelDevices"); } void CSelDevices::getSlectedDevices(QSet& keys) { keys.clear(); const int N = listWidget->count(); for(int n = 0; n < N; n++) { QListWidgetItem * item = listWidget->item(n); if(item->checkState() == Qt::Checked) { keys << item->data(Qt::UserRole).toString(); } } } qmapshack-1.5.1/src/gis/WptIcons.cpp000644 001750 000144 00000014002 12622435720 020302 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "WptIcons.h" #include "helpers/CAppSetup.h" #include static const char * wptDefault = "://icons/waypoints/32x32/Default.png"; static QMap wptIcons; void initWptIcons() { wptIcons["Default"] = icon_t(wptDefault, 16, 16); wptIcons["City (Capitol)"] = icon_t("://icons/waypoints/32x32/CityCapitol.png", 16, 16); wptIcons["City (Large)"] = icon_t("://icons/waypoints/32x32/CityLarge.png", 16, 16); wptIcons["City (Medium)"] = icon_t("://icons/waypoints/32x32/CityMedium.png", 16, 16); wptIcons["City (Small)"] = icon_t("://icons/waypoints/32x32/CitySmall.png", 16, 16); // wptIcons["Small City"] = ":/icons/wpt/small_city15x15.png"; // wptIcons["Geocache"] = ":/icons/wpt/geocache15x15.png"; // wptIcons["Geocache Found"] = ":/icons/wpt/geocache_fnd15x15.png"; wptIcons["Residence"] = icon_t("://icons/waypoints/32x32/Residence.png", 16, 16); wptIcons["Flag, Red"] = icon_t("://icons/waypoints/32x32/FlagRed.png", 0, 32); wptIcons["Flag, Blue"] = icon_t("://icons/waypoints/32x32/FlagBlue.png", 0, 32); wptIcons["Flag, Green"] = icon_t("://icons/waypoints/32x32/FlagGreen.png", 0, 32); wptIcons["Pin, Red"] = icon_t("://icons/waypoints/32x32/PinRed.png", 0, 32); wptIcons["Pin, Blue"] = icon_t("://icons/waypoints/32x32/PinBlue.png", 0, 32); wptIcons["Pin, Green"] = icon_t("://icons/waypoints/32x32/PinGreen.png", 0, 32); wptIcons["Block, Red"] = icon_t("://icons/waypoints/32x32/BoxRed.png", 16, 16); wptIcons["Block, Blue"] = icon_t("://icons/waypoints/32x32/BoxBlue.png", 16, 16); wptIcons["Block, Green"] = icon_t("://icons/waypoints/32x32/BoxGreen.png", 16, 16); wptIcons["Blue Diamond"] = icon_t("://icons/waypoints/32x32/DiamondBlue.png", 16, 16); wptIcons["Green Diamond"] = icon_t("://icons/waypoints/32x32/DiamondGreen.png", 16, 16); wptIcons["Red Diamond"] = icon_t("://icons/waypoints/32x32/DiamondRed.png", 16, 16); wptIcons["Parking Area"] = icon_t("://icons/cache/32x32/parking.png", 16, 16); wptIcons["Trailhead"] = icon_t("://icons/cache/32x32/trailhead.png", 16, 16); wptIcons["Waypoint"] = icon_t("://icons/waypoints/32x32/Waypoint.png", 16, 16); setWptIconByName("Traditional Cache", "://icons/cache/32x32/traditional.png"); setWptIconByName("Multi-cache", "://icons/cache/32x32/multi.png"); setWptIconByName("Unknown Cache", "://icons/cache/32x32/unknown.png"); setWptIconByName("Wherigo Cache", "://icons/cache/32x32/wherigo.png"); setWptIconByName("Event Cache", "://icons/cache/32x32/event.png"); setWptIconByName("Earthcache", "://icons/cache/32x32/earth.png"); setWptIconByName("Letterbox Hybrid", "://icons/cache/32x32/letterbox.png"); setWptIconByName("Virtual Cache", "://icons/cache/32x32/virtual.png"); setWptIconByName("Webcam Cache", "://icons/cache/32x32/webcam.png"); QDir dirIcon = CAppSetup::getPlattformInstance()->configDir("WaypointIcons"); QString filename; QStringList filenames = dirIcon.entryList(QDir::Files); foreach(filename, filenames) { QFileInfo fi(filename); QString name = fi.baseName(); setWptIconByName(name, dirIcon.filePath(filename)); } } const QMap& getWptIcons() { return wptIcons; } void setWptIconByName(const QString& name, const QString& filename) { QPixmap icon(filename); wptIcons[name] = icon_t(filename, icon.width()>>1, icon.height()>>1); } void setWptIconByName(const QString& name, const QPixmap& icon) { QDir dirIcon = CAppSetup::getPlattformInstance()->configDir("WaypointIcons"); QString filename = dirIcon.filePath(name + ".png"); icon.save(filename); wptIcons[name] = icon_t(filename, icon.width()>>1, icon.height()>>1); } QPixmap loadIcon(const QString& path) { QFileInfo finfo(path); if(finfo.completeSuffix() != "bmp") { return QPixmap(path); } else { QImage img = QPixmap(path).toImage().convertToFormat(QImage::Format_Indexed8); img.setColor(0,qRgba(0,0,0,0)); return QPixmap::fromImage(img); } return QPixmap(); } QPixmap getWptIconByName(const QString& name, QPointF &focus, QString * src) { QPixmap icon; QString path; if(wptIcons.contains(name)) { focus = wptIcons[name].focus; path = wptIcons[name].path; } else { focus = wptIcons["Default"].focus; path = wptIcons["Default"].path; } if(path.isEmpty()) { path = wptDefault; } if(src) { *src = path; } icon = loadIcon(path); // Limit icon size to 22 pixel max. if(icon.width() > 22 || icon.height() > 22) { qreal s; if(icon.width() > icon.height()) { s = 22.0/icon.width(); } else { s = 22.0/icon.height(); } focus = focus * s; icon = icon.scaled(icon.size()*s,Qt::KeepAspectRatio,Qt::SmoothTransformation); } return icon; } qmapshack-1.5.1/src/gis/IGisItem.cpp000644 001750 000144 00000047340 12622435720 020221 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "canvas/CCanvas.h" #include "device/IDevice.h" #include "gis/CGisDraw.h" #include "gis/CGisListWks.h" #include "gis/IGisItem.h" #include "gis/db/macros.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CDraw.h" #include "units/IUnit.h" #include #include #include QMutex IGisItem::mutexItems(QMutex::Recursive); const QString IGisItem::noKey; const QString IGisItem::noName = QObject::tr("[no name]"); const IGisItem::color_t IGisItem::colorMap[] = { {"Black", QColor(Qt::black), QString("://icons/8x8/bullet_black.png")} ,{"DarkRed", QColor(Qt::darkRed), QString("://icons/8x8/bullet_dark_red.png")} ,{"DarkGreen", QColor(Qt::darkGreen), QString("://icons/8x8/bullet_dark_green.png")} ,{"DarkYellow", QColor(Qt::darkYellow), QString("://icons/8x8/bullet_dark_yellow.png")} ,{"DarkBlue", QColor(Qt::darkBlue), QString("://icons/8x8/bullet_dark_blue.png")} ,{"DarkMagenta", QColor(Qt::darkMagenta), QString("://icons/8x8/bullet_dark_magenta.png")} ,{"DarkCyan", QColor(Qt::darkCyan), QString("://icons/8x8/bullet_dark_cyan.png")} ,{"LightGray", QColor(Qt::lightGray), QString("://icons/8x8/bullet_gray.png")} ,{"DarkGray", QColor(Qt::darkGray), QString("://icons/8x8/bullet_dark_gray.png")} ,{"Red", QColor(Qt::red), QString("://icons/8x8/bullet_red.png")} ,{"Green", QColor(Qt::green), QString("://icons/8x8/bullet_green.png")} ,{"Yellow", QColor(Qt::yellow), QString("://icons/8x8/bullet_yellow.png")} ,{"Blue", QColor(Qt::blue), QString("://icons/8x8/bullet_blue.png")} ,{"Magenta", QColor(Qt::magenta), QString("://icons/8x8/bullet_magenta.png")} ,{"Cyan", QColor(Qt::cyan), QString("://icons/8x8/bullet_cyan.png")} ,{"White", QColor(Qt::white), QString("://icons/8x8/bullet_white.png")} ,{"Transparent", QColor(Qt::transparent), QString("")} }; IGisItem::IGisItem(IGisProject *parent, type_e typ, int idx) : QTreeWidgetItem(parent, typ) { int n = -1; setFlags(QTreeWidgetItem::flags() & ~Qt::ItemIsDropEnabled); if(parent == 0) { return; } key.project = parent->getKey(); key.device = parent->getDeviceKey(); if(idx >= 0) { parent->removeChild(this); parent->insertChild(idx, this); } else { if(type() == eTypeTrk) { for(n = parent->childCount() - 2; n >= 0; n--) { /** @note The order of item types to test is given by the order items read from the GPX file in the CGpxProject constructor. */ int childType = parent->child(n)->type(); if(childType == eTypeTrk) { parent->removeChild(this); parent->insertChild(n+1, this); break; } } } else if(type() == eTypeRte) { for(n = parent->childCount() - 2; n >= 0; n--) { /** @note The order of item types to test is given by the order items read from the GPX file in the CGpxProject constructor. */ int childType = parent->child(n)->type(); if( childType == eTypeRte || childType == eTypeTrk) { parent->removeChild(this); parent->insertChild(n+1, this); break; } } } else if(type() == eTypeWpt) { for(n = parent->childCount() - 2; n >= 0; n--) { /** @note The order of item types to test is given by the order items read from the GPX file in the CGpxProject constructor. */ int childType = parent->child(n)->type(); if(childType == eTypeWpt || childType == eTypeRte || childType == eTypeTrk) { parent->removeChild(this); parent->insertChild(n+1, this); break; } } } else if(type() == eTypeOvl) { for(n = parent->childCount() - 2; n >= 0; n--) { /** @note The order of item types to test is given by the order items read from the GPX file in the CGpxProject constructor. */ int childType = parent->child(n)->type(); if(childType == eTypeOvl || childType == eTypeWpt || childType == eTypeRte || childType == eTypeTrk) { parent->removeChild(this); parent->insertChild(n+1, this); break; } } } if(n < 0) { parent->removeChild(this); parent->insertChild(0, this); } } } IGisItem::~IGisItem() { } void IGisItem::genKey() { if(key.item.isEmpty()) { QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); *this >> stream; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(buffer); key.item = md5.result().toHex(); } if(key.project.isEmpty()) { IGisProject * project = dynamic_cast(parent()); if(project) { key.project = project->getKey(); } } } void IGisItem::loadFromDb(quint64 id, QSqlDatabase& db) { QSqlQuery query(db); query.prepare("SELECT data, key FROM items WHERE id=:id"); query.bindValue(":id", id); QUERY_EXEC(return ); if(query.next()) { QByteArray data(query.value(0).toByteArray()); QDataStream in(&data, QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in.setVersion(QDataStream::Qt_5_2); in >> history; loadHistory(history.histIdxCurrent); if(key.item.isEmpty()) { QString keyFromDB = query.value(1).toString(); /*[Issue #72] Database/Workspace inconsisteny in QMS 1.4.0 The root cause is a missing key in the serialized data. This is fixed by calling getKey() in setupHistory(). As the database has a valid key the complete history data has to be fixed with that key. */ const int N = history.events.size(); for(int i = 0; i < N; i++) { loadHistory(i); key.item = keyFromDB; updateHistory(); } } } } QString IGisItem::getNameEx() const { QString str = getName(); IGisProject * project = dynamic_cast(parent()); if(project) { str += " @ " + project->getName(); } IDevice * device = dynamic_cast(parent()->parent()); if(device) { str += " @ " + device->getName(); } return str; } void IGisItem::updateDecoration(mark_e enable, mark_e disable) { // update text and icon setToolTip(CGisListWks::eColumnName,getInfo()); setText(CGisListWks::eColumnName, getName()); setSymbol(); // update project if necessary IGisProject * project = dynamic_cast(parent()); if(project && (enable & eMarkChanged)) { project->setChanged(); } // set marks in column 1 quint32 mask = data(1,Qt::UserRole).toUInt(); mask |= enable; mask &= ~disable; setData(1, Qt::UserRole, mask); QString str; if(mask & eMarkChanged) { str += "*"; } setText(CGisListWks::eColumnDecoration, str); } void IGisItem::changed(const QString &what, const QString &icon) { /* If item gets changed but if it's origin is not QMapShack then it is assumed to be tainted, as imported data should never be changed without notice. */ if(!(flags & eFlagCreatedInQms)) { flags |= eFlagTainted; } // forget all history entries after the current entry for(int i = history.events.size() - 1; i > history.histIdxCurrent; i--) { history.events.pop_back(); } // append history by new entry history.events << history_event_t(); history_event_t& event = history.events.last(); event.time = QDateTime::currentDateTimeUtc(); event.comment = what; event.icon = icon; QDataStream stream(&event.data, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); *this >> stream; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(event.data); event.hash = md5.result().toHex(); history.histIdxCurrent = history.events.size() - 1; updateDecoration(eMarkChanged, eMarkNone); } void IGisItem::updateHistory() { if(history.histIdxCurrent == NOIDX) { return; } history_event_t& event = history.events[history.histIdxCurrent]; event.data.clear(); QDataStream stream(&event.data, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); *this >> stream; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(event.data); event.hash = md5.result().toHex(); updateDecoration(eMarkChanged, eMarkNone); } void IGisItem::setupHistory() { getKey(); history.histIdxInitial = NOIDX; history.histIdxCurrent = NOIDX; // if history is empty setup an initial item if(history.events.isEmpty()) { history.events << history_event_t(); history_event_t& event = history.events.last(); event.time = QDateTime::currentDateTimeUtc(); event.comment = QObject::tr("Initial version."); event.icon = "://icons/48x48/Start.png"; } // search for the first item with data for(int i = 0; i < history.events.size(); i++) { if(!history.events[i].data.isEmpty()) { history.histIdxInitial = i; break; } } // if no initial item can be found fill the last item with data // and make it the initial item if(history.histIdxInitial == NOIDX) { history_event_t& event = history.events.last(); QDataStream stream(&event.data, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); *this >> stream; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(event.data); event.hash = md5.result().toHex(); history.histIdxInitial = history.events.size() - 1; } history.histIdxCurrent = history.events.size() - 1; } void IGisItem::loadHistory(int idx) { // test for bad index if((idx >= history.events.size()) || (idx < 0)) { return; } history_event_t& event = history.events[idx]; // test for no data if(event.data.isEmpty()) { return; } // restore item from history entry QDataStream stream(&event.data, QIODevice::ReadOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); *this << stream; history.histIdxCurrent = idx; } void IGisItem::cutHistory() { while(history.events.size() > (history.histIdxCurrent + 1)) { history.events.pop_back(); } } bool IGisItem::isReadOnly() const { return !(flags & eFlagWriteAllowed) || isOnDevice(); } bool IGisItem::isTainted() const { return flags & eFlagTainted; } bool IGisItem::isOnDevice() const { IGisProject * project = dynamic_cast(parent()); if(project == 0) { return false; } return project->isOnDevice(); } bool IGisItem::setReadOnlyMode(bool readOnly) { // if the item is on a device no change is allowed if(isOnDevice()) { return false; } // test if it is a change at all if(isReadOnly() == readOnly) { return true; } // warn if item is external and read only if(!(flags & (eFlagCreatedInQms|eFlagTainted))) { if(isReadOnly() && !readOnly) { CCanvas::setOverrideCursor(Qt::ArrowCursor, "setReadOnlyMode"); QString str = QObject::tr("

%1

This element is probably read-only because it was not created within QMapShack. Usually you should not want to change imported data. But if you think that is ok press 'Ok'.").arg(getName()); int res = QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Read Only Mode..."), str, QMessageBox::Ok|QMessageBox::Abort, QMessageBox::Ok); CCanvas::restoreOverrideCursor("setReadOnlyMode"); if(res != QMessageBox::Ok) { return false; } } } // finally change flag if(readOnly) { flags &= ~eFlagWriteAllowed; } else { flags |= eFlagWriteAllowed; } updateHistory(); return true; } const IGisItem::key_t &IGisItem::getKey() { if(key.item.isEmpty() || key.project.isEmpty()) { genKey(); } return key; } const QString& IGisItem::getHash() { if(history.histIdxCurrent == NOIDX) { return noKey; } return history.events[history.histIdxCurrent].hash; } QColor IGisItem::str2color(const QString& name) { for(size_t i = 0; i < sizeof(colorMap) / sizeof(color_t); i++) { if(QString(colorMap[i].name).toUpper() == name.toUpper()) { return colorMap[i].color; } } return QColor(name); } QString IGisItem::color2str(const QColor& color) { for(size_t i = 0; i < sizeof(colorMap) / sizeof(color_t); i++) { if(colorMap[i].color == color) { return colorMap[i].name; } } return ""; } void IGisItem::splitLineToViewport(const QPolygonF& line, const QRectF& extViewport, QList& lines) { int i; QPointF pt, ptt, pt1; QPolygonF subline; const int size = line.size(); if(line.isEmpty()) { return; } pt = line[0]; subline << pt; for(i = 1; i < size; i++) { pt1 = line[i]; if(!GPS_Math_LineCrossesRect(pt, pt1, extViewport)) { pt = pt1; if(subline.size() > 1) { lines << subline; } subline.clear(); subline << pt; continue; } ptt = pt1 - pt; if(ptt.manhattanLength() < 5) { continue; } subline << pt1; pt = pt1; } if(subline.size() > 1) { lines << subline; } } QString IGisItem::removeHtml(const QString &str) { QTextDocument html; html.setHtml(str); return html.toPlainText(); } QString IGisItem::toLink(bool isReadOnly, const QString& href, const QString& str, const QString &key) { if(isReadOnly) { return QString("%1").arg(str); } if(key.isEmpty()) { return QString("%2").arg(href).arg(str); } else { return QString("%2").arg(href).arg(str).arg(key); } } QString IGisItem::createText(bool isReadOnly, const QString& cmt, const QString& desc, const QList& links, const QString &key) { QString str; bool isEmpty; isEmpty = removeHtml(desc).simplified().isEmpty(); if(!isReadOnly || !isEmpty) { str += toLink(isReadOnly, "description", QObject::tr("

Description:

"), key); if(removeHtml(desc).simplified().isEmpty()) { str += QObject::tr("

--- no description ---

"); } else { str += desc; } } isEmpty = removeHtml(cmt).simplified().isEmpty(); if(!isReadOnly || !isEmpty) { str += toLink(isReadOnly, "comment", QObject::tr("

Comment:

"), key); if(isEmpty) { str += QObject::tr("

--- no comment ---

"); } else { str += cmt; } } isEmpty = links.isEmpty(); if(!isReadOnly || !isEmpty) { str += toLink(isReadOnly, "links", QObject::tr("

Links:

"), key); if(isEmpty) { str += QObject::tr("

--- no links ---

"); } else { foreach(const link_t &link, links) { if(link.text.isEmpty()) { str += QString("

%2

").arg(link.uri.toString()).arg(link.uri.toString()); } else { str += QString("

%2

").arg(link.uri.toString()).arg(link.text); } } } } return str; } QString IGisItem::createText(bool isReadOnly, const QString& desc, const QList& links, const QString& key) { QString str; bool isEmpty; isEmpty = removeHtml(desc).simplified().isEmpty(); if(!isReadOnly || !isEmpty) { str += toLink(isReadOnly, "description", QObject::tr("

Description:

"), key); if(removeHtml(desc).simplified().isEmpty()) { str += QObject::tr("

--- no description ---

"); } else { str += desc; } } isEmpty = links.isEmpty(); if(!isReadOnly || !isEmpty) { str += toLink(isReadOnly, "links", QObject::tr("

Links:

"), key); if(isEmpty) { str += QObject::tr("

--- no links ---

"); } else { foreach(const link_t &link, links) { if(link.text.isEmpty()) { str += QString("

%2

").arg(link.uri.toString()).arg(link.uri.toString()); } else { str += QString("

%2

").arg(link.uri.toString()).arg(link.text); } } } } return str; } bool IGisItem::isVisible(const QRectF &rect, const QPolygonF& viewport, CGisDraw *gis) { QPolygonF tmp1; tmp1 << rect.topLeft(); tmp1 << rect.topRight(); tmp1 << rect.bottomRight(); tmp1 << rect.bottomLeft(); gis->convertRad2Px(tmp1); QPolygonF tmp2 = viewport; gis->convertRad2Px(tmp2); return tmp2.boundingRect().intersects(tmp2.boundingRect()); } bool IGisItem::isVisible(const QPointF& point, const QPolygonF& viewport, CGisDraw * gis) { QPolygonF tmp2 = viewport; gis->convertRad2Px(tmp2); QPointF pt = point; gis->convertRad2Px(pt); return tmp2.boundingRect().contains(pt); } bool IGisItem::isChanged() const { return text(CGisListWks::eColumnDecoration) == "*"; } qmapshack-1.5.1/src/gis/CGisDraw.h000644 001750 000144 00000002347 12622435723 017660 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISDRAW_H #define CGISDRAW_H #include "canvas/IDrawContext.h" class CCanvas; class CGisDraw : public IDrawContext { public: CGisDraw(CCanvas *parent); virtual ~CGisDraw(); using IDrawContext::draw; void draw(QPainter& p, const QRect& rect); protected: void drawt(buffer_t& currentBuffer); }; #endif //CGISDRAW_H qmapshack-1.5.1/src/gis/CGisWidget.cpp000644 001750 000144 00000041354 12622435720 020537 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "device/IDevice.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/IGisItem.h" #include "gis/db/CDBProject.h" #include "gis/db/CSelectDBFolder.h" #include "gis/db/CSetupFolder.h" #include "gis/gpx/CGpxProject.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/prj/IGisProject.h" #include "gis/qms/CQmsProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "gis/wpt/CProjWpt.h" #include "helpers/CSelectCopyAction.h" #include "helpers/CSelectProjectDialog.h" #include "helpers/CSettings.h" #include #include CGisWidget * CGisWidget::pSelf = 0; CGisWidget::CGisWidget(QMenu *menuProject, QWidget *parent) : QWidget(parent) { pSelf = this; setupUi(this); treeWks->setExternalMenu(menuProject); SETTINGS; treeWks->header()->restoreState(cfg.value("Workspace/treeWks/state", treeWks->header()->saveState()).toByteArray()); treeDB->header()->restoreState(cfg.value("Workspace/treeDB/state", treeDB->header()->saveState()).toByteArray()); connect(treeWks, SIGNAL(sigChanged()), SIGNAL(sigChanged())); connect(treeDB, SIGNAL(sigChanged()), SLOT(slotHelpText())); slotHelpText(); } CGisWidget::~CGisWidget() { SETTINGS; cfg.setValue("Workspace/treeWks/state", treeWks->header()->saveState()); cfg.setValue("Workspace/treeDB/state", treeDB->header()->saveState()); /* Explicitly delete workspace here, as database projects use CGisWidget upon destruction to signal the database their destruction. */ delete treeWks; } void CGisWidget::postEventForWks(QEvent * event) { QCoreApplication::postEvent(treeWks, event); } void CGisWidget::postEventForDb(QEvent * event) { QCoreApplication::postEvent(treeDB, event); } void CGisWidget::loadGisProject(const QString& filename) { // add project to workspace CCanvas::setOverrideCursor(Qt::WaitCursor, "loadGisProject"); treeWks->blockSignals(true); QMutexLocker lock(&IGisItem::mutexItems); IGisProject * item = 0; QString suffix = QFileInfo(filename).suffix().toLower(); if(suffix == "gpx") { item = new CGpxProject(filename, treeWks); } else if(suffix == "qms") { item = new CQmsProject(filename, treeWks); } if(item && !item->isValid()) { delete item; item = 0; } // skip if project is already loaded if(item && treeWks->hasProject(item)) { QMessageBox::information(this, tr("Load project..."), tr("The project \"%1\" is already in the workspace.").arg(item->getName()), QMessageBox::Abort); delete item; item = 0; } treeWks->blockSignals(false); CCanvas::restoreOverrideCursor("loadGisProject"); emit sigChanged(); } void CGisWidget::slotHelpText() { if(treeDB->topLevelItemCount() == 0) { frameHelp->show(); } else { frameHelp->hide(); } } void CGisWidget::slotSaveAll() { CCanvas::setOverrideCursor(Qt::WaitCursor, "slotSaveAll"); QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { IGisProject * item = dynamic_cast(treeWks->topLevelItem(i)); if(item == 0) { continue; } item->save(); } CCanvas::restoreOverrideCursor("slotSaveAll"); } IGisProject * CGisWidget::selectProject() { QString key, name; CSelectProjectDialog::type_e type; CSelectProjectDialog dlg(key, name, type, treeWks); dlg.exec(); IGisProject * project = 0; if(!key.isEmpty()) { QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { project = dynamic_cast(treeWks->topLevelItem(i)); if(project == 0) { continue; } if(key == project->getKey()) { break; } } } else if(type == CSelectProjectDialog::eTypeDb) { quint64 idParent; QString db; IDBFolder::type_e type; CSelectDBFolder dlg1(idParent, db, this); if(dlg1.exec() == QDialog::Rejected) { return 0; } CSetupFolder dlg2(type, name, false, this); if(dlg2.exec() == QDialog::Rejected) { return 0; } QMutexLocker lock(&IGisItem::mutexItems); CEvtW2DCreate evt(name, type, idParent, db); QApplication::sendEvent(treeDB, &evt); if(evt.idChild) { while(project == 0) { QApplication::processEvents(QEventLoop::WaitForMoreEvents|QEventLoop::ExcludeUserInputEvents, 100); project = treeWks->getProjectById(evt.idChild, db); } } } else if(!name.isEmpty()) { QMutexLocker lock(&IGisItem::mutexItems); if(type == CSelectProjectDialog::eTypeGpx) { project = new CGpxProject(name, treeWks); } else if (type == CSelectProjectDialog::eTypeQms) { project = new CQmsProject(name, treeWks); } } return project; } void CGisWidget::getItemsByPos(const QPointF& pos, QList& items) { QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { QTreeWidgetItem * item = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item); if(project) { project->getItemsByPos(pos, items); continue; } IDevice * device = dynamic_cast(item); if(device) { device->getItemsByPos(pos, items); continue; } } } void CGisWidget::mouseMove(const QPointF& pos) { QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { QTreeWidgetItem * item = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item); if(project) { project->mouseMove(pos); continue; } } } IGisItem * CGisWidget::getItemByKey(const IGisItem::key_t& key) { IGisItem * item = 0; QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { QTreeWidgetItem * item1 = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item1); if(project) { if(project->getKey() != key.project) { continue; } item = project->getItemByKey(key); if(item != 0) { break; } continue; } IDevice * device = dynamic_cast(item1); if(device) { if(device->getKey() != key.device) { continue; } item = device->getItemByKey(key); if(item != 0) { break; } } } return item; } void CGisWidget::delItemByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); QMessageBox::StandardButtons last = QMessageBox::NoButton; for(int i = 0; i < treeWks->topLevelItemCount(); i++) { IGisProject * project = dynamic_cast(treeWks->topLevelItem(i)); if(project == 0) { continue; } if(project->delItemByKey(key, last)) { // update database tree if that is a database project CDBProject * dbp = dynamic_cast(project); if(dbp) { dbp->postStatus(); } } if(last == QMessageBox::Cancel) { break; } } emit sigChanged(); } void CGisWidget::editItemByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { QTreeWidgetItem * item = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item); if(project != 0) { project->editItemByKey(key); continue; } IDevice * device = dynamic_cast(item); if(device != 0) { device->editItemByKey(key); continue; } } emit sigChanged(); } void CGisWidget::copyItemByKey(const IGisItem::key_t &key) { QMutexLocker lock(&IGisItem::mutexItems); IGisItem * item = getItemByKey(key); if(item == 0) { return; } IGisProject * project = selectProject(); if(project == 0) { return; } int lastResult = CSelectCopyAction::eResultNone; project->insertCopyOfItem(item, NOIDX, lastResult); emit sigChanged(); } void CGisWidget::projWptByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemWpt * wpt = dynamic_cast(getItemByKey(key)); if(wpt != 0) { CProjWpt dlg(*wpt, 0); dlg.exec(); } emit sigChanged(); } void CGisWidget::moveWptByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemWpt * wpt = dynamic_cast(getItemByKey(key)); if(wpt != 0) { if(!wpt->setReadOnlyMode(false)) { return; } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas != 0) { canvas->setMouseMoveWpt(*wpt); } } } void CGisWidget::toggleWptBubble(const IGisItem::key_t &key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemWpt * wpt = dynamic_cast(getItemByKey(key)); if(wpt != 0) { wpt->toggleBubble(); } } void CGisWidget::focusTrkByKey(bool yes, const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(getItemByKey(key)); if(trk != 0) { trk->gainUserFocus(yes); } emit sigChanged(); } void CGisWidget::focusRteByKey(bool yes, const IGisItem::key_t &key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemRte * rte = dynamic_cast(getItemByKey(key)); if(rte != 0) { rte->gainUserFocus(yes); } emit sigChanged(); } void CGisWidget::cutTrkByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(getItemByKey(key)); if(trk != 0 && trk->cut()) { int res = QMessageBox::question(this, tr("Cut Track..."), tr("Do you want to delete the original track?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::Ok); if(res == QMessageBox::Ok) { delete trk; } } emit sigChanged(); } void CGisWidget::reverseTrkByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(getItemByKey(key)); if(trk) { trk->reverse(); } emit sigChanged(); } void CGisWidget::combineTrkByKey(const IGisItem::key_t& key) { QList keys; keys << key; combineTrkByKey(keys); } void CGisWidget::combineTrkByKey(const QList& keys) { if(keys.isEmpty()) { return; } QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(getItemByKey(keys.first())); if(trk) { trk->combine(keys); } emit sigChanged(); } void CGisWidget::editTrkByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(getItemByKey(key)); if(trk != 0) { if(!trk->setReadOnlyMode(false)) { return; } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas != 0) { canvas->setMouseEditTrk(*trk); } } } void CGisWidget::rangeTrkByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemTrk * trk = dynamic_cast(getItemByKey(key)); if(trk != 0) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas != 0) { canvas->setMouseRangeTrk(*trk); } } } void CGisWidget::editRteByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemRte * rte = dynamic_cast(getItemByKey(key)); if(rte != 0) { if(!rte->setReadOnlyMode(false)) { return; } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas != 0) { canvas->setMouseEditRte(*rte); } } } void CGisWidget::calcRteByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemRte * rte = dynamic_cast(getItemByKey(key)); if(rte != 0) { rte->calc(); } } void CGisWidget::resetRteByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemRte * rte = dynamic_cast(getItemByKey(key)); if(rte != 0) { rte->reset(); } } void CGisWidget::editAreaByKey(const IGisItem::key_t& key) { QMutexLocker lock(&IGisItem::mutexItems); CGisItemOvlArea * area = dynamic_cast(getItemByKey(key)); if(area != 0) { if(!area->setReadOnlyMode(false)) { return; } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas != 0) { canvas->setMouseEditArea(*area); } } } void CGisWidget::draw(QPainter& p, const QPolygonF& viewport, CGisDraw * gis) { QFontMetricsF fm(CMainWindow::self().getMapFont()); QList blockedAreas; QMutexLocker lock(&IGisItem::mutexItems); // draw mandatory stuff first for(int i = 0; i < treeWks->topLevelItemCount(); i++) { if(gis->needsRedraw()) { break; } QTreeWidgetItem * item = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item); if(project) { project->drawItem(p, viewport, blockedAreas, gis); continue; } IDevice * device = dynamic_cast(item); if(device) { device->drawItem(p, viewport, blockedAreas, gis); continue; } } // draw optional labels second for(int i = 0; i < treeWks->topLevelItemCount(); i++) { if(gis->needsRedraw()) { break; } QTreeWidgetItem * item = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item); if(project) { project->drawLabel(p, viewport, blockedAreas, fm, gis); continue; } IDevice * device = dynamic_cast(item); if(device) { device->drawLabel(p, viewport, blockedAreas, fm, gis); continue; } } } void CGisWidget::fastDraw(QPainter& p, const QRectF& viewport, CGisDraw *gis) { /* Mutex locking will make map moving very slow if there are many GIS items visible. Remove it for now. But I am not sure if that is a good idea. */ //QMutexLocker lock(&IGisItem::mutexItems); for(int i = 0; i < treeWks->topLevelItemCount(); i++) { QTreeWidgetItem * item = treeWks->topLevelItem(i); IGisProject * project = dynamic_cast(item); if(project) { project->drawItem(p, viewport, gis); continue; } IDevice * device = dynamic_cast(item); if(device) { device->drawItem(p, viewport, gis); continue; } } } qmapshack-1.5.1/src/gis/CGisListWks.h000644 001750 000144 00000010141 12622435723 020352 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISLISTWKS_H #define CGISLISTWKS_H #include #include #include struct action_t; class QAction; class CSearchGoogle; class IGisProject; class CDBProject; class IDeviceWatcher; class CGisListWks : public QTreeWidget { Q_OBJECT public: CGisListWks(QWidget * parent); virtual ~CGisListWks(); enum column_e { eColumnDecoration = 0 ,eColumnIcon = 0 ,eColumnName = 1 }; void setExternalMenu(QMenu * project); bool hasProject(IGisProject *project); IGisProject * getProjectByKey(const QString& key); CDBProject * getProjectById(quint64 id, const QString& db); bool event(QEvent * e); void removeDevice(const QString& key); signals: void sigChanged(); protected: void dragMoveEvent (QDragMoveEvent * e ); void dropEvent ( QDropEvent * e ); private slots: void slotSaveWorkspace(); void slotLoadWorkspace(); void slotContextMenu(const QPoint& point); void slotSaveProject(); void slotSaveAsProject(); void slotEditPrj(); void slotCloseProject(); void slotDeleteProject(); void slotShowOnMap(); void slotHideFrMap(); void slotItemDoubleClicked(QTreeWidgetItem * item, int); void slotItemChanged(QTreeWidgetItem * item, int column); void slotEditItem(); void slotDeleteItem(); void slotBubbleWpt(); void slotProjWpt(); void slotMoveWpt(); void slotFocusTrk(bool on); void slotEditTrk(); void slotReverseTrk(); void slotCombineTrk(); void slotRangeTrk(); void slotFocusRte(bool on); void slotCalcRte(); void slotResetRte(); void slotEditRte(); void slotEditArea(); void slotAddEmptyProject(); void slotCloseAllProjects(); void slotSearchGoogle(bool on); void slotCopyItem(); void slotSyncWksDev(); void slotSyncDevWks(); void slotRteFromWpt(); private: void configDB(); void initDB(); void migrateDB(int version); void migrateDB1to2(); void setVisibilityOnMap(bool visible); QSqlDatabase db; QMenu * menuProjectWks; QAction * actionSave; QAction * actionSaveAs; QAction * actionEditPrj; QAction * actionCloseProj; QAction * actionShowOnMap; QAction * actionHideFrMap; QAction * actionSyncWksDev; QMenu * menuProjectDev; QAction * actionDelProj; QAction * actionSyncDevWks; QMenu * menuProjectTrash; QMenu * menuItem; QMenu * menuItemTrk; QMenu * menuItemWpt; QMenu * menuItemRte; QMenu * menuItemOvl; QAction * actionEditDetails; QAction * actionCopyItem; QAction * actionDelete; QAction * actionBubbleWpt; QAction * actionProjWpt; QAction * actionMoveWpt; QAction * actionFocusTrk; QAction * actionEditTrk; QAction * actionReverseTrk; QAction * actionCombineTrk; QAction * actionRangeTrk; QAction * actionFocusRte; QAction * actionCalcRte; QAction * actionResetRte; QAction * actionEditRte; QAction * actionEditArea; QAction * actionRteFromWpt; QMenu * menuNone = 0; QPointer searchGoogle; bool saveOnExit = true; qint32 saveEvery = 5; IDeviceWatcher * deviceWatcher = 0; }; #endif //CGISLISTWKS_H qmapshack-1.5.1/src/gis/CGisDraw.cpp000644 001750 000144 00000004113 12622435720 020201 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "helpers/CDraw.h" #include CGisDraw::CGisDraw(CCanvas *parent) : IDrawContext("gis", CCanvas::eRedrawGis, parent) { connect(&CGisWidget::self(), SIGNAL(sigChanged()), this, SLOT(emitSigCanvasUpdate())); } CGisDraw::~CGisDraw() { } void CGisDraw::draw(QPainter& p, const QRect& rect) { CGisWidget::self().fastDraw(p, rect, this); } void CGisDraw::drawt(buffer_t& currentBuffer) { QPointF pt1 = currentBuffer.ref1; QPointF pt2 = currentBuffer.ref2; QPointF pt3 = currentBuffer.ref3; QPointF pt4 = currentBuffer.ref4; qreal left, right, top, bottom; left = (pt1.x() < pt4.x() ? pt1.x() : pt4.x()); right = (pt2.x() > pt3.x() ? pt2.x() : pt3.x()); top = (pt1.y() < pt2.y() ? pt1.y() : pt2.y()); bottom = (pt4.y() > pt3.y() ? pt4.y() : pt3.y()); QPointF pp = currentBuffer.ref1; convertRad2Px(pp); QRectF rect(QPointF(left,top), QPointF(right, bottom)); QPolygonF viewport; viewport << pt1 << pt2 << pt3 << pt4; QPainter p(¤tBuffer.image); USE_ANTI_ALIASING(p,true); p.translate(-pp); CGisWidget::self().draw(p,viewport, this); } qmapshack-1.5.1/src/gis/CGisListDB.cpp000644 001750 000144 00000032371 12622435720 020434 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "config.h" #include "gis/CGisListDB.h" #include "gis/CGisWidget.h" #include "gis/db/CDBFolderDatabase.h" #include "gis/db/CDBFolderLostFound.h" #include "gis/db/CDBItem.h" #include "gis/db/CSetupDatabase.h" #include "gis/db/CSetupFolder.h" #include "gis/db/macros.h" #include "helpers/CSettings.h" #include #include class CGisListDBEditLock { public: CGisListDBEditLock(bool waitCursor, CGisListDB * widget, const QString& src) : widget(widget), waitCursor(waitCursor), src(src) { if(waitCursor) { CCanvas::setOverrideCursor(Qt::WaitCursor, "CGisListDBEditLock: " + src); } widget->isInternalEdit += 1; } ~CGisListDBEditLock() { if(waitCursor) { CCanvas::restoreOverrideCursor("~CGisListDBEditLock: " + src); } widget->isInternalEdit -= 1; } private: CGisListDB * widget; bool waitCursor; QString src; }; CGisListDB::CGisListDB(QWidget *parent) : QTreeWidget(parent) { SETTINGS; QStringList names = cfg.value("Database/names").toStringList(); QStringList files = cfg.value("Database/files").toStringList(); const int N = names.count(); for(int i = 0; i < N; i++) { addDatabase(names[i], files[i]); } menuNone = new QMenu(this); actionAddDatabase = menuNone->addAction(QIcon("://icons/32x32/Add.png"), tr("Add Database"), this, SLOT(slotAddDatabase())); menuFolder = new QMenu(this); actionAddFolder = menuFolder->addAction(QIcon("://icons/32x32/Add.png"), tr("Add Folder"), this, SLOT(slotAddFolder())); actionDelFolder = menuFolder->addAction(QIcon("://icons/32x32/DeleteOne.png"), tr("Delete Folder"), this, SLOT(slotDelFolder())); menuItem = new QMenu(this); actionDelItem = menuItem->addAction(QIcon("://icons/32x32/DeleteOne.png"), tr("Delete Item"), this, SLOT(slotDelItem())); menuDatabase = new QMenu(this); menuDatabase->addAction(actionAddFolder); actionDelDatabase = menuDatabase->addAction(QIcon("://icons/32x32/DeleteOne.png"), tr("Remove Database"), this, SLOT(slotDelDatabase())); menuLostFound = new QMenu(this); actionDelLostFound = menuLostFound->addAction(QIcon("://icons/32x32/Empty.png"), tr("Empty"), this, SLOT(slotDelLostFound())); menuLostFoundItem = new QMenu(this); actionDelLostFoundItem = menuLostFoundItem->addAction(QIcon("://icons/32x32/DeleteOne.png"), tr("Delete Item"), this, SLOT(slotDelLostFoundItem())); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint))); connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*))); connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int))); } CGisListDB::~CGisListDB() { SETTINGS; QStringList names; QStringList files; const int N = topLevelItemCount(); for(int n = 0; n < N; n++) { CDBFolderDatabase * database = dynamic_cast(topLevelItem(n)); if(database) { names << database->text(CGisListDB::eColumnName); files << database->getFilename(); } } cfg.setValue("Database/names", names); cfg.setValue("Database/files", files); } CDBFolderDatabase * CGisListDB::getDataBase(const QString& name) { CGisListDBEditLock lock(true, this, "getDataBase"); const int N = topLevelItemCount(); for(int n = 0; n < N; n++) { CDBFolderDatabase * database = dynamic_cast(topLevelItem(n)); if(database && (database->getDBName() == name)) { return database; } } return 0; } bool CGisListDB::hasDatabase(const QString& name) { CGisListDBEditLock lock(true, this, "hasDatabase"); const int N = topLevelItemCount(); for(int i = 0; i < N; i++) { CDBFolderDatabase * folder = dynamic_cast(topLevelItem(i)); if(folder && (folder->text(CGisListDB::eColumnName) == name)) { return true; } } return false; } bool CGisListDB::event(QEvent * e) { switch(e->type()) { case eEvtW2DAckInfo: { CGisListDBEditLock lock(true, this, "event"); CEvtW2DAckInfo * evt = (CEvtW2DAckInfo*)e; CDBFolderDatabase * folder = getDataBase(evt->db); if(folder) { folder->update(evt); if(evt->updateLostFound) { folder->updateLostFound(); } } e->accept(); return true; } case eEvtW2DCreate: { CGisListDBEditLock lock(true, this, "event"); CEvtW2DCreate * evt = (CEvtW2DCreate*)e; CDBFolderDatabase * db = getDataBase(evt->db); if(db) { quint64 idChild = 0; IDBFolder * folder = db->getFolder(evt->idParent); if(folder) { idChild = folder->addFolder(evt->type, evt->name); } else { idChild = IDBFolder::addFolderToDb(evt->type, evt->name, evt->idParent, db->getDb()); } if(idChild) { evt->idChild = idChild; CEvtD2WShowFolder * evt1 = new CEvtD2WShowFolder(idChild, evt->db); CGisWidget::self().postEventForWks(evt1); } } e->accept(); return true; } } return QTreeWidget::event(e); } void CGisListDB::slotContextMenu(const QPoint& point) { QPoint p = mapToGlobal(point); if(selectedItems().isEmpty()) { menuNone->exec(p); return; } CDBFolderDatabase * database = dynamic_cast(currentItem()); if(database) { menuDatabase->exec(p); return; } CDBFolderLostFound * lostFound = dynamic_cast(currentItem()); if(lostFound) { menuLostFound->exec(p); return; } IDBFolder * folder = dynamic_cast(currentItem()); if(folder) { menuFolder->exec(p); return; } CDBItem * item = dynamic_cast(currentItem()); if(item) { CDBFolderLostFound * lostFound = dynamic_cast(item->parent()); if(lostFound) { menuLostFoundItem->exec(p); } else { menuItem->exec(p); } return; } } void CGisListDB::slotAddDatabase() { QString name, filename("-"); CSetupDatabase dlg(name, filename, *this); if(dlg.exec() != QDialog::Accepted) { return; } addDatabase(name, filename); emit sigChanged(); } void CGisListDB::addDatabase(const QString& name, const QString& filename) { new CDBFolderDatabase(filename, name, this); } void CGisListDB::slotDelDatabase() { CDBFolderDatabase * folder = dynamic_cast(currentItem()); if(folder == 0) { return; } int res = QMessageBox::question(this, tr("Remove database..."), tr("Do you really want to remove '%1' from the list?").arg(folder->text(CGisListDB::eColumnName)), QMessageBox::Ok|QMessageBox::Abort, QMessageBox::Ok); if(res != QMessageBox::Ok) { return; } delete folder; emit sigChanged(); } void CGisListDB::slotAddFolder() { CGisListDBEditLock lock(false, this, "slotAddFolder"); IDBFolder * folder = dynamic_cast(currentItem()); if(folder == 0) { return; } IDBFolder::type_e type = IDBFolder::eTypeProject; QString name; CSetupFolder dlg(type, name, true, this); if(dlg.exec() != QDialog::Accepted) { return; } folder->addFolder(type, name); } void CGisListDB::slotDelFolder() { CGisListDBEditLock lock(false, this, "slotDelFolder"); IDBFolder * folder = dynamic_cast(currentItem()); if(folder == 0) { return; } int res = QMessageBox::question(this, tr("Delete database folder..."), tr("Are you sure you want to delete \"%1\" from the database?").arg(folder->text(1)), QMessageBox::Ok|QMessageBox::No); if(res != QMessageBox::Ok) { return; } CDBFolderDatabase * dbfolder = folder->getDBFolder(); folder->remove(); delete folder; if(dbfolder) { dbfolder->updateLostFound(); } } void CGisListDB::slotDelLostFound() { CGisListDBEditLock lock(false, this, "slotDelLostFound"); CDBFolderLostFound * folder = dynamic_cast(currentItem()); if(folder == 0) { return; } int res = QMessageBox::question(this, tr("Remove items..."), tr("Are you sure you want to delete all items from Lost&Found? This will remove them permanently."), QMessageBox::Ok|QMessageBox::No); if(res != QMessageBox::Ok) { return; } CCanvas::setOverrideCursor(Qt::WaitCursor, "slotDelLostFound"); folder->clear(); CCanvas::restoreOverrideCursor("slotDelLostFound"); } void CGisListDB::slotDelLostFoundItem() { CGisListDBEditLock lock(false, this, "slotDelLostFoundItem"); int res = QMessageBox::question(this, tr("Remove items..."), tr("Are you sure you want to delete all selected items from Lost&Found? This will remove them permanently."), QMessageBox::Ok|QMessageBox::No); if(res != QMessageBox::Ok) { return; } CCanvas::setOverrideCursor(Qt::WaitCursor, "slotDelLostFoundItem"); QSet folders; QList delItems; QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { CDBItem * dbItem = dynamic_cast(item); CDBFolderLostFound * folder = dynamic_cast(dbItem->parent()); if(folder && dbItem) { if(folder->delItem(dbItem)) { delItems << dbItem; folders << folder; } } } qDeleteAll(delItems); foreach(CDBFolderLostFound* folder, folders) { folder->update(); } CCanvas::restoreOverrideCursor("slotDelLostFoundItem"); } void CGisListDB::slotItemExpanded(QTreeWidgetItem * item) { CGisListDBEditLock lock(true, this, "slotItemExpanded"); IDBFolder * folder = dynamic_cast(item); if(folder == 0) { return; } folder->expanding(); } void CGisListDB::slotDelItem() { CGisListDBEditLock lock(false, this, "slotDelItem"); int last = QMessageBox::NoButton; QList dbItems; QSet dbFolders; QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { CDBItem * dbItem = dynamic_cast(item); if(dbItem == 0) { continue; } IDBFolder * folder = dynamic_cast(dbItem->parent()); if(folder == 0) { continue; } if(last != QMessageBox::YesToAll) { QString msg = QObject::tr("Are you sure you want to delete '%1' from folder '%2'?").arg(dbItem->text(CGisListDB::eColumnName)).arg(folder->text(CGisListDB::eColumnName)); last = QMessageBox::question(CMainWindow::getBestWidgetForParent(), QObject::tr("Delete..."), msg, QMessageBox::YesToAll|QMessageBox::Cancel|QMessageBox::Ok|QMessageBox::No, QMessageBox::Ok); } if(last == QMessageBox::No) { continue; } if(last == QMessageBox::Cancel) { return; } dbItem->remove(); dbItems << dbItem; dbFolders << folder->getDBFolder(); } qDeleteAll(dbItems); foreach(CDBFolderDatabase * dbFolder, dbFolders) { dbFolder->updateLostFound(); } } void CGisListDB::slotItemChanged(QTreeWidgetItem * item, int column) { if(isInternalEdit) { return; } CGisListDBEditLock lock(true, this, "slotItemChanged"); if(column == CGisListDB::eColumnCheckbox) { IDBFolder * folder = dynamic_cast(item); if(folder != 0) { folder->toggle(); return; } CDBItem * dbItem = dynamic_cast(item); if(dbItem != 0) { dbItem->toggle(); return; } } } qmapshack-1.5.1/src/gis/IGisItem.h000644 001750 000144 00000034210 12624271115 017654 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IGISITEM_H #define IGISITEM_H #include #include #include #include #include #include #include #include #include #include #include "units/IUnit.h" class CGisDraw; class IScrOpt; class IMouse; class QSqlDatabase; class IGisProject; class IGisItem : public QTreeWidgetItem { public: struct history_event_t { QDateTime time; QString hash; QString icon; QString comment; QByteArray data; }; struct history_t { history_t() : histIdxInitial(NOIDX), histIdxCurrent(NOIDX) { } qint32 histIdxInitial; qint32 histIdxCurrent; QList events; }; struct link_t { QUrl uri; QString text; QString type; }; struct wpt_t { wpt_t() : lat(NOFLOAT), lon(NOFLOAT), ele(NOINT), magvar(NOINT), geoidheight(NOINT), sat(NOINT), hdop(NOINT), vdop(NOINT), pdop(NOINT), ageofdgpsdata(NOINT), dgpsid(NOINT) { } // -- all gpx tags - start qreal lat; qreal lon; qint32 ele; QDateTime time; qint32 magvar; qint32 geoidheight; QString name; QString cmt; QString desc; QString src; QList links; QString sym; QString type; QString fix; qint32 sat; qint32 hdop; qint32 vdop; qint32 pdop; qint32 ageofdgpsdata; qint32 dgpsid; // -- all gpx tags - stop QMap extensions; }; /// never ever change these numbers. it will break binary data files enum type_e { eTypeWpt = 1 , eTypeTrk = 2 , eTypeRte = 3 , eTypeOvl = 4 , eTypeMax = 5 }; enum mark_e { eMarkNone = 0 ,eMarkChanged = 0x00000001 }; struct key_t { bool operator==(const key_t& k) const { return (item == k.item) && (project == k.project) && (device == k.device); } bool operator!=(const key_t& k) const { return (item != k.item) || (project != k.project) || (device != k.device); } void clear() { item.clear(); project.clear(); device.clear(); } QString item; QString project; QString device; }; IGisItem(IGisProject *parent, type_e typ, int idx); virtual ~IGisItem(); /// this mutex has to be locked when ever the item list is accessed. static QMutex mutexItems; /** @brief Update the visual representation of the QTreeWidgetItem @param enable @param disable */ virtual void updateDecoration(mark_e enable, mark_e disable); /** @brief Save the item's data into a GPX structure @param gpx the files tag to attach the data to */ virtual void save(QDomNode& gpx) = 0; /** @brief Get key string to identify object @return */ const key_t& getKey(); /** @brief Get a hash over the items data. Every entry in the history has a hash over the item's serialized data. If the data changes a new history entry is created and a new hash calculated. Thus the has can be used to detect if an item has been changed between the last time the hash was read. @return The hash as a string reference. */ const QString& getHash(); /** @brief Get the icon attached to object @return */ virtual const QPixmap& getIcon() const { return icon; } /** @brief Get name of this item. @return A reference to the internal string object */ virtual const QString& getName() const = 0; /** @brief Get name of this item extended by the project name @return A string object. */ virtual QString getNameEx() const; /** @brief Get a short string with the items properties to be displayed in tool tips or similar @return A string object. */ virtual QString getInfo(bool allowEdit = false) const = 0; virtual const QString& getComment() const = 0; virtual const QString& getDescription() const = 0; virtual const QList& getLinks() const = 0; virtual void setComment(const QString& str) = 0; virtual void setDescription(const QString& str) = 0; virtual void setLinks(const QList& links) = 0; /** @brief Edit content of item. This is quite dependent on the item. The default implementation does nothing. It has to be overwritten and the item has to generate what ever is needed to edit/view it's details. */ virtual void edit() { } /** @brief Get the dimension of the item All coordinates are in Rad. Items with no @return */ virtual const QRectF& getBoundingRect() const { return boundingRect; } /** @brief Get screen option object to display and handle actions for this item. @param mouse a pointer to the mouse object initiating the action @return A null pointer is returned if no screen option are available */ virtual IScrOpt * getScreenOptions(const QPoint& origin, IMouse * mouse) { return 0; } /** @brief Get a point of the item that is close by the given screen pixel coordinate @param point a point in screen pixels @return If no point is found NOPOINTF is returned. */ virtual QPointF getPointCloseBy(const QPoint& point) { return NOPOINTF; } /** @brief Test if the item is close to a given pixel coordinate of the screen @param pos the coordinate on the screen in pixel @return If no point can be found NOPOINTF is returned. */ virtual bool isCloseTo(const QPointF& pos) = 0; /** @brief Receive the current mouse position The default does nothing. Override if needed. @param pos the mouse position on the screen in pixel */ virtual void mouseMove(const QPointF& pos) { Q_UNUSED(pos); } void mousePress(const QPointF& pos) { Q_UNUSED(pos); } void mouseRelease(const QPointF& pos) { Q_UNUSED(pos); } /** @brief Query if this item is read only @return True if it is read only. */ bool isReadOnly() const; /** @brief Query if the item is imported and was changed @return True if content was changed. */ bool isTainted() const; /** @brief Check if item is on a GPS device @return True if the item is stored on a device */ bool isOnDevice() const; /** @brief Check if there are any pending unsaved changes @return True if the are changes to be safed */ bool isChanged() const; /** @brief Set the read only mode. This is quite dependent on the item. The default implementation will display a message box with a warning and ask the user to confirm. @param readOnly set true to make item read only @return Return true if the mode change has been accepted. */ virtual bool setReadOnlyMode(bool readOnly); virtual void drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis) = 0; virtual void drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis) { } virtual void drawLabel(QPainter& p, const QPolygonF& viewport,QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis) = 0; virtual void drawHighlight(QPainter& p) = 0; virtual void gainUserFocus(bool yes) = 0; /** @brief Check for user focus @return True if the item has user focus. The default implementation is always false. */ virtual bool hasUserFocus() const { return false; } /** @brief Serialize object out of a QDataStream See CGisSerialization.cpp for implementation @param stream the binary data stream @return The stream object. */ virtual QDataStream& operator<<(QDataStream& stream) = 0; /** @brief Serialize object into a QDataStream See CGisSerialization.cpp for implementation @param stream the binary data stream @return The stream object. */ virtual QDataStream& operator>>(QDataStream& stream) const = 0; /** @brief Get read access to history of changes @return A reference to the history structure. */ const history_t& getHistory() const { return history; } /** @brief Load a given state of change from the history @param idx */ void loadHistory(int idx); /** @brief Remove all history entries younger than the current selected one. */ void cutHistory(); /** @brief Remove all HTML tags from a string @param str the string @return A string without HTML tags */ static QString removeHtml(const QString &str); /** @brief Create a HTML formated text with comment, description and link section. Depending on the isReadOnly flag the section headers are links to trigger a function @param isReadOnly true if the text should have no active links @param cmt the comment string @param desc the description string @param links a list of links @param key some key to be sent with the header links @return The formated text ready to be used. */ static QString createText(bool isReadOnly, const QString& cmt, const QString& desc, const QList& links, const QString& key = ""); /** @brief Create a HTML formated text with description and link section. Depending on the isReadOnly flag the section headers are links to trigger a function @param isReadOnly true if the text should have no active links @param desc the description string @param links a list of links @param key some key to be sent with the header links @return The formated text ready to be used. */ static QString createText(bool isReadOnly, const QString& desc, const QList& links, const QString& key = ""); /** @brief Create a HTML formated text with a link. Depending on the isReadOnly flag the section headers are links to trigger a function @param isReadOnly true if the text should have no active links @param href the link address @param str the link's string @param key some key to be sent with the link @return The formated text ready to be used. */ static QString toLink(bool isReadOnly, const QString& href, const QString& str, const QString& key); /// a no key value that can be used to nullify references. const static QString noKey; const static QString noName; struct color_t { const char *name; const QColor color; const QString bullet; }; static const color_t colorMap[]; protected: /// set icon of QTreeWidgetItem virtual void setSymbol() = 0; /// read waypoint data from an XML snippet void readWpt(const QDomNode& xml, wpt_t &wpt); /// write waypoint data to an XML snippet void writeWpt(QDomElement &xml, const wpt_t &wpt); /// generate a unique key from item's data virtual void genKey(); /// setup the history structure right after the creation of the item void setupHistory(); /// update current history entry (e.g. to save the flags) void updateHistory(); /// convert a color string from GPX to a QT color QColor str2color(const QString& name); /// convert a QT color to a string to be used in a GPX file QString color2str(const QColor &color); /// to optimize drawing of large polylines split the line into sections that are visible void splitLineToViewport(const QPolygonF& line, const QRectF& extViewport, QList& lines); /// call when ever you make a change to the item's data virtual void changed(const QString& what, const QString& icon); virtual void loadFromDb(quint64 id, QSqlDatabase& db); bool isVisible(const QRectF& rect, const QPolygonF& viewport, CGisDraw * gis); bool isVisible(const QPointF& point, const QPolygonF& viewport, CGisDraw * gis); quint32 flags = 0; key_t key; QPixmap icon; QRectF boundingRect; history_t history; enum flags_e { eFlagCreatedInQms = 0x00000001 ,eFlagWriteAllowed = 0x00000002 ,eFlagTainted = 0x00000004 ,eFlagWptBubble = 0x00000100 }; static inline bool isBlocked(const QRectF& rect, const QList &blockedAreas) { foreach(const QRectF &r, blockedAreas) { if(rect.intersects(r)) { return true; } } return false; } }; QDataStream& operator>>(QDataStream& stream, IGisItem::history_t& h); QDataStream& operator<<(QDataStream& stream, const IGisItem::history_t& h); #endif //IGISITEM_H qmapshack-1.5.1/src/gis/trk/CSelectActivity.h000644 001750 000144 00000002541 12622435723 022050 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSELECTACTIVITY_H #define CSELECTACTIVITY_H #include "ui_ISelectActivity.h" #include class CSelectActivity : public QDialog, private Ui::ISelectActivity { Q_OBJECT public: CSelectActivity(quint32& flag, QString& name, QString& icon, QWidget * parent); virtual ~CSelectActivity(); private slots: void slotActivitySelected(bool); private: quint32& flag; QString& name; QString& icon; }; #endif //CSELECTACTIVITY_H qmapshack-1.5.1/src/gis/trk/ISelectActivity.ui000644 001750 000144 00000001161 12616741641 022243 0ustar00oeichlerusers000000 000000 ISelectActivity 0 0 171 273 Activities... Select one: qmapshack-1.5.1/src/gis/trk/IScrOptTrk.ui000644 001750 000144 00000014221 12623647641 021206 0ustar00oeichlerusers000000 000000 IScrOptTrk 0 0 300 65 Form 3 3 3 3 3 3 View details and edit properties of track. ... :/icons/32x32/EditDetails.png:/icons/32x32/EditDetails.png Copy track into another project. ... :/icons/32x32/Copy.png:/icons/32x32/Copy.png Delete track from project. ... :/icons/32x32/DeleteOne.png:/icons/32x32/DeleteOne.png Qt::Vertical Show on-screen profile and detailed information about points. ... :/icons/32x32/TrkProfile.png:/icons/32x32/TrkProfile.png true Select a range of points. ... :/icons/32x32/SelectRange.png:/icons/32x32/SelectRange.png Edit position of track points. ... :/icons/32x32/LineMove.png:/icons/32x32/LineMove.png Reverse track. ... :/icons/32x32/Reverse.png:/icons/32x32/Reverse.png Combine tracks. ... :/icons/32x32/Combine.png:/icons/32x32/Combine.png Cut track at selected point. You can use this to: * remove bad points at the start or end of the track * use the track parts to plan a new tour * cut a long track into stages ... :/icons/32x32/TrkCut.png:/icons/32x32/TrkCut.png Qt::Horizontal 40 20 TextLabel Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse qmapshack-1.5.1/src/gis/trk/CCombineTrk.cpp000644 001750 000144 00000014737 12622435720 021513 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/prj/IGisProject.h" #include "gis/trk/CCombineTrk.h" #include "gis/trk/CGisItemTrk.h" #include "plot/CPlotTrack.h" #include CCombineTrk::CCombineTrk(CGisItemTrk& trk, const QList &keysPreSel, IGisProject& project, QWidget * parent) : QDialog(parent) , trk(trk) , project(project) { setupUi(this); const int N = project.childCount(); for(int i = 0; i < N; i++) { CGisItemTrk * trk1 = dynamic_cast(project.child(i)); if(trk1 == 0) { continue; } if(keysPreSel.contains(trk1->getKey())) { continue; } const IGisItem::key_t& key = trk1->getKey(); QListWidgetItem * item = new QListWidgetItem(listAvailable); item->setText(trk1->getName()); item->setIcon(trk1->getIcon()); item->setData(Qt::UserRole + 1, key.item); item->setData(Qt::UserRole + 2, key.project); item->setData(Qt::UserRole + 3, key.device); } foreach(const IGisItem::key_t& key, keysPreSel) { IGisItem * gisItem = dynamic_cast(project.getItemByKey(key)); if(gisItem == 0) { continue; } QListWidgetItem * item = new QListWidgetItem(listSelected); item->setText(gisItem->getName()); item->setIcon(gisItem->getIcon()); item->setData(Qt::UserRole + 1, key.item); item->setData(Qt::UserRole + 2, key.project); item->setData(Qt::UserRole + 3, key.device); } connect(listAvailable, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); connect(listSelected, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); connect(toolSelect, SIGNAL(clicked()), this, SLOT(slotSelect())); connect(toolRemove, SIGNAL(clicked()), this, SLOT(slotRemove())); connect(toolUp, SIGNAL(clicked()), this, SLOT(slotUp())); connect(toolDown, SIGNAL(clicked()), this, SLOT(slotDown())); listAvailable->setCurrentItem(0); listSelected->setCurrentItem(0); slotSelectionChanged(); updatePreview(); } CCombineTrk::~CCombineTrk() { } void CCombineTrk::accept() { for(int i = 0; i < listSelected->count(); i++) { IGisItem::key_t key; key.item = listSelected->item(i)->data(Qt::UserRole + 1).toString(); key.project = listSelected->item(i)->data(Qt::UserRole + 2).toString(); key.device = listSelected->item(i)->data(Qt::UserRole + 3).toString(); CGisItemTrk * trk1 = dynamic_cast(project.getItemByKey(key)); if(trk1 == 0) { continue; } keys << key; } QDialog::accept(); } void CCombineTrk::slotSelectionChanged() { QListWidgetItem * item; item = listAvailable->currentItem(); toolSelect->setEnabled(item != 0); item = listSelected->currentItem(); toolRemove->setEnabled(item != 0); toolUp->setEnabled(item != 0); toolDown->setEnabled(item != 0); if(item) { if(listSelected->row(item) == 0) { toolUp->setEnabled(false); } if(listSelected->row(item) == (listSelected->count() - 1)) { toolDown->setEnabled(false); } } buttonBox->button(QDialogButtonBox::Ok)->setEnabled(listSelected->count() > 1); } void CCombineTrk::slotSelect() { QListWidgetItem * item; item = listAvailable->currentItem(); if(item == 0) { return; } listAvailable->takeItem(listAvailable->row(item)); listSelected->addItem(item); slotSelectionChanged(); updatePreview(); } void CCombineTrk::slotRemove() { QListWidgetItem * item; item = listSelected->currentItem(); if(item == 0) { return; } IGisItem::key_t key; key.item = item->data(Qt::UserRole + 1).toString(); key.project = item->data(Qt::UserRole + 2).toString(); key.device = item->data(Qt::UserRole + 3).toString(); if(key == trk.getKey()) { return; } listSelected->takeItem(listSelected->row(item)); listAvailable->addItem(item); slotSelectionChanged(); updatePreview(); } void CCombineTrk::slotUp() { QListWidgetItem * item = listSelected->currentItem(); if(item) { int row = listSelected->row(item); if(row == 0) { return; } listSelected->takeItem(row); row = row - 1; listSelected->insertItem(row,item); listSelected->setCurrentItem(item); } updatePreview(); } void CCombineTrk::slotDown() { QListWidgetItem * item = listSelected->currentItem(); if(item) { int row = listSelected->row(item); if(row == (listSelected->count() - 1)) { return; } listSelected->takeItem(row); row = row + 1; listSelected->insertItem(row,item); listSelected->setCurrentItem(item); } updatePreview(); } void CCombineTrk::updatePreview() { QPolygonF line; for(int i = 0; i < listSelected->count(); i++) { IGisItem::key_t key; key.item = listSelected->item(i)->data(Qt::UserRole + 1).toString(); key.project = listSelected->item(i)->data(Qt::UserRole + 2).toString(); key.device = listSelected->item(i)->data(Qt::UserRole + 3).toString(); CGisItemTrk * trk1 = dynamic_cast(project.getItemByKey(key)); if(trk1 == 0) { continue; } QPolygonF line1; trk1->getPolylineFromData(line1); line += line1; } plotTrack->setTrack(line); plotTrack->update(); } qmapshack-1.5.1/src/gis/trk/CPropertyTrk.cpp000644 001750 000144 00000006226 12623647641 021765 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CGisItemTrk.h" #include "gis/trk/CKnownExtension.h" #include "gis/trk/CPropertyTrk.h" #include "units/IUnit.h" #include CPropertyTrk::CPropertyTrk(const CGisItemTrk& trk) : trk(trk) { setupData(); } void CPropertyTrk::setupData() { properties.clear(); property_t propNull { "" , "" , QIcon() , CPlotData::eAxisLinear , "" , "" , 1.0 , nullptr , nullptr }; properties << propNull; property_t propProgress { "progress" , QObject::tr("Progress") , QIcon("://icons/32x32/Progress.png") , CPlotData::eAxisTime , QObject::tr("time") , QObject::tr("distance [%1]").arg(IUnit::self().baseunit) , IUnit::self().basefactor , [](const CGisItemTrk::trkpt_t &p) {return p.time.toTime_t(); } , [](const CGisItemTrk::trkpt_t &p) {return p.distance; } }; properties << propProgress; QStringList keys = trk.getExistingDataSources(); foreach(const QString &key, keys) { // skip elevation as it is covered by the profile plot if(key == "ele") { continue; } const CKnownExtension &ext = CKnownExtension::get(key); QString name = (ext.known ? ext.name : key); property_t property { key , name , QIcon(ext.icon) , CPlotData::eAxisLinear , QObject::tr("distance [%1]") , QString("%1 [%2]").arg(name).arg(ext.unit) , ext.factor , [](const CGisItemTrk::trkpt_t &p) {return p.distance; } , ext.valueFunc }; // lame hack if(key == "speed") { property.min = 0; } properties << property; } } void CPropertyTrk::fillComboBox(QComboBox * box) const { box->clear(); foreach(const property_t &p, properties) { box->addItem(p.icon, p.name, p.key); } } void CPropertyTrk::setupPlot(CPlot * plot, int idx) const { if(idx >= properties.size()) { return; } const property_t& p = properties[idx]; plot->setup(p.axisType, p.xLabel, p.yLabel, p.factor, p.getX, p.getY); plot->setLimits(p.min, p.max); } qmapshack-1.5.1/src/gis/trk/CScrOptTrk.cpp000644 001750 000144 00000010562 12622435720 021341 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisWidget.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/CScrOptTrk.h" #include "helpers/CDraw.h" #include "mouse/IMouse.h" CScrOptTrk::CScrOptTrk(CGisItemTrk * trk, const QPoint& point, IMouse *parent) : IScrOpt(parent) { key = trk->getKey(); setupUi(this); setOrigin(point); label->setFont(CMainWindow::self().getMapFont()); label->setText(trk->getInfo()); adjustSize(); toolProfile->setChecked(trk->hasUserFocus()); bool isOnDevice = trk->isOnDevice(); toolCut->setDisabled(isOnDevice); toolEdit->setDisabled(isOnDevice); toolReverse->setDisabled(isOnDevice); toolRange->setDisabled(isOnDevice); anchor = trk->getPointCloseBy(point); if((anchor - point).manhattanLength() > 50) { anchor = point; toolCut->setEnabled(false); } move(anchor.toPoint() + QPoint(-width()/2,SCR_OPT_OFFSET)); show(); connect(toolEditDetails, SIGNAL(clicked()), this, SLOT(hide())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(hide())); connect(toolProfile, SIGNAL(toggled(bool)), this, SLOT(hide())); connect(toolCut, SIGNAL(clicked()), this, SLOT(hide())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(hide())); connect(toolReverse, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCombine, SIGNAL(clicked()), this, SLOT(hide())); connect(toolRange, SIGNAL(clicked()), this, SLOT(hide())); connect(toolEditDetails, SIGNAL(clicked()), this, SLOT(slotEditDetails())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelete())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(slotCopy())); connect(toolProfile, SIGNAL(toggled(bool)), this, SLOT(slotProfile(bool))); connect(toolCut, SIGNAL(clicked()), this, SLOT(slotCut())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(slotEdit())); connect(toolReverse, SIGNAL(clicked()), this, SLOT(slotReverse())); connect(toolCombine, SIGNAL(clicked()), this, SLOT(slotCombine())); connect(toolRange, SIGNAL(clicked()), this, SLOT(slotRange())); // reset user focus if the track has it trk->setMouseFocusByPoint(NOPOINT, CGisItemTrk::eFocusMouseMove, "CScrOptTrk"); trk->setMouseFocusByPoint(point, CGisItemTrk::eFocusMouseClick, "CScrOptTrk"); } CScrOptTrk::~CScrOptTrk() { } void CScrOptTrk::slotDelete() { CGisWidget::self().delItemByKey(key); deleteLater(); } void CScrOptTrk::slotCopy() { CGisWidget::self().copyItemByKey(key); deleteLater(); } void CScrOptTrk::slotEditDetails() { CGisWidget::self().editItemByKey(key); deleteLater(); } void CScrOptTrk::slotProfile(bool on) { CGisWidget::self().focusTrkByKey(on, key); deleteLater(); } void CScrOptTrk::slotCut() { CGisWidget::self().cutTrkByKey(key); deleteLater(); } void CScrOptTrk::slotEdit() { CGisWidget::self().editTrkByKey(key); deleteLater(); } void CScrOptTrk::slotReverse() { CGisWidget::self().reverseTrkByKey(key); deleteLater(); } void CScrOptTrk::slotCombine() { CGisWidget::self().combineTrkByKey(key); deleteLater(); } void CScrOptTrk::slotRange() { CGisWidget::self().rangeTrkByKey(key); deleteLater(); } void CScrOptTrk::draw(QPainter& p) { IGisItem * item = CGisWidget::self().getItemByKey(key); if(item == 0) { QWidget::deleteLater(); return; } item->drawHighlight(p); CDraw::bubble(p, geometry(), anchor.toPoint()); } qmapshack-1.5.1/src/gis/trk/CSelectActivity.cpp000644 001750 000144 00000004367 12622435720 022410 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CActivityTrk.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/CSelectActivity.h" #include CSelectActivity::CSelectActivity(quint32 &flag, QString &name, QString &icon, QWidget * parent) : QDialog(parent) , flag(flag) , name(name) , icon(icon) { setupUi(this); int i = 0; QLayout * l = layout(); const CActivityTrk::desc_t* actDesc = CActivityTrk::getActivityDescriptors(); while(!actDesc[i].name.isEmpty()) { const CActivityTrk::desc_t& desc = actDesc[i]; QCheckBox * check = new QCheckBox(this); check->setText(desc.name); check->setIcon(QIcon(desc.iconLarge)); check->setProperty("flag", desc.flag); check->setProperty("name", desc.name); check->setProperty("symbol", desc.iconLarge); connect(check, SIGNAL(clicked(bool)), this, SLOT(slotActivitySelected(bool))); l->addWidget(check); i++; } l->addItem(new QSpacerItem(0,0,QSizePolicy::Maximum, QSizePolicy::MinimumExpanding)); } CSelectActivity::~CSelectActivity() { } void CSelectActivity::slotActivitySelected(bool) { QObject * s = sender(); bool ok = false; flag = s->property("flag").toUInt(&ok); if(ok) { name = s->property("name").toString(); icon = s->property("symbol").toString(); } QDialog::accept(); } qmapshack-1.5.1/src/gis/trk/CKnownExtension.cpp000644 001750 000144 00000013275 12623647641 022453 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CKnownExtension.h" #include "units/IUnit.h" QHash CKnownExtension::knownExtensions; CKnownExtension::CKnownExtension(QString name, qreal defLimitLow, qreal defLimitHigh, qreal minimum, qreal maximum, qreal factor, QString unit, QString icon, bool known, fTrkPtGetVal valueFunc ) : name(name), defLimitLow(defLimitLow), defLimitHigh(defLimitHigh), minimum(minimum), maximum(maximum), factor(factor), unit(unit), icon(icon), known(known), valueFunc(valueFunc) { } static fTrkPtGetVal getExtensionValueFunc(const QString ext) { return [ext](const CGisItemTrk::trkpt_t &p) { bool ok; qreal val = p.extensions.value(ext).toReal(&ok); return ok ? val : NOFLOAT; }; } void CKnownExtension::init(IUnit &units) { const QString &speedunit = units.speedunit; const qreal &speedfactor = units.speedfactor; const QString &baseunit = units.baseunit; const qreal &basefactor = units.basefactor; knownExtensions = { {"slope", { QObject::tr("Slope (directed)"), -10., 10., -90., 90., 1., "°", "://icons/32x32/CSrcSlope.png", true, [](const CGisItemTrk::trkpt_t &p) { return p.slope1; }} }, {"speed", { QObject::tr("Speed"), 1., 14., 0., 600., speedfactor, speedunit, "://icons/32x32/CSrcSpeed.png", true, [](const CGisItemTrk::trkpt_t &p) { return p.speed; }} }, {"ele", { QObject::tr("Elevation"), 200., 800., 0., 100000., basefactor, baseunit, "://icons/32x32/CSrcElevation.png", true, [](const CGisItemTrk::trkpt_t &p) { return (NOINT == p.ele) ? NOFLOAT : p.ele; }} }, // support for the Garmin TrackPointExtension v1 // https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd {"gpxtpx:TrackPointExtension|gpxtpx:hr", { QObject::tr("Heart Rate"), 100., 200., 0., 300., 1., "bpm", "://icons/32x32/CSrcHR.png", true, getExtensionValueFunc("gpxtpx:TrackPointExtension|gpxtpx:hr")} }, {"gpxtpx:TrackPointExtension|gpxtpx:cad", { QObject::tr("Cadence"), 50., 110., 0., 500., 1., "rpm", "://icons/32x32/CSrcCAD.png", true, getExtensionValueFunc("gpxtpx:TrackPointExtension|gpxtpx:cad")} }, {"gpxtpx:TrackPointExtension|gpxtpx:atemp", { QObject::tr("Air Temperature"), 10., 30., -100., 100., 1., "°C", "://icons/32x32/CSrcATemp.png", true, getExtensionValueFunc("gpxtpx:TrackPointExtension|gpxtpx:atemp")} }, {"gpxtpx:TrackPointExtension|gpxtpx:wtemp", { QObject::tr("Water Temperature"), 10., 30., -100., 100., 1., "°C", "://icons/32x32/CSrcWTemp.png", true, getExtensionValueFunc("gpxtpx:TrackPointExtension|gpxtpx:wtemp")} }, {"gpxtpx:TrackPointExtension|gpxtpx:depth", { QObject::tr("Depth"), 0., 200., 0., 12000., basefactor, baseunit, "://icons/32x32/CSrcDepth.png", true, getExtensionValueFunc("gpxtpx:TrackPointExtension|gpxtpx:depth")} }, {"tp1:TrackPointExtension|tp1:hr", { QObject::tr("Heart Rate"), 100., 200., 0., 300., 1., "bpm", "://icons/32x32/CSrcHR.png", true, getExtensionValueFunc("tp1:TrackPointExtension|tp1:hr")} }, {"tp1:TrackPointExtension|tp1:cad", { QObject::tr("Cadence"), 50., 110., 0., 500., 1., "rpm", "://icons/32x32/CSrcCAD.png", true, getExtensionValueFunc("tp1:TrackPointExtension|tp1:cad")} }, {"tp1:TrackPointExtension|tp1:atemp", { QObject::tr("Air Temperature"), 10., 30., -100., 100., 1., "°C", "://icons/32x32/CSrcATemp.png", true, getExtensionValueFunc("tp1:TrackPointExtension|tp1:atemp")} }, {"tp1:TrackPointExtension|tp1:wtemp", { QObject::tr("Water Temperature"), 10., 30., -100., 100., 1., "°C", "://icons/32x32/CSrcWTemp.png", true, getExtensionValueFunc("tp1:TrackPointExtension|tp1:wtemp")} }, {"tp1:TrackPointExtension|tp1:depth", { QObject::tr("Depth"), 0., 200., 0., 12000., basefactor, baseunit, "://icons/32x32/CSrcDepth.png", true, getExtensionValueFunc("tp1:TrackPointExtension|tp1:depth")} } }; } const CKnownExtension CKnownExtension::get(const QString &name) { CKnownExtension def("", 0., 100., -100000., 100000., 1., "", "://icons/32x32/CSrcUnknown.png", false, getExtensionValueFunc(name) ); return knownExtensions.value(name, def); } bool CKnownExtension::isKnown(const QString &name) { return knownExtensions.contains(name); } qmapshack-1.5.1/src/gis/trk/ICombineTrk.ui000644 001750 000144 00000015625 12527654570 021363 0ustar00oeichlerusers000000 000000 ICombineTrk 0 0 473 369 Combine Tracks... Qt::Vertical 20 40 false ... :/icons/32x32/Right.png:/icons/32x32/Right.png Qt::Vertical 20 40 false ... :/icons/32x32/Left.png:/icons/32x32/Left.png Qt::Vertical 20 40 Qt::Vertical 20 40 false ... :/icons/32x32/Up.png:/icons/32x32/Up.png Qt::Vertical 20 40 false ... :/icons/32x32/Down.png:/icons/32x32/Down.png Qt::Vertical 20 40 Qt::Horizontal 40 20 150 150 Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok CPlotTrack QWidget
plot/CPlotTrack.h
1
buttonBox accepted() ICombineTrk accept() 248 254 157 274 buttonBox rejected() ICombineTrk reject() 316 260 286 274
qmapshack-1.5.1/src/gis/trk/CGisItemTrk.h000644 001750 000144 00000063502 12624271115 021135 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISITEMTRK_H #define CGISITEMTRK_H #include "gis/IGisItem.h" #include "gis/IGisLine.h" #include "gis/trk/CActivityTrk.h" #include "helpers/INotifiable.h" #include #include #include class QDomNode; class IGisProject; class IPlot; class CDetailsTrk; class CScrOptTrk; class QSqlDatabase; class CQlgtTrack; class IQlgtOverlay; class QDir; class CProgressDialog; class CPropertyTrk; #define TRK_N_COLORS 17 #define ASCEND_THRESHOLD 5 #include class CGisItemTrk : public IGisItem, public IGisLine { public: struct trk_t; struct trkpt_t; enum focusmode_e { eFocusMouseMove ,eFocusMouseClick }; enum mode_e { eModeNormal , eModeRange }; /** @brief Used to create a new track from a part of an existing track @param name @param idx1 @param idx2 @param srctrk @param project */ CGisItemTrk(const QString& name, qint32 idx1, qint32 idx2, const trk_t &srctrk, IGisProject *project); /** @brief Used to create a copy of track with new parent @param parentTrk @param project @param idx @param clone */ CGisItemTrk(const CGisItemTrk& parentTrk, IGisProject * project, int idx, bool clone); /** @brief Used to restore a track from a line of coordinates @param l @param name @param project @param idx */ CGisItemTrk(const SGisLine &l, const QString &name, IGisProject *project, int idx); /** @brief Used to create track from GPX file @param xml @param project */ CGisItemTrk(const QDomNode &xml, IGisProject *project); /** @brief Used to restore track from history structure @param hist @param project */ CGisItemTrk(const history_t& hist, IGisProject * project); /** @brief Used to restore track from database @param id @param db @param project */ CGisItemTrk(quint64 id, QSqlDatabase& db, IGisProject * project); /** @brief Clone QLandkarte GT track @param trk1 */ CGisItemTrk(const CQlgtTrack& trk1); /** @brief Load track from file (e.g. TwoNav *trk) @param filename @param project */ CGisItemTrk(const QString& filename, IGisProject * project); CGisItemTrk(const IQlgtOverlay& ovl); virtual ~CGisItemTrk(); /** @brief Save track to GPX tree @param gpx The node to append by the track */ void save(QDomNode& gpx); /** @brief Save track to TwoNav track file @param dir the path to store the file */ bool saveTwoNav(const QString& filename); /** @brief Read serialized track from a binary data stream @param stream the data stream to read from @return A reference to the stream */ QDataStream& operator<<(QDataStream& stream); /** @brief Serialize track into a binary data stream @param stream the data stream to write to. @return A reference to the stream */ QDataStream& operator>>(QDataStream& stream) const; /// get name of track const QString& getName() const { return trk.name.isEmpty() ? noName : trk.name; } /// get the track color as index into the Garmin color table int getColorIdx() const { return colorIdx; } /// get the track color a Qt color object const QColor& getColor() const { return color; } /** @brief get a summary of the track @param allowEdit if true the track name is a link to allow interactions like edit @return */ QString getInfo(bool allowEdit = false) const; /// get a summary of a selected range QString getInfoRange(); /// get a summary of a selected range defined by two track points QString getInfoRange(const trkpt_t& pt1, const trkpt_t& pt2); /// get a summary for a track point QString getInfoTrkPt(const trkpt_t& pt); /// get a progress summary for a selected track point QString getInfoProgress(const trkpt_t& pt); quint32 getTotalElapsedSeconds() const { return totalElapsedSeconds; } quint32 getTotalElapsedSecondsMoving() { return totalElapsedSecondsMoving; } qreal getTotalAscend() const { return totalAscend; } qreal getTotalDescend() const { return totalDescend; } qreal getTotalDistance() const { return totalDistance; } const QString& getComment() const { return trk.cmt; } const QString& getDescription() const { return trk.desc; } const QList& getLinks() const { return trk.links; } /// get the track as a simple coordinate polyline void getPolylineFromData(QPolygonF &l); /// get the track as polyline with elevation, pixel and GIS coordinates. void getPolylineFromData(SGisLine& l); const QDateTime& getTimeStart() const { return timeStart; } qint32 getNumberOfVisiblePoints() const { return cntVisiblePoints; } const CActivityTrk& getActivities() const { return activities; } const CPropertyTrk * getPropertyHandler() const { return propHandler; } /** @defgroup ColorSource Stuff related to coloring tracks using data from different sources @{ */ public: static const struct ColorizeSource unknownColorizeSource; /** @brief Set the colorize source to the source specified. @param src The new source to use. */ void setColorizeSource(QString src); /** @brief Get the current colorize source. @return The new source to use. */ QString getColorizeSource() { return colorSource; } QStringList getExistingDataSources() const; void setColorizeLimitLow(qreal limit); qreal getColorizeLimitLow() const { return limitLow; } void setColorizeLimitHigh(qreal limit); qreal getColorizeLimitHigh() const { return limitHigh; } const QString getColorizeUnit() const; void getExtrema(qreal &min, qreal &max, const QString &source) const; private: QString colorSource = ""; // the low and high limit for (slope-)colored drawing of tracks qreal limitLow = -10; qreal limitHigh = 10; void drawColorized(QPainter &p); /**@}*/ /** @brief Get the indices of visible points for a selected range If no range is selected both indices will be NOIDX. @param idx1 a reference to receive the first index @param idx2 a reference to receive the second index */ public: void getSelectedVisiblePoints(qint32& idx1, qint32& idx2); void setName(const QString& str); void setColor(int idx); bool setMode(mode_e m, const QString &owner); void setComment(const QString& str); void setDescription(const QString& str); void setLinks(const QList& links); void setDataFromPolyline(const SGisLine &l); /** @brief display the track screen options @param origin the point on screen to anchor the options @param mouse the mouse object causing the request @return a pointer to the screen option widget */ IScrOpt * getScreenOptions(const QPoint &origin, IMouse * mouse); /** @brief Get a screen pixel of the track close to the given position on the screen @param screenPos Screen position as pixel coordinate @return The screen coordinates as pixel of a track point close by */ QPointF getPointCloseBy(const QPoint& screenPos); /** @brief isCloseTo @param pos Screen position as pixel coordinate @return True if point is considered close enough */ bool isCloseTo(const QPointF& pos); void drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis); void drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis); void drawLabel(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis); void drawHighlight(QPainter& p); void drawRange(QPainter& p); /** @brief Switch user focus on and off. If the focus is switched on any other track having the focus will loose it. @param yes set true to gain focus. */ void gainUserFocus(bool yes); /** @brief Make sure the track has lost focus. If the track has the focus, keyUserFocus will be reset. In all other cases nothing will be done. */ void looseUserFocus(); /** @brief Make sure a CDetailsTrk widget is registered with the main tab widget */ void edit(); /** @brief Cut track at mouseClickFocus @return Return true on success. */ bool cut(); /** @brief Reverse the complete track @note All timestamps will be removed */ void reverse(); /** @brief Combine this track with several others. @param keysPreSel list of pre-selected track item keys Handle the complete process of selecting tracks, choosing the order and the final name with dialogs. */ void combine(const QList &keysPreSel); /** @brief Set the trkpt_t::eHidden flag The flag is set for all track points between mouseClickFocus and mouseMoveFocus, regardless of their previous state. */ void hideSelectedPoints(); /** @brief Reset the trkpt_t::eHidden flag The flag is reset for all track points between mouseClickFocus and mouseMoveFocus, regardless of their previous state. */ void showSelectedPoints(); /** @brief Set the activity flag for all track points @param flag one of trkpt_t::flag_e::eAct... @param name the name of the activity @param icon a resource icon string to display with the activity */ void setActivity(quint32 flag, const QString &name, const QString &icon); /** @brief Sets the activity flag for a selected range of track points The range has to be selected already. The activity will be selected by a dialog displayed in this method. */ void setActivity(); /** @brief Copy a section into a new track object The section is defined by mouseClickFocus and mouseMoveFocus, All points are copied, including the hidden (trkpt_t::eHidden) ones. */ void copySelectedPoints(); /** @brief Check for user focus @return True if the track has user focus */ bool hasUserFocus() const { return key == keyUserFocus; } /** @brief Get the key of the current track with user focus @return If no track has the focus an empty string is returned */ static const key_t& getKeyUserFocus() { return keyUserFocus; } /** @brief Each plot widget that operates on the track must register during it's construction see registeredPlots for a detailed discussion @param plot */ void registerPlot(IPlot * plot); /** @brief Each plot widget that operates on the track must unregister during it's destruction see registeredPlots for a detailed discussion @param plot */ void unregisterPlot(IPlot * plot); /** @brief Use point with the distance from start matching best the given distance. @param dist the distance in [m] @param initiator a pointer to an initiating IPlot object, or 0 */ bool setMouseFocusByDistance(qreal dist, focusmode_e fmode, const QString& owner); /** @brief Use point with time from start matching best the given time delta @param time a time delta in [s] relative to the start time @param initiator a pointer to an initiating IPlot object, or 0 */ bool setMouseFocusByTime(quint32 time, focusmode_e fmode, const QString& owner); /** @brief Use the point that is closest to the given point on the screen. @param pt a point on the screen in pixel. */ QPointF setMouseFocusByPoint(const QPoint& pt, focusmode_e fmode, const QString& owner); /** @brief Use point with given index counter @param idx */ bool setMouseFocusByTotalIndex(qint32 idx, focusmode_e fmode, const QString& owner); /** @brief Reduce the amount of visible track points with the help of the Douglas Peuker algorithm @note All filter implementations are found in src/gis/trk/filter/filter.cpp @param dist the Douglas Peuker distance in meters */ void filterReducePoints(qreal dist); /** @brief Remove track points without valid location at the beginning of the track @note All filter implementations are found in src/gis/trk/filter/filter.cpp */ void filterRemoveNullPoints(); /** @brief filterReset @note All filter implementations are found in src/gis/trk/filter/filter.cpp */ void filterReset(); /** @brief filterDelete @note All filter implementations are found in src/gis/trk/filter/filter.cpp */ void filterDelete(); /** @brief filterSmoothProfile @note All filter implementations are found in src/gis/trk/filter/filter.cpp @param points size of Median filter */ void filterSmoothProfile(int points); /** @brief filterReplaceElevation */ void filterReplaceElevation(); /** @brief filterOffsetElevation @note All filter implementations are found in src/gis/trk/filter/filter.cpp @param offset elevation offset in meters */ void filterOffsetElevation(int offset); /** @brief filterNewDate @note All filter implementations are found in src/gis/trk/filter/filter.cpp @param date new date for start of track */ void filterNewDate(const QDateTime& date); /** @brief filterObscureDate @note All filter implementations are found in src/gis/trk/filter/filter.cpp @param delta intervall to increase timestamps in seconds */ void filterObscureDate(int delta); /** @brief filterSpeed @note All filter implementations are found in src/gis/trk/filter/filter.cpp @param speed speed in meter per seconds */ void filterSpeed(qreal speed); /** @brief Correlate waypoints with the track points If a waypoint correlates with a trackpoint it's key is written to trkpt_t::keyWpt. @param progress a progress dialog as this operation can take quite some time @param current the current progress if the operation is done for several tracks */ void findWaypointsCloseBy(CProgressDialog &progress, quint32 ¤t); private: void setSymbol(); /** @brief Read track data from section in GPX file @param xml The XML section @param trk The track structure to fill */ void readTrk(const QDomNode& xml, trk_t& trk); /** @brief Restore track from TwoNav *trk file @param filename */ bool readTwoNav(const QString& filename); /** @brief Derive secondary data from the track data This has to be called each time the track data is changed. */ void deriveSecondaryData(); /** @defgroup ExtremaExtensions Stuff related to calculation of extremas/extensions @{ */ public: struct limits_t { qreal min; qreal max; }; /**@}*/ private: QSet existingExtensions; QHash extrema; void updateExtremaAndExtensions(); /** @brief Try to get access Nth visible point matching the idx This will iterate over all segments and count the visible points. If the count matches idx a pointer to the track point is returned. @param idx The index into all visible points @return A null pointer of no point is found. */ const trkpt_t *getTrkPtByVisibleIndex(qint32 idx); /** @brief Try to get access Nth point This will iterate over all segments. If the index matches a pointer to the track point is returned. @param idx The index into all points @return A null pointer of no point is found. */ const trkpt_t *getTrkPtByTotalIndex(qint32 idx); /** @brief Check if the track point at index it the last one visible @param idxTotal The point's index @return True if it is the last one visible */ bool isTrkPtLastVisible(qint32 idxTotal); /** @brief Check if the track point at index it the first one visible @param idxTotal The point's index @return True if it is the first one visible */ bool isTrkPtFirstVisible(qint32 idxTotal); /** @brief Tell the point of focus to all plots and the detail dialog @param pt A pointer to the point itself @param fmode The reason for the focus @param owner A string to identify owner of the operation */ bool publishMouseFocus(const trkpt_t * pt, focusmode_e fmode, const QString &owner); void publishMouseFocusNormalMode(const trkpt_t * pt, focusmode_e fmode); void publishMouseFocusRangeMode(const trkpt_t * pt, focusmode_e fmode); /** @brief Replace all trackpoints by the coordinates stored in the polyline The DEM layer will be queried for elevation data. All other data is lost. @param l A polyline with coordinates [rad] */ void readTrackDataFromGisLine(const SGisLine &l); /** @brief Overide IGisItem::changed() method As the CDetailsTrk is no modal dialog that blocks the GUI from any other input the track can be changed while the widget is visible. Therefore it needs some feedback to update the CDetailsTrk widget. Usually this would be a signal. However CGisItemTrk is a QTreeWidgetItem and therefor no QObject. Fortunately there the dlgDetails pointer. So CDetailsTrk::setupGui() can be called from changed() @param what The reason string @param icon An icon string */ void changed(const QString& what, const QString& icon); /// setup colorIdx, color, bullet and icon void setColor(const QColor& c); /// setup track icon by color void setIcon(const QString& iconColor); public: struct trkpt_t : public wpt_t { trkpt_t() { reset(); } void reset() { deltaDistance = NOFLOAT; distance = NOFLOAT; ascend = NOFLOAT; descend = NOFLOAT; elapsedSeconds = NOFLOAT; elapsedSecondsMoving = NOFLOAT; slope1 = NOFLOAT; slope2 = NOFLOAT; speed = NOFLOAT; idxVisible = NOIDX; } enum flag_e { eHidden = 0x00000004 ///< mark point as deleted // activity flags ,eActNone = 0x00000000 ,eActFoot = 0x80000000 ,eActCycle = 0x40000000 ,eActBike = 0x20000000 ,eActCar = 0x10000000 ,eActCable = 0x08000000 ,eActSwim = 0x04000000 ,eActShip = 0x02000000 ,eActAero = 0x01000000 ,eActMask = 0xFF000000 ///< mask for activity flags ,eActMaxNum = 8 ///< maximum number of activity flags. this is defined by the mask }; quint32 flags = 0; /// index within the complete track qint32 idxTotal; /// offset into lineSimple qint32 idxVisible; /// the distance to the last point qreal deltaDistance; /// the distance from the start of the track qreal distance; /// the ascend from the start of the track qreal ascend; /// the descend from the start of the track qreal descend; /// the slope [°] over several points close by qreal slope1; /// the slope [%] over several points close by qreal slope2; /// the speed over several points close by qreal speed; /// the seconds since the start of the track qreal elapsedSeconds; /// the seconds since the start of the track with moving speed qreal elapsedSecondsMoving; /// the key of an attached waypoint key_t keyWpt; /// track point extensions QHash extensions; }; struct trkseg_t { QVector pts; }; struct trk_t { trk_t() { } // -- all gpx tags - start QString name; QString cmt; QString desc; QString src; QList links; quint64 number = 0; QString type; QVector segs; // -- all gpx tags - stop QString color; }; /** @brief Read only access to the track data. @return */ const trk_t& getTrackData() const { return trk; } void registerNotification(INotifiable *obj); void unregisterNotification(INotifiable *obj); private: /// this is the GPX structure oriented data of the track trk_t trk; /// the key of the track having the user focus. static key_t keyUserFocus; /// background (border) color of all tracks static const QPen penBackground; /// drawing and mouse interaction is dependent on the mode mode_e mode = eModeNormal; /** \defgroup TrackStatistics Some statistical values over the complete track */ /**@{*/ qint32 cntTotalPoints = 0; qint32 cntVisiblePoints = 0; QDateTime timeStart; QDateTime timeEnd; qreal totalDistance = 0; qreal totalAscend = 0; qreal totalDescend = 0; qreal totalElapsedSeconds = 0; qreal totalElapsedSecondsMoving = 0; /**@}*/ /** \defgroup DrawUtilies Objects used to draw the track */ /**@{*/ /// the track line color by index unsigned colorIdx = 4; /// the track line color QColor color; /// the pen with the actual track color QPen penForeground {Qt::blue, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; /// the trackpoint bullet icon QPixmap bullet; /// the current track line as screen pixel coordinates QPolygonF lineSimple; /// visible and invisible points QPolygonF lineFull; /**@}*/ /** A list of plot objects that need to get informed on any change in data. @note This is necessary because QTreeWidgetItem is not derived from QObject. Thus no signals and slots can be handled. Probably this is because the signal/slot system would be a huge overhead on treewidgets with a large amount of items. Anyway we need some kind of signaling between the track object and the plot objects displaying the data. And we have to keep in mind that the track can be delete by the user at any time. That is why no other object is allowed to save a pointer to the track. It must store the key. But accessing the track via key is expensive. That is why we make an exception here. As the track will delete all registered plot objects upon destruction, it should be ok to store the track object in the plot object, too. By that plot and track can easily communicate with each other. */ QSet registeredPlots; QSet notifyOnChange; void notifyChange(); /** \defgroup FocusRange Variables to handle mouse focus and range selection */ /**@{*/ enum rangestate_e { eRangeStateIdle , eRangeState1st , eRangeState2nd }; /// state variable for range selection rangestate_e rangeState = eRangeStateIdle; /** @brief Identify source of current range selection Each range selection operation has to provide an owner string. If mouseFocusOwner is not empty and different to the passed owner string the operation must be rejected. */ QString mouseFocusOwner; /// the current track point selected by mouse movement const trkpt_t * mouseMoveFocus = 0; /// the last track point the user clicked on const trkpt_t * mouseClickFocus = 0; /// the first point of a range selection const trkpt_t * mouseRange1 = 0; /// the second point of a range selection const trkpt_t * mouseRange2 = 0; /**@}*/ /// the track's details dialog if any QPointer dlgDetails; /// the track's screen option if visible QPointer scrOpt; /// all function concerning track activities have been moved to CActivityTrk CActivityTrk activities = {this}; /// all functions and data concerning graphs CPropertyTrk * propHandler = nullptr; }; using fTrkPtGetVal = std::function; #endif //CGISITEMTRK_H qmapshack-1.5.1/src/gis/trk/CKnownExtension.h000644 001750 000144 00000004277 12622665354 022122 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2015 Christian Eichler code@christian-eichler.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CKNOWNEXTENSION_H #define CKNOWNEXTENSION_H #include "gis/trk/CGisItemTrk.h" #include class CKnownExtension { public: static void init(IUnit &units); static const CKnownExtension get(const QString &name); static bool isKnown(const QString &name); QString name; //< userfriendly name ("Speed" "Heart Rate") qreal defLimitLow; //< the default lower limit qreal defLimitHigh; //< the default high limit qreal minimum; //< hard (enforced) minimum, cannot go lower qreal maximum; //< hard (enforced) maximum, cannot go higher qreal factor; //< factor used to convert a value to match the users' units QString unit; //< the unit (to be displayed) QString icon; //< path to an icon bool known; fTrkPtGetVal valueFunc; //< the function used to retrieve the value private: CKnownExtension(QString name, qreal defLimitLow, qreal defLimitHigh, qreal minimum, qreal maximum, qreal factor, QString unit, QString icon, bool known, fTrkPtGetVal valueFunc ); static QHash knownExtensions; }; #endif // CKNOWNEXTENSION_H qmapshack-1.5.1/src/gis/trk/CActivityTrk.cpp000644 001750 000144 00000034453 12622435720 021730 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CActivityTrk.h" #include "gis/trk/CGisItemTrk.h" #include "units/IUnit.h" CActivityTrk::desc_t CActivityTrk::actDescriptor[] = { { "Foot" , CGisItemTrk::trkpt_t::eActFoot , QObject::tr("Foot") , "://icons/48x48/ActFoot.png" , "://icons/16x16/ActFoot.png" }, { "Cycle" , CGisItemTrk::trkpt_t::eActCycle , QObject::tr("Bicycle") , "://icons/48x48/ActCycle.png" , "://icons/16x16/ActCycle.png" }, { "Bike" , CGisItemTrk::trkpt_t::eActBike , QObject::tr("Motor Bike") , "://icons/48x48/ActBike.png" , "://icons/16x16/ActBike.png" }, { "Car" , CGisItemTrk::trkpt_t::eActCar , QObject::tr("Car") , "://icons/48x48/ActCar.png" , "://icons/16x16/ActCar.png" }, { "Cable" , CGisItemTrk::trkpt_t::eActCable , QObject::tr("Cable Car") , "://icons/48x48/ActCable.png" , "://icons/16x16/ActCable.png" }, { "Swim" , CGisItemTrk::trkpt_t::eActSwim , QObject::tr("Swim") , "://icons/48x48/ActSwim.png" , "://icons/16x16/ActSwim.png" }, { "Ship" , CGisItemTrk::trkpt_t::eActShip , QObject::tr("Ship") , "://icons/48x48/ActShip.png" , "://icons/16x16/ActShip.png" }, { "Aeronautik" , CGisItemTrk::trkpt_t::eActAero , QObject::tr("Aeronautik") , "://icons/48x48/ActAero.png" , "://icons/16x16/ActAero.png" }, { "" , 0 , "" , "" } }; CActivityTrk::CActivityTrk(CGisItemTrk * trk) : trk(trk) , allFlags(0) , activitySummary(CGisItemTrk::trkpt_t::eActMaxNum + 1) { actDescriptor[0].name = QObject::tr("Foot"); actDescriptor[1].name = QObject::tr("Bicycle"); actDescriptor[2].name = QObject::tr("Motor Bike"); actDescriptor[3].name = QObject::tr("Car"); actDescriptor[4].name = QObject::tr("Cable Car"); actDescriptor[5].name = QObject::tr("Swim"); actDescriptor[6].name = QObject::tr("Ship"); actDescriptor[7].name = QObject::tr("Aeronautics"); } void CActivityTrk::update() { allFlags = 0; activityRanges.clear(); for(int i = 0; i < activitySummary.size(); i++) { activitySummary[i].reset(); } const CGisItemTrk::trk_t& data = trk->getTrackData(); const CGisItemTrk::trkpt_t * lastTrkpt = 0; const CGisItemTrk::trkpt_t * startTrkpt = 0; quint32 lastFlag = 0xFFFFFFFF; foreach(const CGisItemTrk::trkseg_t &seg, data.segs) { foreach(const CGisItemTrk::trkpt_t &pt, seg.pts) { allFlags |= pt.flags; if(pt.flags & CGisItemTrk::trkpt_t::eHidden) { continue; } lastTrkpt = &pt; if(pt.flags != lastFlag) { if(startTrkpt != 0) { activity_summary_t& summary = getSummary(activitySummary, lastFlag); summary.distance += pt.distance - startTrkpt->distance; summary.ascend += pt.ascend - startTrkpt->ascend; summary.descend += pt.descend - startTrkpt->descend; summary.ellapsedSeconds += pt.elapsedSeconds - startTrkpt->elapsedSeconds; summary.ellapsedSecondsMoving += pt.elapsedSecondsMoving - startTrkpt->elapsedSecondsMoving; activityRanges << activity_range_t(); activity_range_t& activity = activityRanges.last(); activity.d1 = startTrkpt->distance; activity.d2 = pt.distance; activity.t1 = startTrkpt->time.toTime_t(); activity.t2 = pt.time.toTime_t(); const desc_t& desc = getDescriptor(lastFlag); activity.name = desc.name; activity.icon = desc.iconSmall; } startTrkpt = &pt; lastFlag = pt.flags; } } } activity_summary_t& summary = getSummary(activitySummary, lastFlag); summary.distance += lastTrkpt->distance - startTrkpt->distance; summary.ascend += lastTrkpt->ascend - startTrkpt->ascend; summary.descend += lastTrkpt->descend - startTrkpt->descend; summary.ellapsedSeconds += lastTrkpt->elapsedSeconds - startTrkpt->elapsedSeconds; summary.ellapsedSecondsMoving += lastTrkpt->elapsedSecondsMoving - startTrkpt->elapsedSecondsMoving; activityRanges << activity_range_t(); activity_range_t& activity = activityRanges.last(); activity.d1 = startTrkpt->distance; activity.d2 = lastTrkpt->distance; activity.t1 = startTrkpt->time.toTime_t(); activity.t2 = lastTrkpt->time.toTime_t(); const desc_t& desc = getDescriptor(lastFlag); activity.name = desc.name; activity.icon = desc.iconSmall; allFlags &= CGisItemTrk::trkpt_t::eActMask; // for(int i = 0; i < 9; i++) // { // activity_summary_t& stat = summaries[i]; // qDebug() << "--------------" << i << "--------------"; // qDebug() << "stat.distance" << stat.distance; // qDebug() << "stat.ascend" << stat.ascend; // qDebug() << "stat.descend" << stat.descend; // qDebug() << "stat.timeMoving" << stat.ellapsedSecondsMoving; // qDebug() << "stat.timeTotal" << stat.ellapsedSeconds; // } } void CActivityTrk::printSummary(QString& str) const { printSummary(activitySummary, allFlags, str); } void CActivityTrk::printSummary(const QVector& summary, quint32 flags, QString& str) { quint32 mask; QString val, unit; if((flags == 0) && (summary.size() >= (int)CGisItemTrk::trkpt_t::eActMaxNum)) { const activity_summary_t& s = summary[CGisItemTrk::trkpt_t::eActMaxNum]; str += ""; IUnit::self().meter2distance(s.distance, val, unit); str += "").arg(val).arg(unit); IUnit::self().meter2elevation(s.ascend, val, unit); str += "").arg(val).arg(unit); IUnit::self().meter2elevation(s.descend, val, unit); str += "").arg(val).arg(unit); IUnit::self().meter2speed(s.distance/s.ellapsedSecondsMoving, val, unit); str += "").arg(val).arg(unit); IUnit::self().meter2speed(s.distance/s.ellapsedSeconds, val, unit); str += "").arg(val).arg(unit); IUnit::self().seconds2time(s.ellapsedSecondsMoving, val, unit); str += "").arg(val).arg(unit); IUnit::self().seconds2time(s.ellapsedSeconds, val, unit); str += "").arg(val).arg(unit); str += "
" + QObject::tr("Distance:") + QString("  %1 %2
" + QObject::tr("Ascend:") + QString("  %1 %2
" + QObject::tr("Descend:") + QString("  %1 %2
" + QObject::tr("Speed Moving:") + QString("  %1 %2
" + QObject::tr("Speed Total:") + QString("  %1 %2
" + QObject::tr("Time Moving:") + QString("  %1 %2
" + QObject::tr("Time Total:") + QString("  %1 %2
"; return; } const int N = qMin((int)CGisItemTrk::trkpt_t::eActMaxNum, summary.size()); str += ""; // ############### build header ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { str += QString("").arg(actDescriptor[i].iconSmall); } mask >>= 1; } str += ""; // ############### build Distance row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().meter2distance(s.distance, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; // ############### build Ascend row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().meter2elevation(s.ascend, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; // ############### build Descend row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().meter2elevation(s.descend, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; // ############### build Speed Moving row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().meter2speed(s.distance/s.ellapsedSecondsMoving, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; // ############### build Speed row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().meter2speed(s.distance/s.ellapsedSeconds, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; // ############### build Time Moving row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().seconds2time(s.ellapsedSecondsMoving, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; // ############### build Time Moving row ############### str += ""; str += ""; mask = 0x80000000; for(int i = 0; i < N; i++) { if(actDescriptor[i].objName.isEmpty()) { break; } if((flags & mask) != 0) { const activity_summary_t& s = getSummary(summary, mask); IUnit::self().seconds2time(s.ellapsedSeconds, val, unit); str += QString("").arg(val).arg(unit); } mask >>= 1; } str += ""; str += "
" + QObject::tr("Distance:") + "  %1 %2
" + QObject::tr("Ascend:") + "  %1 %2
" + QObject::tr("Descend:") + "  %1 %2
" + QObject::tr("Speed Moving:") + "  %1 %2
" + QObject::tr("Speed Total:") + "  %1 %2
" + QObject::tr("Time Moving:") + "  %1 %2
" + QObject::tr("Time Total:") + "  %1 %2
"; } void CActivityTrk::sumUp(QVector &summary) const { const int N = qMin(activitySummary.size(), summary.size()); for(int i = 0; i < N; i++) { const activity_summary_t& sum1 = activitySummary[i]; activity_summary_t& sum2 = summary[i]; sum2.distance += sum1.distance; sum2.ascend += sum1.ascend; sum2.descend += sum1.descend; sum2.ellapsedSeconds += sum1.ellapsedSeconds; sum2.ellapsedSecondsMoving += sum1.ellapsedSecondsMoving; } } const CActivityTrk::activity_summary_t &CActivityTrk::getSummary(const QVector& summary, quint32 flag) { qint32 cnt = 0; flag >>= 24; while(((flag & 0x01) == 0) && (cnt < qMin((int)CGisItemTrk::trkpt_t::eActMaxNum,summary.size()))) { cnt++; flag >>= 1; } return summary[cnt]; } CActivityTrk::activity_summary_t& CActivityTrk::getSummary(QVector &summary, quint32 flag) { qint32 cnt = 0; flag >>= 24; while(((flag & 0x01) == 0) && (cnt < qMin((int)CGisItemTrk::trkpt_t::eActMaxNum,summary.size()))) { cnt++; flag >>= 1; } return summary[cnt]; } const CActivityTrk::desc_t& CActivityTrk::getDescriptor(quint32 flag) { int i = 0; while(!actDescriptor[i].objName.isEmpty()) { if(actDescriptor[i].flag == flag) { break; } i++; } return actDescriptor[i]; } qmapshack-1.5.1/src/gis/trk/CPropertyTrk.h000644 001750 000144 00000004422 12623647641 021426 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CPROPERTYTRK_H #define CPROPERTYTRK_H #include "gis/trk/CGisItemTrk.h" #include "plot/CPlot.h" #include "plot/CPlotData.h" #include class QComboBox; class CPropertyTrk { public: virtual ~CPropertyTrk() = default; struct property_t { property_t() = default; property_t(const QString& key, const QString& name, const QIcon& icon, CPlotData::axistype_e axisType, const QString& xLabel, const QString& yLabel, qreal factor, fTrkPtGetVal getX, fTrkPtGetVal getY) : key(key) , name(name) , icon(icon) , axisType(axisType) , xLabel(xLabel) , yLabel(yLabel) , factor(factor) , getX(getX) , getY(getY) { } QString key; QString name; QIcon icon; CPlotData::axistype_e axisType = CPlotData::eAxisLinear; QString xLabel; QString yLabel; qreal min = NOFLOAT; qreal max = NOFLOAT; qreal factor = 1.0; fTrkPtGetVal getX = nullptr; fTrkPtGetVal getY = nullptr; }; void fillComboBox(QComboBox * box) const; void setupData(); void setupPlot(CPlot * plot, int idx) const; private: friend class CGisItemTrk; CPropertyTrk(const CGisItemTrk &trk); const CGisItemTrk& trk; QList properties; }; #endif //CPROPERTYTRK_H qmapshack-1.5.1/src/gis/trk/ICutTrk.ui000644 001750 000144 00000005666 12623647641 020544 0ustar00oeichlerusers000000 000000 ICutTrk 0 0 400 164 Cut Track Delete first part of the track and keep second one Keep both parts of the track true Keep first part of the track and delete second one Qt::Vertical 20 40 false Check this to store the result into a new track. If you keep both parts of the track you have to create new ones. If you want to keep just one half you can simply remove the points, or check this to create a new track. Create a new track true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ICutTrk accept() 248 254 157 274 buttonBox rejected() ICutTrk reject() 316 260 286 274 qmapshack-1.5.1/src/gis/trk/CGisItemTrk.cpp000644 001750 000144 00000202673 12624154220 021471 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "gis/CGisDraw.h" #include "gis/CGisWidget.h" #include "gis/prj/IGisProject.h" #include "gis/trk/CCombineTrk.h" #include "gis/trk/CCutTrk.h" #include "gis/trk/CDetailsTrk.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/CKnownExtension.h" #include "gis/trk/CPropertyTrk.h" #include "gis/trk/CScrOptTrk.h" #include "gis/trk/CSelectActivity.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CDraw.h" #include "helpers/CProgressDialog.h" #include "plot/IPlot.h" #include #include #include using std::numeric_limits; #define DEFAULT_COLOR 4 #define MIN_DIST_CLOSE_TO 10 #define MIN_DIST_FOCUS 200 #define WPT_FOCUS_DIST_IN (50*50) #define WPT_FOCUS_DIST_OUT (200*200) struct trkwpt_t { trkwpt_t() : x(0), y(0) { } QString name; qreal x; qreal y; IGisItem::key_t key; }; struct activity_t { QString name; QString icon; }; const QPen CGisItemTrk::penBackground(Qt::white, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); IGisItem::key_t CGisItemTrk::keyUserFocus; CGisItemTrk::CGisItemTrk(const QString &name, qint32 idx1, qint32 idx2, const trk_t& srctrk, IGisProject * project) : IGisItem(project, eTypeTrk, NOIDX) { flags = eFlagCreatedInQms; foreach(const trkseg_t &srcseg, srctrk.segs) { trkseg_t seg; foreach(const trkpt_t &srcpt, srcseg.pts) { if(srcpt.idxTotal < idx1) { continue; } if(srcpt.idxTotal > idx2) { break; } seg.pts << srcpt; } if(!seg.pts.isEmpty()) { trk.segs << seg; } } trk.name = name; trk.cmt = srctrk.cmt; trk.desc = srctrk.desc; trk.src = srctrk.src; trk.links = srctrk.links; trk.number = srctrk.number; trk.type = srctrk.type; deriveSecondaryData(); setupHistory(); updateDecoration(eMarkChanged, eMarkNone); } CGisItemTrk::CGisItemTrk(const CGisItemTrk& parentTrk, IGisProject *project, int idx, bool clone) : IGisItem(project, eTypeTrk, idx) { // copy track via history history = parentTrk.history; loadHistory(history.histIdxCurrent); // if track should be a clone clear history and key and // build new ones. if(clone) { trk.name += QObject::tr("_Clone"); key.clear(); history.events.clear(); setupHistory(); } if(parentTrk.isOnDevice() || !parentTrk.isReadOnly()) { flags |= eFlagWriteAllowed; } else { flags &= ~eFlagWriteAllowed; } deriveSecondaryData(); updateDecoration(eMarkChanged, eMarkNone); } CGisItemTrk::CGisItemTrk(const SGisLine& l, const QString& name, IGisProject * project, int idx) : IGisItem(project, eTypeTrk, idx) { trk.name = name; readTrackDataFromGisLine(l); flags |= eFlagCreatedInQms | eFlagWriteAllowed; setColor(str2color("")); setupHistory(); updateDecoration(eMarkChanged, eMarkNone); } CGisItemTrk::CGisItemTrk(const QDomNode& xml, IGisProject *project) : IGisItem(project, eTypeTrk, project->childCount()) { // --- start read and process data ---- setColor(penForeground.color()); readTrk(xml, trk); // --- stop read and process data ---- setupHistory(); updateDecoration(eMarkNone, eMarkNone); } CGisItemTrk::CGisItemTrk(const QString& filename, IGisProject * project) : IGisItem(project, eTypeTrk, project->childCount()) { // --- start read and process data ---- setColor(penForeground.color()); if(!readTwoNav(filename)) { throw -1; } // --- stop read and process data ---- setupHistory(); updateDecoration(eMarkNone, eMarkNone); } CGisItemTrk::CGisItemTrk(const history_t& hist, IGisProject * project) : IGisItem(project, eTypeTrk, project->childCount()) { history = hist; loadHistory(hist.histIdxCurrent); } CGisItemTrk::CGisItemTrk(quint64 id, QSqlDatabase& db, IGisProject * project) : IGisItem(project, eTypeTrk, NOIDX) { loadFromDb(id, db); } CGisItemTrk::~CGisItemTrk() { // reset user focus if focused on this track if(key == keyUserFocus) { keyUserFocus.clear(); } /** Delete all registered plot as they can't exist without the item. As the plot objects will unregister via unregisterPlot() in their destructor things will get a bit complicated here. Better create a copy of the list before we start to delete. */ qDeleteAll(registeredPlots.toList()); qDeleteAll(notifyOnChange.toList()); delete dlgDetails; // delete it after the detail dialog as it is used by the detail dialog delete propHandler; } void CGisItemTrk::setSymbol() { setColor(str2color(trk.color)); notifyChange(); } void CGisItemTrk::setDataFromPolyline(const SGisLine &l) { QMutexLocker lock(&mutexItems); /* as this will change the line significantly we better stop all focus operations and close the detail dialog. */ mouseClickFocus = 0; mouseMoveFocus = 0; mouseRange1 = 0; mouseRange2 = 0; rangeState = eRangeStateIdle; delete dlgDetails; readTrackDataFromGisLine(l); flags |= eFlagTainted; changed(QObject::tr("Changed trackpoints, sacrificed all previous data."), "://icons/48x48/LineMove.png"); } void CGisItemTrk::getPolylineFromData(QPolygonF &l) { QMutexLocker lock(&mutexItems); l.clear(); foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(!(pt.flags & trkpt_t::eHidden)) { l << QPointF(pt.lon * DEG_TO_RAD, pt.lat * DEG_TO_RAD); } } } } void CGisItemTrk::getPolylineFromData(SGisLine &l) { QMutexLocker lock(&mutexItems); l.clear(); foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(!(pt.flags & trkpt_t::eHidden)) { l << point_t(QPointF(pt.lon*DEG_TO_RAD, pt.lat * DEG_TO_RAD)); } } } } void CGisItemTrk::readTrackDataFromGisLine(const SGisLine &l) { QMutexLocker lock(&mutexItems); trk.segs.clear(); trk.segs.resize(1); trkseg_t& seg = trk.segs.first(); for(int i = 0; i < l.size(); i++) { seg.pts << trkpt_t(); trkpt_t& trkpt = seg.pts.last(); const point_t& pt = l[i]; trkpt.lon = pt.coord.x() * RAD_TO_DEG; trkpt.lat = pt.coord.y() * RAD_TO_DEG; trkpt.ele = pt.ele; for(int n = 0; n < pt.subpts.size(); n++) { seg.pts << trkpt_t(); trkpt_t& trkpt = seg.pts.last(); const subpt_t& sub = pt.subpts[n]; trkpt.lon = sub.coord.x() * RAD_TO_DEG; trkpt.lat = sub.coord.y() * RAD_TO_DEG; trkpt.ele = sub.ele; } } deriveSecondaryData(); notifyChange(); } void CGisItemTrk::registerPlot(IPlot * plot) { registeredPlots << plot; } void CGisItemTrk::unregisterPlot(IPlot * plot) { registeredPlots.remove(plot); } void CGisItemTrk::registerNotification(INotifiable *obj) { notifyOnChange << obj; } void CGisItemTrk::unregisterNotification(INotifiable *obj) { notifyOnChange.remove(obj); } void CGisItemTrk::notifyChange() { foreach(INotifiable *obj, notifyOnChange) { obj->notify(); } } QString CGisItemTrk::getInfo(bool allowEdit) const { QString val1, unit1, val2, unit2; QString str = "
"; if(allowEdit) { str += "" + toLink(isReadOnly(), "name", getName(), "") + ""; } else { str += "
" + getName() + "
"; } if(cntVisiblePoints == 0) { return str; } IUnit::self().meter2distance(totalDistance, val1, unit1); str += "
\n"; str += QObject::tr("Length: %1 %2").arg(val1).arg(unit1); if(totalAscend != NOFLOAT && totalDescend != NOFLOAT) { IUnit::self().meter2elevation(totalAscend, val1, unit1); IUnit::self().meter2elevation(totalDescend, val2, unit2); str += QObject::tr(", %1%2 %3, %4%5 %6").arg(QChar(0x2197)).arg(val1).arg(unit1).arg(QChar(0x2198)).arg(val2).arg(unit2); } if(totalElapsedSeconds != NOTIME) { IUnit::self().seconds2time(totalElapsedSeconds, val1, unit1); str += "
\n"; str += QObject::tr("Time: %1").arg(val1); IUnit::self().meter2speed(totalDistance / totalElapsedSeconds, val1, unit1); str += QObject::tr(", Speed: %1 %2").arg(val1).arg(unit1); } if(totalElapsedSecondsMoving != NOTIME) { IUnit::self().seconds2time(totalElapsedSecondsMoving, val1, unit1); str += "
\n"; str += QObject::tr("Moving: %1").arg(val1); IUnit::self().meter2speed(totalDistance / totalElapsedSecondsMoving, val1, unit1); str += QObject::tr(", Speed: %1 %2").arg(val1).arg(unit1); } if(timeStart.isValid()) { str += "
\n"; str += QObject::tr("Start: %1").arg(IUnit::datetime2string(timeStart, false, boundingRect.center())); } if(timeEnd.isValid()) { str += "
\n"; str += QObject::tr("End: %1").arg(IUnit::datetime2string(timeEnd, false, boundingRect.center())); } str += "
\n"; str += QObject::tr("Points: %1 (%2)").arg(cntVisiblePoints).arg(cntTotalPoints); str += "
"; return str; } QString CGisItemTrk::getInfoRange() { qreal tmp, d, slope1, slope2; QString str, val, unit; if(mouseRange1 == 0 || mouseRange2 == 0) { return str; } int idx1 = mouseRange1->idxTotal; int idx2 = mouseRange2->idxTotal; const trkpt_t * pt1, * pt2; if(idx1 < idx2) { pt1 = mouseRange1; pt2 = mouseRange2; } else { pt1 = mouseRange2; pt2 = mouseRange1; } while(pt1->flags & trkpt_t::eHidden) { if(pt1->idxTotal == (cntTotalPoints - 1)) { break; } pt1++; } while(pt2->flags & trkpt_t::eHidden) { if(pt2->idxTotal == 0) { break; } pt2--; } bool timeIsValid = pt1->time.isValid() && pt2->time.isValid(); qreal deltaTime = pt2->time.toTime_t() - pt1->time.toTime_t(); d = tmp = pt2->distance - pt1->distance; IUnit::self().meter2distance(tmp, val, unit); str += QString("%3 %1%2 ").arg(val).arg(unit).arg(QChar(0x21A6)); if(timeIsValid) { quint32 t = pt2->time.toTime_t() - pt1->time.toTime_t(); quint32 hh = t / 3600; quint32 mm = (t - hh * 3600) / 60; quint32 ss = (t - hh * 3600 - mm * 60); str += QString("%4 %1:%2:%3").arg(hh,2,10,QChar('0')).arg(mm,2,10,QChar('0')).arg(ss,2,10,QChar('0')).arg(QChar(0x231a)); IUnit::self().meter2speed(d/deltaTime, val, unit); str += QString(", %3 %1%2").arg(val).arg(unit).arg(QChar(0x21A3)); } str += "\n"; qreal deltaAscend = pt2->ascend - pt1->ascend; qreal deltaDescend = pt2->descend - pt1->descend; tmp = qAtan(deltaAscend/d); slope1 = qAbs(tmp * 360.0/(2 * M_PI)); slope2 = qTan(slope1 * DEG_TO_RAD) * 100; IUnit::self().meter2elevation(deltaAscend, val, unit); str += QString("%3 %1%2 (%4%5, %6%)").arg(val).arg(unit).arg(QChar(0x2197)).arg(qRound(slope1)).arg(QChar(0260)).arg(qRound(slope2)); if(timeIsValid) { IUnit::self().meter2speed(deltaAscend/deltaTime, val, unit); str += QString(", %1%2").arg(val).arg(unit); } str += "\n"; tmp = qAtan(deltaDescend/d); slope1 = qAbs(tmp * 360.0/(2 * M_PI)); slope2 = qTan(slope1 * DEG_TO_RAD) * 100; IUnit::self().meter2elevation(deltaDescend, val, unit); str += QString("%3 %1%2 (%4%5, %6%)").arg(val).arg(unit).arg(QChar(0x2198)).arg(qRound(slope1)).arg(QChar(0260)).arg(qRound(slope2)); if(timeIsValid) { IUnit::self().meter2speed(deltaDescend/deltaTime, val, unit); str += QString(", %1%2").arg(val).arg(unit); } return str + "\n"; } QString CGisItemTrk::getInfoTrkPt(const trkpt_t& pt) { QString str, val1, unit1; if(totalElapsedSeconds != 0) { str += IUnit::datetime2string(pt.time, false, QPointF(pt.lon, pt.lat) * DEG_TO_RAD); } str += "\n"; IUnit::self().meter2elevation(pt.ele, val1, unit1); str += QObject::tr("Ele.: %1 %2").arg(val1).arg(unit1); if(pt.slope1 != NOFLOAT) { str += QObject::tr(" slope: %1%3 (%2%)").arg(pt.slope1, 2, 'f', 0).arg(pt.slope2, 2, 'f', 0).arg(QChar(0260)); } if(pt.speed != NOFLOAT) { IUnit::self().meter2speed(pt.speed, val1, unit1); str += QObject::tr(" speed: %1%2").arg(val1).arg(unit1); } QStringList keys = pt.extensions.keys(); keys.sort(); qint32 more = keys.size() - 10; if(more > 0) { keys = keys.mid(0, 10); } foreach(const QString &key, keys) { const CKnownExtension &ext = CKnownExtension::get(key); if(ext.known) { str += "\n" + ext.name + ": " + QString("%1%2").arg(ext.valueFunc(pt),0,'f',1).arg(ext.unit); } else { QStringList tags = key.split("|"); str += "\n" + tags.last() + ": " + pt.extensions[key].toString(); } } if(more > 0) { str += "\n" + QObject::tr("... and %1 tags not displayed").arg(more); } return str; } QString CGisItemTrk::getInfoProgress(const trkpt_t& pt) { QString str, val, unit; if(pt.ascend != NOFLOAT) { IUnit::self().meter2elevation(pt.ascend, val, unit); str += QObject::tr("Ascend: %1%2 (%3%)").arg(val).arg(unit).arg(pt.ascend * 100/totalAscend, 2,'f',0); } else { str += QObject::tr("Ascend: - (-)"); } if(pt.descend != NOFLOAT) { IUnit::self().meter2elevation(pt.descend, val, unit); str += QObject::tr(" Descend: %1%2 (%3%)").arg(val).arg(unit).arg(pt.descend * 100/totalDescend, 2,'f',0); } else { str += QObject::tr(" Descend: - (-) "); } str += "\n"; if(pt.distance != NOFLOAT) { IUnit::self().meter2distance(pt.distance, val, unit); str += QObject::tr("Dist.: %1%2 (%3%)").arg(val).arg(unit).arg(pt.distance * 100/totalDistance, 2,'f',0); } else { str += QObject::tr("Dist.: - (-)"); } if(pt.elapsedSeconds != NOFLOAT) { IUnit::self().seconds2time(pt.elapsedSecondsMoving, val, unit); str += QObject::tr(" Moving: %1%2 (%3%)").arg(val).arg(unit).arg(pt.elapsedSecondsMoving * 100/totalElapsedSecondsMoving, 2,'f',0); } else { str += QObject::tr(" Moving: - (-) "); } return str; } QString CGisItemTrk::getInfoRange(const trkpt_t& pt1, const trkpt_t& pt2) { QString str, val, unit; qreal dt = NOFLOAT; if(pt1.time.isValid() && pt2.time.isValid()) { dt = pt2.time.toTime_t() - pt1.time.toTime_t(); } if((pt1.ascend != NOFLOAT) && (pt2.ascend != NOFLOAT)) { IUnit::self().meter2elevation(pt2.ascend - pt1.ascend, val, unit); str += QObject::tr("Ascend: %1%2").arg(val).arg(unit); if(dt != NOFLOAT) { IUnit::self().meter2speed((pt2.ascend - pt1.ascend)/dt, val, unit); str += QObject::tr(", %1%2").arg(val).arg(unit); } } else { str += QObject::tr("Ascend: -"); } if((pt1.descend != NOFLOAT) && (pt2.descend != NOFLOAT)) { IUnit::self().meter2elevation(pt2.descend - pt1.descend, val, unit); str += QObject::tr(" Descend: %1%2").arg(val).arg(unit); if(dt != NOFLOAT) { IUnit::self().meter2speed((pt2.descend - pt1.descend)/dt, val, unit); str += QObject::tr(", %1%2").arg(val).arg(unit); } } else { str += QObject::tr("Descend: -"); } str += "\n"; IUnit::self().meter2distance(pt2.distance - pt1.distance, val, unit); str += QObject::tr("Dist.: %1%2").arg(val).arg(unit); if(dt != NOFLOAT) { IUnit::self().seconds2time(dt, val, unit); str += QObject::tr(" Time: %1%2").arg(val).arg(unit); } return str; } IScrOpt * CGisItemTrk::getScreenOptions(const QPoint& origin, IMouse * mouse) { if(scrOpt.isNull()) { scrOpt = new CScrOptTrk(this, origin, mouse); } return scrOpt; } QPointF CGisItemTrk::getPointCloseBy(const QPoint& screenPos) { QMutexLocker lock(&mutexItems); qint32 i = 0; qint32 idx = NOIDX; qint32 d = NOINT; foreach(const QPointF &point, lineSimple) { int tmp = (screenPos - point).manhattanLength(); if(tmp < d) { idx = i; d = tmp; } i++; } if(idx < 0) { return NOPOINTF; } return lineSimple[idx]; } void CGisItemTrk::getSelectedVisiblePoints(qint32& idx1, qint32& idx2) { if((mouseRange1 == 0) || (mouseRange2 == 0)) { idx1 = NOIDX; idx2 = NOIDX; return; } idx1 = mouseRange1->idxVisible; idx2 = mouseRange2->idxVisible; if(idx1 > idx2) { qSwap(idx1,idx2); } } static inline void updateExtrema(CGisItemTrk::limits_t &extrema, qreal val) { extrema = { qMin(extrema.min, val), qMax(extrema.max, val) }; } void CGisItemTrk::updateExtremaAndExtensions() { extrema = QHash(); limits_t extremaSpeed = { numeric_limits::max(), numeric_limits::lowest() }; limits_t extremaSlope = { numeric_limits::max(), numeric_limits::lowest() }; limits_t extremaEle = { numeric_limits::max(), numeric_limits::lowest() }; existingExtensions = QSet(); QSet nonRealExtensions; foreach(const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.flags & trkpt_t::eHidden) { continue; } existingExtensions.unite(pt.extensions.keys().toSet()); foreach(const QString &key, pt.extensions.keys()) { bool isReal = false; qreal val = pt.extensions.value(key).toReal(&isReal); if(isReal) { const limits_t ¤t = extrema.value(key, { numeric_limits::max(), numeric_limits::lowest() }); extrema[key] = { qMin(current.min, val), qMax(current.max, val) }; } else { nonRealExtensions << key; } } if(NOFLOAT != pt.speed) { updateExtrema(extremaSpeed, pt.speed); } updateExtrema(extremaEle, pt.ele); updateExtrema(extremaSlope, pt.slope1); } } if(extremaEle.min < extremaEle.max) { existingExtensions << "ele"; extrema["ele"] = extremaEle; } if(extremaSlope.min < extremaSlope.max) { existingExtensions << "slope"; extrema["slope"] = extremaSlope; } if(numeric_limits::max() != extremaSpeed.min) { existingExtensions << "speed"; extrema["speed"] = extremaSpeed; } existingExtensions.subtract(nonRealExtensions); } void CGisItemTrk::deriveSecondaryData() { qreal north = -90; qreal east = -180; qreal south = 90; qreal west = 180; // reset all secondary data cntTotalPoints = 0; cntVisiblePoints = 0; timeStart = QDateTime(); timeEnd = QDateTime(); totalDistance = NOFLOAT; totalAscend = NOFLOAT; totalDescend = NOFLOAT; totalElapsedSeconds = NOTIME; totalElapsedSecondsMoving = NOTIME; // remove empty segments QVector::iterator i = trk.segs.begin(); while(i != trk.segs.end()) { if((*i).pts.isEmpty()) { i = trk.segs.erase(i); continue; } i++; } // no segments -> no data -> nothing to do if(trk.segs.isEmpty()) { return; } trkpt_t * lastTrkpt = 0; qreal timestampStart = NOFLOAT; qreal lastEle = NOFLOAT; for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int p = 0; p < seg.pts.size(); p++) { trkpt_t& trkpt = seg.pts[p]; trkpt.idxTotal = cntTotalPoints++; if(trkpt.flags & trkpt_t::eHidden) { trkpt.reset(); continue; } trkpt.idxVisible = cntVisiblePoints++; if(trkpt.lon < west) { west = trkpt.lon; } if(trkpt.lon > east) { east = trkpt.lon; } if(trkpt.lat < south) { south = trkpt.lat; } if(trkpt.lat > north) { north = trkpt.lat; } if(lastTrkpt != 0) { trkpt.deltaDistance = GPS_Math_Distance(lastTrkpt->lon * DEG_TO_RAD, lastTrkpt->lat * DEG_TO_RAD, trkpt.lon * DEG_TO_RAD, trkpt.lat * DEG_TO_RAD); trkpt.distance = lastTrkpt->distance + trkpt.deltaDistance; trkpt.elapsedSeconds = trkpt.time.toMSecsSinceEpoch()/1000.0 - timestampStart; // ascend descend if(lastEle != NOFLOAT) { qreal delta = trkpt.ele - lastEle; qreal absDelta = qAbs(delta); trkpt.ascend = lastTrkpt->ascend; trkpt.descend = lastTrkpt->descend; if(absDelta > ASCEND_THRESHOLD) { if(delta > 0) { trkpt.ascend += delta; } else { trkpt.descend -= delta; } lastEle = trkpt.ele; } } // time moving trkpt.elapsedSecondsMoving = lastTrkpt->elapsedSecondsMoving; qreal dt = (trkpt.time.toMSecsSinceEpoch() - lastTrkpt->time.toMSecsSinceEpoch()) / 1000.0; if(dt > 0 && ((trkpt.deltaDistance / dt) > 0.2)) { trkpt.elapsedSecondsMoving += dt; } } else { timeStart = trkpt.time; timestampStart = timeStart.toMSecsSinceEpoch()/1000.0; lastEle = trkpt.ele; trkpt.deltaDistance = 0; trkpt.distance = 0; trkpt.ascend = 0; trkpt.descend = 0; trkpt.elapsedSeconds = 0; trkpt.elapsedSecondsMoving = 0; } lastTrkpt = &trkpt; } } boundingRect = QRectF(QPointF(west * DEG_TO_RAD, north * DEG_TO_RAD), QPointF(east * DEG_TO_RAD,south * DEG_TO_RAD)); // speed and slope (short average +-25m) for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int p = 0; p < seg.pts.size(); p++) { trkpt_t& trkpt = seg.pts[p]; if(trkpt.flags & trkpt_t::eHidden) { continue; } qreal d1 = trkpt.distance; qreal e1 = trkpt.ele; qreal t1 = trkpt.time.toMSecsSinceEpoch() / 1000.0; int n = p; while(n>0) { trkpt_t & trkpt2 = seg.pts[n]; if((trkpt2.flags & trkpt_t::eHidden) || (trkpt2.ele == NOINT)) { n--; continue; } if(trkpt.distance - trkpt2.distance >= 25) { d1 = trkpt2.distance; e1 = trkpt2.ele; t1 = trkpt2.time.toMSecsSinceEpoch()/1000.0; break; } n--; } qreal d2 = trkpt.distance; qreal e2 = trkpt.ele; qreal t2 = trkpt.time.toMSecsSinceEpoch() / 1000.0; n = p; while(n < seg.pts.size()) { trkpt_t & trkpt2 = seg.pts[n]; if((trkpt2.flags & trkpt_t::eHidden) || (trkpt2.ele == NOINT)) { n++; continue; } if(trkpt2.distance - trkpt.distance >= 25) { d2 = trkpt2.distance; e2 = trkpt2.ele; t2 = trkpt2.time.toMSecsSinceEpoch() / 1000.0; break; } n++; } qreal a = qAtan((e2 - e1)/(d2 - d1)); trkpt.slope1 = a * 360.0/(2 * M_PI); trkpt.slope2 = qTan(trkpt.slope1 * DEG_TO_RAD) * 100; if((t2 - t1) > 0) { trkpt.speed = (d2 - d1) / (t2 - t1); } else { trkpt.speed = NOFLOAT; } } } if(lastTrkpt != 0) { timeEnd = lastTrkpt->time; totalDistance = lastTrkpt->distance; totalAscend = lastTrkpt->ascend; totalDescend = lastTrkpt->descend; totalElapsedSeconds = lastTrkpt->elapsedSeconds; totalElapsedSecondsMoving = lastTrkpt->elapsedSecondsMoving; } activities.update(); updateExtremaAndExtensions(); // make sure we have a graph properties object by now if(propHandler == nullptr) { propHandler = new CPropertyTrk(*this); } else { propHandler->setupData(); } foreach(IPlot * plot, registeredPlots) { plot->updateData(); } // qDebug() << "--------------" << getName() << "------------------"; // qDebug() << "totalDistance" << totalDistance; // qDebug() << "totalAscend" << totalAscend; // qDebug() << "totalDescend" << totalDescend; // qDebug() << "totalElapsedSeconds" << totalElapsedSeconds; // qDebug() << "totalElapsedSecondsMoving" << totalElapsedSecondsMoving; } void CGisItemTrk::findWaypointsCloseBy(CProgressDialog& progress, quint32& current) { IGisProject * project = dynamic_cast(parent()); if(project == 0) { return; } quint32 lastCurrent = current; bool withDoubles = project->getSorting() != IGisProject::eSortTrackWithoutDouble; QVector line; // combine all segments to a single line const int M = trk.segs.size(); for(int m = 0; m < M; m++) { trkseg_t& seg = trk.segs[m]; const int N = seg.pts.size(); for(int n = 0; n < N; n++) { trkpt_t& pt = seg.pts[n]; pt.keyWpt.clear(); if(pt.flags & CGisItemTrk::trkpt_t::eHidden) { continue; } pointDP dp; dp.x = pt.lon * DEG_TO_RAD; dp.y = pt.lat * DEG_TO_RAD; dp.idx = pt.idxVisible; line << dp; } } if(line.isEmpty()) { return; } // convert coordinates of all waypoints into meter coordinates relative to the first track point point3D pt0 = line[0]; QList trkwpts; for(int i=0; i < project->childCount(); i++) { CGisItemWpt * wpt = dynamic_cast(project->child(i)); if(wpt == 0) { continue; } QPointF pos; pos = wpt->getPosition(); trkwpt_t trkwpt; trkwpt.x = pos.x() * DEG_TO_RAD; trkwpt.y = pos.y() * DEG_TO_RAD; trkwpt.key = wpt->getKey(); qreal a1, a2; qreal d = GPS_Math_Distance(pt0.x, pt0.y, trkwpt.x, trkwpt.y, a1, a2); trkwpt.x = qCos(a1 * DEG_TO_RAD) * d; trkwpt.y = qSin(a1 * DEG_TO_RAD) * d; trkwpt.name = wpt->getName(); trkwpts << trkwpt; } // convert all coordinates into meter relative to the first track point. for(int i = 0; i < line.size(); i++) { qreal d, a1 = 0, a2 = 0; pointDP& pt1 = line[i]; d = GPS_Math_Distance(pt0.x, pt0.y, pt1.x, pt1.y, a1, a2); pt1.x = qCos(a1 * DEG_TO_RAD) * d; pt1.y = qSin(a1 * DEG_TO_RAD) * d; } foreach(const trkwpt_t &trkwpt, trkwpts) { qreal minD = WPT_FOCUS_DIST_IN; qint32 index = NOIDX; foreach(const pointDP &pt, line) { current++; qreal d = (trkwpt.x - pt.x)*(trkwpt.x - pt.x) + (trkwpt.y - pt.y)*(trkwpt.y - pt.y); if(d < WPT_FOCUS_DIST_IN) { if(d < minD) { index = pt.idx; minD = d; } } else if(withDoubles && (d > WPT_FOCUS_DIST_OUT)) { trkpt_t * trkpt = const_cast(getTrkPtByVisibleIndex(index)); if(trkpt) { trkpt->keyWpt = trkwpt.key; } index = NOIDX; minD = WPT_FOCUS_DIST_IN; } if(current - lastCurrent > 100) { lastCurrent = current; PROGRESS(current, return ); } } if(index != NOIDX) { trkpt_t * trkpt = const_cast(getTrkPtByVisibleIndex(index)); if(trkpt) { trkpt->keyWpt = trkwpt.key; } } } if(!dlgDetails.isNull()) { dlgDetails->setupGui(); } foreach(IPlot * plot, registeredPlots) { plot->updateData(); } } bool CGisItemTrk::isCloseTo(const QPointF& pos) { QMutexLocker lock(&mutexItems); qreal dist = GPS_Math_DistPointPolyline(lineSimple, pos); return dist < 20; } void CGisItemTrk::gainUserFocus(bool yes) { keyUserFocus = yes ? key : key_t(); } void CGisItemTrk::looseUserFocus() { if(keyUserFocus == key) { keyUserFocus.clear(); } } void CGisItemTrk::edit() { if(dlgDetails.isNull()) { dlgDetails = new CDetailsTrk(*this, 0); dlgDetails->setObjectName(getName()); } CMainWindow::self().addWidgetToTab(dlgDetails); } bool CGisItemTrk::cut() { if(mouseClickFocus == 0) { return false; } CCutTrk dlg(CMainWindow::getBestWidgetForParent()); if(dlg.exec() == QDialog::Rejected) { return false; } qint32 idxMouse = mouseClickFocus->idxTotal; CCutTrk::mode_e mode = dlg.getMode(); // if the cut action results into cloning a track, the calling method should // ask if the original track should be removed. As a track can't delete itself // this has to be done from the outside of this method. bool askToDeleteOriginal = dlg.createClone() || (mode == CCutTrk::eModeKeepBoth); // askToDeleteOriginal = store result as clone if(askToDeleteOriginal) { QString name1; IGisProject * project; // clone first part? if((mode & (CCutTrk::eModeKeepBoth|CCutTrk::eModeKeepFirst)) != 0) { name1 = getName() + QString(" (%1 - %2)").arg(0).arg(idxMouse); name1 = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new track name."), QLineEdit::Normal, name1); if(name1.isEmpty()) { return false; } project = CGisWidget::self().selectProject(); if(project == 0) { return false; } new CGisItemTrk(name1, 0, idxMouse, trk, project); } // clone second part? if((mode & (CCutTrk::eModeKeepBoth|CCutTrk::eModeKeepSecond)) != 0) { name1 = getName() + QString(" (%1 - %2)").arg(idxMouse).arg(cntTotalPoints-1); name1 = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new track name."), QLineEdit::Normal, name1); if(name1.isEmpty()) { return false; } project = CGisWidget::self().selectProject(); if(project == 0) { return false; } new CGisItemTrk(name1, idxMouse, cntTotalPoints-1, trk, project); } } else { // if the result is not a clone, the track's list of segments and trackpoints // has to be reduced. This is done by copying points into a new trackpoint list // that replaces the original one. if((mode & CCutTrk::eModeKeepFirst) != 0) { for(int i = 0; i < trk.segs.size(); i++) { QVector pts; trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; if(pt.idxTotal > idxMouse) { break; } pts << pt; } seg.pts = pts; } deriveSecondaryData(); changed(QObject::tr("Permanently removed points %1..%2").arg(idxMouse+1).arg(cntTotalPoints-1), "://icons/48x48/TrkCut.png"); } else if((mode & CCutTrk::eModeKeepSecond) != 0) { for(int i = 0; i < trk.segs.size(); i++) { QVector pts; trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; if(pt.idxTotal < idxMouse) { continue; } pts << pt; } seg.pts = pts; } deriveSecondaryData(); changed(QObject::tr("Permanently removed points %1..%2").arg(0).arg(idxMouse-1), "://icons/48x48/TrkCut.png"); } } return askToDeleteOriginal; } void CGisItemTrk::reverse() { QString name1 = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new track name."), QLineEdit::Normal, getName() + "_rev"); if(name1.isEmpty()) { return; } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } // start with a 1:1 copy of the first track CGisItemTrk * trk1 = new CGisItemTrk(*this, project, NOIDX, false); trk1->trk.name = name1; /* clear track data, item key and history. To clear the history is important as the original track's history would restore the original key */ trk1->trk.segs.clear(); trk1->key.clear(); trk1->history.events.clear(); foreach(const trkseg_t &seg, trk.segs) { trkseg_t seg1; foreach(const trkpt_t &pt, seg.pts) { trkpt_t pt1 = pt; pt1.time = QDateTime(); seg1.pts.push_front(pt1); } trk1->trk.segs.push_front(seg1); } // restore secondary data and create a new history trk1->deriveSecondaryData(); trk1->setupHistory(); trk1->updateDecoration(eMarkChanged, eMarkNone); } void CGisItemTrk::combine(const QList& keysPreSel) { IGisProject * project = dynamic_cast(parent()); if(project == 0) { return; } CCombineTrk dlg(*this, keysPreSel, *project, CMainWindow::getBestWidgetForParent()); dlg.exec(); QList keys = dlg.getTrackKeys(); if(keys.isEmpty()) { return; } QString name1 = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new track name."), QLineEdit::Normal, getName() + " & other"); if(name1.isEmpty()) { return; } IGisProject * projectNew = CGisWidget::self().selectProject(); if(projectNew == 0) { return; } // start with a 1:1 copy of the first track CGisItemTrk * trk1 = new CGisItemTrk(*this, projectNew, NOIDX, false); // replace name trk1->trk.name = name1; /* clear track data, item key and history. To clear the history is important as the original track's history would restore the original key */ trk1->trk.segs.clear(); trk1->key.clear(); trk1->history.events.clear(); // copy the segments of all tracks to new track foreach(const IGisItem::key_t &key, keys) { CGisItemTrk * trk2 = dynamic_cast(project->getItemByKey(key)); if(trk2 == 0) { continue; } trk1->trk.segs += trk2->trk.segs; } // restore secondary data and create a new history trk1->deriveSecondaryData(); trk1->setupHistory(); trk1->updateDecoration(eMarkChanged, eMarkNone); } void CGisItemTrk::hideSelectedPoints() { if(!setReadOnlyMode(false)) { return; } if((mouseRange1 == 0) && (mouseRange2 == 0)) { return; } // read start/stop indices qint32 idx1 = mouseRange1->idxTotal; qint32 idx2 = mouseRange2->idxTotal; if(idx1 > idx2) { qSwap(idx1,idx2); } // if first index is the first point adjust index to hide it, too if(isTrkPtFirstVisible(idx1)) { idx1--; } // if second index is the last point adjust index to hide it, too if(isTrkPtLastVisible(idx2)) { idx2++; } // special case for a single point if(idx1 == idx2) { for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int i = 0; i < seg.pts.size(); i++) { trkpt_t& trkpt = seg.pts[i]; if(idx1 == trkpt.idxTotal) { trkpt.flags |= trkpt_t::eHidden; } } } } else { // iterate over all segments and delete points between idx1 and idx2 for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int i = 0; i < seg.pts.size(); i++) { trkpt_t& trkpt = seg.pts[i]; if((idx1 < trkpt.idxTotal) && (trkpt.idxTotal < idx2)) { trkpt.flags |= trkpt_t::eHidden; } } } } mouseRange1 = 0; mouseRange2 = 0; rangeState = eRangeStateIdle; deriveSecondaryData(); changed(QObject::tr("Hide points."), "://icons/48x48/PointHide.png"); } void CGisItemTrk::showSelectedPoints() { if(!setReadOnlyMode(false)) { return; } if((mouseRange1 == 0) && (mouseRange2 == 0)) { return; } qint32 idx1 = mouseRange1->idxTotal; qint32 idx2 = mouseRange2->idxTotal; if(idx1 > idx2) { qSwap(idx1,idx2); } for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int i = 0; i < seg.pts.size(); i++) { trkpt_t& trkpt = seg.pts[i]; if((idx1 <= trkpt.idxTotal) && (trkpt.idxTotal <= idx2)) { trkpt.flags &= ~trkpt_t::eHidden; } } } mouseRange1 = 0; mouseRange2 = 0; rangeState = eRangeStateIdle; deriveSecondaryData(); changed(QObject::tr("Show points."), "://icons/48x48/PointShow.png"); } void CGisItemTrk::copySelectedPoints() { if((mouseRange1 == 0) && (mouseRange2 == 0)) { return; } quint32 idx1 = mouseRange1->idxTotal; quint32 idx2 = mouseRange2->idxTotal; if(idx1 > idx2) { qSwap(idx1,idx2); } IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } QString name1 = getName() + QString(" (%1 - %2)").arg(idx1).arg(idx2); name1 = QInputDialog::getText(CMainWindow::getBestWidgetForParent(), QObject::tr("Edit name..."), QObject::tr("Enter new track name."), QLineEdit::Normal, name1); if(name1.isEmpty()) { return; } new CGisItemTrk(name1, idx1, idx2, trk, project); } void CGisItemTrk::drawItem(QPainter& p, const QPolygonF& viewport, QList &blockedAreas, CGisDraw *gis) { QMutexLocker lock(&mutexItems); lineSimple.clear(); lineFull.clear(); if(!isVisible(boundingRect, viewport,gis)) { return; } if(trk.segs.isEmpty()) { return; } QPointF pt1; QPointF p1 = viewport[0]; QPointF p2 = viewport[2]; gis->convertRad2Px(p1); gis->convertRad2Px(p2); QRectF extViewport(p1,p2); if(mode == eModeNormal) { // in normal mode the trackline without points marked as deleted is drawn foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.flags & trkpt_t::eHidden) { continue; } pt1.setX(pt.lon); pt1.setY(pt.lat); pt1 *= DEG_TO_RAD; lineSimple << pt1; } } } else { // in full mode the complete track including points marked as deleted // is drawn as gray line first. Then the track without points marked as // deleted is drawn with it's configured color foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { pt1.setX(pt.lon); pt1.setY(pt.lat); pt1 *= DEG_TO_RAD; lineFull << pt1; if(pt.flags & trkpt_t::eHidden) { continue; } lineSimple << pt1; } } } gis->convertRad2Px(lineSimple); gis->convertRad2Px(lineFull); // draw the full line first if(mode == eModeRange) { QList lines; splitLineToViewport(lineFull, extViewport, lines); p.setPen(QPen(Qt::lightGray,5,Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); foreach(const QPolygonF &l, lines) { p.drawPolyline(l); } QPixmap bullet("://icons/8x8/bullet_dark_gray.png"); foreach(const QPolygonF &l, lines) { foreach(const QPointF &pt, l) { p.drawPixmap(pt.x() - 3, pt.y() - 3, bullet); } } } // ------------------------- // draw the reduced track line QList lines; splitLineToViewport(lineSimple, extViewport, lines); if(key == keyUserFocus) { p.setPen(QPen(Qt::red,11,Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); foreach(const QPolygonF &l, lines) { p.drawPolyline(l); } } p.setBrush(color); p.setPen(penBackground); foreach(const QPolygonF &l, lines) { p.drawPolyline(l); CDraw::arrows(l, extViewport, p, 10, 80); } if(colorSource.isEmpty()) { // use the track's ordinary color penForeground.setColor(color); p.setPen(penForeground); foreach(const QPolygonF &l, lines) { p.drawPolyline(l); } } else { drawColorized(p); } // ------------------------- } void CGisItemTrk::drawColorized(QPainter &p) { auto valueFunc = CKnownExtension::get(colorSource).valueFunc; QImage colors(1, 256, QImage::Format_RGB888); QPainter colorsPainter(&colors); QLinearGradient colorsGradient(colors.rect().topLeft(), colors.rect().bottomLeft()); colorsGradient.setColorAt(1.00, QColor( 0, 0, 255)); // blue colorsGradient.setColorAt(0.60, QColor( 0, 255, 0)); // green colorsGradient.setColorAt(0.40, QColor(255, 255, 0)); // yellow colorsGradient.setColorAt(0.00, QColor(255, 0, 0)); // red colorsPainter.fillRect(colors.rect(), colorsGradient); const qreal factor = CKnownExtension::get(colorSource).factor; foreach(const trkseg_t &segment, trk.segs) { const trkpt_t *ptPrev = NULL; QColor colorStart; foreach(const trkpt_t &pt, segment.pts) { if(pt.flags & trkpt_t::eHidden) { continue; } if(NULL == ptPrev) { ptPrev = &pt; continue; } float colorAt = ( factor * valueFunc(pt) - limitLow ) / (limitHigh - limitLow); if(colorAt > 1.f) { colorAt = 1.f; } if(colorAt < 0.f) { colorAt = 0.f; } const QColor &colorEnd = colors.pixel(0, ((1.f - colorAt) * 255.f)); if(!colorStart.isValid()) { colorStart = colorEnd; } QLinearGradient grad(lineSimple[ptPrev->idxVisible], lineSimple[pt.idxVisible]); grad.setColorAt(0.f, colorStart); grad.setColorAt(1.f, colorEnd); QPen pen; pen.setBrush(QBrush(grad)); pen.setWidth(3); p.setPen(pen); p.drawLine(lineSimple[ptPrev->idxVisible], lineSimple[pt.idxVisible]); ptPrev = &pt; colorStart = colorEnd; } } } void CGisItemTrk::getExtrema(qreal &min, qreal &max, const QString &source) const { min = extrema.value(source).min * CKnownExtension::get(source).factor; max = extrema.value(source).max * CKnownExtension::get(source).factor; } QStringList CGisItemTrk::getExistingDataSources() const { QStringList known; QStringList unknown; foreach(const QString &key, existingExtensions) { if(CKnownExtension::isKnown(key)) { known << key; } else { unknown << key; } } auto stringSort = [] (const QString &s1, const QString &s2) { return s1.toLower() < s2.toLower(); }; qSort(known.begin(), known.end(), stringSort); qSort(unknown.begin(), unknown.end(), stringSort); return known + unknown; } void CGisItemTrk::setColorizeSource(QString src) { if(src != colorSource) { colorSource = src; const CKnownExtension ext = CKnownExtension::get(src); if(ext.known) { limitLow = ext.defLimitLow; limitHigh = ext.defLimitHigh; } else { getExtrema(limitLow, limitHigh, src); if(limitHigh - limitLow < 0.1) { limitHigh = limitLow + 0.1; } } notifyChange(); updateHistory(); } } void CGisItemTrk::setColorizeLimitLow(qreal limit) { limitLow = limit; notifyChange(); updateHistory(); } void CGisItemTrk::setColorizeLimitHigh(qreal limit) { limitHigh = limit; notifyChange(); updateHistory(); } const QString CGisItemTrk::getColorizeUnit() const { return CKnownExtension::get(colorSource).unit; } void CGisItemTrk::drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis) { QMutexLocker lock(&mutexItems); if(trk.segs.isEmpty()) { return; } if(hasUserFocus() && mouseMoveFocus && (mode != eModeRange)) { // derive anchor QPointF anchor(mouseMoveFocus->lon, mouseMoveFocus->lat); anchor *= DEG_TO_RAD; gis->convertRad2Px(anchor); // create trackpoint info text QString str, val1, unit1, val2, unit2; str = getInfoTrkPt(*mouseMoveFocus); // calculate bounding box of text QFont f = CMainWindow::self().getMapFont(); QFontMetrics fm(f); QRect rectText = fm.boundingRect(QRect(0,0,500,0), Qt::AlignLeft|Qt::AlignTop|Qt::TextWordWrap, str); // create info box int w = rectText.width() + 5 + 5; int h = rectText.height() + 5 + (fm.height() + 8); if(totalElapsedSeconds != 0) { h += 5 + fm.height() + 8; } p.setFont(f); // draw the bubble QRect box(0, 0, w, h); box.moveBottomLeft(anchor.toPoint() + QPoint(-50,-50)); CDraw::bubble(p, box, anchor.toPoint(), 18 /* px */, 21 /* px */); p.save(); p.translate(box.topLeft()); // draw progress bar distance p.translate(5,5); QRect rectBar1(0,0,rectText.width(), fm.height()); p.setPen(QColor(150,150,255)); p.setBrush(QColor(150,150,255)); p.drawRect(rectBar1); qreal d = mouseMoveFocus->distance * rectBar1.width() / totalDistance; p.setPen(QColor(150,255,150)); p.setBrush(QColor(150,255,150)); p.drawRect(0,0,d,fm.height()); IUnit::self().meter2distance(mouseMoveFocus->distance, val1, unit1); IUnit::self().meter2distance(totalDistance - mouseMoveFocus->distance, val2, unit2); p.setPen(Qt::darkBlue); p.drawText(QRect(0,1,rectBar1.width(),fm.height()), Qt::AlignVCenter|Qt::AlignLeft, QString("%1%2").arg(val1).arg(unit1)); p.drawText(QRect(0,1,rectBar1.width(),fm.height()), Qt::AlignCenter, QString("%1%").arg(mouseMoveFocus->distance * 100 / totalDistance, 0, 'f', 0)); p.drawText(QRect(0,1,rectBar1.width(),fm.height()), Qt::AlignVCenter|Qt::AlignRight, QString("%1%2").arg(val2).arg(unit2)); // draw progress bar time if(totalElapsedSeconds != 0) { p.translate(0,fm.height() + 5); QRect rectBar2(0,0,rectText.width(), fm.height()); p.setPen(QColor(150,150,255)); p.setBrush(QColor(150,150,255)); p.drawRect(rectBar2); qreal t = mouseMoveFocus->elapsedSecondsMoving * rectBar2.width() / totalElapsedSecondsMoving; p.setPen(QColor(150,255,150)); p.setBrush(QColor(150,255,150)); p.drawRect(0,0,t,fm.height()); IUnit::self().seconds2time(mouseMoveFocus->elapsedSecondsMoving, val1, unit1); IUnit::self().seconds2time(totalElapsedSecondsMoving - mouseMoveFocus->elapsedSecondsMoving, val2, unit2); p.setPen(Qt::darkBlue); p.drawText(QRect(0,1,rectBar1.width(),fm.height()), Qt::AlignVCenter|Qt::AlignLeft, QString("%1%2").arg(val1).arg(unit1)); p.drawText(QRect(0,1,rectBar1.width(),fm.height()), Qt::AlignCenter, QString("%1%").arg(mouseMoveFocus->elapsedSecondsMoving * 100 / totalElapsedSecondsMoving, 0, 'f', 0)); p.drawText(QRect(0,1,rectBar1.width(),fm.height()), Qt::AlignVCenter|Qt::AlignRight, QString("%1%2").arg(val2).arg(unit2)); p.translate(0,fm.height() + 5); } else { p.translate(0, 5); } // draw text p.translate(0, 3); p.setPen(Qt::darkBlue); p.drawText(rectText, Qt::AlignLeft|Qt::AlignTop|Qt::TextWordWrap,str); p.restore(); } if(!scrOpt.isNull() && mouseClickFocus) { QPointF anchor(mouseClickFocus->lon, mouseClickFocus->lat); anchor *= DEG_TO_RAD; gis->convertRad2Px(anchor); p.drawPixmap(anchor - QPointF(4,4), QPixmap(IGisItem::colorMap[colorIdx].bullet)); } drawRange(p); } void CGisItemTrk::drawLabel(QPainter& p, const QPolygonF &viewport, QList &blockedAreas, const QFontMetricsF &fm, CGisDraw *gis) { // tracks have no labels } void CGisItemTrk::drawHighlight(QPainter& p) { QMutexLocker lock(&mutexItems); if(lineSimple.isEmpty() || hasUserFocus()) { return; } p.setPen(QPen(QColor(255,0,0,100),11,Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(lineSimple); } void CGisItemTrk::drawRange(QPainter& p) { QMutexLocker lock(&mutexItems); if((mouseRange1 != 0) && (mouseRange2 != 0)) { const QPolygonF& line = (mode == eModeRange) ? lineFull : lineSimple; int idx1 = (mode == eModeRange) ? mouseRange1->idxTotal : mouseRange1->idxVisible; int idx2 = (mode == eModeRange) ? mouseRange2->idxTotal : mouseRange2->idxVisible; if(idx1 > idx2) { qSwap(idx1,idx2); } QPolygonF seg = line.mid(idx1, idx2 - idx1 + 1); p.setPen(QPen(Qt::darkGreen, 11, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(seg); p.setPen(QPen(Qt::green, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolyline(seg); } } bool CGisItemTrk::setMode(mode_e m, const QString& owner) { if(!mouseFocusOwner.isEmpty() && owner != mouseFocusOwner) { return false; } mode = m; // always reset the range statemachine rangeState = eRangeStateIdle; mouseRange1 = 0; mouseRange2 = 0; mouseFocusOwner = mode == eModeRange ? owner : ""; CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } return true; } void CGisItemTrk::setName(const QString& str) { setText(CGisListWks::eColumnName, str); trk.name = str; changed(QObject::tr("Changed name"), "://icons/48x48/EditText.png"); } void CGisItemTrk::setComment(const QString& str) { trk.cmt = str; changed(QObject::tr("Changed comment"), "://icons/48x48/EditText.png"); } void CGisItemTrk::setDescription(const QString& str) { trk.desc = str; changed(QObject::tr("Changed description"), "://icons/48x48/EditText.png"); } void CGisItemTrk::setLinks(const QList& links) { trk.links = links; changed(QObject::tr("Changed links"), "://icons/48x48/Link.png"); } void CGisItemTrk::setColor(int idx) { if(idx < TRK_N_COLORS) { setColor(IGisItem::colorMap[idx].color); //changed(QObject::tr("Changed color"), "://icons/48x48/SelectColor.png"); updateHistory(); notifyChange(); } } void CGisItemTrk::setActivity(quint32 flag, const QString& name, const QString& icon) { for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int i = 0; i < seg.pts.size(); i++) { trkpt_t& trkpt = seg.pts[i]; trkpt.flags &= ~trkpt_t::eActMask; trkpt.flags |= flag; } } deriveSecondaryData(); changed(QObject::tr("Changed activity to '%1' for complete track.").arg(name), icon); notifyChange(); } void CGisItemTrk::setActivity() { if((mouseRange1 == 0) && (mouseRange2 == 0)) { return; } quint32 flag = 0; QString name; QString icon; CSelectActivity dlg(flag, name, icon, CMainWindow::getBestWidgetForParent()); if(dlg.exec() != QDialog::Accepted) { return; } if(!setReadOnlyMode(false)) { return; } // read start/stop indices qint32 idx1 = mouseRange1->idxTotal; qint32 idx2 = mouseRange2->idxTotal; if(idx1 > idx2) { qSwap(idx1,idx2); } // special case for a single point if(idx1 == idx2) { for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int i = 0; i < seg.pts.size(); i++) { trkpt_t& trkpt = seg.pts[i]; if(idx1 == trkpt.idxTotal) { trkpt.flags &= ~trkpt_t::eActMask; trkpt.flags |= flag; } } } } else { // iterate over all segments and delete points between idx1 and idx2 for(int s = 0; s < trk.segs.size(); s++) { trkseg_t& seg = trk.segs[s]; for(int i = 0; i < seg.pts.size(); i++) { trkpt_t& trkpt = seg.pts[i]; if((idx1 < trkpt.idxTotal) && (trkpt.idxTotal < idx2)) { trkpt.flags &= ~trkpt_t::eActMask; trkpt.flags |= flag; } } } } mouseRange1 = 0; mouseRange2 = 0; rangeState = eRangeStateIdle; deriveSecondaryData(); changed(QObject::tr("Changed activity to '%1' for range(%2..%3).").arg(name).arg(idx1).arg(idx2), icon); notifyChange(); } void CGisItemTrk::setColor(const QColor& c) { colorIdx = DEFAULT_COLOR; for(int n = 0; n < TRK_N_COLORS; n++) { if(c == IGisItem::colorMap[n].color) { colorIdx = n; break; } } color = IGisItem::colorMap[colorIdx].color; qDebug() << color; bullet = QPixmap(IGisItem::colorMap[colorIdx].bullet); setIcon(color2str(color)); notifyChange(); } void CGisItemTrk::setIcon(const QString& iconColor) { trk.color = iconColor; icon = QPixmap("://icons/48x48/Track.png"); QPixmap mask( icon.size() ); mask.fill( str2color(iconColor) ); mask.setMask( icon.createMaskFromColor( Qt::transparent ) ); icon = mask.scaled(22,22, Qt::KeepAspectRatio, Qt::SmoothTransformation); QTreeWidgetItem::setIcon(CGisListWks::eColumnIcon,icon); } bool CGisItemTrk::setMouseFocusByDistance(qreal dist, focusmode_e fmode, const QString &owner) { const trkpt_t * newPointOfFocus = 0; if(dist != NOFLOAT) { qreal delta = totalDistance; /// @todo: optimize search by single out segment and then do a binary search foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.flags & trkpt_t::eHidden) { continue; } qreal d = qAbs(pt.distance - dist); if(d <= delta) { newPointOfFocus = &pt; delta = d; } else { break; } } } } return publishMouseFocus(newPointOfFocus, fmode, owner); } bool CGisItemTrk::setMouseFocusByTime(quint32 time, focusmode_e fmode, const QString &owner) { const trkpt_t * newPointOfFocus = 0; if(time != NOTIME) { /// @todo: optimize search by single out segment and then do a binary search qreal delta = totalElapsedSeconds; foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.flags & trkpt_t::eHidden) { continue; } qreal d = qAbs(qreal(pt.time.toTime_t()) - qreal(time)); if(d <= delta) { newPointOfFocus = &pt; delta = d; } else { break; } } } } return publishMouseFocus(newPointOfFocus, fmode, owner); } QPointF CGisItemTrk::setMouseFocusByPoint(const QPoint& pt, focusmode_e fmode, const QString &owner) { QMutexLocker lock(&mutexItems); const trkpt_t * newPointOfFocus = 0; quint32 idx = 0; const QPolygonF& line = (mode == eModeRange) ? lineFull : lineSimple; if(pt != NOPOINT && GPS_Math_DistPointPolyline(line, pt) < MIN_DIST_FOCUS) { /* Iterate over the polyline used to draw the track as it contains screen coordinates. The polyline is a linear representation of the segments in the track. That is why the index into the polyline can't be used directly. In a second step we have to iterate over all segments and points of the trk_t object until the index is reached. This is done by either getTrkPtByVisibleIndex(), or getTrkPtByTotalIndex(). Depending on the current mode. */ quint32 i = 0; qint32 d1 = NOINT; foreach(const QPointF &point, line) { int tmp = (pt - point).manhattanLength(); if(tmp < d1) { idx = i; d1 = tmp; } i++; } newPointOfFocus = (mode == eModeRange) ? getTrkPtByTotalIndex(idx) : getTrkPtByVisibleIndex(idx); } if(!publishMouseFocus(newPointOfFocus, fmode, owner)) { newPointOfFocus = 0; } /* Test for line size before applying index. This fixes random assertions because of an invalid index. The reason for this is unknown. */ return newPointOfFocus ? ((int)idx < line.size() ? line[idx] : NOPOINTF) : NOPOINTF; } bool CGisItemTrk::setMouseFocusByTotalIndex(qint32 idx, focusmode_e fmode, const QString &owner) { const trkpt_t * newPointOfFocus = 0; foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.idxTotal == idx) { newPointOfFocus = &pt; return publishMouseFocus(newPointOfFocus, fmode, owner); } } } return false; } const CGisItemTrk::trkpt_t * CGisItemTrk::getTrkPtByVisibleIndex(qint32 idx) { foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.idxVisible == idx) { return &pt; } } } return 0; } const CGisItemTrk::trkpt_t * CGisItemTrk::getTrkPtByTotalIndex(qint32 idx) { foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { if(pt.idxTotal == idx) { return &pt; } } } return 0; } bool CGisItemTrk::isTrkPtLastVisible(qint32 idxTotal) { foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt1, seg.pts) { if((pt1.idxTotal > idxTotal) && !(pt1.flags & trkpt_t::eHidden)) { return false; } } } return true; } bool CGisItemTrk::isTrkPtFirstVisible(qint32 idxTotal) { foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt1, seg.pts) { if((pt1.idxTotal < idxTotal)) { if(!(pt1.flags & trkpt_t::eHidden)) { return false; } } else { return true; } } } return true; } bool CGisItemTrk::publishMouseFocus(const trkpt_t * pt, focusmode_e fmode, const QString& owner) { if(mode == eModeRange) { if(mouseFocusOwner != owner) { return false; } publishMouseFocusRangeMode(pt, fmode); } else { publishMouseFocusNormalMode(pt, fmode); } return true; } void CGisItemTrk::publishMouseFocusRangeMode(const trkpt_t * pt, focusmode_e fmode) { switch(rangeState) { case eRangeStateIdle: { if((fmode == eFocusMouseClick) && (pt != 0)) { mouseRange1 = pt; rangeState = eRangeState1st; } break; } case eRangeState1st: { mouseRange2 = pt; if((fmode == eFocusMouseClick) && (pt != 0)) { rangeState = eRangeState2nd; } break; } case eRangeState2nd: { if(fmode == eFocusMouseClick) { mouseRange1 = 0; mouseRange2 = 0; rangeState = eRangeStateIdle; } break; } } foreach(IPlot * plot, registeredPlots) { plot->setMouseFocus(pt); plot->setMouseRangeFocus(mouseRange1, mouseRange2); } if(!dlgDetails.isNull()) { dlgDetails->setMouseFocus(pt); dlgDetails->setMouseRangeFocus(mouseRange1, mouseRange2); } } void CGisItemTrk::publishMouseFocusNormalMode(const trkpt_t * pt, focusmode_e fmode) { switch(fmode) { case eFocusMouseMove: if(pt != mouseMoveFocus) { mouseMoveFocus = pt; foreach(IPlot * plot, registeredPlots) { plot->setMouseFocus(pt); plot->setMouseRangeFocus(0, 0); } if(!dlgDetails.isNull()) { dlgDetails->setMouseFocus(pt); dlgDetails->setMouseRangeFocus(0, 0); } } break; case eFocusMouseClick: if(pt != mouseClickFocus) { mouseClickFocus = pt; if(!dlgDetails.isNull()) { dlgDetails->setMouseClickFocus(pt); } } default: ; } } void CGisItemTrk::changed(const QString& what, const QString& icon) { IGisItem::changed(what, icon); if(!dlgDetails.isNull()) { dlgDetails->setupGui(); } } qmapshack-1.5.1/src/gis/trk/CActivityTrk.h000644 001750 000144 00000006752 12622435723 021401 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CACTIVITYTRK_H #define CACTIVITYTRK_H #include #include #include class CGisItemTrk; class CActivityTrk { public: virtual ~CActivityTrk() = default; struct activity_summary_t { activity_summary_t() : distance(0), ascend(0), descend(0), ellapsedSeconds(0), ellapsedSecondsMoving(0) { } void reset() { distance = 0; ascend = 0; descend = 0; ellapsedSeconds = 0; ellapsedSecondsMoving = 0; } qreal distance; qreal ascend; qreal descend; qreal ellapsedSeconds; qreal ellapsedSecondsMoving; }; /** @brief Update internal summary array */ void update(); /** @brief Get sum of all flags seen in the track @return A 32 bit field with all available activity flags set. */ quint32 getAllFlags() const { return allFlags; } /** @brief Convert internal summary to HTML table @param str string to receive HTML */ void printSummary(QString& str) const; /** @brief Convert array of summaries to HTML table @param summary The array of summaries @param str string to receive HTML */ static void printSummary(const QVector &summary, quint32 flags, QString& str); /** @brief Add internal summary to given array of summaries @param summary an array of summaries to hold the sum */ void sumUp(QVector &summary) const; struct activity_range_t { qreal d1; qreal d2; qreal t1; qreal t2; QString icon; QString name; }; const QList& getActivityRanges() const { return activityRanges; } struct desc_t { QString objName; quint32 flag; QString name; QString iconLarge; QString iconSmall; }; static const desc_t* getActivityDescriptors() { return actDescriptor; } private: friend class CGisItemTrk; CActivityTrk(CGisItemTrk * trk); static desc_t actDescriptor[]; static activity_summary_t& getSummary(QVector &summary, quint32 flag); static const activity_summary_t& getSummary(const QVector &summary, quint32 flag); const desc_t& getDescriptor(quint32 flag); CGisItemTrk * trk; quint32 allFlags; QList activityRanges; QVector activitySummary; }; #endif //CACTIVITYTRK_H qmapshack-1.5.1/src/gis/trk/CScrOptTrk.h000644 001750 000144 00000003063 12622435723 021007 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTTRK_H #define CSCROPTTRK_H #include "gis/IGisItem.h" #include "mouse/IScrOpt.h" #include "ui_IScrOptTrk.h" class CGisItemTrk; class IMouse; class CScrOptTrk : public IScrOpt, private Ui::IScrOptTrk { Q_OBJECT public: CScrOptTrk(CGisItemTrk * trk, const QPoint &point, IMouse *parent); virtual ~CScrOptTrk(); void draw(QPainter& p); private slots: void slotDelete(); void slotCopy(); void slotEditDetails(); void slotProfile(bool on); void slotCut(); void slotEdit(); void slotReverse(); void slotCombine(); void slotRange(); private: IGisItem::key_t key; QPointF anchor; }; #endif //CSCROPTTRK_H qmapshack-1.5.1/src/gis/trk/CCutTrk.cpp000644 001750 000144 00000004663 12623413746 020674 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CCutTrk.h" #include "helpers/CSettings.h" #include CCutTrk::CCutTrk(QWidget *parent) : QDialog(parent) { setupUi(this); connect(radioKeepFirst, SIGNAL(toggled(bool)), this, SLOT(slotClicked())); connect(radioKeepBoth, SIGNAL(toggled(bool)), this, SLOT(slotClicked())); connect(radioKeepSecond, SIGNAL(toggled(bool)), this, SLOT(slotClicked())); SETTINGS; cfg.beginGroup("TrackCut"); checkCreateClone->setChecked(cfg.value("checkCreateClone", true).toBool()); switch(cfg.value("mode", eModeKeepBoth).toInt()) { case eModeKeepFirst: radioKeepFirst->setChecked(true); break; case eModeKeepBoth: radioKeepBoth->setChecked(true); break; case eModeKeepSecond: radioKeepSecond->setChecked(true); break; } cfg.endGroup(); } void CCutTrk::accept() { SETTINGS; cfg.beginGroup("TrackCut"); cfg.setValue("checkCreateClone", checkCreateClone->isChecked()); cfg.setValue("mode", radioKeepFirst->isChecked() ? eModeKeepFirst : radioKeepBoth->isChecked() ? eModeKeepBoth : radioKeepSecond->isChecked() ? eModeKeepSecond : eModeNone); cfg.endGroup(); if(radioKeepFirst->isChecked()) { mode = eModeKeepFirst; } else if(radioKeepBoth->isChecked()) { mode = eModeKeepBoth; } else if(radioKeepSecond->isChecked()) { mode = eModeKeepSecond; } QDialog::accept(); } void CCutTrk::slotClicked() { checkCreateClone->setEnabled(!radioKeepBoth->isChecked()); } qmapshack-1.5.1/src/gis/trk/IDetailsTrk.ui000644 001750 000144 00000067122 12623413746 021366 0ustar00oeichlerusers000000 000000 IDetailsTrk 0 0 992 404 Form 3 3 3 3 3 Qt::Vertical 0 3 0 0 - - Qt::Vertical 0 0 - - Qt::Vertical 0 0 - - Qt::Horizontal QSizePolicy::Expanding 40 20 3 QFrame::StyledPanel QFrame::Raised 3 3 3 3 3 0 0 Toggle read only mode. You have to open the lock to edit the item. ... :/icons/32x32/UnLock.png :/icons/32x32/Lock.png:/icons/32x32/UnLock.png 22 22 true true 0 0 25 25 <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> :/icons/32x32/Tainted.png true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse Qt::Vertical 20 40 0 0 0 Info 3 0 0 0 0 false Style 0 0 60 100 16777215 300 0 0 from Data 0 0 Source 0 0 0 0 Maximum 0 0 1 -100.000000000000000 0 0 Minimum 0 0 1 -100.000000000000000 0 0 from Data 0 0 Solid color 0 0 Qt::Vertical 20 40 Qt::Horizontal 40 20 Graphs Qt::Vertical 20 40 Qt::Horizontal 40 20 Profile Graph 3 Graph 2 Graph 1 Activity 3 3 3 3 3 3 Qt::Vertical Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop To differentiate the track statistics select an activity from the list for the complete track. Or select a part of the track to assign an activity. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::Horizontal 40 20 Points 0 0 0 0 0 0 0 true false false false 50 # Time Ele. Delta Dist. Speed Slope Ascend Descend Position Filter 0 0 0 0 0 QAbstractItemView::NoSelection false 1 Hist. 0 0 0 0 0 32 32 CHistoryListWidget QListWidget
widgets/CHistoryListWidget.h
CPlotTrack QWidget
plot/CPlotTrack.h
1
CDoubleSpinBox QDoubleSpinBox
widgets/CDoubleSpinBox.h
CColorLegend QWidget
widgets/CColorLegend.h
1
qmapshack-1.5.1/src/gis/trk/CDetailsTrk.h000644 001750 000144 00000005453 12622435723 021167 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDETAILSTRK_H #define CDETAILSTRK_H #include "plot/CPlot.h" #include "ui_IDetailsTrk.h" #include class CGisItemTrk; class CPlotProfile; class CDetailsTrk : public QWidget, private Ui::IDetailsTrk { Q_OBJECT public: CDetailsTrk(CGisItemTrk &trk, QWidget * parent); virtual ~CDetailsTrk(); void setMouseFocus(const CGisItemTrk::trkpt_t * pt); void setMouseRangeFocus(const CGisItemTrk::trkpt_t * pt1, const CGisItemTrk::trkpt_t * pt2); void setMouseClickFocus(const CGisItemTrk::trkpt_t * pt); public slots: void setupGui(); private slots: void slotShowPlots(); void slotColorChanged(int idx); void slotChangeReadOnlyMode(bool on); void slotItemSelectionChanged(); void slotLinkActivated(const QUrl& url); void slotLinkActivated(const QString& url); void slotMouseClickState(int); void slotActivitySelected(bool checked); void slotColorSourceChanged(int idx, float valueLow = HUGE_VALF, float valueHigh = HUGE_VALF); void slotColorLimitHighChanged(); void slotColorLimitLowChanged(); void slotLimitLowFromData(); void slotLimitHighFromData(); void slotSetupGraph(int idx); private: enum columns_t { eColNum ,eColTime ,eColEle ,eColDelta ,eColDist ,eColSpeed ,eColSlope ,eColAscend ,eColDescend ,eColPosition ,eColMax }; enum tabs_t { eTabInfo ,eTabStyle ,eTabGraphs ,eTabActivity ,eTabPoints ,eTabFilter ,eTabHistory }; /** @brief Pointer to track item It is ok to store the pointer as this widget is created by the track item. The track item will destroy this object on it's own destruction. */ CGisItemTrk& trk; bool originator = false; CPlotProfile * plot1; CPlot * plot2; CPlot * plot3; }; #endif //CDETAILSTRK_H qmapshack-1.5.1/src/gis/trk/CDetailsTrk.cpp000644 001750 000144 00000053731 12624067074 021526 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/CDetailsTrk.h" #include "gis/trk/CKnownExtension.h" #include "gis/trk/CPropertyTrk.h" #include "gis/trk/filter/CFilterInvalid.h" #include "gis/trk/filter/CFilterDelete.h" #include "gis/trk/filter/CFilterDouglasPeuker.h" #include "gis/trk/filter/CFilterMedian.h" #include "gis/trk/filter/CFilterNewDate.h" #include "gis/trk/filter/CFilterObscureDate.h" #include "gis/trk/filter/CFilterOffsetElevation.h" #include "gis/trk/filter/CFilterReplaceElevation.h" #include "gis/trk/filter/CFilterReset.h" #include "gis/trk/filter/CFilterSpeed.h" #include "helpers/CLinksDialog.h" #include "helpers/CSettings.h" #include "plot/CPlotProfile.h" #include "units/IUnit.h" #include "widgets/CTextEditWidget.h" #include #include CDetailsTrk::CDetailsTrk(CGisItemTrk& trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); QPixmap icon(16,8); for(int i=0; i < TRK_N_COLORS; ++i) { icon.fill(IGisItem::colorMap[i].color); comboColor->addItem(icon, IGisItem::colorMap[i].name, IGisItem::colorMap[i].color); } widgetColorLayout->setAlignment(Qt::AlignTop); int i = 0; const CActivityTrk::desc_t* actDesc = CActivityTrk::getActivityDescriptors(); while(!actDesc[i].name.isEmpty()) { const CActivityTrk::desc_t& desc = actDesc[i]; QCheckBox * check = new QCheckBox(this); check->setText(desc.name); check->setIcon(QIcon(desc.iconLarge)); check->setProperty("flag", desc.flag); check->setProperty("name", desc.name); check->setProperty("symbol", desc.iconLarge); check->setObjectName("check" + desc.objName); connect(check, SIGNAL(clicked(bool)), this, SLOT(slotActivitySelected(bool))); layoutActivities->addWidget(check); i++; } layoutActivities->addItem(new QSpacerItem(0,0,QSizePolicy::Maximum, QSizePolicy::MinimumExpanding)); setupGui(); const CPropertyTrk * propHandler = trk.getPropertyHandler(); propHandler->fillComboBox(comboGraph2); propHandler->fillComboBox(comboGraph3); plot1 = new CPlotProfile(&trk, IPlot::eModeNormal, this); plot1->setMinimumSize(QSize(0, 100)); plot1->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); plot1->show(); layoutPlot->addWidget(plot1); plot2 = new CPlot(&trk, this); plot2->setMinimumSize(QSize(0, 100)); plot2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); plot2->show(); layoutPlot->addWidget(plot2); plot3 = new CPlot(&trk, this); plot3->setMinimumSize(QSize(0, 100)); plot3->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); plot3->show(); layoutPlot->addWidget(plot3); if(trk.isOnDevice()) { toolLock->setDisabled(true); } QTreeWidgetItem * item, * item0; item0 = new QTreeWidgetItem(treeFilter); item0->setIcon(0, QIcon("://icons/48x48/PointHide.png")); item0->setText(0, tr("Reduce visible track points")); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterDouglasPeuker(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterInvalid(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterReset(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterDelete(trk, treeFilter)); item0 = new QTreeWidgetItem(treeFilter); item0->setIcon(0, QIcon("://icons/48x48/SetEle.png")); item0->setText(0, tr("Change elevation of track points")); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterMedian(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterReplaceElevation(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterOffsetElevation(trk, treeFilter)); item0 = new QTreeWidgetItem(treeFilter); item0->setIcon(0, QIcon("://icons/48x48/Time.png")); item0->setText(0, tr("Change timestamp of track points")); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterNewDate(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterObscureDate(trk, treeFilter)); item = new QTreeWidgetItem(item0); treeFilter->setItemWidget(item,0, new CFilterSpeed(trk, treeFilter)); item0 = new QTreeWidgetItem(treeFilter); item0->setIcon(0, QIcon("://icons/48x48/TrkCut.png")); item0->setText(0, tr("Cut track into pieces")); SETTINGS; cfg.beginGroup("TrackDetails"); checkGraph1->setChecked(cfg.value("showGraph1", true).toBool()); checkGraph2->setChecked(cfg.value("showGraph2", true).toBool()); checkGraph3->setChecked(cfg.value("showGraph3", true).toBool()); splitter->restoreState (cfg.value("splitterSizes").toByteArray()); treeWidget->header()->restoreState(cfg.value("trackPointListState").toByteArray()); tabWidget->setCurrentIndex(cfg.value("visibleTab", 0).toInt()); cfg.endGroup(); connect(checkGraph1, SIGNAL(clicked()), this, SLOT(slotShowPlots())); connect(checkGraph2, SIGNAL(clicked()), this, SLOT(slotShowPlots())); connect(checkGraph3, SIGNAL(clicked()), this, SLOT(slotShowPlots())); connect(comboColor, SIGNAL(currentIndexChanged(int)), this, SLOT(slotColorChanged(int))); connect(toolLock, SIGNAL(toggled(bool)), this, SLOT(slotChangeReadOnlyMode(bool))); connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged())); connect(textCmtDesc, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotLinkActivated(QUrl))); connect(labelInfo, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(plot3, SIGNAL(sigMouseClickState(int)), this, SLOT(slotMouseClickState(int))); connect(plot1, SIGNAL(sigMouseClickState(int)), this, SLOT(slotMouseClickState(int))); connect(plot2, SIGNAL(sigMouseClickState(int)), this, SLOT(slotMouseClickState(int))); connect(comboColorSource, SIGNAL(currentIndexChanged(int)), this, SLOT(slotColorSourceChanged(int))); connect(spinLimitHigh, SIGNAL(valueChangedByStep(double)), this, SLOT(slotColorLimitHighChanged())); connect(spinLimitHigh, SIGNAL(editingFinished()), this, SLOT(slotColorLimitHighChanged())); connect(spinLimitLow, SIGNAL(valueChangedByStep(double)), this, SLOT(slotColorLimitLowChanged())); connect(spinLimitLow, SIGNAL(editingFinished()), this, SLOT(slotColorLimitLowChanged())); connect(btnMaxFromData, SIGNAL(clicked()), this, SLOT(slotLimitHighFromData())); connect(btnMinFromData, SIGNAL(clicked()), this, SLOT(slotLimitLowFromData())); connect(listHistory, SIGNAL(sigChanged()), this, SLOT(setupGui())); connect(comboGraph2, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetupGraph(int))); connect(comboGraph3, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetupGraph(int))); // setting up the graph properties will trigger the signals // this is good because the signals are connected at this point // invoking the slots cfg.beginGroup("TrackDetails"); i = comboGraph2->findData(cfg.value("propGraph2","speed").toString()); if(i != NOIDX) { comboGraph2->setCurrentIndex(i); } i = comboGraph3->findData(cfg.value("propGraph3","progress").toString()); if(i != NOIDX) { comboGraph3->setCurrentIndex(i); } cfg.endGroup(); slotShowPlots(); } CDetailsTrk::~CDetailsTrk() { SETTINGS; cfg.beginGroup("TrackDetails"); cfg.setValue("showGraph1", checkGraph1->isChecked()); cfg.setValue("showGraph2", checkGraph2->isChecked()); cfg.setValue("showGraph3", checkGraph3->isChecked()); if(comboGraph2->currentIndex() != 0) { cfg.setValue("propGraph2", comboGraph2->currentData().toString()); } if(comboGraph3->currentIndex() != 0) { cfg.setValue("propGraph3", comboGraph3->currentData().toString()); } cfg.setValue("splitterSizes", splitter->saveState()); cfg.setValue("trackPointListState", treeWidget->header()->saveState()); cfg.setValue("visibleTab", tabWidget->currentIndex()); cfg.endGroup(); } void CDetailsTrk::slotLimitLowFromData() { qreal min, max; trk.getExtrema(min, max, trk.getColorizeSource()); spinLimitLow->setValue(min); slotColorLimitLowChanged(); slotColorLimitHighChanged(); } void CDetailsTrk::slotLimitHighFromData() { qreal min, max; trk.getExtrema(min, max, trk.getColorizeSource()); spinLimitHigh->setValue(max); slotColorLimitHighChanged(); slotColorLimitLowChanged(); } void CDetailsTrk::setupGui() { if(originator) { return; } CCanvas::setOverrideCursor(Qt::WaitCursor, "CDetailsTrk::setupGui"); originator = true; QString str, val, unit; bool isReadOnly = trk.isReadOnly(); tabWidget->widget(eTabFilter)->setEnabled(!isReadOnly); tabWidget->widget(eTabActivity)->setEnabled(!isReadOnly); labelTainted->setVisible(trk.isTainted()); labelInfo->setText(trk.getInfo(true)); comboColor->setCurrentIndex(trk.getColorIdx()); toolLock->setChecked(isReadOnly); QList items; const CGisItemTrk::trk_t& t = trk.getTrackData(); foreach (const CGisItemTrk::trkseg_t& seg, t.segs) { foreach(const CGisItemTrk::trkpt_t& trkpt, seg.pts) { QTreeWidgetItem * item = new QTreeWidgetItem(); item->setTextAlignment(eColNum, Qt::AlignLeft); item->setTextAlignment(eColEle, Qt::AlignRight); item->setTextAlignment(eColDelta, Qt::AlignRight); item->setTextAlignment(eColDist, Qt::AlignRight); item->setTextAlignment(eColAscend, Qt::AlignRight); item->setTextAlignment(eColDescend, Qt::AlignRight); item->setTextAlignment(eColSpeed, Qt::AlignRight); QBrush b( trkpt.flags & CGisItemTrk::trkpt_t::eHidden ? Qt::gray : Qt::black ); for(int i = 0; i < eColMax; i++) { item->setForeground(i, b); } item->setText(eColNum,QString::number(trkpt.idxTotal)); if(trkpt.time.isValid()) { item->setText(eColTime, IUnit::self().datetime2string(trkpt.time, true, QPointF(trkpt.lon, trkpt.lat)*DEG_TO_RAD)); } else { item->setText(eColTime, "-"); } if(trkpt.ele != NOINT) { IUnit::self().meter2elevation(trkpt.ele, val, unit); str = tr("%1 %2").arg(val).arg(unit); } else { str = "-"; } item->setText(eColEle,str); IUnit::self().meter2distance(trkpt.deltaDistance, val, unit); item->setText(eColDelta, tr("%1 %2").arg(val).arg(unit)); IUnit::self().meter2distance(trkpt.distance, val, unit); item->setText(eColDist, tr("%1 %2").arg(val).arg(unit)); // speed if(trkpt.speed != NOFLOAT) { IUnit::self().meter2speed(trkpt.speed, val, unit); str = tr("%1 %2").arg(val).arg(unit); } else { str = "-"; } item->setText(eColSpeed,str); if(trkpt.slope1 != NOFLOAT) { str = QString("%1°(%2%)").arg(trkpt.slope1,2,'f',0).arg(trkpt.slope2,2,'f',0); } else { str = "-"; } item->setText(eColSlope,str); IUnit::self().meter2elevation(trkpt.ascend, val, unit); item->setText(eColAscend, tr("%1 %2").arg(val).arg(unit)); IUnit::self().meter2elevation(trkpt.descend, val, unit); item->setText(eColDescend, tr("%1 %2").arg(val).arg(unit)); // position IUnit::degToStr(trkpt.lon, trkpt.lat, str); item->setText(eColPosition,str); items << item; } } treeWidget->clear(); treeWidget->addTopLevelItems(items); treeWidget->header()->resizeSections(QHeaderView::ResizeToContents); textCmtDesc->document()->clear(); textCmtDesc->append(IGisItem::createText(isReadOnly, trk.getComment(), trk.getDescription(), trk.getLinks())); textCmtDesc->moveCursor (QTextCursor::Start); textCmtDesc->ensureCursorVisible(); quint32 flags = trk.getActivities().getAllFlags(); int i = 0; const CActivityTrk::desc_t* actDesc = CActivityTrk::getActivityDescriptors(); while(!actDesc[i].objName.isEmpty()) { const CActivityTrk::desc_t& desc = actDesc[i]; QCheckBox * check = findChild("check" + desc.objName); if(check) { check->setChecked((flags & desc.flag) == desc.flag); } i++; } str.clear(); trk.getActivities().printSummary(str); labelActivityInfo->setText(str); bool hasActivity = 0 != (flags & CGisItemTrk::trkpt_t::eActMask); labelActivityHelp->setVisible(!hasActivity); labelActivityInfo->setVisible(hasActivity); plotTrack->setTrack(&trk); listHistory->setupHistory(trk); QTabWidget * tabWidget = dynamic_cast(parentWidget() ? parentWidget()->parentWidget() : 0); if(tabWidget) { int idx = tabWidget->indexOf(this); if(idx != NOIDX) { setObjectName(trk.getName()); tabWidget->setTabText(idx, trk.getName()); } } comboColorSource->blockSignals(true); comboColorSource->clear(); // the first entry `solid color`, it is always available comboColorSource->addItem(QIcon("://icons/32x32/CSrcSolid.png"), tr("Solid color")); foreach(const QString &key, trk.getExistingDataSources()) { const CKnownExtension &ext = CKnownExtension::get(key); QIcon icon(ext.icon); comboColorSource->addItem(icon, ext.known ? ext.name : key, key); } int currentIdx = comboColorSource->findData(trk.getColorizeSource()); if(-1 == currentIdx) { currentIdx = 0; } comboColorSource->setCurrentIndex(currentIdx); comboColorSource->blockSignals(false); bool enabled = (0 < currentIdx); spinLimitLow->setEnabled (enabled); spinLimitHigh->setEnabled (enabled); widgetColorLabel->setEnabled(enabled); btnMinFromData->setEnabled (enabled); btnMaxFromData->setEnabled (enabled); if(enabled) { const CKnownExtension &ext = CKnownExtension::get(comboColorSource->itemData(currentIdx).toString()); spinLimitLow->blockSignals(true); spinLimitLow->setMinimum(ext.minimum); spinLimitLow->setMaximum(ext.maximum); spinLimitLow->setSuffix (ext.unit); spinLimitLow->setValue (trk.getColorizeLimitLow()); spinLimitLow->blockSignals(false); spinLimitHigh->blockSignals(true); spinLimitHigh->setMinimum(ext.minimum); spinLimitHigh->setMaximum(ext.maximum); spinLimitHigh->setSuffix (ext.unit); spinLimitHigh->setValue (trk.getColorizeLimitHigh()); spinLimitHigh->blockSignals(false); widgetColorLabel->setMinimum(spinLimitLow->value()); widgetColorLabel->setMaximum(spinLimitHigh->value()); widgetColorLabel->setUnit(ext.unit); } // refill comboboxes to select track property to be displayed by graphs const CPropertyTrk * p = trk.getPropertyHandler(); comboGraph2->blockSignals(true); p->fillComboBox(comboGraph2); comboGraph2->blockSignals(false); comboGraph3->blockSignals(true); p->fillComboBox(comboGraph3); comboGraph3->blockSignals(false); // try to restore last graph setup // signals are unblocked by now changing the combobox will trigger a graph update SETTINGS; cfg.beginGroup("TrackDetails"); i = comboGraph2->findData(cfg.value("propGraph2","speed").toString()); if(i != NOIDX) { comboGraph2->setCurrentIndex(i); } i = comboGraph3->findData(cfg.value("propGraph3","progress").toString()); if(i != NOIDX) { comboGraph3->setCurrentIndex(i); } cfg.endGroup(); originator = false; CCanvas::restoreOverrideCursor("CDetailsTrk::setupGui"); } void CDetailsTrk::setMouseFocus(const CGisItemTrk::trkpt_t * pt) { if(pt != 0) { plotTrack->setMouseFocus(pt->lon, pt->lat); labelInfoTrkPt->setText(trk.getInfoTrkPt(*pt)); labelInfoProgress->setText(trk.getInfoProgress(*pt)); } else { labelInfoTrkPt->setText("-\n-"); labelInfoProgress->setText("-\n-"); } } void CDetailsTrk::setMouseRangeFocus(const CGisItemTrk::trkpt_t * pt1, const CGisItemTrk::trkpt_t * pt2) { if(pt1 && pt2) { labelInfoRange->setText(trk.getInfoRange(*pt1, *pt2)); } else { labelInfoRange->setText("-\n-"); } } void CDetailsTrk::setMouseClickFocus(const CGisItemTrk::trkpt_t * pt) { if(pt != 0) { treeWidget->blockSignals(true); treeWidget->setCurrentItem(treeWidget->topLevelItem(pt->idxTotal)); treeWidget->blockSignals(false); } } void CDetailsTrk::slotMouseClickState(int s) { if(s == IPlot::eMouseClickIdle) { labelInfoRange->setText("-\n-"); plot3->setMouseRangeFocus(0,0); plot1->setMouseRangeFocus(0,0); plot2->setMouseRangeFocus(0,0); } } void CDetailsTrk::slotShowPlots() { plot1->setVisible(checkGraph1->isChecked()); plot2->setVisible(checkGraph2->isChecked()); plot3->setVisible(checkGraph3->isChecked()); } void CDetailsTrk::slotColorChanged(int idx) { if(trk.getColorIdx() != idx) { trk.setColor(idx); } } void CDetailsTrk::slotColorSourceChanged(int idx, float valueLow, float valueHigh) { trk.setColorizeSource(comboColorSource->itemData(idx).toString()); setupGui(); } void CDetailsTrk::slotColorLimitHighChanged() { const double val = spinLimitHigh->value(); trk.setColorizeLimitHigh(val); widgetColorLabel->setMaximum(val); if(spinLimitLow->value() >= val) { spinLimitLow->setValue(val - .1f); } } void CDetailsTrk::slotColorLimitLowChanged() { const double val = spinLimitLow->value(); trk.setColorizeLimitLow(val); widgetColorLabel->setMinimum(val); if(spinLimitHigh->value() <= val) { spinLimitHigh->setValue(val + .1f); } } void CDetailsTrk::slotChangeReadOnlyMode(bool on) { trk.setReadOnlyMode(on); setupGui(); } void CDetailsTrk::slotItemSelectionChanged() { QTreeWidgetItem * item = treeWidget->currentItem(); if(item != 0) { quint32 idx = item->text(eColNum).toUInt(); trk.setMouseFocusByTotalIndex(idx, CGisItemTrk::eFocusMouseMove, "CDetailsTrk"); } } void CDetailsTrk::slotLinkActivated(const QString& url) { if(url == "name") { QString name = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new track name."), QLineEdit::Normal, trk.getName()); if(name.isEmpty()) { return; } trk.setName(name); setupGui(); } } void CDetailsTrk::slotLinkActivated(const QUrl& url) { if(url.toString() == "comment") { CTextEditWidget dlg(this); dlg.setHtml(trk.getComment()); if(dlg.exec() == QDialog::Accepted) { trk.setComment(dlg.getHtml()); } setupGui(); } else if(url.toString() == "description") { CTextEditWidget dlg(this); dlg.setHtml(trk.getDescription()); if(dlg.exec() == QDialog::Accepted) { trk.setDescription(dlg.getHtml()); } setupGui(); } else if(url.toString() == "links") { QList links = trk.getLinks(); CLinksDialog dlg(links, this); if(dlg.exec() == QDialog::Accepted) { trk.setLinks(links); } setupGui(); } else { QDesktopServices::openUrl(url); } } void CDetailsTrk::slotActivitySelected(bool checked) { if(!checked) { if(QMessageBox::warning(this, tr("Reset activities..."), tr("This will remove all activities from the track. Proceed?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::Ok) != QMessageBox::Ok) { setupGui(); return; } trk.setActivity(CGisItemTrk::trkpt_t::eActNone, tr("None"), "://icons/48x48/ActNone.png"); return; } QObject * s = sender(); bool ok = false; quint32 flag = s->property("flag").toUInt(&ok); if(ok) { trk.setActivity(flag, s->property("name").toString(), s->property("symbol").toString()); } } void CDetailsTrk::slotSetupGraph(int idx) { const CPropertyTrk * propHandler = trk.getPropertyHandler(); CPlot * plot = 0; QObject * s = sender(); if(s == comboGraph2) { plot = plot2; } else if(s == comboGraph3) { plot = plot3; } if(plot) { propHandler->setupPlot(plot, idx); } } qmapshack-1.5.1/src/gis/trk/filter/CFilterObscureDate.h000644 001750 000144 00000002472 12622435723 023752 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTEROBSCUREDATE_H #define CFILTEROBSCUREDATE_H #include "ui_IFilterObscureDate.h" #include class CGisItemTrk; class CFilterObscureDate : public QWidget, private Ui::IFilterObscureDate { Q_OBJECT public: CFilterObscureDate(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterObscureDate(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTEROBSCUREDATE_H qmapshack-1.5.1/src/gis/trk/filter/CFilterNewDate.h000644 001750 000144 00000002432 12622435723 023075 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERNEWDATE_H #define CFILTERNEWDATE_H #include "ui_IFilterNewDate.h" #include class CGisItemTrk; class CFilterNewDate : public QWidget, private Ui::IFilterNewDate { Q_OBJECT public: CFilterNewDate(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterNewDate(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERNEWDATE_H qmapshack-1.5.1/src/gis/trk/filter/CFilterSpeed.cpp000644 001750 000144 00000003346 12622435720 023143 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterSpeed.h" #include "helpers/CSettings.h" #include "units/IUnit.h" CFilterSpeed::CFilterSpeed(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); doubleSpinBox->setSuffix(IUnit::self().speedunit); SETTINGS; doubleSpinBox->setValue(cfg.value("TrackDetails/Filter/Speed/speed",1).toDouble()); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterSpeed::~CFilterSpeed() { SETTINGS; cfg.setValue("TrackDetails/Filter/Speed/speed", doubleSpinBox->value()); } void CFilterSpeed::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor, "CFilterSpeed"); trk.filterSpeed(doubleSpinBox->value()/IUnit::self().speedfactor); CCanvas::restoreOverrideCursor("CFilterSpeed"); } qmapshack-1.5.1/src/gis/trk/filter/CFilterReset.cpp000644 001750 000144 00000002603 12623413746 023165 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterReset.h" CFilterReset::CFilterReset(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterReset::~CFilterReset() { } void CFilterReset::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterReset"); trk.filterReset(); CCanvas::restoreOverrideCursor("CFilterReset"); } qmapshack-1.5.1/src/gis/trk/filter/IFilterReset.ui000644 001750 000144 00000005121 12527654570 023031 0ustar00oeichlerusers000000 000000 IFilterReset 0 0 787 82 Form 3 0 0 0 3 <b>Reset Hidden Track Points</b> 3 Make all trackpoints visible again. ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal 0 0 :/icons/48x48/PointHide.png qmapshack-1.5.1/src/gis/trk/filter/CFilterDelete.cpp000644 001750 000144 00000002613 12623413746 023306 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterDelete.h" CFilterDelete::CFilterDelete(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterDelete::~CFilterDelete() { } void CFilterDelete::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterDelete"); trk.filterDelete(); CCanvas::restoreOverrideCursor("CFilterDelete"); } qmapshack-1.5.1/src/gis/trk/filter/CFilterNewDate.cpp000644 001750 000144 00000003144 12622435720 023426 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterNewDate.h" CFilterNewDate::CFilterNewDate(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); labelTimeZone->setText(QDateTime::currentDateTime().timeZone().abbreviation(QDateTime::currentDateTime())); dateTimeEdit->setDateTime(QDateTime::currentDateTime()); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterNewDate::~CFilterNewDate() { } void CFilterNewDate::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterNewDate"); trk.filterNewDate(dateTimeEdit->dateTime().toUTC()); CCanvas::restoreOverrideCursor("CFilterNewDate"); } qmapshack-1.5.1/src/gis/trk/filter/IFilterInvalid.ui000644 001750 000144 00000005474 12623413746 023343 0ustar00oeichlerusers000000 000000 IFilterInvalid 0 0 787 82 Form 3 0 0 0 3 75 true Hide Invalid Points Qt::PlainText 3 Hide points with invalid coordinates at the beginning of the track. ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal 0 0 :/icons/48x48/PointHide.png qmapshack-1.5.1/src/gis/trk/filter/IFilterOffsetElevation.ui000644 001750 000144 00000007472 12527654570 025057 0ustar00oeichlerusers000000 000000 IFilterOffsetElevation 0 0 690 82 Form 3 0 0 0 3 0 0 :/icons/48x48/SetEle.png <b>Offset Elevation</b> 3 0 0 Add offset of 0 0 m -8000 8000 to track points elevation. Qt::Horizontal 40 20 ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal qmapshack-1.5.1/src/gis/trk/filter/IFilterSpeed.ui000644 001750 000144 00000006172 12527654570 023016 0ustar00oeichlerusers000000 000000 IFilterSpeed 0 0 997 56 Form 3 0 0 0 3 <b>Change Speed</b> Set speed to km/h 1 0.100000000000000 Qt::Horizontal 40 20 Qt::Horizontal 0 0 :/icons/48x48/Time.png ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 qmapshack-1.5.1/src/gis/trk/filter/CFilterDelete.h000644 001750 000144 00000002422 12623413746 022751 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERDELETE_H #define CFILTERDELETE_H #include "ui_IFilterDelete.h" #include class CGisItemTrk; class CFilterDelete : public QWidget, private Ui::IFilterDelete { Q_OBJECT public: CFilterDelete(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterDelete(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERDELETE_H qmapshack-1.5.1/src/gis/trk/filter/CFilterDouglasPeuker.h000644 001750 000144 00000002511 12622435723 024316 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERDOUGLASPEUKER_H #define CFILTERDOUGLASPEUKER_H #include "ui_IFilterDouglasPeuker.h" #include class CGisItemTrk; class CFilterDouglasPeuker : public QWidget, private Ui::IFilterDouglasPeuker { Q_OBJECT public: CFilterDouglasPeuker(CGisItemTrk& trk, QWidget *parent); virtual ~CFilterDouglasPeuker(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERDOUGLASPEUKER_H qmapshack-1.5.1/src/gis/trk/filter/CFilterDouglasPeuker.cpp000644 001750 000144 00000003474 12622435720 024657 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterDouglasPeuker.h" #include "helpers/CSettings.h" #include "units/IUnit.h" #include CFilterDouglasPeuker::CFilterDouglasPeuker(CGisItemTrk &trk, QWidget * parent) : QWidget(parent) , trk(trk) { setupUi(this); spinBox->setSuffix(IUnit::self().baseunit); SETTINGS; spinBox->setValue(cfg.value("TrackDetails/Filter/DouglasPeuker/distance",5).toInt()); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterDouglasPeuker::~CFilterDouglasPeuker() { SETTINGS; cfg.setValue("TrackDetails/Filter/DouglasPeuker/distance", spinBox->value()); } void CFilterDouglasPeuker::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor, "CFilterDouglasPeuker"); trk.filterReducePoints(spinBox->value()/IUnit::self().basefactor); CCanvas::restoreOverrideCursor("CFilterDouglasPeuker"); } qmapshack-1.5.1/src/gis/trk/filter/CFilterInvalid.h000644 001750 000144 00000002460 12623413746 023137 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERINVALID_H #define CFILTERINVALID_H #include "ui_IFilterInvalid.h" #include #include class CGisItemTrk; class CFilterInvalid : public QWidget, private Ui::IFilterInvalid { Q_OBJECT public: CFilterInvalid(CGisItemTrk& trk, QWidget *parent); virtual ~CFilterInvalid(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif // CFILTERINVALID_H qmapshack-1.5.1/src/gis/trk/filter/CFilterMedian.h000644 001750 000144 00000002422 12622435723 022742 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERMEDIAN_H #define CFILTERMEDIAN_H #include "ui_IFilterMedian.h" #include class CGisItemTrk; class CFilterMedian : public QWidget, private Ui::IFilterMedian { Q_OBJECT public: CFilterMedian(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterMedian(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERMEDIAN_H qmapshack-1.5.1/src/gis/trk/filter/CFilterInvalid.cpp000644 001750 000144 00000002637 12623413746 023500 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/trk/filter/CFilterInvalid.h" #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" CFilterInvalid::CFilterInvalid(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterInvalid::~CFilterInvalid() { } void CFilterInvalid::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor, "CFilterInvalid"); trk.filterRemoveNullPoints(); CCanvas::restoreOverrideCursor("CFilterInvalid"); } qmapshack-1.5.1/src/gis/trk/filter/IFilterNewDate.ui000644 001750 000144 00000006053 12542334171 023270 0ustar00oeichlerusers000000 000000 IFilterNewDate 0 0 896 58 Form 3 0 0 0 3 <b>Change Time</b> Change start of track to dd.MM.yy HH:mm:ss true - Qt::Horizontal 40 20 ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal :/icons/48x48/Time.png qmapshack-1.5.1/src/gis/trk/filter/CFilterOffsetElevation.h000644 001750 000144 00000002532 12622435723 024644 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTEROFFSETELEVATION_H #define CFILTEROFFSETELEVATION_H #include "ui_IFilterOffsetElevation.h" #include class CGisItemTrk; class CFilterOffsetElevation : public QWidget, private Ui::IFilterOffsetElevation { Q_OBJECT public: CFilterOffsetElevation(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterOffsetElevation(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTEROFFSETELEVATION_H qmapshack-1.5.1/src/gis/trk/filter/CFilterReplaceElevation.cpp000644 001750 000144 00000002721 12623413746 025326 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterReplaceElevation.h" CFilterReplaceElevation::CFilterReplaceElevation(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterReplaceElevation::~CFilterReplaceElevation() { } void CFilterReplaceElevation::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterMedian"); trk.filterReplaceElevation(); CCanvas::restoreOverrideCursor("CFilterMedian"); } qmapshack-1.5.1/src/gis/trk/filter/CFilterObscureDate.cpp000644 001750 000144 00000003261 12622435720 024277 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterObscureDate.h" #include "helpers/CSettings.h" CFilterObscureDate::CFilterObscureDate(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); SETTINGS; spinBox->setValue(cfg.value("TrackDetails/Filter/ObscureTimestamp/delta",0).toInt()); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterObscureDate::~CFilterObscureDate() { SETTINGS; cfg.setValue("TrackDetails/Filter/ObscureTimestamp/delta", spinBox->value()); } void CFilterObscureDate::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterObscureDate"); trk.filterObscureDate(spinBox->value()); CCanvas::restoreOverrideCursor("CFilterObscureDate"); } qmapshack-1.5.1/src/gis/trk/filter/CFilterReset.h000644 001750 000144 00000002412 12623413746 022630 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERRESET_H #define CFILTERRESET_H #include "ui_IFilterReset.h" #include class CGisItemTrk; class CFilterReset : public QWidget, private Ui::IFilterReset { Q_OBJECT public: CFilterReset(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterReset(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERRESET_H qmapshack-1.5.1/src/gis/trk/filter/filter.cpp000644 001750 000144 00000022737 12624107504 022122 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "gis/trk/CGisItemTrk.h" #include #include void CGisItemTrk::filterReducePoints(qreal dist) { QVector line; bool nothingDone = true; foreach (const trkseg_t &seg, trk.segs) { foreach(const trkpt_t &pt, seg.pts) { pointDP dp; dp.x = pt.lon * DEG_TO_RAD; dp.y = pt.lat * DEG_TO_RAD; dp.z = pt.ele; dp.used = !(pt.flags & CGisItemTrk::trkpt_t::eHidden); line << dp; } } if(line.size() < 3) { return; } point3D pt0 = line[0]; line[0].x = 0; line[0].y = 0; for(int i = 1; i < line.size(); i++) { qreal d, a1, a2; pointDP& pt1 = line[i - 1]; pointDP& pt2 = line[i]; d = GPS_Math_Distance(pt0.x, pt0.y, pt2.x, pt2.y, a1, a2); pt0 = pt2; pt2.x = pt1.x + qCos(a1 * DEG_TO_RAD) * d; pt2.y = pt1.y + qSin(a1 * DEG_TO_RAD) * d; } GPS_Math_DouglasPeucker(line, dist); int cnt = 0; for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; if(line[cnt].used) { pt.flags &= ~trkpt_t::eHidden; } else { if((pt.flags & trkpt_t::eHidden) == 0) { nothingDone = false; pt.flags |= trkpt_t::eHidden; } } cnt++; } } if(nothingDone) { return; } deriveSecondaryData(); QString val, unit; IUnit::self().meter2distance(dist, val, unit); changed(QObject::tr("Hide points by Douglas Peuker algorithm (%1%2)").arg(val).arg(unit), "://icons/48x48/PointHide.png"); } void CGisItemTrk::filterRemoveNullPoints() { bool nothingDone = true; bool done = false; for(int i = 0; i < trk.segs.size() && !done; i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size() && !done; n++) { trkpt_t& pt = seg.pts[n]; if( (NOFLOAT == pt.lat || 0. == pt.lat) && (NOFLOAT == pt.lon || 0. == pt.lon) ) { pt.flags |= trkpt_t::eHidden; nothingDone = false; } else { done = true; } } } if(nothingDone) { return; } deriveSecondaryData(); changed(QObject::tr("Hide points with invalid coordinates at the beginning of the track"), "://icons/48x48/PointHide.png"); } void CGisItemTrk::filterReset() { for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; pt.flags &= ~trkpt_t::eHidden; } } deriveSecondaryData(); changed(QObject::tr("Reset all hidden track points to visible"), "://icons/48x48/PointHide.png"); } void CGisItemTrk::filterDelete() { bool nothingDone = true; for(int i = 0; i < trk.segs.size(); i++) { QVector pts; trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; if(pt.flags & trkpt_t::eHidden) { nothingDone = false; continue; } pts << pt; } seg.pts = pts; } if(nothingDone) { return; } deriveSecondaryData(); changed(QObject::tr("Permanently removed all hidden track points"), "://icons/48x48/PointHide.png"); } void CGisItemTrk::filterSmoothProfile(int points) { QVector window(points, 0); QVector ele1, ele2; for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; ele1 << pt.ele; ele2 << pt.ele; } } if(ele1.size() < (points + 1)) { return; } int d = points >> 1; for(int i = d; i < ele1.size() - d; i++) { for(int n = i - d, m = 0; m < points; n++, m++) { window[m] = ele1[n]; } qSort(window); ele2[i] = window[d]; } int cnt = 0; for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; pt.ele = ele2[cnt++]; } } deriveSecondaryData(); changed(QObject::tr("Smoothed profile with a Median filter of size %1").arg(points), "://icons/48x48/SetEle.png"); } void CGisItemTrk::filterReplaceElevation() { QPolygonF line; for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; line << QPointF(pt.lon * DEG_TO_RAD, pt.lat * DEG_TO_RAD); } } QPolygonF ele(line.size()); CMainWindow::self().getEelevationAt(line, ele); int cnt = 0; for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; pt.ele = ele[cnt++].y(); } } deriveSecondaryData(); changed(QObject::tr("Replaced elevation data with data from DEM files."), "://icons/48x48/SetEle.png"); } void CGisItemTrk::filterOffsetElevation(int offset) { for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; if(pt.ele != NOINT) { pt.ele += offset; } } } QString val, unit; IUnit::self().meter2elevation(offset, val, unit); deriveSecondaryData(); changed(QObject::tr("Offset elevation data by %1%2.").arg(val).arg(unit), "://icons/48x48/SetEle.png"); } void CGisItemTrk::filterNewDate(const QDateTime& date) { qint64 delta = qint64(date.toTime_t()) - qint64(timeStart.toUTC().toTime_t()); for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; pt.time = pt.time.addSecs(delta); } } deriveSecondaryData(); changed(QObject::tr("Changed start of track to %1.").arg(date.toString()), "://icons/48x48/Time.png"); } void CGisItemTrk::filterObscureDate(int delta) { if(delta == 0) { for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; pt.time = QDateTime(); } } deriveSecondaryData(); changed(QObject::tr("Remove timestamps."), "://icons/48x48/Time.png"); } else { QDateTime timestamp = timeStart; if(!timestamp.isValid()) { timestamp = QDateTime::currentDateTime().toUTC(); } for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; pt.time = timestamp; timestamp = timestamp.addSecs(delta); } } deriveSecondaryData(); changed(QObject::tr("Set artificial timestamps with delta of %1 sec.").arg(delta), "://icons/48x48/Time.png"); } } void CGisItemTrk::filterSpeed(qreal speed) { QDateTime timestamp = timeStart; if(!timestamp.isValid()) { timestamp = QDateTime::currentDateTime().toUTC(); } for(int i = 0; i < trk.segs.size(); i++) { trkseg_t& seg = trk.segs[i]; for(int n = 0; n < seg.pts.size(); n++) { trkpt_t& pt = seg.pts[n]; if(pt.flags & trkpt_t::eHidden) { continue; } qreal dmsec = 1000 * pt.deltaDistance/speed; timestamp = timestamp.addMSecs(qRound(dmsec)); pt.time = timestamp; } } deriveSecondaryData(); QString val, unit; IUnit::self().meter2speed(speed, val, unit); changed(QObject::tr("Changed speed to %1%2.").arg(val).arg(unit), "://icons/48x48/Time.png"); } qmapshack-1.5.1/src/gis/trk/filter/CFilterOffsetElevation.cpp000644 001750 000144 00000003467 12622435720 025204 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterOffsetElevation.h" #include "helpers/CSettings.h" #include "units/IUnit.h" CFilterOffsetElevation::CFilterOffsetElevation(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); spinBox->setSuffix(IUnit::self().baseunit); SETTINGS; spinBox->setValue(cfg.value("TrackDetails/Filter/OffsetElevation/offset",0).toInt()); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterOffsetElevation::~CFilterOffsetElevation() { SETTINGS; cfg.setValue("TrackDetails/Filter/OffsetElevation/offset", spinBox->value()); } void CFilterOffsetElevation::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterOffsetElevation"); trk.filterOffsetElevation(spinBox->value()/IUnit::self().basefactor); CCanvas::restoreOverrideCursor("CFilterOffsetElevation"); } qmapshack-1.5.1/src/gis/trk/filter/IFilterReplaceElevation.ui000644 001750 000144 00000005204 12623413746 025166 0ustar00oeichlerusers000000 000000 IFilterReplaceElevation 0 0 996 69 Form 3 0 0 0 3 0 0 :/icons/48x48/SetEle.png <b>Replace Elevation Data</b> 3 Replace elevation of track points with the values from loaded DEM files. ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal qmapshack-1.5.1/src/gis/trk/filter/CFilterReplaceElevation.h000644 001750 000144 00000002542 12623413746 024774 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERREPLACEELEVATION_H #define CFILTERREPLACEELEVATION_H #include "ui_IFilterReplaceElevation.h" #include class CGisItemTrk; class CFilterReplaceElevation : public QWidget, private Ui::IFilterReplaceElevation { Q_OBJECT public: CFilterReplaceElevation(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterReplaceElevation(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERREPLACEELEVATION_H qmapshack-1.5.1/src/gis/trk/filter/IFilterDouglasPeuker.ui000644 001750 000144 00000006600 12527654570 024524 0ustar00oeichlerusers000000 000000 IFilterDouglasPeuker 0 0 1001 82 Form 3 0 0 0 3 :/icons/48x48/PointHide.png <b>Hide Points (Douglas Peuker)</b> 3 Hide track points if the distance to a line between neighboring points is less than 0 0 m 1 Qt::Horizontal 40 20 Apply filter now. ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal label_2 line label_3 qmapshack-1.5.1/src/gis/trk/filter/CFilterSpeed.h000644 001750 000144 00000002412 12622435723 022604 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CFILTERSPEED_H #define CFILTERSPEED_H #include "ui_IFilterSpeed.h" #include class CGisItemTrk; class CFilterSpeed : public QWidget, private Ui::IFilterSpeed { Q_OBJECT public: CFilterSpeed(CGisItemTrk& trk, QWidget * parent); virtual ~CFilterSpeed(); private slots: void slotApply(); private: CGisItemTrk& trk; }; #endif //CFILTERSPEED_H qmapshack-1.5.1/src/gis/trk/filter/IFilterDelete.ui000644 001750 000144 00000005123 12527654570 023153 0ustar00oeichlerusers000000 000000 IFilterDelete 0 0 811 82 Form 3 0 0 0 3 0 0 :/icons/48x48/PointHide.png <b>Remove Track Points</b> 3 Remove all hidden track points permanently. ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal qmapshack-1.5.1/src/gis/trk/filter/CFilterMedian.cpp000644 001750 000144 00000003221 12622435720 023270 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "canvas/CCanvas.h" #include "gis/trk/CGisItemTrk.h" #include "gis/trk/filter/CFilterMedian.h" #include "helpers/CSettings.h" #include "units/IUnit.h" CFilterMedian::CFilterMedian(CGisItemTrk &trk, QWidget *parent) : QWidget(parent) , trk(trk) { setupUi(this); SETTINGS; spinBox->setValue(cfg.value("TrackDetails/Filter/Median/points",5).toInt()); connect(toolApply, SIGNAL(clicked()), this, SLOT(slotApply())); } CFilterMedian::~CFilterMedian() { SETTINGS; cfg.setValue("TrackDetails/Filter/Median/points", spinBox->value()); } void CFilterMedian::slotApply() { CCanvas::setOverrideCursor(Qt::WaitCursor,"CFilterMedian"); trk.filterSmoothProfile(spinBox->value()); CCanvas::restoreOverrideCursor("CFilterMedian"); } qmapshack-1.5.1/src/gis/trk/filter/IFilterObscureDate.ui000644 001750 000144 00000005763 12527654570 024163 0ustar00oeichlerusers000000 000000 IFilterObscureDate 0 0 812 58 Form 3 0 0 0 3 :/icons/48x48/Time.png <b>Obscure Timestamps</b> Increase timestamp by sec. with each track point. 0 sec. will remove timestamps. Qt::Horizontal 40 20 ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal qmapshack-1.5.1/src/gis/trk/filter/IFilterMedian.ui000644 001750 000144 00000007052 12527654570 023151 0ustar00oeichlerusers000000 000000 IFilterMedian 0 0 820 58 Form 3 0 0 0 3 0 0 :/icons/48x48/SetEle.png <b>Smooth Profile (Median Method)</b> 3 Smooth deviation of the track points elevation with a Median filter of size 0 0 points 5 9 2 Qt::Horizontal 40 20 ... :/icons/32x32/Apply.png:/icons/32x32/Apply.png 22 22 Qt::Horizontal qmapshack-1.5.1/src/gis/trk/CCutTrk.h000644 001750 000144 00000003043 12623647641 020333 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014-2015 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCUTTRK_H #define CCUTTRK_H #include "ui_ICutTrk.h" #include class CCutTrk : public QDialog, private Ui::ICutTrk { Q_OBJECT public: CCutTrk(QWidget * parent); virtual ~CCutTrk() = default; enum mode_e { eModeNone = 0 , eModeKeepFirst = 1 , eModeKeepBoth = 2 , eModeKeepSecond = 4 }; mode_e getMode() const { return mode; } bool createClone() { return checkCreateClone->isChecked(); } public slots: void accept(); private slots: void slotClicked(); private: mode_e mode = eModeNone; }; #endif //CCUTTRK_H qmapshack-1.5.1/src/gis/trk/CCombineTrk.h000644 001750 000144 00000003233 12622435723 021150 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CCOMBINETRK_H #define CCOMBINETRK_H #include "gis/IGisItem.h" #include "ui_ICombineTrk.h" #include class CGisItemTrk; class IGisProject; class CCombineTrk : public QDialog, private Ui::ICombineTrk { Q_OBJECT public: CCombineTrk(CGisItemTrk& trk, const QList& keysPreSel, IGisProject &project, QWidget * parent); virtual ~CCombineTrk(); const QList& getTrackKeys() { return keys; } public slots: void accept(); private slots: void slotSelectionChanged(); void slotSelect(); void slotRemove(); void slotUp(); void slotDown(); private: void updatePreview(); CGisItemTrk& trk; IGisProject& project; QList keys; }; #endif //CCOMBINETRK_H qmapshack-1.5.1/src/gis/IGisWidget.ui000644 001750 000144 00000010507 12527654570 020406 0ustar00oeichlerusers000000 000000 IGisWidget 0 0 402 500 Form 0 0 0 0 0 Qt::Vertical Qt::CustomContextMenu true QAbstractItemView::InternalMove QAbstractItemView::ExtendedSelection 20 20 14 100 Name Qt::CustomContextMenu QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows 20 20 10 - Name QFrame::StyledPanel QFrame::Raised 0 0 :/icons/48x48/Help.png To add a database do a right click on the database list above. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true CGisListWks QTreeWidget
gis/CGisListWks.h
CGisListDB QTreeWidget
gis/CGisListDB.h
qmapshack-1.5.1/src/gis/CGisListDB.h000644 001750 000144 00000004600 12622435723 020076 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISLISTDB_H #define CGISLISTDB_H #include #include #include struct action_t; class QMenu; class CDBFolderDatabase; class CGisListDB : public QTreeWidget { Q_OBJECT public: CGisListDB(QWidget * parent); virtual ~CGisListDB(); enum column_e { eColumnCheckbox = 0 ,eColumnName = 1 }; bool hasDatabase(const QString& name); bool event(QEvent * e); signals: void sigChanged(); private slots: void slotContextMenu(const QPoint& point); void slotAddFolder(); void slotDelFolder(); void slotDelLostFound(); void slotDelLostFoundItem(); void slotItemExpanded(QTreeWidgetItem * item); void slotItemChanged(QTreeWidgetItem * item, int column); void slotAddDatabase(); void slotDelDatabase(); void slotDelItem(); private: friend class CGisListDBEditLock; CDBFolderDatabase *getDataBase(const QString& name); void addDatabase(const QString& name, const QString& filename); int isInternalEdit = 0; QMenu * menuNone; QAction * actionAddDatabase; QMenu * menuFolder; QAction * actionAddFolder; QAction * actionDelFolder; QMenu * menuDatabase; QAction * actionDelDatabase; QMenu * menuItem; QAction * actionDelItem; QMenu * menuLostFound; QAction * actionDelLostFound; QMenu * menuLostFoundItem; QAction * actionDelLostFoundItem; // CDBFolderDatabase * folderDatabase; }; #endif //CGISLISTDB_H qmapshack-1.5.1/src/gis/prj/IGisProject.cpp000644 001750 000144 00000043417 12622435720 021525 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "device/IDevice.h" #include "gis/CGisDraw.h" #include "gis/CGisListWks.h" #include "gis/IGisItem.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/prj/CDetailsPrj.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CProgressDialog.h" #include "helpers/CSelectCopyAction.h" #include const QString IGisProject::filedialogAllSupported = "All Supported (*.gpx *.qms)"; const QString IGisProject::filedialogFilterGPX = "GPS Exchange Format (*.gpx)"; const QString IGisProject::filedialogFilterQMS = "QMapShack Binary (*.qms)"; const QString IGisProject::filedialogSaveFilters = filedialogFilterGPX + ";; " + filedialogFilterQMS; const QString IGisProject::filedialogLoadFilters = filedialogAllSupported +";; " + filedialogFilterGPX + ";; " + filedialogFilterQMS; IGisProject::IGisProject(type_e type, const QString &filename, CGisListWks *parent) : QTreeWidgetItem(parent) , type(type) , filename(filename) { memset(cntItemsByType, 0, sizeof(cntItemsByType)); setCheckState(CGisListWks::eColumnDecoration, Qt::Checked); if(parent) { // move project up the list until there a re only projects, no devices int newIdx = NOIDX; const int myIdx = parent->topLevelItemCount() - 1; for(int i = myIdx - 1; i >= 0; i--) { IDevice * device = dynamic_cast(parent->topLevelItem(i)); if(device != 0) { newIdx = i; continue; } break; } if(newIdx != NOIDX) { parent->takeTopLevelItem(myIdx); parent->insertTopLevelItem(newIdx, this); } } } IGisProject::IGisProject(type_e type, const QString &filename, IDevice *parent) : QTreeWidgetItem(parent) , type(type) , filename(filename) { memset(cntItemsByType, 0, sizeof(cntItemsByType)); setCheckState(CGisListWks::eColumnDecoration, Qt::Checked); nameSuffix = parent->getName(); } IGisProject::~IGisProject() { delete dlgDetails; } bool IGisProject::askBeforClose() { int res = QMessageBox::Ok; if(isChanged()) { CCanvas::setOverrideCursor(Qt::ArrowCursor, "askBeforClose"); res = QMessageBox::question(CMainWindow::getBestWidgetForParent(), QObject::tr("Save project?"), QObject::tr("

%1

The project was changed. Save before closing it?").arg(getName()), QMessageBox::Save|QMessageBox::No|QMessageBox::Abort, QMessageBox::No); CCanvas::restoreOverrideCursor("askBeforClose"); if(res == QMessageBox::Save) { save(); } } return res == QMessageBox::Abort; } bool IGisProject::isVisible() const { return checkState(CGisListWks::eColumnDecoration) == Qt::Checked; } void IGisProject::genKey() { if(key.isEmpty()) { QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); *this >> stream; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(buffer); key = md5.result().toHex(); } } QString IGisProject::getDeviceKey() const { IDevice * device = dynamic_cast(parent()); if(device) { return device->getKey(); } return ""; } QPixmap IGisProject::getIcon() const { return icon(CGisListWks::eColumnIcon).pixmap(22,22); } bool IGisProject::isOnDevice() const { IDevice * device = dynamic_cast(parent()); return device != 0; } bool IGisProject::isChanged() const { return text(CGisListWks::eColumnDecoration) == "*"; } void IGisProject::edit() { if(dlgDetails.isNull()) { dlgDetails = new CDetailsPrj(*this, 0); dlgDetails->setObjectName(getName()); } CMainWindow::self().addWidgetToTab(dlgDetails); } void IGisProject::setName(const QString& str) { metadata.name = str; setText(CGisListWks::eColumnName, getNameEx()); setChanged(); } void IGisProject::setKeywords(const QString& str) { metadata.keywords = str; setChanged(); } void IGisProject::setDescription(const QString& str) { metadata.desc = str; setChanged(); } void IGisProject::setLinks(const QList& links) { metadata.links = links; setChanged(); } void IGisProject::setSorting(sorting_e s) { sorting = s; setChanged(); } void IGisProject::setChanged() { setText(CGisListWks::eColumnDecoration,"*"); updateItems(); } void IGisProject::switchOnCorrelation() { noCorrelation = false; hashTrkWpt[0].clear(); hashTrkWpt[1].clear(); updateItems(); } void IGisProject::updateItems() { if(noUpdate) { return; } updateItemCounters(); if(noCorrelation || getItemCountByType(IGisItem::eTypeTrk) == 0 || getItemCountByType(IGisItem::eTypeWpt) == 0) { return; } if(hashTrkWpt[0] == hashTrkWpt[1]) { return; } quint32 total = cntTrkPts * cntWpts; quint32 current = 0; PROGRESS_SETUP(QObject::tr("%1: Correlate tracks and waypoints.").arg(getName()), 0, total, CMainWindow::getBestWidgetForParent()); for(int i = 0; i < childCount(); i++) { CGisItemTrk * trk = dynamic_cast(child(i)); if(trk) { trk->findWaypointsCloseBy(progress, current); if(progress.wasCanceled()) { QString msg = QObject::tr("

%1

Did that take too long for you? Do you want to skip correlation of tracks and waypoints for this project in the future?").arg(getNameEx()); int res = QMessageBox::question(&progress, QObject::tr("Canceled correlation..."), msg, QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); noCorrelation = res == QMessageBox::Yes; break; } } } } void IGisProject::setupName(const QString &defaultName) { if(metadata.name.isEmpty()) { metadata.name = defaultName; } setText(CGisListWks::eColumnName, getName()); } void IGisProject::markAsSaved() { setText(CGisListWks::eColumnDecoration,""); for(int i = 0; i < childCount(); i++) { IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } item->updateDecoration(IGisItem::eMarkNone, IGisItem::eMarkChanged); } } QString IGisProject::getName() const { return metadata.name; } QString IGisProject::getNameEx() const { if(nameSuffix.isEmpty()) { return metadata.name; } else { return metadata.name + " @ " + nameSuffix; } } QString IGisProject::getInfo() const { QString str = metadata.name.isEmpty() ? text(CGisListWks::eColumnName) : metadata.name; str = "
" + str + "
"; if(metadata.time.isValid()) { str += "
\n"; str += IUnit::datetime2string(metadata.time, false); } QString desc = IGisItem::removeHtml(metadata.desc).simplified(); if(!desc.isEmpty()) { str += "
\n"; if(desc.count() < 100) { str += desc; } else { str += desc.left(97) + "..."; } } if(!filename.isEmpty()) { str += QObject::tr("
\nFilename: %1").arg(filename); } if(cntItemsByType[IGisItem::eTypeWpt]) { str += "
\n" + QObject::tr("Waypoints: %1").arg(cntItemsByType[IGisItem::eTypeWpt]); } if(cntItemsByType[IGisItem::eTypeTrk]) { str += "
\n" + QObject::tr("Tracks: %1").arg(cntItemsByType[IGisItem::eTypeTrk]); } if(cntItemsByType[IGisItem::eTypeRte]) { str += "
\n" + QObject::tr("Routes: %1").arg(cntItemsByType[IGisItem::eTypeRte]); } if(cntItemsByType[IGisItem::eTypeOvl]) { str += "
\n" + QObject::tr("Areas: %1").arg(cntItemsByType[IGisItem::eTypeOvl]); } return str; } IGisItem * IGisProject::getItemByKey(const IGisItem::key_t& key) { for(int i = 0; i < childCount(); i++) { IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } if(item->getKey() == key) { return item; } } return 0; } void IGisProject::getItemsByPos(const QPointF& pos, QList &items) { if(!isVisible()) { return; } for(int i = 0; i < childCount(); i++) { IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } if(item->isCloseTo(pos)) { items << item; } } } void IGisProject::mouseMove(const QPointF& pos) { if(!isVisible()) { return; } for(int i = 0; i < childCount(); i++) { IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } item->mouseMove(pos); } } bool IGisProject::delItemByKey(const IGisItem::key_t& key, QMessageBox::StandardButtons& last) { for(int i = childCount(); i > 0; i--) { IGisItem * item = dynamic_cast(child(i-1)); if(item == 0) { continue; } if(item->getKey() == key) { if(last != QMessageBox::YesToAll) { QString msg = QObject::tr("Are you sure you want to delete '%1' from project '%2'?").arg(item->getName()).arg(text(CGisListWks::eColumnName)); last = QMessageBox::question(CMainWindow::getBestWidgetForParent(), QObject::tr("Delete..."), msg, QMessageBox::YesToAll|QMessageBox::Cancel|QMessageBox::Ok|QMessageBox::No, QMessageBox::Ok); if((last == QMessageBox::No) || (last == QMessageBox::Cancel)) { // as each item in the project has to be unique, we can stop searching. return false; } } delete item; /* Database projects are a bit different. Deleting an item does not really mean the project is changed as the item is still stored in the database. */ if(type != eTypeDb) { setChanged(); } // as each item in the project has to be unique, we can stop searching. return true; } } return false; } void IGisProject::editItemByKey(const IGisItem::key_t& key) { for(int i = childCount(); i > 0; i--) { IGisItem * item = dynamic_cast(child(i-1)); if(item == 0) { continue; } if(item->getKey() == key) { item->edit(); } } } void IGisProject::insertCopyOfItem(IGisItem * item, int off, int& lastResult) { bool clone = false; IGisItem::key_t key = item->getKey(); key.project = getKey(); key.device = getDeviceKey(); IGisItem * item2 = getItemByKey(key); if(item2 != 0) { int result = lastResult; if(lastResult == CSelectCopyAction::eResultNone) { CSelectCopyAction dlg(item, item2, CMainWindow::getBestWidgetForParent()); dlg.exec(); result = dlg.getResult(); if(dlg.allOthersToo()) { lastResult = result; } } if(result == CSelectCopyAction::eResultSkip) { return; } if(result == CSelectCopyAction::eResultNone) { return; } if(result == CSelectCopyAction::eResultClone) { clone = true; } else { // replace item2 with item if(item != item2) { delete item2; } else { // replacing an item with itself does not make sense return; } } } switch(item->type()) { case IGisItem::eTypeTrk: { CGisItemTrk * trk = dynamic_cast(item); if(trk != 0) { CGisItemTrk * newTrk = new CGisItemTrk(*trk, this, off, clone); // if the track is on a device, remove hidden trackpoints if(isOnDevice()) { newTrk->filterDelete(); } } break; } case IGisItem::eTypeWpt: { CGisItemWpt * wpt = dynamic_cast(item); if(wpt != 0) { new CGisItemWpt(*wpt, this, off, clone); } break; } case IGisItem::eTypeRte: { CGisItemRte * rte = dynamic_cast(item); if(rte != 0) { new CGisItemRte(*rte, this, off, clone); } break; } case IGisItem::eTypeOvl: { CGisItemOvlArea * area = dynamic_cast(item); if(area != 0) { new CGisItemOvlArea(*area, this, off, clone); } break; } } } void IGisProject::drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis) { if(!isVisible()) { return; } for(int i = 0; i < childCount(); i++) { if(gis->needsRedraw()) { break; } IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } item->drawItem(p, viewport, blockedAreas, gis); } } void IGisProject::drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis) { if(!isVisible()) { return; } for(int i = 0; i < childCount(); i++) { IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } item->drawItem(p, viewport, gis); } } void IGisProject::drawLabel(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis) { if(!isVisible()) { return; } for(int i = 0; i < childCount(); i++) { if(gis->needsRedraw()) { break; } IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } item->drawLabel(p, viewport, blockedAreas, fm, gis); } } void IGisProject::mount() { if(!isOnDevice()) { return; } IDevice * device = dynamic_cast(parent()); if(device) { device->mount(); } } void IGisProject::umount() { if(!isOnDevice()) { return; } IDevice * device = dynamic_cast(parent()); if(device) { device->umount(); } } bool IGisProject::remove() { mount(); /* Check if parent is a device and give it a chance to take care of data. e.g. Garmin devices remove images attached to the project. */ IDevice * device = dynamic_cast(parent()); if(device) { device->aboutToRemoveProject(this); } QFileInfo fi(filename); if(fi.isFile()) { QFile::remove(filename); } else if(fi.isDir()) { QDir(filename).removeRecursively(); } umount(); return true; } void IGisProject::updateItemCounters() { // count number of items by type memset(cntItemsByType, 0, sizeof(cntItemsByType)); cntTrkPts = 0; cntWpts = 0; totalDistance = 0; totalAscend = 0; totalDescend = 0; totalElapsedSeconds = 0; totalElapsedSecondsMoving = 0; QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream.setVersion(QDataStream::Qt_5_2); for(int i = 0; i < childCount(); i++) { IGisItem * item = dynamic_cast(child(i)); if(item == 0) { continue; } cntItemsByType[item->type()]++; CGisItemTrk * trk = dynamic_cast(item); if(trk) { cntTrkPts += trk->getNumberOfVisiblePoints(); totalDistance += trk->getTotalDistance(); totalAscend += trk->getTotalAscend(); totalDescend += trk->getTotalDescend(); totalElapsedSeconds += trk->getTotalElapsedSeconds(); totalElapsedSecondsMoving += trk->getTotalElapsedSecondsMoving(); stream << trk->getHash(); } CGisItemWpt * wpt = dynamic_cast(item); if(wpt) { cntWpts++; stream << wpt->getHash(); } } QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(buffer); hashTrkWpt[1] = hashTrkWpt[0]; hashTrkWpt[0] = md5.result().toHex(); } void IGisProject::blockUpdateItems(bool yes) { noUpdate = yes; if(noUpdate == false) { updateItems(); } } qmapshack-1.5.1/src/gis/prj/IDetailsPrj.ui000644 001750 000144 00000010714 12527654570 021354 0ustar00oeichlerusers000000 000000 IDetailsPrj 0 0 873 511 Form 3 3 3 3 3 0 0 Keywords: - - 0 0 Keep order of project Sort by time Sort along track (multiple) Sort along track (single) ... :/icons/32x32/UnLock.png :/icons/32x32/Lock.png:/icons/32x32/UnLock.png 22 22 true Print diary ... :/icons/32x32/Print.png:/icons/32x32/Print.png 22 22 Rebuild diary. ... :/icons/32x32/Reset.png:/icons/32x32/Reset.png 22 22 Qt::ScrollBarAlwaysOn true qmapshack-1.5.1/src/gis/prj/IGisProject.h000644 001750 000144 00000027317 12622435723 021176 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef IGISPROJECT_H #define IGISPROJECT_H #include "gis/IGisItem.h" #include #include #include class CGisListWks; class IGisItem; class CGisDraw; class QDataStream; class CDetailsPrj; class IDevice; class IGisProject : public QTreeWidgetItem { public: enum type_e { eTypeGoogle , eTypeQms , eTypeGpx , eTypeDb , eTypeLostFound , eTypeTwoNav }; enum sorting_e { eSortNone , eSortTime , eSortTrackWithDouble , eSortTrackWithoutDouble }; struct person_t { QString name; QString id; QString domain; IGisItem::link_t link; }; struct copyright_t { QString author; QString year; QString license; }; struct metadata_t { metadata_t() : time(QDateTime::currentDateTimeUtc()) { } QString name; QString desc; person_t author; copyright_t copyright; QList links; QDateTime time; QString keywords; QRectF bounds; // -- all gpx tags - stop QMap extensions; }; static const QString filedialogAllSupported; static const QString filedialogFilterGPX; static const QString filedialogFilterQMS; static const QString filedialogSaveFilters; static const QString filedialogLoadFilters; IGisProject(type_e type, const QString& filename, CGisListWks * parent); IGisProject(type_e type, const QString &filename, IDevice *parent); virtual ~IGisProject(); /** @brief Ask to save the project before it is closed. If the project is closed, the user is asked if the project should be saved and saved on user request. @return True if the operation is aborted. False on "save" and "no". */ bool askBeforClose(); IGisProject& operator=(const IGisProject& p) { key = p.key; metadata = p.metadata; return *this; } /** @brief Summon the project details dialog. */ void edit(); /** @brief Save the project using it's native format. */ virtual bool save() = 0; /** @brief Save the project selecting one of the available formats. */ virtual bool saveAs() = 0; virtual void setFilename(const QString& fn) { filename = fn; } virtual QString getFilename() const { return filename; } /** @brief Get the project type enumeration. @Note: usually dynamic_cast should be used to get a pointer of correct type. However if the project is serialized, a type id is needed. @return One of type_e */ type_e getType() const { return type; } /** @brief Get unique project key. @return A MD5 hash string */ const QString& getKey() { genKey(); return key; } /** @brief Get the unique key of the device the project is attached to @return If the project is not attached to a device the string is empty */ QString getDeviceKey() const; QPixmap getIcon() const; /** @brief Get the project's name @return The name from metadata.name */ QString getName() const; /** @brief Get the project's name extended with the parent's name. @return The name from metadata.nam appended with either the device name or the database parent folder's name. */ QString getNameEx() const; const QDateTime& getTime() const { return metadata.time; } const QString& getKeywords() const { return metadata.keywords; } const QString& getDescription() const { return metadata.desc; } const QList& getLinks() const { return metadata.links; } /** @brief Get the sorting mode @return One of sorting_e */ sorting_e getSorting() const { return sorting; } void setName(const QString& str); void setKeywords(const QString& str); void setDescription(const QString& str); void setLinks(const QList& links); /** @brief Set change mark */ void setChanged(); /** @brief Set the sorting mode_t This will mark the project as changed. @param s the mode */ void setSorting(sorting_e s); /** @brief Get a short metadata summary @return Informational string. */ virtual QString getInfo() const; /** @brief Get a temporary pointer to the item with matching key @param key @return If no item is found 0 is returned. */ IGisItem * getItemByKey(const IGisItem::key_t &key); /** @brief Get a list of items that are close to a given pixel coordinate of the screen @note: The returned pointers are just for temporary use. Best you use them to get the item's key. @param pos the coordinate on the screen in pixel @param items a list the item's pointer is stored to. */ void getItemsByPos(const QPointF& pos, QList& items); int getItemCountByType(IGisItem::type_e type) { return cntItemsByType[type]; } qreal getTotalDistance() const { return totalDistance; } qreal getTotalAscend() const { return totalAscend; } qreal getTotalDescend() const { return totalDescend; } qreal getTotalElapsedSeconds() const { return totalElapsedSeconds; } qreal getTotalElapsedSecondsMoving() const { return totalElapsedSecondsMoving; } bool doCorrelation() { return !noCorrelation; } void switchOnCorrelation(); /** @brief Receive the current mouse position Iterate over all items and pass the position @param pos the mouse position on the screen in pixel */ virtual void mouseMove(const QPointF& pos); /** @brief Delete items with matching key @param key */ bool delItemByKey(const IGisItem::key_t &key, QMessageBox::StandardButtons &last); /** @brief Call IGisItem::edit() method for items with given key @param key a MD5 hash key */ void editItemByKey(const IGisItem::key_t &key); /** @brief Add a copy if the given item to the project Before the item is inserted the method will use it's key to find a duplicate item. If there is an item with the same item key a copy option dialog is shown. Depending the result the action is performed or aborted. The result will be copied into lastResult to repeat the same decision on subsequent items. @param item pointer to item @param off the offset into the tree widget, -1 for none @param lastResult a reference to hold the last result of the copy option dialog */ void insertCopyOfItem(IGisItem *item, int off, int &lastResult); /** @brief Check if the project was initialized correctly. For example a if a GPX file does not load correctly the project is invalid. @return True if project is valid */ bool isValid() const { return valid; } /** @brief Test if visibility check mark is set @return True if project is visible */ bool isVisible() const; /** @brief Test if this project is handled by a device @return True if handled by a device */ bool isOnDevice() const; /** @brief Test if project has been changed @return True if changed. */ bool isChanged() const; void drawItem(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, CGisDraw * gis); void drawLabel(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis); void drawItem(QPainter& p, const QRectF& viewport, CGisDraw * gis); /** @brief Serialize object out of a QDataStream See CGisSerialization.cpp for implementation @param stream the binary data stream @return The stream object. */ virtual QDataStream& operator<<(QDataStream& stream); /** @brief Serialize object into a QDataStream See CGisSerialization.cpp for implementation @param stream the binary data stream @return The stream object. */ virtual QDataStream& operator>>(QDataStream& stream); /** @brief writeMetadata @param doc @return */ QDomNode writeMetadata(QDomDocument& doc); /** @brief Mount volume the project's file is stored at This is only valid for projects located on GPS devices. For all other projects the method does nothing. */ void mount(); /** @brief Umount volume the project's file is stored at This is only valid for projects located on GPS devices. For all other projects the method does nothing. */ void umount(); /** @brief Removed the projects file from disk. This is only valid for projects located on GPS devices. For all other projects the method does nothing. */ bool remove(); /** @brief Block update of items. Use this to speed up actions with many items, e.g. copy actions. If the blocking is stopped (yes == false) updateItems() is called. @param yes set true to block updating items */ void blockUpdateItems(bool yes); /** @brief Return state of current update block @return True if updates are blocked. */ bool blockUpdateItems() const { return noUpdate; } protected: void genKey(); virtual void setupName(const QString& defaultName); void markAsSaved(); void readMetadata(const QDomNode& xml, metadata_t& metadata); void updateItems(); void updateItemCounters(); // Those are the URIs of the GPX extensions we support static const QString gpxx_ns; static const QString gpxtpx_ns; static const QString wptx1_ns; static const QString rmc_ns; static const QString ql_ns; static const QString gs_ns; // Those are standard GPX/XML namespaces static const QString gpx_ns; static const QString xsi_ns; type_e type; QString key; QString filename; bool valid = false; bool noUpdate = false; bool noCorrelation = false; metadata_t metadata; QString nameSuffix; QPointer dlgDetails; sorting_e sorting = eSortNone; qint32 cntItemsByType[IGisItem::eTypeMax]; qint32 cntTrkPts = 0; qint32 cntWpts = 0; qreal totalDistance = 0; qreal totalAscend = 0; qreal totalDescend = 0; quint32 totalElapsedSeconds = 0; quint32 totalElapsedSecondsMoving = 0; QString hashTrkWpt[2]; }; #endif //IGISPROJECT_H qmapshack-1.5.1/src/gis/prj/CDetailsPrj.h000644 001750 000144 00000006103 12622435723 021146 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDETAILSPRJ_H #define CDETAILSPRJ_H #include "ui_IDetailsPrj.h" #include #include #include class CDetailsPrj; class IGisProject; class CGisItemTrk; class CGisItemWpt; class CGisItemOvlArea; class CGisItemRte; class CProgressDialog; class QTimer; class CDetailsPrj : public QWidget, private Ui::IDetailsPrj { Q_OBJECT public: CDetailsPrj(IGisProject& prj, QWidget * parent); virtual ~CDetailsPrj(); protected: void resizeEvent(QResizeEvent * e); private slots: void slotLinkActivated(const QString& link); void slotLinkActivated(const QUrl& url); void slotPrint(); void slotLock(bool on); void slotSortMode(int idx); void slotSetupGui(); void slotSetScrollbar(); private: void getTrackProfile(CGisItemTrk * trk, QImage& image); void getTrackOverview(CGisItemTrk * trk, QImage& image); void draw(QTextDocument& doc, bool printable); void drawInfo(QTextCursor& cursor, bool isReadOnly); void drawTrackSummary(QTextCursor& cursor, const QList trks, bool isReadOnly); void drawByGroup(QTextCursor& cursor, QList &trks, QList &wpts, CProgressDialog &progress, int &n, bool printable); void drawByTrack(QTextCursor& cursor, QList &trks, QList &wpts, CProgressDialog &progress, int &n, bool printable); void drawArea(QTextCursor& cursor, QList &areas, CProgressDialog &progress, int &n, bool printable); void drawRoute(QTextCursor& cursor, QList &rtes, CProgressDialog &progress, int &n, bool printable); enum eTblCol1 {eSym1, eInfo1, eComment1, eMax1}; enum eTblCol2 {eSym2, eInfo2, eData2, eComment2, eMax2}; IGisProject& prj; QTextFrameFormat fmtFrameStandard; QTextFrameFormat fmtFrameTrackSummary; QTextCharFormat fmtCharStandard; QTextBlockFormat fmtBlockStandard; QTextFrameFormat fmtFrameRoot; QTextTableFormat fmtTableStandard; QTextTableFormat fmtTableHidden; QTextTableFormat fmtTableInfo; QTextCharFormat fmtCharHeader; int scrollVal = 0; QTimer * timerUpdateTime; QMutex mutex {QMutex::NonRecursive}; }; #endif //CDETAILSPRJ_H qmapshack-1.5.1/src/gis/prj/CDetailsPrj.cpp000644 001750 000144 00000066310 12622435720 021504 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/IGisItem.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/prj/CDetailsPrj.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CLinksDialog.h" #include "helpers/CProgressDialog.h" #include "plot/CPlotProfile.h" #include "plot/CPlotTrack.h" #include "widgets/CTextEditWidget.h" #include #include CDetailsPrj::CDetailsPrj(IGisProject &prj, QWidget *parent) : QWidget(parent) , prj(prj) { setupUi(this); connect(labelKeywords, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(textDesc, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotLinkActivated(QUrl))); connect(toolPrint, SIGNAL(clicked()), this, SLOT(slotPrint())); connect(toolReload, SIGNAL(clicked()), this, SLOT(slotSetupGui())); connect(comboSort, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSortMode(int))); connect(toolLock, SIGNAL(clicked(bool)), this, SLOT(slotLock(bool))); timerUpdateTime = new QTimer(this); timerUpdateTime->setSingleShot(true); timerUpdateTime->setInterval(20); connect(timerUpdateTime, SIGNAL(timeout()), this, SLOT(slotSetupGui())); timerUpdateTime->start(); } CDetailsPrj::~CDetailsPrj() { } void CDetailsPrj::resizeEvent(QResizeEvent * e) { QWidget::resizeEvent(e); timerUpdateTime->start(); } void CDetailsPrj::getTrackProfile(CGisItemTrk * trk, QImage& image) { CPlotProfile plot(trk, IPlot::eModeIcon, this); plot.setSolid(true); plot.save(image); } void CDetailsPrj::getTrackOverview(CGisItemTrk * trk, QImage& image) { CPlotTrack plot(trk, this); plot.save(image); } void CDetailsPrj::slotSetupGui() { if(!mutex.tryLock()) { /* What is this about? When drawing the diary a progress dialog is used. This dialog is operating the event loop. Consequently new events resulting into drawing the diary can be processed. But slotSetupGui() is not reentrant. That is why we have to block these calls with a mutex. However as something has changed the diary has to be redrawn again. That is why the timer is restarted. */ timerUpdateTime->start(1000); return; } comboSort->blockSignals(true); comboSort->setCurrentIndex(prj.getSorting()); if((prj.getSorting() > IGisProject::eSortTime) && !prj.doCorrelation()) { QString msg = tr("You want to sort waypoints along a track, but you switched off track and waypoint correlation. Do you want to switch it on again?"); int res = QMessageBox::question(this, tr("Correlation..."), msg, QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); if(res == QMessageBox::Yes) { prj.switchOnCorrelation(); } else { comboSort->setCurrentIndex(IGisProject::eSortNone); } timerUpdateTime->start(); mutex.unlock(); return; } comboSort->blockSignals(false); toolLock->blockSignals(true); const int N = prj.childCount(); if(N == 0) { toolLock->setChecked(false); toolLock->setEnabled(false); } else { toolLock->setChecked(true); toolLock->setEnabled(true); for(int n = 0; n < N; n++) { IGisItem * item = dynamic_cast(prj.child(n)); if(item && !item->isReadOnly()) { toolLock->setChecked(false); break; } } } toolLock->blockSignals(false); textDesc->document()->setTextWidth(textDesc->size().width() - 20); draw(*textDesc->document(), false); QTabWidget * tabWidget = dynamic_cast(parentWidget() ? parentWidget()->parentWidget() : 0); if(tabWidget) { int idx = tabWidget->indexOf(this); if(idx != NOIDX) { setObjectName(prj.getName()); tabWidget->setTabText(idx, prj.getName()); } } mutex.unlock(); } #define ROOT_FRAME_MARGIN 5 #define CHAR_PER_LINE 130 bool sortTrkByTime(const CGisItemTrk * trk1, const CGisItemTrk * trk2) { return trk1->getTimeStart() < trk2->getTimeStart(); } bool sortWptByTime(const CGisItemWpt * wpt1, const CGisItemWpt * wpt2) { return wpt1->getTime() < wpt2->getTime(); } void CDetailsPrj::draw(QTextDocument& doc, bool printable) { int w = doc.textWidth(); int nItems = 0; QFontMetrics fm(QFont(font().family(),12)); int pointSize = ((10 * (w - 2 * ROOT_FRAME_MARGIN)) / (CHAR_PER_LINE * fm.width("X"))); if(pointSize == 0) { return; } QFont f = textDesc->font(); f.setPointSize(pointSize); textDesc->setFont(f); fmtFrameStandard.setTopMargin(5); fmtFrameStandard.setBottomMargin(5); fmtFrameStandard.setWidth(w - 2 * ROOT_FRAME_MARGIN); fmtFrameTrackSummary.setBackground(Qt::white); fmtFrameTrackSummary.setBorder(1); fmtFrameTrackSummary.setPadding(10); fmtCharStandard.setFont(f); fmtBlockStandard.setTopMargin(10); fmtBlockStandard.setBottomMargin(10); fmtBlockStandard.setAlignment(Qt::AlignJustify); fmtFrameRoot.setTopMargin(0); fmtFrameRoot.setBottomMargin(ROOT_FRAME_MARGIN); fmtFrameRoot.setLeftMargin(ROOT_FRAME_MARGIN); fmtFrameRoot.setRightMargin(ROOT_FRAME_MARGIN); fmtTableStandard.setBorder(1); fmtTableStandard.setBorderBrush(Qt::black); fmtTableStandard.setCellPadding(4); fmtTableStandard.setCellSpacing(0); fmtTableStandard.setHeaderRowCount(1); fmtTableStandard.setTopMargin(10); fmtTableStandard.setBottomMargin(20); fmtTableStandard.setWidth(w - 4 * ROOT_FRAME_MARGIN); QVector constraints1; constraints1 << QTextLength(QTextLength::FixedLength, 32); constraints1 << QTextLength(QTextLength::VariableLength, 50); constraints1 << QTextLength(QTextLength::VariableLength, 100); fmtTableStandard.setColumnWidthConstraints(constraints1); fmtTableHidden.setBorder(0); fmtTableHidden.setCellPadding(4); fmtTableHidden.setCellSpacing(0); fmtTableHidden.setTopMargin(0); fmtTableHidden.setBottomMargin(0); QVector constraints2; constraints2 << QTextLength(QTextLength::PercentageLength, 70); constraints2 << QTextLength(QTextLength::PercentageLength, 30); fmtTableHidden.setColumnWidthConstraints(constraints2); fmtTableInfo.setBorder(0); fmtCharHeader.setFont(f); fmtCharHeader.setBackground(Qt::darkBlue); fmtCharHeader.setFontWeight(QFont::Bold); fmtCharHeader.setForeground(Qt::white); bool isReadOnly = printable || prj.isOnDevice() || toolLock->isChecked(); setWindowTitle(prj.getName()); labelTime->setText(IUnit::datetime2string(prj.getTime(), false)); QString keywords = prj.getKeywords(); if(keywords.isEmpty()) { keywords = tr("none"); } labelKeywords->setText(IGisItem::toLink(isReadOnly, "keywords", keywords, "")); scrollVal = textDesc->verticalScrollBar()->value(); doc.clear(); doc.rootFrame()->setFrameFormat(fmtFrameRoot); QTextCursor cursor = doc.rootFrame()->firstCursorPosition(); cursor.insertHtml(IGisItem::toLink(isReadOnly, "name", QString("

%1

").arg(prj.getNameEx()), "")); QList trks; QList rtes; QList wpts; QList areas; const int N = prj.childCount(); for(int i = 0; i < N; i++) { CGisItemTrk * trk = dynamic_cast(prj.child(i)); if(trk != 0) { trks << trk; nItems++; continue; } CGisItemRte * rte = dynamic_cast(prj.child(i)); if(rte != 0) { rtes << rte; nItems++; continue; } CGisItemWpt * wpt = dynamic_cast(prj.child(i)); if(wpt != 0) { wpts << wpt; nItems++; continue; } CGisItemOvlArea * area = dynamic_cast(prj.child(i)); if(area != 0) { areas << area; nItems++; continue; } } QTextFrame * diaryFrame = cursor.insertFrame(fmtFrameStandard); { QTextCursor cursor1(diaryFrame); cursor1.setCharFormat(fmtCharStandard); cursor1.setBlockFormat(fmtBlockStandard); QTextTable * table = cursor1.insertTable(1, 2, fmtTableHidden); QTextCursor cursor2 = table->cellAt(0,0).firstCursorPosition(); drawInfo(cursor2, isReadOnly); if(prj.getItemCountByType(IGisItem::eTypeTrk) != 0) { QTextCursor cursor3 = table->cellAt(0,1).firstCursorPosition(); drawTrackSummary(cursor3, trks, isReadOnly); } } int n=1; PROGRESS_SETUP(tr("Build diary..."), 0, nItems, this); if(comboSort->currentIndex() > IGisProject::eSortTime) { drawByTrack(cursor, trks, wpts, progress, n, isReadOnly); } else { drawByGroup(cursor, trks, wpts, progress, n, isReadOnly); } drawRoute(cursor, rtes, progress, n, isReadOnly); drawArea(cursor, areas, progress, n, isReadOnly); QTimer::singleShot(1, this, SLOT(slotSetScrollbar())); } void CDetailsPrj::slotSetScrollbar() { textDesc->verticalScrollBar()->setValue(scrollVal); comboSort->setEnabled(true); } void CDetailsPrj::drawInfo(QTextCursor& cursor, bool isReadOnly) { QTextFrame * diaryFrame = cursor.insertFrame(fmtFrameStandard); QTextCursor cursor1(diaryFrame); cursor1.setCharFormat(fmtCharStandard); cursor1.setBlockFormat(fmtBlockStandard); cursor1.insertHtml(IGisItem::createText(isReadOnly, prj.getDescription(), prj.getLinks())); } void CDetailsPrj::drawTrackSummary(QTextCursor& cursor, const QList trks, bool isReadOnly) { quint32 flags = 0; QVector summaries(CGisItemTrk::trkpt_t::eActMaxNum + 1); foreach(const CGisItemTrk* trk, trks) { const CActivityTrk& activities = trk->getActivities(); flags |= activities.getAllFlags(); activities.sumUp(summaries); } QTextFrame * diaryFrame = cursor.insertFrame(fmtFrameTrackSummary); QTextCursor cursor1(diaryFrame); cursor1.setCharFormat(fmtCharStandard); cursor1.setBlockFormat(fmtBlockStandard); QString str; str += tr("Summary over all tracks in project
"); CActivityTrk::printSummary(summaries, flags, str); cursor1.insertHtml(str); } void CDetailsPrj::drawByGroup(QTextCursor &cursor, QList& trks, QList& wpts, CProgressDialog& progress, int& n, bool printable) { int cnt, w = cursor.document()->textWidth(); if(comboSort->currentIndex() == IGisProject::eSortTime) { qSort(trks.begin(), trks.end(), sortTrkByTime); qSort(wpts.begin(), wpts.end(), sortWptByTime); } if(!wpts.isEmpty()) { cursor.insertHtml(tr("

Waypoints

")); QTextTable * table = cursor.insertTable(wpts.count()+1, eMax1, fmtTableStandard); table->cellAt(0,eSym1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).setFormat(fmtCharHeader); table->cellAt(0,eComment1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).firstCursorPosition().insertText(tr("Info")); table->cellAt(0,eComment1).firstCursorPosition().insertText(tr("Comment")); cnt = 1; foreach(CGisItemWpt * wpt, wpts) { PROGRESS(n++, return ); table->cellAt(cnt,eSym1).firstCursorPosition().insertImage(wpt->getIcon().toImage().scaledToWidth(16, Qt::SmoothTransformation)); table->cellAt(cnt,eInfo1).firstCursorPosition().insertHtml(wpt->getInfo()); table->cellAt(cnt,eComment1).firstCursorPosition().insertHtml(IGisItem::createText(wpt->isReadOnly()||printable, wpt->getComment(), wpt->getDescription(), wpt->getLinks(), wpt->getKey().item)); cnt++; } cursor.setPosition(table->lastPosition() + 1); } if(!trks.isEmpty()) { cursor.insertHtml(tr("

Tracks

")); QTextTable * table = cursor.insertTable(trks.count()+1, eMax1, fmtTableStandard); table->cellAt(0,eSym1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).setFormat(fmtCharHeader); table->cellAt(0,eComment1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).firstCursorPosition().insertText(tr("Info")); table->cellAt(0,eComment1).firstCursorPosition().insertText(tr("Comment")); cnt = 1; foreach(CGisItemTrk * trk, trks) { PROGRESS(n++, return ); table->cellAt(cnt,eSym1).firstCursorPosition().insertImage(trk->getIcon().toImage().scaledToWidth(16, Qt::SmoothTransformation)); int w1 = qRound(w/3.5 > 300 ? 300 : w/3.5); int h1 = qRound(w1/2.0); if(w1 < 300) { table->cellAt(cnt,eInfo1).firstCursorPosition().insertHtml(trk->getInfo()); QTextTable * table1 = table->cellAt(cnt,eInfo1).lastCursorPosition().insertTable(1, 2, fmtTableInfo); QImage profile(w1,h1,QImage::Format_ARGB32); getTrackProfile(trk, profile); table1->cellAt(0,0).firstCursorPosition().insertImage(profile); QImage overview(h1,h1,QImage::Format_ARGB32); getTrackOverview(trk, overview); table1->cellAt(0,1).firstCursorPosition().insertImage(overview); } else { QTextTable * table1 = table->cellAt(cnt,eInfo1).firstCursorPosition().insertTable(1, 3, fmtTableInfo); table1->cellAt(0,0).firstCursorPosition().insertHtml(trk->getInfo()); QImage profile(w1,h1,QImage::Format_ARGB32); getTrackProfile(trk, profile); table1->cellAt(0,1).firstCursorPosition().insertImage(profile); QImage overview(h1,h1,QImage::Format_ARGB32); getTrackOverview(trk, overview); table1->cellAt(0,2).firstCursorPosition().insertImage(overview); } table->cellAt(cnt,eComment1).firstCursorPosition().insertHtml(IGisItem::createText(trk->isReadOnly()||printable, trk->getComment(), trk->getDescription(), trk->getLinks(), trk->getKey().item)); cnt++; } cursor.setPosition(table->lastPosition() + 1); } } struct wpt_info_t { IGisItem::key_t key; qreal distance; qreal ascend; qreal descend; }; void CDetailsPrj::drawByTrack(QTextCursor& cursor, QList &trks, QList &wpts, CProgressDialog &progress, int &n, bool printable) { int cnt, w = cursor.document()->textWidth(); if(comboSort->currentIndex() == IGisProject::eSortTime) { qSort(trks.begin(), trks.end(), sortTrkByTime); } const qreal w1 = qRound(w/3.5 > 300 ? 300 : w/3.5); const qreal h1 = qRound(w1/2.0); foreach(CGisItemTrk * trk, trks) { QList wptInfo; const CGisItemTrk::trk_t& t = trk->getTrackData(); foreach (const CGisItemTrk::trkseg_t& seg, t.segs) { foreach(const CGisItemTrk::trkpt_t& trkpt, seg.pts) { if((trkpt.flags & CGisItemTrk::trkpt_t::eHidden) || trkpt.keyWpt.item.isEmpty()) { continue; } wptInfo << wpt_info_t(); wpt_info_t& info = wptInfo.last(); info.key = trkpt.keyWpt; info.distance = trkpt.distance; info.ascend = trkpt.ascend; info.descend = trkpt.descend; } } cursor.insertHtml(QString("

%1

").arg(trk->getName())); QTextTable * table = cursor.insertTable(wptInfo.count()+2, eMax2, fmtTableStandard); table->cellAt(0,eSym2).setFormat(fmtCharHeader); table->cellAt(0,eInfo2).setFormat(fmtCharHeader); table->cellAt(0,eData2).setFormat(fmtCharHeader); table->cellAt(0,eComment2).setFormat(fmtCharHeader); table->cellAt(0,eInfo2).firstCursorPosition().insertText(tr("Info")); table->cellAt(0,eComment2).firstCursorPosition().insertText(tr("Comment")); cnt = 1; foreach(const wpt_info_t &info, wptInfo) { PROGRESS(n++, return ); CGisItemWpt * wpt = dynamic_cast(prj.getItemByKey(info.key)); if(wpt != 0) { table->cellAt(cnt,eSym2).firstCursorPosition().insertImage(wpt->getIcon().toImage().scaledToWidth(16, Qt::SmoothTransformation)); table->cellAt(cnt,eInfo2).firstCursorPosition().insertHtml(wpt->getInfo()); QTextTable * table1 = table->cellAt(cnt,eData2).lastCursorPosition().insertTable(1, 2, fmtTableInfo); QString text, val, unit; IUnit::self().meter2distance(info.distance, val, unit); text += "
"+ tr("distance: %1%2").arg(val).arg(unit) + "
"; IUnit::self().meter2elevation(info.ascend, val, unit); text += "
"+ tr("ascent: %1%2").arg(val).arg(unit) + "
"; IUnit::self().meter2elevation(info.descend, val, unit); text += "
"+ tr("descend: %1%2").arg(val).arg(unit) + "
"; table1->cellAt(0,0).firstCursorPosition().insertHtml(text); const QList& images = wpt->getImages(); if(!images.isEmpty()) { QImage image(images.first().pixmap); qDebug() << image.size(); int w = image.width(); int h = image.height(); if(w < h) { h *= 100.0 / w; w = 100; } else { h *= 200.0 / w; w = 200; } qDebug() << w << h; image = image.scaled(w,h,Qt::KeepAspectRatio, Qt::SmoothTransformation); table1->cellAt(0,1).firstCursorPosition().insertImage(image); } table->cellAt(cnt,eComment2).firstCursorPosition().insertHtml(IGisItem::createText(wpt->isReadOnly()||printable, wpt->getComment(), wpt->getDescription(), wpt->getLinks(), wpt->getKey().item)); } cnt++; } table->cellAt(cnt,eSym2).firstCursorPosition().insertImage(trk->getIcon().toImage().scaledToWidth(16, Qt::SmoothTransformation)); table->cellAt(cnt,eInfo2).firstCursorPosition().insertHtml(trk->getInfo()); QTextTable * table1 = table->cellAt(cnt,eData2).lastCursorPosition().insertTable(1, 2, fmtTableInfo); QImage profile(w1,h1,QImage::Format_ARGB32); getTrackProfile(trk, profile); table1->cellAt(0,0).firstCursorPosition().insertImage(profile); QImage overview(h1,h1,QImage::Format_ARGB32); getTrackOverview(trk, overview); table1->cellAt(0,1).firstCursorPosition().insertImage(overview); table->cellAt(cnt,eComment2).firstCursorPosition().insertHtml(IGisItem::createText(trk->isReadOnly()||printable, trk->getComment(), trk->getDescription(), trk->getLinks(), trk->getKey().item)); cursor.setPosition(table->lastPosition() + 1); } } void CDetailsPrj::drawArea(QTextCursor& cursor, QList &areas, CProgressDialog &progress, int &n, bool printable) { if(areas.isEmpty()) { return; } cursor.insertHtml(tr("

Areas

")); QTextTable * table = cursor.insertTable(areas.count()+1, eMax1, fmtTableStandard); table->cellAt(0,eSym1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).setFormat(fmtCharHeader); table->cellAt(0,eComment1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).firstCursorPosition().insertText(tr("Info")); table->cellAt(0,eComment1).firstCursorPosition().insertText(tr("Comment")); int cnt = 1; foreach(CGisItemOvlArea * area, areas) { PROGRESS(n++, return ); table->cellAt(cnt,eSym1).firstCursorPosition().insertImage(area->getIcon().toImage().scaledToWidth(16, Qt::SmoothTransformation)); table->cellAt(cnt,eInfo1).firstCursorPosition().insertHtml(area->getInfo()); table->cellAt(cnt,eComment1).firstCursorPosition().insertHtml(IGisItem::createText(area->isReadOnly()||printable, area->getComment(), area->getDescription(), area->getLinks(), area->getKey().item)); cnt++; } cursor.setPosition(table->lastPosition() + 1); } void CDetailsPrj::drawRoute(QTextCursor& cursor, QList &rtes, CProgressDialog &progress, int &n, bool printable) { if(rtes.isEmpty()) { return; } cursor.insertHtml(tr("

Routes

")); QTextTable * table = cursor.insertTable(rtes.count()+1, eMax1, fmtTableStandard); table->cellAt(0,eSym1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).setFormat(fmtCharHeader); table->cellAt(0,eComment1).setFormat(fmtCharHeader); table->cellAt(0,eInfo1).firstCursorPosition().insertText(tr("Info")); table->cellAt(0,eComment1).firstCursorPosition().insertText(tr("Comment")); int cnt = 1; foreach(CGisItemRte * rte, rtes) { PROGRESS(n++, return ); table->cellAt(cnt,eSym1).firstCursorPosition().insertImage(rte->getIcon().toImage().scaledToWidth(16, Qt::SmoothTransformation)); table->cellAt(cnt,eInfo1).firstCursorPosition().insertHtml(rte->getInfo()); table->cellAt(cnt,eComment1).firstCursorPosition().insertHtml(IGisItem::createText(rte->isReadOnly()||printable, rte->getComment(), rte->getDescription(), rte->getLinks(), rte->getKey().item)); cnt++; } cursor.setPosition(table->lastPosition() + 1); } void CDetailsPrj::slotLinkActivated(const QString& link) { if(link == "name") { QString name = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new project name."), QLineEdit::Normal, prj.getName()); if(name.isEmpty()) { return; } prj.setName(name); } else if(link == "keywords") { QString keywords = QInputDialog::getText(this, tr("Edit keywords..."), tr("Enter keywords."), QLineEdit::Normal, prj.getKeywords()); if(keywords.isEmpty()) { return; } if(keywords == tr("none")) { keywords.clear(); } prj.setKeywords(keywords); } slotSetupGui(); } void CDetailsPrj::slotLinkActivated(const QUrl& url) { if(url.path() == "name") { QString name = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new project name."), QLineEdit::Normal, prj.getName()); if(!name.isEmpty()) { prj.setName(name); } slotSetupGui(); } else if(url.path() == "description") { if(url.hasQuery()) { IGisItem::key_t key; key.project = prj.getKey(); QString query = url.query(); if(query.startsWith("key=")) { key.item = query.mid(4); } IGisItem * item = prj.getItemByKey(key); if(item) { CTextEditWidget dlg(this); dlg.setHtml(item->getDescription()); if(dlg.exec() == QDialog::Accepted) { item->setDescription(dlg.getHtml()); } } } else { CTextEditWidget dlg(0); dlg.setHtml(prj.getDescription()); if(dlg.exec() == QDialog::Accepted) { prj.setDescription(dlg.getHtml()); } } slotSetupGui(); } else if(url.path() == "comment") { if(url.hasQuery()) { IGisItem::key_t key; key.project = prj.getKey(); QString query = url.query(); if(query.startsWith("key=")) { key.item = query.mid(4); } IGisItem * item = prj.getItemByKey(key); if(item) { CTextEditWidget dlg(this); dlg.setHtml(item->getComment()); if(dlg.exec() == QDialog::Accepted) { item->setComment(dlg.getHtml()); } } } slotSetupGui(); } else if(url.path() == "links") { if(url.hasQuery()) { IGisItem::key_t key; key.project = prj.getKey(); QString query = url.query(); if(query.startsWith("key=")) { key.item = query.mid(4); } IGisItem * item = prj.getItemByKey(key); if(item) { QList links = item->getLinks(); CLinksDialog dlg(links, this); if(dlg.exec() == QDialog::Accepted) { item->setLinks(links); } } } else { QList links = prj.getLinks(); CLinksDialog dlg(links, this); if(dlg.exec() == QDialog::Accepted) { prj.setLinks(links); } } slotSetupGui(); } else { QDesktopServices::openUrl(url); } } void CDetailsPrj::slotPrint() { QPrinter printer; printer.setResolution(200); printer.setPageSize(QPrinter::A4); QPrintDialog dialog(&printer, this); dialog.setWindowTitle(tr("Print Diary")); if (dialog.exec() != QDialog::Accepted) { return; } QTextDocument doc; QSizeF pageSize = printer.pageRect(QPrinter::DevicePixel).size(); doc.setPageSize(pageSize); draw(doc, true); doc.print(&printer); slotSetupGui(); } void CDetailsPrj::slotLock(bool on) { prj.blockUpdateItems(true); const int N = prj.childCount(); for(int n = 0; n < N; n++) { IGisItem * item = dynamic_cast(prj.child(n)); if(item && (item->isReadOnly() != on)) { item->setReadOnlyMode(on); } } prj.blockUpdateItems(false); slotSetupGui(); } void CDetailsPrj::slotSortMode(int idx) { comboSort->setEnabled(false); prj.setSorting(IGisProject::sorting_e(idx)); slotSetupGui(); } qmapshack-1.5.1/src/gis/search/CSearchGoogle.cpp000644 001750 000144 00000012035 12622435720 022452 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #include "gis/CGisListWks.h" #include "gis/WptIcons.h" #include "gis/search/CSearchGoogle.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CSettings.h" #include "helpers/CWptIconDialog.h" #include #include #include CSearchGoogle::CSearchGoogle(CGisListWks * parent) : IGisProject(eTypeGoogle, "", parent) { QPointF focus; SETTINGS; QString symName = cfg.value("Search/symbol","Default").toString(); parent->takeTopLevelItem(parent->indexOfTopLevelItem(this)); parent->insertTopLevelItem(0, this); edit = new QLineEdit(parent); actSymbol = edit->addAction(getWptIconByName(symName, focus), QLineEdit::TrailingPosition); actSymbol->setObjectName(symName); connect(actSymbol, SIGNAL(triggered()), this, SLOT(slotChangeSymbol())); parent->setItemWidget(this, CGisListWks::eColumnName, edit); connect(edit, SIGNAL(returnPressed()), this, SLOT(slotStartSearch())); connect(&networkAccessManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(slotRequestFinished(QNetworkReply*))); setIcon(CGisListWks::eColumnDecoration, QIcon("://icons/32x32/SearchGoogle.png")); } CSearchGoogle::~CSearchGoogle() { } void CSearchGoogle::slotChangeSymbol() { CWptIconDialog dlg(actSymbol); dlg.exec(); SETTINGS; cfg.setValue("Search/symbol", actSymbol->objectName()); } void CSearchGoogle::slotStartSearch() { qDeleteAll(takeChildren()); QString addr = edit->text(); QUrl url("http://maps.googleapis.com"); url.setPath("/maps/api/geocode/xml"); QUrlQuery urlQuery; urlQuery.addQueryItem("address",addr.replace(" ","+")); urlQuery.addQueryItem("sensor","false"); url.setQuery(urlQuery); QNetworkRequest request; request.setUrl(url); networkAccessManager.get(request); edit->setEnabled(false); } void CSearchGoogle::slotRequestFinished(QNetworkReply* reply) { edit->setEnabled(true); if(reply->error() != QNetworkReply::NoError) { reply->deleteLater(); return; } QByteArray data = reply->readAll(); reply->deleteLater(); if(data.isEmpty()) { return; } QString status; QDomDocument xml; xml.setContent(data); // qDebug() << xml.toString(); QDomElement root = xml.documentElement(); if(root.tagName() != "GeocodeResponse") { status = tr("Unknown response"); QTreeWidgetItem * item = new QTreeWidgetItem(this); item->setText(CGisListWks::eColumnName, status); item->setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/Error.png")); return; } status = root.namedItem("status").toElement().text(); if(status != "OK") { status = tr("Error: "); status += root.namedItem("error_message").toElement().text(); QTreeWidgetItem * item = new QTreeWidgetItem(this); item->setText(CGisListWks::eColumnName, status); item->setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/Error.png")); return; } { QDomNodeList xmlEntries = root.elementsByTagName("result"); const qint32 N = xmlEntries.size(); if(N) { for(int i = 0; i < N; i++) { QString address; QDomElement xmlEntry = xmlEntries.item(i).toElement(); QDomElement xmlAddress = xmlEntry.namedItem("formatted_address").toElement(); if(xmlAddress.isElement()) { address = xmlAddress.text(); } QDomNode xmlGeometry = xmlEntry.namedItem("geometry"); QDomNode xmlLocation = xmlGeometry.namedItem("location"); qreal lon = xmlLocation.namedItem("lng").toElement().text().toDouble(); qreal lat = xmlLocation.namedItem("lat").toElement().text().toDouble(); new CGisItemWpt(QPointF(lon,lat), address, actSymbol->objectName(), this); } } } setExpanded(true); CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } } qmapshack-1.5.1/src/gis/search/CSearchGoogle.h000644 001750 000144 00000003074 12622435723 022125 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSEARCHGOOGLE_H #define CSEARCHGOOGLE_H #include #include #include class CGisListWks; class QLineEdit; class CSearchGoogle : public QObject, public IGisProject { Q_OBJECT public: CSearchGoogle(CGisListWks * parent); virtual ~CSearchGoogle(); bool save() { return false; } bool saveAs() { return false; } private slots: void slotChangeSymbol(); void slotStartSearch(); void slotRequestFinished(QNetworkReply* reply); private: QLineEdit * edit; QAction * actSymbol; QNetworkAccessManager networkAccessManager; }; #endif //CSEARCHGOOGLE_H qmapshack-1.5.1/src/gis/ISelDevices.ui000644 001750 000144 00000002742 12527654570 020550 0ustar00oeichlerusers000000 000000 ISelDevices 0 0 321 354 Select devices... Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ISelDevices accept() 248 254 157 274 buttonBox rejected() ISelDevices reject() 316 260 286 274 qmapshack-1.5.1/src/gis/ovl/CDetailsOvlArea.cpp000644 001750 000144 00000014601 12622435720 022303 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/ovl/CDetailsOvlArea.h" #include "gis/ovl/CGisItemOvlArea.h" #include "helpers/CLinksDialog.h" #include "widgets/CTextEditWidget.h" #include CDetailsOvlArea::CDetailsOvlArea(CGisItemOvlArea &area, QWidget * parent) : QDialog(parent) , area(area) { setupUi(this); QPixmap icon(64,24); for(int i=0; i < OVL_N_COLORS; ++i) { icon.fill(CGisItemOvlArea::lineColors[i]); comboColor->addItem(icon,"",CGisItemOvlArea::lineColors[i]); } for(int i = 0; i < OVL_N_STYLES; i++) { icon.fill(Qt::white); QPainter p(&icon); p.setPen(Qt::black); p.setBrush(CGisItemOvlArea::brushStyles[i]); p.drawRect(icon.rect()); comboStyle->addItem(icon,"",(int)CGisItemOvlArea::brushStyles[i]); } for(int i = 0; i < OVL_N_WIDTHS; i++) { comboBorderWidth->addItem(CGisItemOvlArea::lineWidths[i].string, CGisItemOvlArea::lineWidths[i].width); } setupGui(); if(area.isOnDevice()) { toolLock->setDisabled(true); } connect(comboColor, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetColor(int))); connect(comboBorderWidth, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetWidth(int))); connect(comboStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetStyle(int))); connect(checkOpacity, SIGNAL(toggled(bool)), this, SLOT(slotOpyacity(bool))); connect(toolLock, SIGNAL(toggled(bool)), this, SLOT(slotChangeReadOnlyMode(bool))); connect(textCmtDesc, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotLinkActivated(QUrl))); connect(labelName, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); connect(listHistory, SIGNAL(sigChanged()), this, SLOT(setupGui())); } CDetailsOvlArea::~CDetailsOvlArea() { } void CDetailsOvlArea::slotSetColor(int idx) { if(area.isReadOnly() || originator) { return; } area.setColor(idx); setupGui(); } void CDetailsOvlArea::slotSetWidth(int idx) { if(area.isReadOnly() || originator) { return; } area.setWidth(CGisItemOvlArea::lineWidths[idx].width); setupGui(); } void CDetailsOvlArea::slotSetStyle(int idx) { if(area.isReadOnly() || originator) { return; } area.setStyle(CGisItemOvlArea::brushStyles[idx]); setupGui(); } void CDetailsOvlArea::slotOpyacity(bool yes) { if(area.isReadOnly() || originator) { return; } area.setOpacity(yes); setupGui(); } void CDetailsOvlArea::slotChangeReadOnlyMode(bool on) { area.setReadOnlyMode(on); setupGui(); } void CDetailsOvlArea::slotLinkActivated(const QString& link) { if(link == "name") { QString name = QInputDialog::getText(this, tr("Edit name..."), tr("Enter new area name."), QLineEdit::Normal, area.getName()); if(name.isEmpty()) { return; } area.setName(name); } setupGui(); } void CDetailsOvlArea::slotLinkActivated(const QUrl& url) { if(url.toString() == "comment") { CTextEditWidget dlg(0); dlg.setHtml(area.getComment()); if(dlg.exec() == QDialog::Accepted) { area.setComment(dlg.getHtml()); } setupGui(); } else if(url.toString() == "description") { CTextEditWidget dlg(0); dlg.setHtml(area.getDescription()); if(dlg.exec() == QDialog::Accepted) { area.setDescription(dlg.getHtml()); } setupGui(); } else if(url.toString() == "links") { QList links = area.getLinks(); CLinksDialog dlg(links, this); if(dlg.exec() == QDialog::Accepted) { area.setLinks(links); } setupGui(); } else { QDesktopServices::openUrl(url); } } void CDetailsOvlArea::setupGui() { if(originator) { return; } originator = true; bool isReadOnly = area.isReadOnly(); setWindowTitle(area.getName()); if(area.isTainted()) { labelTainted->show(); } else { labelTainted->hide(); } labelName->setText(IGisItem::toLink(isReadOnly, "name", area.getName(), "")); comboColor->setCurrentIndex(area.getColorIdx()); comboColor->setEnabled(!isReadOnly); comboBorderWidth->setCurrentIndex(comboBorderWidth->findData(area.getWidth())); comboBorderWidth->setEnabled(!isReadOnly); comboStyle->setCurrentIndex(comboStyle->findData(area.getStyle())); comboStyle->setEnabled(!isReadOnly); checkOpacity->setChecked(area.getOpacity()); checkOpacity->setEnabled(!isReadOnly); textCmtDesc->document()->clear(); textCmtDesc->append(IGisItem::createText(isReadOnly, area.getComment(), area.getDescription(), area.getLinks())); textCmtDesc->moveCursor (QTextCursor::Start); textCmtDesc->ensureCursorVisible(); int idx = 0; QList items; const CGisItemOvlArea::area_t& a = area.getAreaData(); foreach(const CGisItemOvlArea::pt_t& pt, a.pts) { QString str; QTreeWidgetItem * item = new QTreeWidgetItem(); item->setText(eColNum,QString::number(idx++)); // position IUnit::degToStr(pt.lon, pt.lat, str); item->setText(eColPosition,str); items << item; } treeWidget->clear(); treeWidget->addTopLevelItems(items); treeWidget->header()->resizeSections(QHeaderView::ResizeToContents); toolLock->setChecked(isReadOnly); listHistory->setupHistory(area); originator = false; } qmapshack-1.5.1/src/gis/ovl/IDetailsOvlArea.ui000644 001750 000144 00000017435 12616742144 022160 0ustar00oeichlerusers000000 000000 IDetailsOvlArea 0 0 400 400 Dialog 3 3 3 3 3 15 75 true - 0 0 0 0 25 25 <html><head/><body><p>The waypoint was imported to QMapShack and was changed. It does not show the original data anymore. Please see history for changes. </p></body></html> :/icons/32x32/Tainted.png true Toggle read only mode. You have to open the lock to edit the item. ... :/icons/32x32/UnLock.png :/icons/32x32/Lock.png:/icons/32x32/UnLock.png 22 22 true true Color 64 24 Border width Style 64 24 Opacity 0 Info 0 0 0 0 0 false Points 0 0 0 0 0 # Position Hist. 0 0 0 0 0 CHistoryListWidget QListWidget
widgets/CHistoryListWidget.h
qmapshack-1.5.1/src/gis/ovl/CScrOptOvlArea.h000644 001750 000144 00000002743 12622435723 021604 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CSCROPTOVLAREA_H #define CSCROPTOVLAREA_H #include "gis/IGisItem.h" #include "mouse/IScrOpt.h" #include "ui_IScrOptOvlArea.h" class CGisItemOvlArea; class IMouse; class CScrOptOvlArea : public IScrOpt, private Ui::IScrOptOvlArea { Q_OBJECT public: CScrOptOvlArea(CGisItemOvlArea * area, const QPoint &point, IMouse *parent); virtual ~CScrOptOvlArea(); void draw(QPainter& p); private slots: void slotEditDetails(); void slotCopy(); void slotDelete(); void slotEdit(); private: IGisItem::key_t key; QPointF anchor; }; #endif //CSCROPTOVLAREA_H qmapshack-1.5.1/src/gis/ovl/CGisItemOvlArea.cpp000644 001750 000144 00000034112 12624154220 022251 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "GeoMath.h" #include "gis/CGisDraw.h" #include "gis/CGisListWks.h" #include "gis/ovl/CDetailsOvlArea.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/ovl/CScrOptOvlArea.h" #include "gis/prj/IGisProject.h" #include "helpers/CDraw.h" #include #include #define DEFAULT_COLOR 4 #define MIN_DIST_CLOSE_TO 10 const QColor CGisItemOvlArea::lineColors[OVL_N_COLORS] = { Qt::black // 0 ,Qt::darkRed // 1 ,Qt::darkGreen // 2 ,Qt::darkYellow // 3 ,Qt::darkBlue // 4 ,Qt::darkMagenta // 5 ,Qt::darkCyan // 6 ,Qt::gray // 7 ,Qt::darkGray // 8 ,Qt::red // 9 ,Qt::green // 10 ,Qt::yellow // 11 ,Qt::blue // 12 ,Qt::magenta // 13 ,Qt::cyan // 14 ,Qt::white // 15 ,Qt::transparent // 16 }; const QString CGisItemOvlArea::bulletColors[OVL_N_COLORS] = { // 0 "://icons/8x8/bullet_black.png" // 1 ,QString("://icons/8x8/bullet_dark_red.png") // 2 ,QString("://icons/8x8/bullet_dark_green.png") // 3 ,QString("://icons/8x8/bullet_dark_yellow.png") // 4 ,QString("://icons/8x8/bullet_dark_blue.png") // 5 ,QString("://icons/8x8/bullet_dark_magenta.png") // 6 ,QString("://icons/8x8/bullet_dark_cyan.png") // 7 ,QString("://icons/8x8/bullet_gray.png") // 8 ,QString("://icons/8x8/bullet_dark_gray.png") // 9 ,QString("://icons/8x8/bullet_red.png") // 10 ,QString("://icons/8x8/bullet_green.png") // 11 ,QString("://icons/8x8/bullet_yellow.png") // 12 ,QString("://icons/8x8/bullet_blue.png") // 13 ,QString("://icons/8x8/bullet_magenta.png") // 14 ,QString("://icons/8x8/bullet_cyan.png") // 15 ,QString("://icons/8x8/bullet_white.png") ,QString("") // 16 }; const CGisItemOvlArea::width_t CGisItemOvlArea::lineWidths[OVL_N_WIDTHS] = { {3, QObject::tr("thin")} ,{5, QObject::tr("normal")} ,{9, QObject::tr("wide")} ,{13, QObject::tr("strong")} }; const Qt::BrushStyle CGisItemOvlArea::brushStyles[OVL_N_STYLES] = { Qt::NoBrush , Qt::HorPattern , Qt::VerPattern , Qt::CrossPattern , Qt::BDiagPattern , Qt::FDiagPattern , Qt::DiagCrossPattern , Qt::SolidPattern }; IGisItem::key_t CGisItemOvlArea::keyUserFocus; CGisItemOvlArea::CGisItemOvlArea(const SGisLine &line, const QString &name, IGisProject * project, int idx) : IGisItem(project, eTypeOvl, idx) { area.name = name; readAreaDataFromGisLine(line); flags |= eFlagCreatedInQms|eFlagWriteAllowed; setColor(str2color("")); setupHistory(); updateDecoration(eMarkChanged, eMarkNone); } CGisItemOvlArea::CGisItemOvlArea(const CGisItemOvlArea& parentArea, IGisProject * project, int idx, bool clone) : IGisItem(project, eTypeOvl, idx) { history = parentArea.history; loadHistory(history.histIdxCurrent); if(clone) { area.name += QObject::tr("_Clone"); key.clear(); history.events.clear(); setupHistory(); } if(parentArea.isOnDevice() || !parentArea.isReadOnly()) { flags |= eFlagWriteAllowed; } else { flags &= ~eFlagWriteAllowed; } deriveSecondaryData(); updateDecoration(eMarkChanged, eMarkNone); } CGisItemOvlArea::CGisItemOvlArea(const QDomNode &xml, IGisProject *project) : IGisItem(project, eTypeOvl, project->childCount()) { // --- start read and process data ---- setColor(penForeground.color()); readArea(xml, area); // --- stop read and process data ---- setupHistory(); updateDecoration(eMarkNone, eMarkNone); } CGisItemOvlArea::CGisItemOvlArea(const history_t& hist, IGisProject * project) : IGisItem(project, eTypeOvl, project->childCount()) { history = hist; loadHistory(hist.histIdxCurrent); } CGisItemOvlArea::CGisItemOvlArea(quint64 id, QSqlDatabase& db, IGisProject * project) : IGisItem(project, eTypeOvl, NOIDX) { loadFromDb(id, db); } CGisItemOvlArea::~CGisItemOvlArea() { // reset user focus if focused on this track if(key == keyUserFocus) { keyUserFocus.clear(); } } void CGisItemOvlArea::setSymbol() { setColor(str2color(area.color)); } bool CGisItemOvlArea::isCloseTo(const QPointF& pos) { QMutexLocker lock(&mutexItems); qreal dist = GPS_Math_DistPointPolyline(polygonArea, pos); return dist < 20; } QPointF CGisItemOvlArea::getPointCloseBy(const QPoint& screenPos) { QMutexLocker lock(&mutexItems); qint32 i = 0; qint32 idx = NOIDX; qint32 d = NOINT; foreach(const QPointF &point, polygonArea) { int tmp = (screenPos - point).manhattanLength(); if(tmp < d) { idx = i; d = tmp; } i++; } if(idx < 0) { return NOPOINTF; } return polygonArea[idx]; } void CGisItemOvlArea::readAreaDataFromGisLine(const SGisLine &l) { QMutexLocker lock(&mutexItems); area.pts.clear(); for(int i = 0; i < l.size(); i++) { area.pts << pt_t(); pt_t& areapt = area.pts.last(); const point_t& pt = l[i]; areapt.lon = pt.coord.x() * RAD_TO_DEG; areapt.lat = pt.coord.y() * RAD_TO_DEG; for(int n = 0; n < pt.subpts.size(); n++) { area.pts << pt_t(); pt_t& areapt = area.pts.last(); const subpt_t& sub = pt.subpts[n]; areapt.lon = sub.coord.x() * RAD_TO_DEG; areapt.lat = sub.coord.y() * RAD_TO_DEG; } } deriveSecondaryData(); } void CGisItemOvlArea::edit() { CDetailsOvlArea dlg(*this, 0); dlg.exec(); deriveSecondaryData(); } void CGisItemOvlArea::deriveSecondaryData() { qreal north = -90; qreal east = -180; qreal south = 90; qreal west = 180; foreach(const pt_t &pt, area.pts) { if(pt.lon < west) { west = pt.lon; } if(pt.lon > east) { east = pt.lon; } if(pt.lat < south) { south = pt.lat; } if(pt.lat > north) { north = pt.lat; } } boundingRect = QRectF(QPointF(west * DEG_TO_RAD, north * DEG_TO_RAD), QPointF(east * DEG_TO_RAD,south * DEG_TO_RAD)); QPolygonF line(area.pts.size()); for(int i = 1; i < area.pts.size(); i++) { qreal a1, a2, d; const pt_t& pt11 = area.pts[i - 1]; const pt_t& pt12 = area.pts[i]; QPointF& pt21 = line[i - 1]; QPointF& pt22 = line[i]; d = GPS_Math_Distance(pt11.lon * DEG_TO_RAD, pt11.lat * DEG_TO_RAD, pt12.lon * DEG_TO_RAD, pt12.lat * DEG_TO_RAD, a1, a2); pt22.rx() = pt21.x() + qCos(a1 * DEG_TO_RAD) * d; pt22.ry() = pt21.y() + qSin(a1 * DEG_TO_RAD) * d; } area.area = 0; int j = line.size() - 1; for(int i = 0; i < line.size(); i++) { area.area += (line[j].x() + line[i].x())*(line[j].y() - line[i].y()); j = i; } area.area = qAbs(area.area/2); } void CGisItemOvlArea::drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis) { QMutexLocker lock(&mutexItems); polygonArea.clear(); if(!isVisible(boundingRect, viewport, gis)) { return; } QPointF pt1; foreach(const pt_t &pt, area.pts) { pt1.setX(pt.lon); pt1.setY(pt.lat); pt1 *= DEG_TO_RAD; polygonArea << pt1; } gis->convertRad2Px(polygonArea); p.save(); p.setOpacity(area.opacity ? 0.3 : 1.0); penBackground.setWidth(area.width + 2); p.setBrush(Qt::NoBrush); p.setPen(penBackground); p.drawPolygon(polygonArea); penForeground.setColor(color); penForeground.setWidth(area.width); p.setBrush(QBrush(color, (Qt::BrushStyle)area.style)); p.setPen(penForeground); p.drawPolygon(polygonArea); p.restore(); } void CGisItemOvlArea::drawLabel(QPainter& p, const QPolygonF &viewport, QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis) { QMutexLocker lock(&mutexItems); if(polygonArea.isEmpty()) { return; } QPointF pt = getPolygonCentroid(polygonArea); QRectF rect = fm.boundingRect(area.name); rect.adjust(-2,-2,2,2); rect.moveCenter(pt); CDraw::text(getName(), p, pt.toPoint(), Qt::darkBlue); blockedAreas << rect; } void CGisItemOvlArea::drawHighlight(QPainter& p) { QMutexLocker lock(&mutexItems); if(polygonArea.isEmpty() || key == keyUserFocus) { return; } p.setBrush(Qt::NoBrush); p.setPen(QPen(QColor(255,0,0,100),11,Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawPolygon(polygonArea); } void CGisItemOvlArea::gainUserFocus(bool yes) { keyUserFocus = yes ? key : key_t(); } QPointF CGisItemOvlArea::getPolygonCentroid(const QPolygonF& polygon) { int i, len; qreal x = 0, y = 0; len = polygon.size(); for(i = 0; i < len; i++) { x = x + polygon[i].x(); y = y + polygon[i].y(); } x = x / len; y = y / len; return QPointF(x,y); } IScrOpt * CGisItemOvlArea::getScreenOptions(const QPoint& origin, IMouse * mouse) { if(scrOpt.isNull()) { scrOpt = new CScrOptOvlArea(this, origin, mouse); } return scrOpt; } QString CGisItemOvlArea::getInfo(bool allowEdit) const { QString unit, val; QString str = "
" + getName() + "
"; IUnit::self().meter2area(area.area, val, unit); str += "
\n" + QObject::tr("Area: %1%2").arg(val).arg(unit); QString desc = removeHtml(area.desc).simplified(); if(desc.count()) { if(!str.isEmpty()) { str += "
\n"; } if(desc.count() < 200) { str += desc; } else { str += desc.left(197) + "..."; } } else { QString cmt = removeHtml(area.cmt).simplified(); if(cmt.count()) { if(!str.isEmpty()) { str += "
\n"; } if(cmt.count() < 200) { str += cmt; } else { str += cmt.left(197) + "..."; } } } return str; } void CGisItemOvlArea::getPolylineFromData(SGisLine &l) { QMutexLocker lock(&mutexItems); l.clear(); foreach(const pt_t &pt, area.pts) { l << point_t(QPointF(pt.lon * DEG_TO_RAD, pt.lat * DEG_TO_RAD)); } } void CGisItemOvlArea::setDataFromPolyline(const SGisLine& l) { QMutexLocker lock(&mutexItems); readAreaDataFromGisLine(l); flags |= eFlagTainted; changed(QObject::tr("Changed area shape."), "://icons/48x48/AreaMove.png"); updateDecoration(eMarkChanged, eMarkNone); } void CGisItemOvlArea::setName(const QString& str) { setText(CGisListWks::eColumnName, str); area.name = str; changed(QObject::tr("Changed name."), "://icons/48x48/EditText.png"); } void CGisItemOvlArea::setWidth(qint32 w) { area.width = w; changed(QObject::tr("Changed border width."), "://icons/48x48/TextBold.png"); } void CGisItemOvlArea::setStyle(qint32 s) { area.style = s; changed(QObject::tr("Changed fill pattern."), "://icons/48x48/Pattern.png"); } void CGisItemOvlArea::setOpacity(bool yes) { area.opacity = yes; changed(QObject::tr("Changed opacity."), "://icons/48x48/Opacity.png"); } void CGisItemOvlArea::setComment(const QString& str) { area.cmt = str; changed(QObject::tr("Changed comment."), "://icons/48x48/EditText.png"); } void CGisItemOvlArea::setDescription(const QString& str) { area.desc = str; changed(QObject::tr("Changed description."), "://icons/48x48/EditText.png"); } void CGisItemOvlArea::setLinks(const QList& links) { area.links = links; changed(QObject::tr("Changed links"), "://icons/48x48/Link.png"); } void CGisItemOvlArea::setColor(int idx) { int N = sizeof(lineColors)/sizeof(QColor); if(idx >= N) { return; } setColor(lineColors[idx]); changed(QObject::tr("Changed color"), "://icons/48x48/SelectColor.png"); } void CGisItemOvlArea::setColor(const QColor& c) { int n; int N = sizeof(lineColors)/sizeof(QColor); for(n = 0; n < N; n++) { if(lineColors[n] == c) { colorIdx = n; color = lineColors[n]; bullet = QPixmap(bulletColors[n]); break; } } if(n == N) { colorIdx = DEFAULT_COLOR; color = lineColors[DEFAULT_COLOR]; bullet = QPixmap(bulletColors[DEFAULT_COLOR]); } setIcon(color.name()); } void CGisItemOvlArea::setIcon(const QString& c) { area.color = c; icon = QPixmap("://icons/48x48/Area.png"); QPixmap mask( icon.size() ); mask.fill( str2color(c) ); mask.setMask( icon.createMaskFromColor( Qt::transparent ) ); icon = mask.scaled(22,22, Qt::KeepAspectRatio, Qt::SmoothTransformation); QTreeWidgetItem::setIcon(CGisListWks::eColumnIcon,icon); } qmapshack-1.5.1/src/gis/ovl/CGisItemOvlArea.h000644 001750 000144 00000011654 12624147155 021735 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISITEMOVLAREA_H #define CGISITEMOVLAREA_H #include "gis/IGisItem.h" #include "gis/IGisLine.h" #include #include class IGisProject; class CScrOptOvlArea; class IQlgtOverlay; #define OVL_N_COLORS 17 #define OVL_N_WIDTHS 4 #define OVL_N_STYLES 8 class CGisItemOvlArea : public IGisItem, public IGisLine { public: CGisItemOvlArea(const SGisLine& line, const QString &name, IGisProject * project, int idx); CGisItemOvlArea(const CGisItemOvlArea &parentArea, IGisProject * project, int idx, bool clone); CGisItemOvlArea(const QDomNode &xml, IGisProject *project); CGisItemOvlArea(const history_t& hist, IGisProject * project); CGisItemOvlArea(quint64 id, QSqlDatabase& db, IGisProject * project); CGisItemOvlArea(const IQlgtOverlay& ovl); virtual ~CGisItemOvlArea(); QDataStream& operator<<(QDataStream& stream); QDataStream& operator>>(QDataStream& stream) const; const QString& getName() const { return area.name.isEmpty() ? noName : area.name; } int getColorIdx() const { return colorIdx; } QString getInfo(bool allowEdit = false) const; void getPolylineFromData(SGisLine& l); const QString& getComment() const { return area.cmt; } const QString& getDescription() const { return area.desc; } const QList& getLinks() const { return area.links; } qint32 getWidth() const { return area.width; } qint32 getStyle() const { return area.style; } bool getOpacity() const { return area.opacity; } void setName(const QString& str); void setColor(int idx); void setDataFromPolyline(const SGisLine& l); void setWidth(qint32 w); void setStyle(qint32 s); void setOpacity(bool yes); void setComment(const QString& str); void setDescription(const QString& str); void setLinks(const QList& links); void save(QDomNode& gpx); void edit(); void drawItem(QPainter& p, const QPolygonF& viewport, QList& blockedAreas, CGisDraw * gis); void drawLabel(QPainter& p, const QPolygonF& viewport,QList& blockedAreas, const QFontMetricsF& fm, CGisDraw * gis); void drawHighlight(QPainter& p); IScrOpt * getScreenOptions(const QPoint &origin, IMouse * mouse); QPointF getPointCloseBy(const QPoint& screenPos); bool isCloseTo(const QPointF& pos); void gainUserFocus(bool yes); struct width_t { int width; QString string; }; static const QColor lineColors[OVL_N_COLORS]; static const QString bulletColors[OVL_N_COLORS]; static const width_t lineWidths[OVL_N_WIDTHS]; static const Qt::BrushStyle brushStyles[OVL_N_STYLES]; protected: void setSymbol(); public: struct pt_t : public wpt_t { }; struct area_t { // -- all gpx tags - start QString name; QString cmt; QString desc; QString src; QList links; quint64 number = 0; QString type; QVector pts; QString color; qint32 width = 5; qint32 style = Qt::BDiagPattern; bool opacity = false; // secondary data; qreal area; }; const area_t& getAreaData() const { return area; } private: void readArea(const QDomNode& xml, area_t& area); void setColor(const QColor& c); void setIcon(const QString& c); void readAreaDataFromGisLine(const SGisLine &line); void deriveSecondaryData(); QPointF getPolygonCentroid(const QPolygonF& polygon); area_t area; static key_t keyUserFocus; QPen penForeground {Qt::blue, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; QPen penBackground {Qt::white, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; /// the track line color QColor color; /// the trackpoint bullet icon QPixmap bullet; /// the track line color by index unsigned colorIdx; QPolygonF polygonArea; QPointer scrOpt; }; #endif //CGISITEMOVLAREA_H qmapshack-1.5.1/src/gis/ovl/CScrOptOvlArea.cpp000644 001750 000144 00000005420 12622435720 022127 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisWidget.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/ovl/CScrOptOvlArea.h" #include "helpers/CDraw.h" #include "mouse/IMouse.h" CScrOptOvlArea::CScrOptOvlArea(CGisItemOvlArea *area, const QPoint &point, IMouse *parent) : IScrOpt(parent) { key = area->getKey(); setupUi(this); setOrigin(point); label->setFont(CMainWindow::self().getMapFont()); label->setText(area->getInfo()); adjustSize(); anchor = area->getPointCloseBy(point); if((anchor - point).manhattanLength() > 50) { anchor = point; } move(anchor.toPoint() + QPoint(-width()/2,SCR_OPT_OFFSET)); show(); connect(toolEditDetails, SIGNAL(clicked()), this, SLOT(hide())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(hide())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(hide())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(hide())); connect(toolEditDetails, SIGNAL(clicked()), this, SLOT(slotEditDetails())); connect(toolDelete, SIGNAL(clicked()), this, SLOT(slotDelete())); connect(toolCopy, SIGNAL(clicked()), this, SLOT(slotCopy())); connect(toolEdit, SIGNAL(clicked()), this, SLOT(slotEdit())); } CScrOptOvlArea::~CScrOptOvlArea() { } void CScrOptOvlArea::slotEditDetails() { CGisWidget::self().editItemByKey(key); deleteLater(); } void CScrOptOvlArea::slotCopy() { CGisWidget::self().copyItemByKey(key); deleteLater(); } void CScrOptOvlArea::slotDelete() { CGisWidget::self().delItemByKey(key); deleteLater(); } void CScrOptOvlArea::slotEdit() { CGisWidget::self().editAreaByKey(key); deleteLater(); } void CScrOptOvlArea::draw(QPainter& p) { IGisItem * item = CGisWidget::self().getItemByKey(key); if(item == 0) { QWidget::deleteLater(); return; } item->drawHighlight(p); CDraw::bubble(p, geometry(), anchor.toPoint()); } qmapshack-1.5.1/src/gis/ovl/CDetailsOvlArea.h000644 001750 000144 00000003256 12622435723 021757 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CDETAILSOVLAREA_H #define CDETAILSOVLAREA_H #include "ui_IDetailsOvlArea.h" #include class CGisItemOvlArea; class CDetailsOvlArea : public QDialog, private Ui::IDetailsOvlArea { Q_OBJECT public: CDetailsOvlArea(CGisItemOvlArea &area, QWidget * parent); virtual ~CDetailsOvlArea(); private slots: void slotSetColor(int idx); void slotSetWidth(int idx); void slotSetStyle(int idx); void slotOpyacity(bool yes); void slotChangeReadOnlyMode(bool on); void slotLinkActivated(const QUrl& url); void slotLinkActivated(const QString& link); void setupGui(); private: enum columns_t { eColNum ,eColPosition ,eColMax }; CGisItemOvlArea& area; bool originator = false; }; #endif //CDETAILSOVLAREA_H qmapshack-1.5.1/src/gis/ovl/IScrOptOvlArea.ui000644 001750 000144 00000007301 12541072144 021765 0ustar00oeichlerusers000000 000000 IScrOptOvlArea 0 0 171 69 Form 3 3 3 3 3 3 View details and edit. ... :/icons/32x32/EditDetails.png:/icons/32x32/EditDetails.png Copy area into another project. ... :/icons/32x32/Copy.png:/icons/32x32/Copy.png Delete area from project. ... :/icons/32x32/DeleteOne.png:/icons/32x32/DeleteOne.png Qt::Vertical Edit shape of the area. ... :/icons/32x32/AreaMove.png:/icons/32x32/AreaMove.png Qt::Horizontal QSizePolicy::Expanding 1 20 TextLabel Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse qmapshack-1.5.1/src/gis/CGisWidget.h000644 001750 000144 00000017634 12622435723 020213 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGISWIDGET_H #define CGISWIDGET_H #include "ui_IGisWidget.h" #include #include #include #include "gis/IGisItem.h" class CGisDraw; class IGisProject; enum event_types_e { eEvtD2WReqInfo = QEvent::User + 1 ,eEvtD2WShowFolder = QEvent::User + 2 ,eEvtD2WHideFolder = QEvent::User + 3 ,eEvtD2WShowItems = QEvent::User + 4 ,eEvtD2WHideItems = QEvent::User + 5 ,eEvtD2WUpdateLnF = QEvent::User + 6 ,eEvtW2DAckInfo = QEvent::User + 100 ,eEvtW2DCreate = QEvent::User + 101 }; struct evt_item_t { evt_item_t(quint64 id, quint32 type) : id(id), type(type) { } quint64 id; quint32 type; }; class CEvtD2WReqInfo : public QEvent { public: CEvtD2WReqInfo(quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtD2WReqInfo)), id(id), db(db) { } quint64 id; QString db; }; class CEvtD2WShowFolder : public QEvent { public: CEvtD2WShowFolder(quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtD2WShowFolder)), id(id), db(db) { } quint64 id; QString db; }; class CEvtD2WHideFolder : public QEvent { public: CEvtD2WHideFolder(quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtD2WHideFolder)), id(id), db(db) { } quint64 id; QString db; }; class CEvtD2WShowItems : public QEvent { public: CEvtD2WShowItems(quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtD2WShowItems)), id(id), db(db) { } quint64 id; QString db; QList items; }; class CEvtD2WHideItems : public QEvent { public: CEvtD2WHideItems(quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtD2WHideItems)), id(id), db(db) { } quint64 id; QString db; QSet keys; }; class CEvtW2DAckInfo : public QEvent { public: CEvtW2DAckInfo(bool loaded, quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtW2DAckInfo)), isLoaded(loaded), updateLostFound(false), id(id), db(db) { } bool isLoaded; bool updateLostFound; quint64 id; QString db; QSet keysChildren; }; class CEvtD2WUpdateLnF : public QEvent { public: CEvtD2WUpdateLnF(quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtD2WUpdateLnF)), id(id), db(db) { } quint64 id; QString db; }; class CEvtW2DCreate : public QEvent { public: CEvtW2DCreate(const QString& name, IDBFolder::type_e type, quint64 id, const QString& db) : QEvent(QEvent::Type(eEvtW2DCreate)), name(name), type(type), idParent(id), idChild(0), db(db) { } QString name; IDBFolder::type_e type; quint64 idParent; quint64 idChild; QString db; }; class CGisWidget : public QWidget, private Ui::IGisWidget { Q_OBJECT public: static CGisWidget& self() { return *pSelf; } virtual ~CGisWidget(); void loadGisProject(const QString& filename); /** @brief Draw all loaded data in the workspace that is visible This method is called from The CGisDraw thread. The thread has to make sure that everything is thread safe. @param p the painter to be used @param viewport the viewport in units of rad @param gis the draw context to be used */ void draw(QPainter& p, const QPolygonF &viewport, CGisDraw *gis); /** @brief Receive the current mouse position Iterate over all projects and pass the position @param pos the mouse position on the screen in pixel */ void mouseMove(const QPointF& pos); /** @brief Draw all data that is time variant and can't wait for a full update This method is called directly from the main thread's paintEvent() method. @param p the painter to be used @param viewport the viewport in units of rad @param gis the draw context to be used */ void fastDraw(QPainter& p, const QRectF& viewport, CGisDraw *gis); /** @brief Get items close to the given point Note: Do not store the pointers of items permanently as they can become invalid once you reach the main event loop again. Store the key instead. @param pos the position in pixel @param items an empty item list that will get filled with temporary pointers */ void getItemsByPos(const QPointF& pos, QList &items); /** @brief Find first item with matching key @param key the item's key as it is returned from IGisItem::getKey() @return If no item is found 0 is returned. */ IGisItem * getItemByKey(const IGisItem::key_t &key); /** @brief Delete all items with matching key from workspace @param key the item's key as it is returned from IGisItem::getKey() */ void delItemByKey(const IGisItem::key_t &key); /** @brief Edit / view item details @param key the item's key as it is returned from IGisItem::getKey() */ void editItemByKey(const IGisItem::key_t &key); /** @brief Select a project and add a copy of the item to the project @param key the item's key as it is returned from IGisItem::getKey() */ void copyItemByKey(const IGisItem::key_t &key); /** @brief Clone waypoint and move clone @param key the item's key as it is returned from IGisItem::getKey() */ void projWptByKey(const IGisItem::key_t &key); /** @brief Move waypoint via mouse @param key the item's key as it is returned from IGisItem::getKey() */ void moveWptByKey(const IGisItem::key_t &key); void toggleWptBubble(const IGisItem::key_t &key); /** @brief Set user focus to track @param yes true if focus is set @param key the item's key as it is returned from IGisItem::getKey() */ void focusTrkByKey(bool yes, const IGisItem::key_t &key); void focusRteByKey(bool yes, const IGisItem::key_t &key); void cutTrkByKey(const IGisItem::key_t &key); void editTrkByKey(const IGisItem::key_t &key); void reverseTrkByKey(const IGisItem::key_t &key); void combineTrkByKey(const IGisItem::key_t &key); void combineTrkByKey(const QList& keys); void rangeTrkByKey(const IGisItem::key_t &key); void editRteByKey(const IGisItem::key_t& key); void calcRteByKey(const IGisItem::key_t& key); void resetRteByKey(const IGisItem::key_t& key); void editAreaByKey(const IGisItem::key_t &key); /** @brief Select a project via dialog If a new project name is entered a new project is created. Else the pointer to an existing project is passed back. @return 0 if no project was selected. */ IGisProject * selectProject(); void postEventForWks(QEvent * event); void postEventForDb(QEvent * event); signals: void sigChanged(); public slots: void slotSaveAll(); private slots: void slotHelpText(); private: friend class CMainWindow; CGisWidget(QMenu * menuProject, QWidget * parent); static CGisWidget * pSelf; }; #endif //CGISWIDGET_H qmapshack-1.5.1/src/gis/tnv/CTwoNavProject.h000644 001750 000144 00000004066 12622435723 021674 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CTWONAVPROJECT_H #define CTWONAVPROJECT_H #include "gis/prj/IGisProject.h" class IDevice; class CGisItemTrk; class CGisItemWpt; class QDir; class CTwoNavProject : public IGisProject { public: CTwoNavProject(const QString &filename, const IGisProject * project, IDevice * parent); CTwoNavProject(const QString &filename, IDevice * parent); virtual ~CTwoNavProject(); struct img_t { QString filename; QString info; QImage image; }; struct wpt_t { wpt_t() : valid(false), lon(0), lat(0), ele(0), prox(0) { } bool valid; QDateTime time; QString name; QString comment; QString description; QString symbol; QString key; QString url; qreal lon; qreal lat; qreal ele; qreal prox; QDomDocument gpx; QList images; }; bool save(); bool saveAs(); private: bool load(const QString& filename); bool loadWpts(const QString& filename, const QDir& dir); bool saveWpts(QList &wpts, const QString &filename, const QDir& dir); }; #endif //CTWONAVPROJECT_H qmapshack-1.5.1/src/gis/tnv/CTwoNavProject.cpp000644 001750 000144 00000016073 12622435720 022225 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "gis/CGisListWks.h" #include "gis/gpx/CGpxProject.h" #include "gis/qms/CQmsProject.h" #include "gis/tnv/CTwoNavProject.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CSelectCopyAction.h" #include "helpers/CSettings.h" #include #include CTwoNavProject::CTwoNavProject(const QString &filename, IDevice * parent) : IGisProject(eTypeTwoNav, filename, parent) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/2NavProject.png")); load(filename); setupName(QFileInfo(filename).baseName().replace("_", " ")); setToolTip(CGisListWks::eColumnName, getInfo()); updateItems(); valid = true; } CTwoNavProject::CTwoNavProject(const QString &filename, const IGisProject * project, IDevice * parent) : IGisProject(eTypeTwoNav, filename, parent) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/2NavProject.png")); *(IGisProject*)this = *project; int res = CSelectCopyAction::eResultNone; const int N = project->childCount(); for(int n = 0; n < N; n++) { IGisItem * item = dynamic_cast(project->child(n)); if(item) { insertCopyOfItem(item, NOIDX, res); } } setupName(QFileInfo(filename).baseName().replace("_", " ")); setToolTip(CGisListWks::eColumnName, getInfo()); updateItems(); valid = true; } CTwoNavProject::~CTwoNavProject() { } bool CTwoNavProject::save() { bool res = true; mount(); QDir().mkpath(filename); QDir dir(filename); try { QFile fileKey(dir.absoluteFilePath(QString("%1.key").arg(getKey()))); if(!fileKey.open(QIODevice::WriteOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Error..."), QObject::tr("Failed to open %1.").arg(fileKey.fileName()), QMessageBox::Abort); throw -1; } fileKey.close(); QList wpts; QList geocaches; const int N = childCount(); for(int n = 0; n < N; n++) { QTreeWidgetItem * item = child(n); CGisItemTrk * trk = dynamic_cast(item); if(trk) { QString fn = trk->getName(); fn = fn.remove(QRegExp("[^A-Za-z0-9_]")); fn = dir.absoluteFilePath(fn + ".trk"); if(!trk->saveTwoNav(fn)) { throw -1; } } CGisItemWpt * wpt = dynamic_cast(item); if(wpt) { if(wpt->isGeocache()) { geocaches << wpt; } else { wpts << wpt; } } } if(!wpts.isEmpty()) { if(!saveWpts(wpts, dir.absoluteFilePath("waypoints.wpt"), dir)) { throw -1; } } if(!geocaches.isEmpty()) { if(!saveWpts(geocaches, dir.absoluteFilePath("geocaches.wpt"), dir)) { throw -1; } } } catch(int) { res = false; } if(res) { markAsSaved(); } umount(); return res; } bool CTwoNavProject::saveAs() { SETTINGS; QString path = cfg.value("Paths/lastGisPath", QDir::homePath()).toString(); QString filter = filedialogFilterGPX; QString fn = QFileDialog::getSaveFileName(CMainWindow::getBestWidgetForParent(), QObject::tr("Save GIS data to..."), path, filedialogSaveFilters, &filter); if(fn.isEmpty()) { return false; } bool res = false; if(filter == filedialogFilterGPX) { res = CGpxProject::saveAs(fn, *this); } else if(filter == filedialogFilterQMS) { res = CQmsProject::saveAs(fn, *this); } else { return false; } path = QFileInfo(fn).absolutePath(); cfg.setValue("Paths/lastGisPath", path); return res; } bool CTwoNavProject::saveWpts(QList& wpts, const QString& filename, const QDir& dir) { QFile file(filename); if(!file.open(QIODevice::WriteOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Error..."), QObject::tr("Failed to open %1.").arg(filename), QMessageBox::Abort); return false; } QTextStream out(&file); out.setCodec(QTextCodec::codecForName("UTF-8")); qreal north = -90.0; qreal south = 90.0; qreal west = 180.0; qreal east = -180.0; foreach(CGisItemWpt * wpt, wpts) { QPointF pt = wpt->getPosition(); if(north < pt.y()) { north = pt.y(); } if(south > pt.y()) { south = pt.y(); } if(west > pt.x()) { west = pt.x(); } if(east < pt.x()) { east = pt.x(); } } out << bom; out << "B UTF-8" << endl; out << "G WGS 84" << endl; out << "U 1" << endl; out << "z " << west << ", " << south << ", " << east << ", " << north << endl; foreach(CGisItemWpt * wpt, wpts) { wpt->saveTwoNav(out, dir); } file.close(); return true; } bool CTwoNavProject::load(const QString& filename) { QDir dir(filename); QStringList entries = dir.entryList(QDir::NoDotAndDotDot|QDir::Dirs|QDir::Files); foreach(const QString &entry, entries) { QFileInfo fi(entry); if(fi.suffix().toLower() == "key") { key = fi.baseName(); break; } } foreach(const QString &entry, entries) { QFileInfo fi(entry); if(fi.suffix().toLower() == "trk") { try { new CGisItemTrk(dir.absoluteFilePath(entry), this); } catch(int) { return false; } } else if(fi.suffix().toLower() == "wpt") { if(!loadWpts(dir.absoluteFilePath(entry), dir)) { return false; } } } return true; } qmapshack-1.5.1/src/gis/tnv/serialization.cpp000644 001750 000144 00000052750 12622435720 022234 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "GeoMath.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include struct twonav_icon_t { const char * twonav; const char * qlgt; }; static const twonav_icon_t TwoNavIcons[] = { {"City (Capitol)","City (Capitol)"} ,{"City (Large)","City (Large)"} ,{"City (Medium)","City (Medium)"} ,{"City (Small)","City (Small)"} ,{"City (Small)","Small City"} ,{"Closed Box","Geocache"} ,{"Open Box","Geocache Found"} ,{"Red Flag","Flag, Red"} ,{"Blue Flag","Flag, Blue"} ,{"Green Flag","Flag, Green"} ,{"Red Booble","Pin, Red"} ,{"Blue Booble","Pin, Blue"} ,{"Green Booble","Pin, Green"} ,{"Red Cube","Block, Red"} ,{"Blue Cube","Block, Blue"} ,{"Green Cube","Block, Green"} ,{"Blue Diamond","Blue Diamond"} ,{"Green Diamond","Green Diamond"} ,{"Red Diamond","Red Diamond"} ,{"Traditional Cache","Traditional Cache"} ,{"Multi-cache","Multi-cache"} ,{"Unknown Cache","Unknown Cache"} ,{"Wherigo","Wherigo Cache"} ,{"Event Cache","Event Cache"} ,{"Earthcache","Earthcache"} ,{"Letterbox","Letterbox Hybrid"} ,{"Virtual Cache","Virtual Cache"} ,{"Webcam Cache","Webcam Cache"} ,{0,0} }; static QStringList writeCompeTime( const QDateTime& t, bool isTrack) { QStringList result; QString dateFormat; if(!t.isValid()) { if(isTrack) { result << "01-Jan-1970" << "00:00:00.000"; } else { result << "01-Jan-1970" << "00:00:00"; } return result; } QDateTime timestamp = t.toTimeSpec(Qt::UTC); QString monthStrs[] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; QString monthStr = monthStrs[timestamp.date().month()]; if(isTrack) { dateFormat = QString("dd-'%1'-yy").arg(monthStr); } else { dateFormat = QString("dd-'%1'-yyyy").arg(monthStr); } result << timestamp.toString(dateFormat); if(isTrack) { result << timestamp.toString("hh:mm:ss.000"); } else { result << timestamp.toString("hh:mm:ss"); } return result; } static QDateTime readCompeTime(QString str, bool isTrack) { QDateTime timestamp; QRegExp re("([0-9]{2})-([A-Za-z]{3})-.*"); if(re.exactMatch(str)) { QString monthStr = re.cap(2); QHash monthStr2Num { {"JAN", "01"} ,{"FEB", "02"} ,{"MAR", "03"} ,{"APR", "04"} ,{"MAY", "05"} ,{"JUN", "06"} ,{"JUL", "07"} ,{"AUG", "08"} ,{"SEP", "09"} ,{"OCT", "10"} ,{"NOV", "11"} ,{"DEC", "12"} }; str.replace(monthStr, monthStr2Num.value(monthStr.toUpper())); if(isTrack) { timestamp = QDateTime::fromString(str, "dd-MM-yy hh:mm:ss.zzz"); if(timestamp.isValid()) { timestamp = timestamp.addYears(100); } } else { timestamp = QDateTime::fromString(str, "dd-MM-yyyy hh:mm:ss"); } } timestamp.setTimeSpec(Qt::UTC); return timestamp; } static QString iconTwoNav2QlGt(const QString& sym) { int i = 0; while(TwoNavIcons[i].qlgt) { if(sym == TwoNavIcons[i].twonav) { return TwoNavIcons[i].qlgt; } i++; } return sym; } static QString iconQlGt2TwoNav(const QString& sym) { int i = 0; while(TwoNavIcons[i].qlgt) { if(sym == TwoNavIcons[i].qlgt) { return TwoNavIcons[i].twonav; } i++; } return sym; } static QString makeUniqueName(const QString& name, const QDir& dir) { int cnt = 0; QFileInfo fi(name); QString tmp(name); while(dir.exists(tmp)) { tmp = QString("%1_%2.%3").arg(fi.baseName()).arg(cnt++).arg(fi.completeSuffix()); } return tmp; } bool CGisItemTrk::saveTwoNav(const QString &filename) { QFile file(filename); if(!file.open(QIODevice::WriteOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Error..."), QObject::tr("Failed to open %1.").arg(filename), QMessageBox::Abort); return false; } QDir dir(QFileInfo(filename).absoluteDir()); IGisProject * project = dynamic_cast(parent()); QTextStream out(&file); out.setCodec(QTextCodec::codecForName("UTF-8")); out << bom; out << "B UTF-8" << endl; out << "G WGS 84" << endl; out << "U 1" << endl; QString name = getName(); name = name.replace(" ","_"); QColor color = getColor(); QStringList list; list << "C"; list << QString::number(color.red()); list << QString::number(color.green()); list << QString::number(color.blue()); list << "5"; // ??? list << "1"; // ??? out << list.join(" ") << endl; out << "s " << name << endl; out << "y " << getKey().item << endl; foreach(const CGisItemTrk::trkseg_t& seg, trk.segs) { foreach(const CGisItemTrk::trkpt_t& trkpt, seg.pts) { list.clear(); list << "T"; list << "A"; list << (trkpt.lat > 0 ? QString("%1%2N") : QString("%1%2S")).arg(trkpt.lat,0,'f').arg(QChar(186)); list << (trkpt.lon > 0 ? QString("%1%2E") : QString("%1%2W")).arg(trkpt.lon,0,'f').arg(QChar(186)); list << writeCompeTime(trkpt.time, true); list << "s"; list << QString("%1").arg(trkpt.ele == NOINT ? 0 : trkpt.ele); list << "0.000000"; list << "0.000000"; list << "0.000000"; list << "0"; list << "-1000.000000"; list << "-1.000000"; list << "-1"; list << "-1.000000"; list << "-1"; list << "-1"; list << "-1"; list << "-1.000000"; out << list.join(" ") << endl; if(!trkpt.keyWpt.item.isEmpty() && project) { CGisItemWpt * wpt = dynamic_cast(project->getItemByKey(trkpt.keyWpt)); if(wpt) { QString iconName = wpt->getIconName(); QPixmap icon = wpt->getIcon(); icon = icon.scaledToWidth(15, Qt::SmoothTransformation); iconName = iconQlGt2TwoNav(iconName); iconName = iconName.replace(" ", "_"); icon.save(dir.absoluteFilePath(iconName + ".png")); list.clear(); list << (iconName + ".png"); list << "1"; list << "3"; list << "0"; list << wpt->getName(); out << "a " << list.join(",") << endl; foreach(const CGisItemWpt::image_t& img, wpt->getImages()) { QString fn = img.info; if(fn.isEmpty()) { fn = QString("picture.png"); } QFileInfo fi(fn); if(!(fi.completeSuffix() == "png")) { fn = fi.baseName() + ".png"; } fn = makeUniqueName(fn, dir); img.pixmap.save(dir.absoluteFilePath(fn)); list.clear(); list << fn; list << "1"; list << "8"; list << "0"; out << "a " << list.join(",") << endl; } QString comment = wpt->getComment(); if(!IGisItem::removeHtml(comment).isEmpty()) { QString filenameCmt = QString("QMS_CMT%1.html").arg(wpt->getKey().item); QFile fileCmt(dir.absoluteFilePath(filenameCmt)); fileCmt.open(QIODevice::WriteOnly); QTextStream stream(&fileCmt); stream << bom << comment; fileCmt.close(); out << "a .\\" << filenameCmt << ",0" << endl; } } } } } return true; } bool CGisItemTrk::readTwoNav(const QString& filename) { QString line("start"); QFile file(filename); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Failed to open %1.").arg(filename),QMessageBox::Abort,QMessageBox::Abort); return false; } QTextStream in(&file); in.setCodec(QTextCodec::codecForName("UTF-8")); trkseg_t seg; while(!line.isEmpty()) { line = in.readLine(); switch(line[0].toLatin1()) { case 'B': { QString name = line.mid(1).simplified(); QTextCodec * codec = QTextCodec::codecForName(name.toLatin1()); if(codec) { in.setCodec(codec); } break; } case 'G': { QString name = line.mid(1).simplified(); if(name != "WGS 84") { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Only support lon/lat WGS 84 format."),QMessageBox::Abort,QMessageBox::Abort); return false; } break; } case 'U': { QString name = line.mid(1).simplified(); if(name != "1") { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Only support lon/lat WGS 84 format."),QMessageBox::Abort,QMessageBox::Abort); return false; } break; } case 'C': { QStringList values = line.split(' ', QString::SkipEmptyParts); if(values.size() > 2) { QColor c(values[1].toInt(),values[2].toInt(),values[3].toInt()); setColor(c); } else { values = values[1].split(',',QString::SkipEmptyParts); if(values.size() >= 3) { QColor c(values[0].toInt(),values[1].toInt(),values[2].toInt()); setColor(c); } } break; } case 'T': { trkpt_t pt; QStringList values = line.split(' ', QString::SkipEmptyParts); if(values.size() < 8) { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Failed to read data."),QMessageBox::Abort,QMessageBox::Abort); return false; } QString lat = values[2].replace(QChar(186),"").replace(QChar(-3),""); QString lon = values[3].replace(QChar(186),"").replace(QChar(-3),""); IUnit::strToDeg(lat + " " + lon, pt.lon, pt.lat); pt.time = readCompeTime(values[4] + " " + values[5], true); pt.ele = values[7].toFloat(); seg.pts << pt; break; } case 's': { trk.name = line.mid(1).simplified(); trk.name = trk.name.replace("_", " "); break; } case 'y': { key.item = line.mid(1).simplified(); break; } } } file.close(); trk.segs << seg; if(trk.name.isEmpty()) { QFileInfo fi(filename); trk.name = fi.baseName(); } deriveSecondaryData(); return true; } void CGisItemWpt::saveTwoNav(QTextStream& out, const QDir& dir) { QString name = getName(); name = name.replace(" ","_"); QString description = getDescription(); description = removeHtml(description); QStringList list; list << "W"; list << name; list << "A"; list << (wpt.lat > 0 ? QString("%1%2N") : QString("%1%2S")).arg(wpt.lat,0,'f').arg(QChar(186)); list << (wpt.lon > 0 ? QString("%1%2E") : QString("%1%2W")).arg(wpt.lon,0,'f').arg(QChar(186)); list << writeCompeTime(wpt.time, false); list << QString("%1").arg(wpt.ele == NOINT ? 0 : wpt.ele); out << list.join(" ") << " "; out << description << endl; list.clear(); list << iconQlGt2TwoNav(getIconName()); list << "0"; //test position list << "-1.0"; list << "0"; list << QString("%1").arg(QColor(Qt::darkBlue).value()); list << "1"; list << "37"; // 1 Name 2 Beschreibung 4 Symbol 8 Hhe 16 URL 32 Radius list << ""; //wpt->link; list << QString("%1").arg(proximity == NOFLOAT ? 0 : proximity,0,'f'); list << getKey().item; out << "w "; out << list.join(","); out << endl; QString comment = getComment(); if(!IGisItem::removeHtml(comment).isEmpty()) { QString filenameCmt = QString("QMS_CMT%1.html").arg(getKey().item); QFile fileCmt(dir.absoluteFilePath(filenameCmt)); fileCmt.open(QIODevice::WriteOnly); QTextStream stream(&fileCmt); stream << bom << comment; fileCmt.close(); out << "a .\\" << filenameCmt << ",0" << endl; } foreach(const image_t &img, images) { QString fn = img.info; if(fn.isEmpty()) { fn = QString("picture.png"); } QFileInfo fi(fn); if(!(fi.completeSuffix().toLower() == "png")) { fn = fi.baseName() + ".png"; } fn = makeUniqueName(fn, dir); img.pixmap.save(dir.absoluteFilePath(fn)); out << "a .\\" << fn << endl; } if(isGeocache()) { // write geocache data QDomDocument doc; QDomElement gpxCache = doc.createElement("groundspeak:cache"); writeGcExt(gpxCache); doc.appendChild(gpxCache); out << "e" << endl; out << doc.toString(); out << "ee" << endl; } } bool CTwoNavProject::loadWpts(const QString& filename, const QDir& dir) { wpt_t wpt; QString line("start"); QFile file(filename); if(!file.open(QIODevice::ReadOnly)) { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Failed to open %1.").arg(filename),QMessageBox::Abort,QMessageBox::Abort); return false; } QTextStream in(&file); in.setCodec(QTextCodec::codecForName("UTF-8")); while(!line.isEmpty()) { line = in.readLine(); switch(line[0].toLatin1()) { case 'B': { QString name = line.mid(1).simplified(); QTextCodec * codec = QTextCodec::codecForName(name.toLatin1()); if(codec) { in.setCodec(codec); } break; } case 'G': { QString name = line.mid(1).simplified(); if(name != "WGS 84") { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Only support lon/lat WGS 84 format."),QMessageBox::Abort,QMessageBox::Abort); return false; } break; } case 'U': { QString name = line.mid(1).simplified(); if(name != "1") { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Only support lon/lat WGS 84 format."),QMessageBox::Abort,QMessageBox::Abort); return false; } break; } case 'W': { if(wpt.valid) { new CGisItemWpt(wpt, this); } wpt = wpt_t(); QStringList values = line.split(' ', QString::SkipEmptyParts); if(values.size() < 8) { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Failed to read data."),QMessageBox::Abort,QMessageBox::Abort); return false; } wpt.name = values[1]; QString lat = values[3].replace(QChar(186),"").replace(QChar(-3),""); QString lon = values[4].replace(QChar(186),"").replace(QChar(-3),""); IUnit::strToDeg(lat + " " + lon, wpt.lon, wpt.lat); wpt.time = readCompeTime(values[5] + " " + values[6], false); wpt.ele = values[7].toFloat(); if(values.size() > 7) { QStringList list = values.mid(8); wpt.description = list.join(" "); } break; } case 'w': { QStringList values = line.mid(1).simplified().split(',', QString::KeepEmptyParts); if(values.size() < 10) { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Failed to read data."),QMessageBox::Abort,QMessageBox::Abort); return false; } wpt.symbol = iconTwoNav2QlGt(values[0]); wpt.url = values[7]; wpt.prox = values[8].toFloat(); wpt.key = values[9]; if(wpt.prox == 0) { wpt.prox = NOFLOAT; } if(wpt.ele == 0) { wpt.ele = NOINT; } if(wpt.key == "0") { wpt.key.clear(); } wpt.name = wpt.name.replace("_", " "); if(!wpt.key.isEmpty()) { QString filenameCmt = QString("QMS_CMT%1.html").arg(wpt.key); if(QFile::exists(dir.absoluteFilePath(filenameCmt))) { QFile fileCmt(dir.absoluteFilePath(filenameCmt)); if(fileCmt.open(QIODevice::ReadOnly)) { wpt.comment = QTextStream(&fileCmt).readAll(); fileCmt.close(); } } } wpt.valid = true; break; } case 'e': { QString str; while(!in.atEnd()) { line = in.readLine(); if(line == "ee") { break; } str += line; } QString errorMsg; int errorLine = 0; int errorColumn = 0; wpt.gpx.setContent(str, &errorMsg, &errorLine, &errorColumn); break; } case 'a': { img_t img; QStringList values = line.mid(1).simplified().split(',', QString::KeepEmptyParts); if(values.size() < 1) { QMessageBox::information(CMainWindow::getBestWidgetForParent(),QObject::tr("Error..."), QObject::tr("Failed to read data."),QMessageBox::Abort,QMessageBox::Abort); return false; } QString fn = values[0].simplified(); #ifndef WIN32 fn = fn.replace("\\","/"); #endif QFileInfo fi(dir.absoluteFilePath(fn)); img.image.load(dir.absoluteFilePath(fn)); if(!img.image.isNull()) { img.filename = fi.fileName(); img.info = fi.baseName(); wpt.images << img; } break; } } } if(wpt.valid) { new CGisItemWpt(wpt, this); } return true; } void CGisItemWpt::readTwoNav(const CTwoNavProject::wpt_t &tnvWpt) { wpt.lon = tnvWpt.lon; wpt.lat = tnvWpt.lat; wpt.ele = tnvWpt.ele; proximity = tnvWpt.prox; wpt.time = tnvWpt.time; wpt.name = tnvWpt.name; wpt.cmt = tnvWpt.comment; wpt.desc = tnvWpt.description; wpt.sym = tnvWpt.symbol; key.item = tnvWpt.key; foreach(const CTwoNavProject::img_t& img, tnvWpt.images) { CGisItemWpt::image_t image; image.fileName = img.filename; image.info = img.info; image.pixmap = img.image; images << image; } const QDomNode& xmlCache = tnvWpt.gpx.namedItem("groundspeak:cache"); if(!xmlCache.isNull()) { readGcExt(xmlCache); } setIcon(); } qmapshack-1.5.1/src/gis/gpx/CGpxProject.cpp000644 001750 000144 00000030556 12622435720 021536 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "device/CDeviceGarmin.h" #include "device/IDevice.h" #include "gis/CGisDraw.h" #include "gis/CGisListWks.h" #include "gis/gpx/CGpxProject.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/qms/CQmsProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CSelectCopyAction.h" #include "helpers/CSettings.h" #include CGpxProject::CGpxProject(const QString &filename, CGisListWks *parent) : IGisProject(eTypeGpx, filename, parent) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/GpxProject.png")); blockUpdateItems(true); loadGpx(filename); blockUpdateItems(false); } CGpxProject::CGpxProject(const QString &filename, IDevice * parent) : IGisProject(eTypeGpx, filename, parent) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/GpxProject.png")); blockUpdateItems(true); loadGpx(filename); blockUpdateItems(false); } CGpxProject::CGpxProject(const QString &filename, const IGisProject * project, IDevice * parent) : IGisProject(eTypeGpx, filename, parent) { setIcon(CGisListWks::eColumnIcon,QIcon("://icons/32x32/GpxProject.png")); *(IGisProject*)this = *project; blockUpdateItems(project->blockUpdateItems()); int res = CSelectCopyAction::eResultNone; const int N = project->childCount(); for(int n = 0; n < N; n++) { IGisItem * item = dynamic_cast(project->child(n)); if(item) { insertCopyOfItem(item, NOIDX, res); } } setupName(QFileInfo(filename).baseName().replace("_", " ")); blockUpdateItems(false); setToolTip(CGisListWks::eColumnName, getInfo()); valid = true; } CGpxProject::~CGpxProject() { } void CGpxProject::loadGpx(const QString& filename) { // cerate file instance QFile file(filename); // if the file does not exist, the filename is assumed to be a name for a new project if(!file.exists()) { IGisProject::filename.clear(); setupName(filename); setToolTip(CGisListWks::eColumnName, getInfo()); valid = true; return; } if(!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Failed to open..."), QObject::tr("Failed to open %1").arg(filename), QMessageBox::Abort); return; } // load file content to xml document QDomDocument xml; QString msg; int line; int column; if(!xml.setContent(&file, false, &msg, &line, &column)) { file.close(); QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Failed to read..."), QObject::tr("Failed to read: %1\nline %2, column %3:\n %4").arg(filename).arg(line).arg(column).arg(msg), QMessageBox::Abort); return; } file.close(); int N; QDomElement xmlGpx = xml.documentElement(); if(xmlGpx.tagName() != "gpx") { QMessageBox::critical(CMainWindow::getBestWidgetForParent(), QObject::tr("Failed to read..."), QObject::tr("Not a GPX file: ") + filename, QMessageBox::Abort); return; } const QDomElement& xmlExtension = xmlGpx.namedItem("extensions").toElement(); if(xmlExtension.namedItem("ql:key").isElement()) { key = xmlExtension.namedItem("ql:key").toElement().text(); } if(xmlExtension.namedItem("ql:sorting").isElement()) { sorting = sorting_e(xmlExtension.namedItem("ql:sorting").toElement().text().toInt()); } if(xmlExtension.namedItem("ql:correlation").isElement()) { noCorrelation = bool(xmlExtension.namedItem("ql:correlation").toElement().text().toInt() == 0); } const QDomNode& xmlMetadata = xmlGpx.namedItem("metadata"); if(xmlMetadata.isElement()) { readMetadata(xmlMetadata, metadata); } /** @note If you change the order of the item types read you have to take care of the order enforced in IGisItem(). */ const QDomNodeList& xmlTrks = xmlGpx.elementsByTagName("trk"); N = xmlTrks.count(); for(int n = 0; n < N; ++n) { const QDomNode& xmlTrk = xmlTrks.item(n); new CGisItemTrk(xmlTrk, this); } const QDomNodeList& xmlRtes = xmlGpx.elementsByTagName("rte"); N = xmlRtes.count(); for(int n = 0; n < N; ++n) { const QDomNode& xmlRte = xmlRtes.item(n); new CGisItemRte(xmlRte, this); } const QDomNodeList& xmlWpts = xmlGpx.elementsByTagName("wpt"); N = xmlWpts.count(); for(int n = 0; n < N; ++n) { const QDomNode& xmlWpt = xmlWpts.item(n); CGisItemWpt * wpt = new CGisItemWpt(xmlWpt, this); /* Special care for waypoints stored on Garmin devices. Images attached to the waypoint are stored in the file system of the device and written as links to the waypoint. Let the device object take care of this. */ IDevice * device = dynamic_cast(parent()); if(device) { device->loadImages(*wpt); } } const QDomNodeList& xmlAreas = xmlExtension.elementsByTagName("ql:area"); N = xmlAreas.count(); for(int n = 0; n < N; ++n) { const QDomNode& xmlArea = xmlAreas.item(n); new CGisItemOvlArea(xmlArea, this); } setupName(QFileInfo(filename).baseName().replace("_", " ")); setToolTip(CGisListWks::eColumnName, getInfo()); valid = true; } bool CGpxProject::save() { if(filename.isEmpty()) { return saveAs(); } else { if(saveAs(filename, *this)) { markAsSaved(); } } return true; } bool CGpxProject::saveAs() { SETTINGS; QString path = cfg.value("Paths/lastGisPath", QDir::homePath()).toString(); path += "/" + getName() + ".gpx"; QString filter = filedialogFilterGPX; QString fn = QFileDialog::getSaveFileName(CMainWindow::getBestWidgetForParent(), QObject::tr("Save GIS data to..."), path, filedialogSaveFilters, &filter); if(fn.isEmpty()) { return false; } bool res = false; if(filter == filedialogFilterGPX) { filename = fn; metadata.name.clear(); setupName(QFileInfo(filename).baseName()); res = saveAs(fn, *this); if(res) { markAsSaved(); } } else if(filter == filedialogFilterQMS) { res = CQmsProject::saveAs(fn, *this); } else { return false; } path = QFileInfo(fn).absolutePath(); cfg.setValue("Paths/lastGisPath", path); return res; } bool CGpxProject::saveAs(const QString& fn, IGisProject& project) { QString _fn_ = fn; QFileInfo fi(_fn_); if(fi.suffix().toLower() != "gpx") { _fn_ += ".gpx"; } project.mount(); // safety check for existing files QFile file(_fn_); if(file.exists()) { file.open(QIODevice::ReadOnly); bool createdByQMS = false; // load file content to xml document QDomDocument xml; if(xml.setContent(&file, false)) { const QDomElement& docElem = xml.documentElement(); const QDomNamedNodeMap& attr = docElem.attributes(); createdByQMS = attr.namedItem("creator").nodeValue().startsWith("QMapShack"); } if(!createdByQMS) { int res = QMessageBox::warning(CMainWindow::getBestWidgetForParent(),QObject::tr("File exists ...") ,QObject::tr("The file exists and it has not been created by QMapShack. " "If you press 'yes' all data in this file will be lost. " "Even if this file contains GPX data and has been loaded by QMapShack, " "QMapShack might not be able to load and store all elements of this file. " "Those elements will be lost. I recommend to use another file. " "Do you really want to overwrite the file?") ,QMessageBox::Yes|QMessageBox::No,QMessageBox::No); if(res == QMessageBox::No) { project.umount(); return false; } } file.close(); } // ---- start content of gpx QDomDocument doc; QDomNode gpx = project.writeMetadata(doc); IDevice * device = dynamic_cast(project.parent()); if(device) { device->startSavingProject(&project); } for(int i = 0; i < project.childCount(); i++) { CGisItemWpt * item = dynamic_cast(project.child(i)); if(item == 0) { continue; } /* Special care for waypoints stored on Garmin devices. Images attached to the waypoint are stored in the file system of the device and written as links to the waypoint. Let the device object take care of this. */ if(device) { device->saveImages(*item); } item->save(gpx); } for(int i = 0; i < project.childCount(); i++) { CGisItemRte * item = dynamic_cast(project.child(i)); if(item == 0) { continue; } item->save(gpx); } for(int i = 0; i < project.childCount(); i++) { CGisItemTrk * item = dynamic_cast(project.child(i)); if(item == 0) { continue; } item->save(gpx); } QDomElement xmlExt = doc.createElement("extensions"); gpx.appendChild(xmlExt); for(int i = 0; i < project.childCount(); i++) { CGisItemOvlArea * item = dynamic_cast(project.child(i)); if(item == 0) { continue; } item->save(xmlExt); } if(!project.getKey().isEmpty()) { QDomElement elem = xmlExt.ownerDocument().createElement("ql:key"); xmlExt.appendChild(elem); QDomText text = xmlExt.ownerDocument().createTextNode(project.getKey()); elem.appendChild(text); } { QDomElement elem = xmlExt.ownerDocument().createElement("ql:sorting"); xmlExt.appendChild(elem); QDomText text = xmlExt.ownerDocument().createTextNode(QString::number(project.getSorting())); elem.appendChild(text); } { QDomElement elem = xmlExt.ownerDocument().createElement("ql:correlation"); xmlExt.appendChild(elem); QDomText text = xmlExt.ownerDocument().createTextNode(QString::number(project.doCorrelation())); elem.appendChild(text); } // ---- stop content of gpx bool res = true; try { if(!file.open(QIODevice::WriteOnly)) { throw QObject::tr("Failed to create file '%1'").arg(_fn_); } QTextStream out(&file); out.setCodec("UTF-8"); out << "" << endl; out << doc.toString(); file.close(); if(file.error() != QFile::NoError) { throw QObject::tr("Failed to write file '%1'").arg(_fn_); } } catch(const QString& msg) { QMessageBox::warning(CMainWindow::getBestWidgetForParent(), QObject::tr("Saving GIS data failed..."), msg, QMessageBox::Abort); res = false; } project.umount(); return res; } qmapshack-1.5.1/src/gis/gpx/CGpxProject.h000644 001750 000144 00000003025 12622435723 021175 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #ifndef CGPXPROJECT_H #define CGPXPROJECT_H #include "gis/prj/IGisProject.h" #include #include #include class CGisListWks; class CGisDraw; class CGpxProject : public IGisProject { public: CGpxProject(const QString &filename, CGisListWks * parent); CGpxProject(const QString &filename, IDevice * parent); CGpxProject(const QString &filename, const IGisProject * project, IDevice * parent); virtual ~CGpxProject(); bool save(); bool saveAs(); static bool saveAs(const QString& fn, IGisProject& project); private: void loadGpx(const QString& filename); }; #endif //CGPXPROJECT_H qmapshack-1.5.1/src/gis/gpx/serialization.cpp000644 001750 000144 00000101531 12623413746 022220 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "gis/WptIcons.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/prj/IGisProject.h" #include "gis/rte/CGisItemRte.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "version.h" #include #include const QString IGisProject::gpx_ns = "http://www.topografix.com/GPX/1/1"; const QString IGisProject::xsi_ns = "http://www.w3.org/2001/XMLSchema-instance"; const QString IGisProject::gpxx_ns = "http://www.garmin.com/xmlschemas/GpxExtensions/v3"; const QString IGisProject::gpxtpx_ns = "http://www.garmin.com/xmlschemas/TrackPointExtension/v1"; const QString IGisProject::wptx1_ns = "http://www.garmin.com/xmlschemas/WaypointExtension/v1"; const QString IGisProject::rmc_ns = "urn:net:trekbuddy:1.0:nmea:rmc"; const QString IGisProject::ql_ns = "http://www.qlandkarte.org/xmlschemas/v1.1"; const QString IGisProject::gs_ns = "http://www.groundspeak.com/cache/1/0"; static void readXml(const QDomNode& xml, const QString& tag, qint32& value) { if(xml.namedItem(tag).isElement()) { bool ok = false; qint32 tmp = xml.namedItem(tag).toElement().text().toInt(&ok); if(!ok) { tmp = qRound(xml.namedItem(tag).toElement().text().toDouble(&ok)); } if(ok) { value = tmp; } } } template static void readXml(const QDomNode& xml, const QString& tag, T& value) { if(xml.namedItem(tag).isElement()) { bool ok = false; T tmp; if(std::is_same::value) { tmp = xml.namedItem(tag).toElement().text().toUInt(&ok); } else if(std::is_same::value) { tmp = xml.namedItem(tag).toElement().text().toULongLong(&ok); } else if(std::is_same::value) { tmp = xml.namedItem(tag).toElement().text().toDouble(&ok); } else if(std::is_same::value) { tmp = xml.namedItem(tag).toElement().text().toInt(&ok); } if(ok) { value = tmp; } } } static void readXml(const QDomNode& xml, const QString& tag, QString& value) { if(xml.namedItem(tag).isElement()) { value = xml.namedItem(tag).toElement().text(); } } static void readXml(const QDomNode& xml, const QString& tag, QString& value, bool& isHtml) { if(xml.namedItem(tag).isElement()) { const QDomNamedNodeMap& attr = xml.namedItem(tag).toElement().attributes(); isHtml = (attr.namedItem("html").nodeValue().toLocal8Bit().toLower() == "true"); value = xml.namedItem(tag).toElement().text(); } } static void readXml(const QDomNode& xml, const QString& tag, QDateTime& value) { if(xml.namedItem(tag).isElement()) { QString time = xml.namedItem(tag).toElement().text(); IUnit::parseTimestamp(time, value); } } static void readXml(const QDomNode& xml, const QString& tag, QList& l) { if(xml.namedItem(tag).isElement()) { const QDomNodeList& links = xml.toElement().elementsByTagName(tag); int N = links.count(); for(int n = 0; n < N; ++n) { const QDomNode& link = links.item(n); IGisItem::link_t tmp; tmp.uri.setUrl(link.attributes().namedItem("href").nodeValue()); readXml(link, "text", tmp.text); readXml(link, "type", tmp.type); l << tmp; } } } static void readXml(const QDomNode& xml, IGisItem::history_t& history) { if(xml.namedItem("ql:history").isElement()) { const QDomElement& xmlHistory = xml.namedItem("ql:history").toElement(); const QDomNodeList& xmlEntries = xmlHistory.elementsByTagName("ql:event"); for(int n = 0; n < xmlEntries.count(); ++n) { const QDomNode& xmlEntry = xmlEntries.item(n); IGisItem::history_event_t entry; readXml(xmlEntry, "ql:icon", entry.icon); readXml(xmlEntry, "ql:time", entry.time); readXml(xmlEntry, "ql:comment", entry.comment); history.events << entry; } history.histIdxInitial = history.events.size() - 1; history.histIdxCurrent = history.histIdxInitial; } } static void readXml(const QDomNode& xml, const QString& tag, QPoint& offsetBubble, quint32& widthBubble) { if(xml.namedItem(tag).isElement()) { const QDomElement& xmlBubble = xml.namedItem(tag).toElement(); int x = xmlBubble.attributes().namedItem("xoff").nodeValue().toInt(); int y = xmlBubble.attributes().namedItem("yoff").nodeValue().toInt(); offsetBubble = QPoint(x,y); widthBubble = xmlBubble.attributes().namedItem("width").nodeValue().toInt(); } } static void writeXml(QDomNode& xml, const QString& tag, qint32 val) { if(val != NOINT) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createTextNode(QString::number(val)); elem.appendChild(text); } } static void writeXml(QDomNode& xml, const QString& tag, quint32 val) { if(val != NOINT) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createTextNode(QString::number(val)); elem.appendChild(text); } } static void writeXml(QDomNode& xml, const QString& tag, quint64 val) { if(val != 0) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createTextNode(QString::number(val)); elem.appendChild(text); } } static void writeXml(QDomNode& xml, const QString& tag, const QString& val) { if(!val.isEmpty()) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createTextNode(val); elem.appendChild(text); } } static void writeXml(QDomNode& xml, const QString& tag, qreal val) { if(val != NOFLOAT) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createTextNode(QString("%1").arg(val,0,'f',8)); elem.appendChild(text); } } static void writeXml(QDomNode& xml, const QString& tag, const QString& val, bool isHtml) { if(!val.isEmpty()) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createCDATASection(val); elem.appendChild(text); elem.setAttribute("html", isHtml ? "True" : "False"); } } static void writeXml(QDomNode& xml, const QString& tag, const QDateTime& time) { if(time.isValid()) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); QDomText text = xml.ownerDocument().createTextNode(time.toString("yyyy-MM-dd'T'hh:mm:ss'Z'")); elem.appendChild(text); } } static void writeXml(QDomNode& xml, const QString& tag, const QList& links) { if(!links.isEmpty()) { foreach(const IGisItem::link_t& link, links) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); elem.setAttribute("href", link.uri.toString()); writeXml(elem, "text", link.text); writeXml(elem, "type", link.type); } } } static void writeXml(QDomNode& xml, const IGisItem::history_t& history) { if(history.events.size() > 1) { QDomElement xmlHistory = xml.ownerDocument().createElement("ql:history"); xml.appendChild(xmlHistory); for(int i = 0; i <= history.histIdxCurrent; i++) { const IGisItem::history_event_t& event = history.events[i]; QDomElement xmlEvent = xml.ownerDocument().createElement("ql:event"); xmlHistory.appendChild(xmlEvent); writeXml(xmlEvent,"ql:icon", event.icon); writeXml(xmlEvent,"ql:time", event.time); writeXml(xmlEvent,"ql:comment", event.comment); } } } static void writeXml(QDomNode& xml, const QString& tag, const QPoint& offsetBubble, quint32 widthBubble) { QDomElement elem = xml.ownerDocument().createElement(tag); xml.appendChild(elem); elem.setAttribute("xoff", offsetBubble.x()); elem.setAttribute("yoff", offsetBubble.y()); elem.setAttribute("width", widthBubble); } static void readXml(const QDomNode& node, const QString& parentTags, QHash& extensions) { QString tag = node.nodeName(); if(tag.left(3) == "ql:") { return; } QString tags = parentTags.isEmpty() ? tag : parentTags + "|" + tag; const QDomNode& next = node.firstChild(); if(next.isText()) { extensions[tags] = node.toElement().text(); } else { const QDomNodeList& list = node.childNodes(); for(int i = 0; i < list.size(); i++) { readXml(list.at(i), tags, extensions); } } } static void readXml(const QDomNode& ext, QHash& extensions) { const QDomNodeList& list = ext.childNodes(); for(int i = 0; i < list.size(); i++) { readXml(list.at(i), "", extensions); } extensions.squeeze(); } static void writeXml(QDomNode& ext, const QHash& extensions) { if(extensions.isEmpty()) { return; } QDomDocument doc = ext.ownerDocument(); QStringList keys = extensions.keys(); keys.sort(); foreach(const QString &key, keys) { QStringList tags = key.split('|', QString::SkipEmptyParts); if(tags.size() == 1) { QDomElement elem = doc.createElement(tags.first()); ext.appendChild(elem); QDomText text = doc.createTextNode(extensions[key].toString()); elem.appendChild(text); } else { QDomNode node = ext; QString lastTag = tags.last(); tags.pop_back(); foreach(const QString &tag, tags) { QDomNode child = node.firstChildElement(tag); if(child.isNull()) { QDomElement elem = doc.createElement(tags.first()); node.appendChild(elem); node = elem; } else { node = child; } } QDomElement elem = doc.createElement(lastTag); node.appendChild(elem); QDomText text = doc.createTextNode(extensions[key].toString()); elem.appendChild(text); } } } void IGisProject::readMetadata(const QDomNode& xml, metadata_t& metadata) { readXml(xml,"name", metadata.name); readXml(xml,"desc", metadata.desc); const QDomNode& xmlAuthor = xml.namedItem("author"); if(xmlAuthor.isElement()) { readXml(xml,"name", metadata.author.name); const QDomNode& xmlEmail = xmlAuthor.namedItem("email"); if(xmlEmail.isElement()) { const QDomNamedNodeMap& attr = xmlEmail.attributes(); metadata.author.id = attr.namedItem("id").nodeValue(); metadata.author.domain = attr.namedItem("domain").nodeValue(); } const QDomNode& xmlLink = xmlAuthor.namedItem("link"); if(xmlLink.isElement()) { metadata.author.link.uri.setUrl(xmlLink.attributes().namedItem("href").nodeValue()); readXml(xmlLink, "text", metadata.author.link.text); readXml(xmlLink, "type", metadata.author.link.type); } } const QDomNode& xmlCopyright = xml.namedItem("copyright"); if(xmlCopyright.isElement()) { metadata.copyright.author = xmlCopyright.attributes().namedItem("author").nodeValue(); readXml(xmlCopyright, "year", metadata.copyright.year); readXml(xmlCopyright, "license", metadata.copyright.license); } readXml(xml,"link", metadata.links); readXml(xml,"time", metadata.time); readXml(xml,"keywords", metadata.keywords); const QDomNode& xmlBounds = xml.namedItem("bounds"); if(xmlBounds.isElement()) { const QDomNamedNodeMap& attr = xmlBounds.attributes(); metadata.bounds.setLeft( attr.namedItem("minlon").nodeValue().toDouble()); metadata.bounds.setTop( attr.namedItem("maxlat").nodeValue().toDouble()); metadata.bounds.setRight( attr.namedItem("maxlon").nodeValue().toDouble()); metadata.bounds.setBottom(attr.namedItem("minlat").nodeValue().toDouble()); } } QDomNode IGisProject::writeMetadata(QDomDocument& doc) { QDomElement gpx = doc.createElement("gpx"); doc.appendChild(gpx); gpx.setAttribute("version","1.1"); gpx.setAttribute("creator","QMapShack " VER_STR " http://www.qlandkarte.org/"); gpx.setAttribute("xmlns", gpx_ns); gpx.setAttribute("xmlns:xsi", xsi_ns); gpx.setAttribute("xmlns:gpxx", gpxx_ns); gpx.setAttribute("xmlns:gpxtpx", gpxtpx_ns); gpx.setAttribute("xmlns:wptx1", wptx1_ns); gpx.setAttribute("xmlns:rmc", rmc_ns); gpx.setAttribute("xmlns:ql", ql_ns); QString schemaLocation = QString() + gpx_ns + " http://www.topografix.com/GPX/1/1/gpx.xsd " + gpxx_ns + " http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd " + gpxtpx_ns + " http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd " + wptx1_ns + " http://www.garmin.com/xmlschemas/WaypointExtensionv1.xsd" + ql_ns + " http://www.qlandkarte.org/xmlschemas/v1.1/ql-extensions.xsd"; gpx.setAttribute("xsi:schemaLocation", schemaLocation); QDomElement xmlMetadata = doc.createElement("metadata"); gpx.appendChild(xmlMetadata); writeXml(xmlMetadata,"name", metadata.name); writeXml(xmlMetadata,"desc", metadata.desc); if(!metadata.author.name.isEmpty()) { QDomElement xmlAuthor = doc.createElement("author"); xmlMetadata.appendChild(xmlAuthor); writeXml(xmlAuthor,"name", metadata.author.name); if(!metadata.author.id.isEmpty() && !metadata.author.domain.isEmpty()) { QDomElement xmlEmail = doc.createElement("email"); xmlAuthor.appendChild(xmlEmail); xmlEmail.setAttribute("id", metadata.author.id); xmlEmail.setAttribute("domain", metadata.author.domain); } if(metadata.author.link.uri.isValid()) { QDomElement xmlLink = doc.createElement("link"); xmlAuthor.appendChild(xmlLink); xmlLink.setAttribute("href", metadata.author.link.uri.toString()); writeXml(xmlLink, "text", metadata.author.link.text); writeXml(xmlLink, "type", metadata.author.link.type); } } if(!metadata.copyright.author.isEmpty()) { QDomElement xmlCopyright = doc.createElement("copyright"); xmlMetadata.appendChild(xmlCopyright); xmlCopyright.setAttribute("author", metadata.copyright.author); writeXml(xmlCopyright, "year", metadata.copyright.year); writeXml(xmlCopyright, "license", metadata.copyright.license); } writeXml(xmlMetadata, "link", metadata.links); writeXml(xmlMetadata, "time", metadata.time); writeXml(xmlMetadata, "keywords", metadata.keywords); if(metadata.bounds.isValid()) { QDomElement xmlBounds = doc.createElement("bounds"); xmlMetadata.appendChild(xmlBounds); xmlBounds.setAttribute("minlat", metadata.bounds.bottom()); xmlBounds.setAttribute("minlon", metadata.bounds.left()); xmlBounds.setAttribute("maxlat", metadata.bounds.top()); xmlBounds.setAttribute("maxlon", metadata.bounds.right()); } return gpx; } void CGisItemWpt::readGpx(const QDomNode& xml) { readWpt(xml, wpt); // decode some well known extensions if(xml.namedItem("extensions").isElement()) { const QDomNode& ext = xml.namedItem("extensions"); readXml(ext, "ql:key", key.item); readXml(ext, "ql:flags", flags); readXml(ext, "ql:bubble", offsetBubble, widthBubble); readXml(ext, history); const QDomNode& wptx1 = ext.namedItem("wptx1:WaypointExtension"); readXml(wptx1, "wptx1:Proximity", proximity); const QDomNode& xmlCache = ext.namedItem("cache"); if(!xmlCache.isNull()) { // read OC cache extensions } } const QDomNode& xmlCache = xml.namedItem("groundspeak:cache"); if(!xmlCache.isNull() && !geocache.hasData) { readGcExt(xmlCache); } } void CGisItemWpt::save(QDomNode& gpx) { QDomDocument doc = gpx.ownerDocument(); QDomElement xmlWpt = doc.createElement("wpt"); gpx.appendChild(xmlWpt); writeWpt(xmlWpt, wpt); // write the key as extension tag QDomElement xmlExt = doc.createElement("extensions"); xmlWpt.appendChild(xmlExt); writeXml(xmlExt, "ql:key", key.item); writeXml(xmlExt, "ql:flags", flags); writeXml(xmlExt, "ql:bubble", offsetBubble, widthBubble); writeXml(xmlExt, history); // write other well known extensions QDomElement wptx1 = doc.createElement("wptx1:WaypointExtension"); xmlExt.appendChild(wptx1); writeXml(wptx1, "wptx1:Proximity", proximity); if(geocache.hasData /*&& geocache.service == eGC*/) { QDomElement xmlCache = doc.createElement("groundspeak:cache"); writeGcExt(xmlCache); xmlWpt.appendChild(xmlCache); } } void CGisItemWpt::readGcExt(const QDomNode& xmlCache) { geocache.service = eGC; const QDomNamedNodeMap& attr = xmlCache.attributes(); geocache.id = attr.namedItem("id").nodeValue().toInt(); geocache.archived = attr.namedItem("archived").nodeValue().toLocal8Bit() == "True"; geocache.available = attr.namedItem("available").nodeValue().toLocal8Bit() == "True"; if(geocache.archived) { geocache.status = QObject::tr("Archived"); } else if(geocache.available) { geocache.status = QObject::tr("Available"); } else { geocache.status = QObject::tr("Not Available"); } readXml(xmlCache, "groundspeak:name", geocache.name); readXml(xmlCache, "groundspeak:placed_by", geocache.owner); readXml(xmlCache, "groundspeak:type", geocache.type); readXml(xmlCache, "groundspeak:container", geocache.container); readXml(xmlCache, "groundspeak:difficulty", geocache.difficulty); readXml(xmlCache, "groundspeak:terrain", geocache.terrain); readXml(xmlCache, "groundspeak:short_description", geocache.shortDesc, geocache.shortDescIsHtml); readXml(xmlCache, "groundspeak:long_description", geocache.longDesc, geocache.longDescIsHtml); readXml(xmlCache, "groundspeak:encoded_hints", geocache.hint); readXml(xmlCache, "groundspeak:country", geocache.country); readXml(xmlCache, "groundspeak:state", geocache.state); const QDomNodeList& logs = xmlCache.toElement().elementsByTagName("groundspeak:log"); uint N = logs.count(); for(uint n = 0; n < N; ++n) { const QDomNode& xmlLog = logs.item(n); const QDomNamedNodeMap& attr = xmlLog.attributes(); geocachelog_t log; log.id = attr.namedItem("id").nodeValue().toUInt(); readXml(xmlLog, "groundspeak:date", log.date); readXml(xmlLog, "groundspeak:type", log.type); if(xmlLog.namedItem("groundspeak:finder").isElement()) { const QDomNamedNodeMap& attr = xmlLog.namedItem("groundspeak:finder").attributes(); log.finderId = attr.namedItem("id").nodeValue(); } readXml(xmlLog, "groundspeak:finder", log.finder); readXml(xmlLog, "groundspeak:text", log.text, log.textIsHtml); geocache.logs << log; } geocache.hasData = true; } void CGisItemWpt::writeGcExt(QDomNode& xmlCache) { QString str; xmlCache.toElement().setAttribute("xmlns:groundspeak", "http://www.groundspeak.com/cache/1/0"); xmlCache.toElement().setAttribute("id", geocache.id); xmlCache.toElement().setAttribute("archived", geocache.archived ? "True" : "False"); xmlCache.toElement().setAttribute("available", geocache.available ? "True" : "False"); writeXml(xmlCache, "groundspeak:name", geocache.name); writeXml(xmlCache, "groundspeak:placed_by", geocache.owner); writeXml(xmlCache, "groundspeak:type", geocache.type); writeXml(xmlCache, "groundspeak:container", geocache.container); if(geocache.difficulty == int(geocache.difficulty)) { str.sprintf("%1.0f", geocache.difficulty); } else { str.sprintf("%1.1f", geocache.difficulty); } writeXml(xmlCache, "groundspeak:difficulty", str); if(geocache.terrain == int(geocache.terrain)) { str.sprintf("%1.0f", geocache.terrain); } else { str.sprintf("%1.1f", geocache.terrain); } writeXml(xmlCache, "groundspeak:terrain", str); writeXml(xmlCache, "groundspeak:short_description", geocache.shortDesc, geocache.shortDescIsHtml); writeXml(xmlCache, "groundspeak:long_description", geocache.longDesc, geocache.longDescIsHtml); writeXml(xmlCache, "groundspeak:encoded_hints", geocache.hint); if(!geocache.logs.isEmpty()) { QDomElement xmlLogs = xmlCache.ownerDocument().createElement("groundspeak:logs"); xmlCache.appendChild(xmlLogs); foreach(const geocachelog_t &log, geocache.logs) { QDomElement xmlLog = xmlCache.ownerDocument().createElement("groundspeak:log"); xmlLogs.appendChild(xmlLog); xmlLog.setAttribute("id", log.id); writeXml(xmlLog, "groundspeak:date", log.date); writeXml(xmlLog, "groundspeak:type", log.type); QDomElement xmlFinder = xmlCache.ownerDocument().createElement("groundspeak:finder"); xmlLog.appendChild(xmlFinder); QDomText _finder_ = xmlCache.ownerDocument().createCDATASection(log.finder); xmlFinder.appendChild(_finder_); xmlFinder.setAttribute("id", log.finderId); writeXml(xmlLog, "groundspeak:text", log.text, log.textIsHtml); } } } void CGisItemTrk::readTrk(const QDomNode& xml, trk_t& trk) { readXml(xml, "name", trk.name); readXml(xml, "cmt", trk.cmt); readXml(xml, "desc", trk.desc); readXml(xml, "src", trk.src); readXml(xml, "link", trk.links); readXml(xml, "number", trk.number); readXml(xml, "type", trk.type); const QDomNodeList& trksegs = xml.toElement().elementsByTagName("trkseg"); int N = trksegs.count(); trk.segs.resize(N); for(int n = 0; n < N; ++n) { const QDomNode& trkseg = trksegs.item(n); trkseg_t& seg = trk.segs[n]; const QDomNodeList& xmlTrkpts = trkseg.toElement().elementsByTagName("trkpt"); int M = xmlTrkpts.count(); seg.pts.resize(M); for(int m = 0; m < M; ++m) { trkpt_t& trkpt = seg.pts[m]; const QDomNode& xmlTrkpt = xmlTrkpts.item(m); readWpt(xmlTrkpt, trkpt); const QDomNode& ext = xmlTrkpt.namedItem("extensions"); if(ext.isElement()) { readXml(ext, "ql:flags", trkpt.flags); readXml(ext, trkpt.extensions); } } } // decode some well known extensions const QDomNode& ext = xml.namedItem("extensions"); if(ext.isElement()) { readXml(ext, "ql:key", key.item); readXml(ext, "ql:flags", flags); readXml(ext, history); const QDomNode& gpxx = ext.namedItem("gpxx:TrackExtension"); readXml(gpxx, "gpxx:DisplayColor", trk.color); setColor(str2color(trk.color)); const QDomNode &extColoring = ext.namedItem("ql:coloring"); QString source; readXml(extColoring, "ql:source", source); setColorizeSource(source); readXml(extColoring, "ql:limitLow", limitLow); readXml(extColoring, "ql:limitHigh", limitHigh); } deriveSecondaryData(); } void CGisItemTrk::save(QDomNode& gpx) { QDomDocument doc = gpx.ownerDocument(); QDomElement xmlTrk = doc.createElement("trk"); gpx.appendChild(xmlTrk); writeXml(xmlTrk, "name", trk.name); writeXml(xmlTrk, "cmt", trk.cmt); writeXml(xmlTrk, "desc", trk.desc); writeXml(xmlTrk, "src", trk.src); writeXml(xmlTrk, "link", trk.links); writeXml(xmlTrk, "number", trk.number); writeXml(xmlTrk, "type", trk.type); // write the key as extension tag QDomElement xmlExt = doc.createElement("extensions"); xmlTrk.appendChild(xmlExt); writeXml(xmlExt, "ql:key", key.item); writeXml(xmlExt, "ql:flags", flags); writeXml(xmlExt, history); // write source for coloring tracks if(!colorSource.isEmpty()) { QDomElement xmlExtColoring = doc.createElement("ql:coloring"); xmlExt.appendChild(xmlExtColoring); writeXml(xmlExtColoring, "ql:source", colorSource); writeXml(xmlExtColoring, "ql:limitLow", limitLow); writeXml(xmlExtColoring, "ql:limitHigh", limitHigh); } // write other well known extensions QDomElement gpxx = doc.createElement("gpxx:TrackExtension"); xmlExt.appendChild(gpxx); writeXml(gpxx, "gpxx:DisplayColor", trk.color); foreach(const trkseg_t &seg, trk.segs) { QDomElement xmlTrkseg = doc.createElement("trkseg"); xmlTrk.appendChild(xmlTrkseg); foreach(const trkpt_t &pt, seg.pts) { QDomElement xmlTrkpt = doc.createElement("trkpt"); xmlTrkseg.appendChild(xmlTrkpt); writeWpt(xmlTrkpt, pt); QDomElement xmlExt = doc.createElement("extensions"); xmlTrkpt.appendChild(xmlExt); writeXml(xmlExt, "ql:flags", pt.flags); writeXml(xmlExt, pt.extensions); } } } void CGisItemRte::readRte(const QDomNode& xml, rte_t& rte) { readXml(xml, "name", rte.name); readXml(xml, "cmt", rte.cmt); readXml(xml, "desc", rte.desc); readXml(xml, "src", rte.src); readXml(xml, "link", rte.links); readXml(xml, "number", rte.number); readXml(xml, "type", rte.type); const QDomNodeList& xmlRtepts = xml.toElement().elementsByTagName("rtept"); int M = xmlRtepts.count(); rte.pts.resize(M); for(int m = 0; m < M; ++m) { rtept_t& rtept = rte.pts[m]; const QDomNode& xmlRtept = xmlRtepts.item(m); readWpt(xmlRtept, rtept); rtept.icon = getWptIconByName(rtept.sym, rtept.focus); } // decode some well known extensions if(xml.namedItem("extensions").isElement()) { const QDomNode& ext = xml.namedItem("extensions"); readXml(ext, "ql:key", key.item); } } void CGisItemRte::save(QDomNode& gpx) { QDomDocument doc = gpx.ownerDocument(); QDomElement xmlRte = doc.createElement("rte"); gpx.appendChild(xmlRte); writeXml(xmlRte, "name", rte.name); writeXml(xmlRte, "cmt", rte.cmt); writeXml(xmlRte, "desc", rte.desc); writeXml(xmlRte, "src", rte.src); writeXml(xmlRte, "link", rte.links); writeXml(xmlRte, "number", rte.number); writeXml(xmlRte, "type", rte.type); // write the key as extension tag QDomElement xmlExt = doc.createElement("extensions"); xmlRte.appendChild(xmlExt); writeXml(xmlExt, "ql:key", key.item); foreach(const rtept_t &pt, rte.pts) { QDomElement xmlRtept = doc.createElement("rtept"); xmlRte.appendChild(xmlRtept); writeWpt(xmlRtept, pt); } } void CGisItemOvlArea::readArea(const QDomNode& xml, area_t& area) { readXml(xml, "ql:name", area.name); readXml(xml, "ql:cmt", area.cmt); readXml(xml, "ql:desc", area.desc); readXml(xml, "ql:src", area.src); readXml(xml, "ql:link", area.links); readXml(xml, "ql:number", area.number); readXml(xml, "ql:type", area.type); readXml(xml, "ql:color", area.color); readXml(xml, "ql:width", area.width); readXml(xml, "ql:style", area.style); readXml(xml, "ql:opacity", area.opacity); readXml(xml, "ql:key", key.item); readXml(xml, "ql:flags", flags); readXml(xml, history); const QDomNodeList& xmlPts = xml.toElement().elementsByTagName("ql:point"); int M = xmlPts.count(); area.pts.resize(M); for(int m = 0; m < M; ++m) { pt_t& pt = area.pts[m]; const QDomNode& xmlPt = xmlPts.item(m); readWpt(xmlPt, pt); } setColor(str2color(area.color)); deriveSecondaryData(); } void CGisItemOvlArea::save(QDomNode& gpx) { QDomDocument doc = gpx.ownerDocument(); QDomElement xmlArea = doc.createElement("ql:area"); gpx.appendChild(xmlArea); writeXml(xmlArea, "ql:name", area.name); writeXml(xmlArea, "ql:cmt", area.cmt); writeXml(xmlArea, "ql:desc", area.desc); writeXml(xmlArea, "ql:src", area.src); writeXml(xmlArea, "ql:link", area.links); writeXml(xmlArea, "ql:number", area.number); writeXml(xmlArea, "ql:type", area.type); writeXml(xmlArea, "ql:color", area.color); writeXml(xmlArea, "ql:width", area.width); writeXml(xmlArea, "ql:style", area.style); writeXml(xmlArea, "ql:opacity", area.opacity); writeXml(xmlArea, "ql:key", key.item); writeXml(xmlArea, "ql:flags", flags); writeXml(xmlArea, history); foreach(const pt_t &pt, area.pts) { QDomElement xmlPt = doc.createElement("ql:point"); xmlArea.appendChild(xmlPt); writeWpt(xmlPt, pt); } } void IGisItem::readWpt(const QDomNode& xml, wpt_t& wpt) { const QDomNamedNodeMap& attr = xml.attributes(); wpt.lat = attr.namedItem("lat").nodeValue().toDouble(); wpt.lon = attr.namedItem("lon").nodeValue().toDouble(); readXml(xml, "ele", wpt.ele); readXml(xml, "time", wpt.time); readXml(xml, "magvar", wpt.magvar); readXml(xml, "geoidheight", wpt.geoidheight); readXml(xml, "name", wpt.name); readXml(xml, "cmt", wpt.cmt); readXml(xml, "desc", wpt.desc); readXml(xml, "src", wpt.src); readXml(xml, "link", wpt.links); readXml(xml, "sym", wpt.sym); readXml(xml, "type", wpt.type); readXml(xml, "fix", wpt.fix); readXml(xml, "sat", wpt.sat); readXml(xml, "hdop", wpt.hdop); readXml(xml, "vdop", wpt.vdop); readXml(xml, "pdop", wpt.pdop); readXml(xml, "ageofdgpsdata", wpt.ageofdgpsdata); readXml(xml, "dgpsid", wpt.dgpsid); // some GPX 1.0 backward compatibility QString url; readXml(xml, "url", url); if(!url.isEmpty()) { link_t link; link.uri.setUrl(url); readXml(xml, "urlname", link.text); wpt.links << link; } } void IGisItem::writeWpt(QDomElement& xml, const wpt_t& wpt) { QString str; str.sprintf("%1.8f", wpt.lat); xml.setAttribute("lat", str); str.sprintf("%1.8f", wpt.lon); xml.setAttribute("lon", str); writeXml(xml, "ele", wpt.ele); writeXml(xml, "time", wpt.time); writeXml(xml, "magvar", wpt.magvar); writeXml(xml, "geoidheight", wpt.geoidheight); writeXml(xml, "name", wpt.name); writeXml(xml, "cmt", IGisItem::removeHtml(wpt.cmt)); writeXml(xml, "desc", IGisItem::removeHtml(wpt.desc)); writeXml(xml, "src", wpt.src); writeXml(xml, "link", wpt.links); writeXml(xml, "sym", wpt.sym); writeXml(xml, "type", wpt.type); writeXml(xml, "fix", wpt.fix); writeXml(xml, "sat", wpt.sat); writeXml(xml, "hdop", wpt.hdop); writeXml(xml, "vdop", wpt.vdop); writeXml(xml, "pdop", wpt.pdop); writeXml(xml, "ageofdgpsdata", wpt.ageofdgpsdata); writeXml(xml, "dgpsid", wpt.dgpsid); } qmapshack-1.5.1/src/gis/CGisListWks.cpp000644 001750 000144 00000142550 12622435720 020714 0ustar00oeichlerusers000000 000000 /********************************************************************************************** Copyright (C) 2014 Oliver Eichler oliver.eichler@gmx.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **********************************************************************************************/ #include "CMainWindow.h" #include "canvas/CCanvas.h" #ifdef Q_OS_LINUX #include "device/CDeviceWatcherLinux.h" #endif #ifdef Q_OS_WIN #include "device/CDeviceWatcherWindows.h" #endif #ifdef Q_OS_MAC #include "device/CDeviceWatcherMac.h" #endif #include "device/IDevice.h" #include "gis/CGisListWks.h" #include "gis/CGisWidget.h" #include "gis/CSelDevices.h" #include "gis/IGisItem.h" #include "gis/db/CDBProject.h" #include "gis/db/CLostFoundProject.h" #include "gis/db/CSelectDBFolder.h" #include "gis/db/CSetupFolder.h" #include "gis/db/macros.h" #include "gis/gpx/CGpxProject.h" #include "gis/ovl/CGisItemOvlArea.h" #include "gis/prj/IGisProject.h" #include "gis/qms/CQmsProject.h" #include "gis/rte/CCreateRouteFromWpt.h" #include "gis/rte/CGisItemRte.h" #include "gis/search/CSearchGoogle.h" #include "gis/trk/CGisItemTrk.h" #include "gis/wpt/CGisItemWpt.h" #include "helpers/CAppSetup.h" #include "helpers/CProgressDialog.h" #include "helpers/CSelectCopyAction.h" #include "helpers/CSelectProjectDialog.h" #include "helpers/CSettings.h" #include #include #include #undef DB_VERSION #define DB_VERSION 2 class CGisListWksEditLock { public: CGisListWksEditLock(bool waitCursor, QMutex& mutex) : mutex(mutex), waitCursor(waitCursor) { if(waitCursor) { CCanvas::setOverrideCursor(Qt::WaitCursor, "CGisListWksEditLock"); } mutex.lock(); } ~CGisListWksEditLock() { if(waitCursor) { CCanvas::restoreOverrideCursor("~CGisListWksEditLock"); } mutex.unlock(); } private: QMutex& mutex; bool waitCursor; }; CGisListWks::CGisListWks(QWidget *parent) : QTreeWidget(parent) { db = QSqlDatabase::addDatabase("QSQLITE","Workspace1"); QString config = CAppSetup::getPlattformInstance()->configDir().filePath("workspace.db"); db.setDatabaseName(config); db.open(); configDB(); menuProjectWks = new QMenu(this); actionEditPrj = menuProjectWks->addAction(QIcon("://icons/32x32/EditDetails.png"),tr("Edit.."), this, SLOT(slotEditPrj())); actionShowOnMap = menuProjectWks->addAction(QIcon("://icons/32x32/ShowAll.png"),tr("Show on Map"), this, SLOT(slotShowOnMap())); actionHideFrMap = menuProjectWks->addAction(QIcon("://icons/32x32/ShowNone.png"),tr("Hide from Map"), this, SLOT(slotHideFrMap())); menuProjectWks->addSeparator(); actionSave = menuProjectWks->addAction(QIcon("://icons/32x32/SaveGIS.png"),tr("Save"), this, SLOT(slotSaveProject())); actionSaveAs = menuProjectWks->addAction(QIcon("://icons/32x32/SaveGISAs.png"),tr("Save As..."), this, SLOT(slotSaveAsProject())); menuProjectWks->addSeparator(); actionSyncWksDev= menuProjectWks->addAction(QIcon("://icons/32x32/Device.png"),tr("Send to Devices"), this, SLOT(slotSyncWksDev())); menuProjectWks->addSeparator(); actionCloseProj = menuProjectWks->addAction(QIcon("://icons/32x32/Close.png"),tr("Close"), this, SLOT(slotCloseProject())); menuProjectDev = new QMenu(this); menuProjectDev->addAction(actionEditPrj); menuProjectDev->addAction(actionSaveAs); menuProjectDev->addAction(actionSave); actionSyncDevWks= menuProjectDev->addAction(QIcon("://icons/32x32/Device.png"),tr("Update Project on Device"), this, SLOT(slotSyncDevWks())); actionDelProj = menuProjectDev->addAction(QIcon("://icons/32x32/DeleteOne.png"),tr("Delete"), this, SLOT(slotDeleteProject())); menuProjectTrash= new QMenu(this); menuProjectTrash->addAction(actionSaveAs); menuProjectTrash->addAction(actionCloseProj); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint))); connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemDoubleClicked(QTreeWidgetItem*,int))); connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int))); menuItemTrk = new QMenu(this); actionEditDetails = menuItemTrk->addAction(QIcon("://icons/32x32/EditDetails.png"),tr("Edit..."), this, SLOT(slotEditItem())); actionCopyItem = menuItemTrk->addAction(QIcon("://icons/32x32/Copy.png"),tr("Copy to..."), this, SLOT(slotCopyItem())); menuItemTrk->addSeparator(); actionFocusTrk = menuItemTrk->addAction(QIcon("://icons/32x32/TrkProfile.png"),tr("Track Profile")); actionFocusTrk->setCheckable(true); actionRangeTrk = menuItemTrk->addAction(QIcon("://icons/32x32/SelectRange.png"),tr("Select Range"), this, SLOT(slotRangeTrk())); actionEditTrk = menuItemTrk->addAction(QIcon("://icons/32x32/LineMove.png"),tr("Edit Track Points"), this, SLOT(slotEditTrk())); actionReverseTrk = menuItemTrk->addAction(QIcon("://icons/32x32/Reverse.png"),tr("Reverse Track"), this, SLOT(slotReverseTrk())); actionCombineTrk = menuItemTrk->addAction(QIcon("://icons/32x32/Combine.png"),tr("Combine Tracks"), this, SLOT(slotCombineTrk())); menuItemTrk->addSeparator(); actionDelete = menuItemTrk->addAction(QIcon("://icons/32x32/DeleteOne.png"),tr("Delete"), this, SLOT(slotDeleteItem())); menuItemWpt = new QMenu(this); menuItemWpt->addAction(actionEditDetails); menuItemWpt->addAction(actionCopyItem); menuItemWpt->addSeparator(); actionBubbleWpt = menuItemWpt->addAction(QIcon("://icons/32x32/Bubble.png"),tr("Show Bubble"), this, SLOT(slotBubbleWpt())); actionBubbleWpt->setCheckable(true); actionMoveWpt = menuItemWpt->addAction(QIcon("://icons/32x32/WptMove.png"),tr("Move Waypoint"), this, SLOT(slotMoveWpt())); actionProjWpt = menuItemWpt->addAction(QIcon("://icons/32x32/WptProj.png"),tr("Proj. Waypoint..."), this, SLOT(slotProjWpt())); menuItemWpt->addSeparator(); menuItemWpt->addAction(actionDelete); menuItemRte = new QMenu(this); menuItemRte->addAction(actionEditDetails); menuItemRte->addAction(actionCopyItem); menuItemRte->addSeparator(); actionFocusRte = menuItemRte->addAction(QIcon("://icons/32x32/RteInstr.png"), tr("Route Instructions")); actionFocusRte->setCheckable(true); actionCalcRte = menuItemRte->addAction(QIcon("://icons/32x32/Apply.png"), tr("Calculate Route"), this, SLOT(slotCalcRte())); actionResetRte = menuItemRte->addAction(QIcon("://icons/32x32/Reset.png"), tr("Reset Route"), this, SLOT(slotResetRte())); actionEditRte = menuItemRte->addAction(QIcon("://icons/32x32/LineMove.png"), tr("Edit Route"), this, SLOT(slotEditRte())); menuItemRte->addSeparator(); menuItemRte->addAction(actionDelete); menuItemOvl = new QMenu(this); menuItemOvl->addAction(actionEditDetails); menuItemOvl->addAction(actionCopyItem); menuItemOvl->addSeparator(); actionEditArea = menuItemOvl->addAction(QIcon("://icons/32x32/AreaMove.png"),tr("Edit Area Points"), this, SLOT(slotEditArea())); menuItemOvl->addSeparator(); menuItemOvl->addAction(actionDelete); menuItem = new QMenu(this); menuItem->addAction(actionCopyItem); actionRteFromWpt = menuItem->addAction(QIcon("://icons/32x32/Route.png"), tr("Create Route"), this, SLOT(slotRteFromWpt())); menuItem->addAction(actionCombineTrk); menuItem->addAction(actionDelete); connect(actionFocusTrk, SIGNAL(triggered(bool)), this, SLOT(slotFocusTrk(bool))); connect(actionFocusRte, SIGNAL(triggered(bool)), this, SLOT(slotFocusRte(bool))); connect(qApp, SIGNAL(aboutToQuit ()), this, SLOT(slotSaveWorkspace())); SETTINGS; saveOnExit = cfg.value("Database/saveOnExit", saveOnExit).toBool(); saveEvery = cfg.value("Database/saveEvery", saveEvery).toInt(); if(saveOnExit && (saveEvery > 0)) { QTimer::singleShot(saveEvery * 60000, this, SLOT(slotSaveWorkspace())); } #ifdef Q_OS_LINUX deviceWatcher = new CDeviceWatcherLinux(this); connect(deviceWatcher, SIGNAL(sigChanged()), SIGNAL(sigChanged())); #endif #ifdef Q_OS_MAC CDeviceWatcherMac* pWatcher = new CDeviceWatcherMac(this); deviceWatcher = pWatcher; connect(deviceWatcher, SIGNAL(sigChanged()), SIGNAL(sigChanged())); connect(qApp, SIGNAL(aboutToQuit()), deviceWatcher, SLOT(slotEndListing())); #endif #ifdef Q_OS_WIN deviceWatcher = new CDeviceWatcherWindows(this); connect(deviceWatcher, SIGNAL(sigChanged()), SIGNAL(sigChanged())); #endif QTimer::singleShot(500, this, SLOT(slotLoadWorkspace())); } CGisListWks::~CGisListWks() { } void CGisListWks::configDB() { QSqlQuery query(db); if(!query.exec("PRAGMA locking_mode=EXCLUSIVE")) { return; } if(!query.exec("PRAGMA synchronous=OFF")) { return; } if(!query.exec("PRAGMA temp_store=MEMORY")) { return; } if(!query.exec("PRAGMA default_cache_size=50")) { return; } if(!query.exec("PRAGMA page_size=8192")) { return; } if(!query.exec("SELECT version FROM versioninfo")) { initDB(); } else if(query.next()) { int version = query.value(0).toInt(); if(version != DB_VERSION) { migrateDB(version); } } else { initDB(); } } void CGisListWks::initDB() { QSqlQuery query(db); if(query.exec( "CREATE TABLE versioninfo ( version TEXT )")) { query.prepare( "INSERT INTO versioninfo (version) VALUES(:version)"); query.bindValue(":version", DB_VERSION); QUERY_EXEC(); } if(!query.exec( "CREATE TABLE workspace (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "type INTEGER NOT NULL," "name TEXT NOT NULL," "key TEXT NOT NULL," "changed BOOLEAN DEFAULT FALSE," "visible BOOLEAN DEFAULT TRUE," "data BLOB NOT NULL" ")")) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } } void CGisListWks::migrateDB(int version) { qDebug() << "workspace.db has version " << version << ", migration to version " << DB_VERSION << " required"; // try to migrate between the database versions step by step (as soon as applicable) if(version < 2) { migrateDB1to2(); } // if(version < 3) { migrateDB2to3(); } // save the new version to the database QSqlQuery query(db); query.prepare( "UPDATE versioninfo set version=:version"); query.bindValue(":version", DB_VERSION); QUERY_EXEC(); } void CGisListWks::migrateDB1to2() { qDebug() << "migrating workspace.db from version 1 to version 2"; // add a new column `visible` to the database // the default value is `true`, as - by default in older versions of QMS - all saved projects // have been loaded and shown on the map directly after starting QSqlQuery query(db); if(!query.exec( "ALTER TABLE workspace ADD COLUMN visible BOOLEAN DEFAULT TRUE;" )) { qDebug() << query.lastQuery(); qDebug() << query.lastError(); } } void CGisListWks::setExternalMenu(QMenu * project) { menuNone = project; connect(CMainWindow::self().findChild("actionAddEmptyProject"), SIGNAL(triggered()), this, SLOT(slotAddEmptyProject())); connect(CMainWindow::self().findChild("actionCloseAllProjects"), SIGNAL(triggered(bool)), this, SLOT(slotCloseAllProjects())); connect(CMainWindow::self().findChild("actionSearchGoogle"), SIGNAL(triggered(bool)), this, SLOT(slotSearchGoogle(bool))); } void CGisListWks::dragMoveEvent (QDragMoveEvent * e ) { CGisListWksEditLock lock(true, IGisItem::mutexItems); QTreeWidgetItem * item1 = currentItem(); QTreeWidgetItem * item2 = itemAt(e->pos()); // changing the item order is only valid for single selected items if(selectedItems().count() == 1) { /* What's happening here? 1) Cast current item and item under cursor to GIS item type 2) If type matches for both test for common parent 2.1) common parent-> move 2.1) different parent -> copy 3) go on with dragMoveEvent(); */ CGisItemTrk * trk1 = dynamic_cast(item1); CGisItemTrk * trk2 = dynamic_cast(item2); if(trk1 && trk2) { if(trk1->parent() == trk2->parent()) { e->setDropAction(Qt::MoveAction); } else { e->setDropAction(Qt::CopyAction); } QTreeWidget::dragMoveEvent(e); return; } CGisItemWpt * wpt1 = dynamic_cast(item1); CGisItemWpt * wpt2 = dynamic_cast(item2); if(wpt1 && wpt2) { if(wpt1->parent() == wpt2->parent()) { e->setDropAction(Qt::MoveAction); } else { e->setDropAction(Qt::CopyAction); } QTreeWidget::dragMoveEvent(e); return; } CGisItemRte * rte1 = dynamic_cast(item1); CGisItemRte * rte2 = dynamic_cast(item2); if(rte1 && rte2) { if(rte1->parent() == rte2->parent()) { e->setDropAction(Qt::MoveAction); } else { e->setDropAction(Qt::CopyAction); } QTreeWidget::dragMoveEvent(e); return; } CGisItemOvlArea * area1 = dynamic_cast(item1); CGisItemOvlArea * area2 = dynamic_cast(item2); if(area1 && area2) { if(area1->parent() == area2->parent()) { e->setDropAction(Qt::MoveAction); } else { e->setDropAction(Qt::CopyAction); } QTreeWidget::dragMoveEvent(e); return; } /* Never move/copy projects on devices. Data has to be removed or changed to store a project and it's items on a device. Moving it back to the workspace would conflict with the original project. To much hassle to resolve this properly. */ IGisProject * proj1 = dynamic_cast(item1); if(proj1 && proj1->isOnDevice()) { e->setDropAction(Qt::IgnoreAction); QTreeWidget::dragMoveEvent(e); return; } } /* Test for other project, to change project order. But if other project is on a device block the request. A project has to be copied to the device via it's device item. */ IGisProject * proj2 = dynamic_cast(item2); if(proj2) { IGisProject * proj1 = dynamic_cast(item1); if(proj1) { e->setDropAction(proj2->isOnDevice() ? Qt::IgnoreAction : Qt::MoveAction); QTreeWidget::dragMoveEvent(e); return; } IGisItem * gisItem1 = dynamic_cast(item1); if(gisItem1) { e->setDropAction(Qt::CopyAction); QTreeWidget::dragMoveEvent(e); return; } } /* Test for device as drop target. A device will copy the project into it's own supported format. */ IDevice * device = dynamic_cast(item2); if(device) { IGisProject * proj1 = dynamic_cast(item1); if(proj1 && !proj1->isOnDevice()) { e->setDropAction(Qt::CopyAction); QTreeWidget::dragMoveEvent(e); return; } } e->setDropAction(Qt::IgnoreAction); QTreeWidget::dragMoveEvent(e); } void CGisListWks::dropEvent ( QDropEvent * e ) { CGisListWksEditLock lock(true, IGisItem::mutexItems); QList items = selectedItems(); if(items.isEmpty()) { return; } int lastResult = CSelectCopyAction::eResultNone; // go on with item insertion /* What's happening here? for single selected items do: 1) Test if item will be inserted above ore below item under cursor. 2) Cast current item and item under cursor to GIS item type 3) If type matches for both test for common parent 3.1) common parent-> go on with default drop event 3.1) different parent -> create a copy and insert it index 4) signal change of project for single and multiple selected items, do: 5) Test if item under cursor is a project 6) If project and project is not item's project create a copy */ if(items.size() == 1) { // calc. index offset (below/above item) QRect r = visualItemRect(itemAt(e->pos())); int y1 = r.top() + r.height()/2; int y2 = e->pos().y(); int off = y2 > y1 ? 1 : 0; IGisProject * prj1 = dynamic_cast(currentItem()); IGisProject * prj2 = dynamic_cast(itemAt(e->pos())); if(prj1 && prj2) { prj2->setFlags(prj2->flags() & ~Qt::ItemIsDropEnabled); QTreeWidget::dropEvent(e); prj2->setFlags(prj2->flags() | Qt::ItemIsDropEnabled); emit sigChanged(); return; } CGisItemWpt * wpt1 = dynamic_cast(currentItem()); CGisItemWpt * wpt2 = dynamic_cast(itemAt(e->pos())); if(wpt1 && wpt2) { if(wpt1->parent() == wpt2->parent()) { QTreeWidget::dropEvent(e); } else { IGisProject * project = dynamic_cast(wpt2->parent()); if(project) { project->insertCopyOfItem(wpt1, off, lastResult); } } emit sigChanged(); return; } CGisItemTrk * trk1 = dynamic_cast(currentItem()); CGisItemTrk * trk2 = dynamic_cast(itemAt(e->pos())); if(trk1 && trk2) { if(trk1->parent() == trk2->parent()) { QTreeWidget::dropEvent(e); } else { IGisProject * project = dynamic_cast(trk2->parent()); if(project) { project->insertCopyOfItem(trk1, off, lastResult); } } emit sigChanged(); return; } CGisItemRte * rte1 = dynamic_cast(currentItem()); CGisItemRte * rte2 = dynamic_cast(itemAt(e->pos())); if(rte1 && rte2) { if(rte1->parent() == rte2->parent()) { QTreeWidget::dropEvent(e); } else { IGisProject * project = dynamic_cast(rte2->parent()); if(project) { project->insertCopyOfItem(rte1, off, lastResult); } } emit sigChanged(); return; } CGisItemOvlArea * area1 = dynamic_cast(currentItem()); CGisItemOvlArea * area2 = dynamic_cast(itemAt(e->pos())); if(area1 && area2) { if(area1->parent() == area2->parent()) { QTreeWidget::dropEvent(e); } else { IGisProject * project = dynamic_cast(area2->parent()); if(project) { project->insertCopyOfItem(area1, off, lastResult); } } emit sigChanged(); return; } } // check if item at position is a project and insert a copy of all selected items IGisProject * project = dynamic_cast(itemAt(e->pos())); if(project) { project->blockUpdateItems(true); int cnt = 1; int N = items.size(); PROGRESS_SETUP(tr("Drop items..."), 0, N, this); foreach(QTreeWidgetItem * item, items) { PROGRESS(cnt++, break); IGisItem * gisItem = dynamic_cast(item); if(gisItem) { project->insertCopyOfItem(gisItem, NOIDX, lastResult); } } project->blockUpdateItems(false); } IDevice * device = dynamic_cast(itemAt(e->pos())); if(device) { IGisProject * project = dynamic_cast(currentItem()); if(project) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->reportStatus("device", tr("Update devices

Update %1
Please wait...

").arg(device->text(CGisListWks::eColumnName))); canvas->update(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } int lastResult = CSelectCopyAction::eResultNone; device->insertCopyOfProject(project, lastResult); if(canvas) { canvas->reportStatus("device", ""); } } } emit sigChanged(); } void CGisListWks::removeDevice(const QString& key) { CGisListWksEditLock lock(true, IGisItem::mutexItems); for(int i = 0; i < topLevelItemCount(); i++) { IDevice * device = dynamic_cast(topLevelItem(i)); if(device && device->getKey() == key) { delete device; emit sigChanged(); return; } } } bool CGisListWks::hasProject(IGisProject * project) { CGisListWksEditLock lock(true, IGisItem::mutexItems); QString key = project->getKey(); for(int i = 0; i < topLevelItemCount(); i++) { IGisProject * item = dynamic_cast(topLevelItem(i)); if(item && item->getKey() == key) { if(item != project) { return true; } } } return false; } IGisProject * CGisListWks::getProjectByKey(const QString& key) { CGisListWksEditLock lock(true, IGisItem::mutexItems); for(int i = 0; i < topLevelItemCount(); i++) { IGisProject * item = dynamic_cast(topLevelItem(i)); if(item && item->getKey() == key) { return item; } } return 0; } CDBProject * CGisListWks::getProjectById(quint64 id, const QString& db) { CGisListWksEditLock lock(true, IGisItem::mutexItems); for(int i = 0; i < topLevelItemCount(); i++) { CDBProject * item = dynamic_cast(topLevelItem(i)); if(item && item->getId() == id && item->getDBName() == db) { return item; } } return 0; } void CGisListWks::slotSaveWorkspace() { CGisListWksEditLock lock(true,IGisItem::mutexItems); if(!saveOnExit) { return; } QSqlQuery query(db); if(!query.exec("DELETE FROM workspace")) { QUERY_EXEC(return ); } qDebug() << "slotSaveWorkspace()"; const int total = topLevelItemCount(); PROGRESS_SETUP(tr("Saving workspace. Please wait."), 0, total, this); for(int i = 0; i < total; i++) { PROGRESS(i, return ); IGisProject * project = dynamic_cast(topLevelItem(i)); if(project == 0) { continue; } QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_2); stream.setByteOrder(QDataStream::LittleEndian); project->IGisProject::operator>>(stream); query.prepare("INSERT INTO workspace (type, key, name, changed, visible, data) VALUES (:type, :key, :name, :changed, :visible, :data)"); query.bindValue(":type", project->getType()); query.bindValue(":key", project->getKey()); query.bindValue(":name", project->getName()); query.bindValue(":changed", project->isChanged()); bool visible = (project->checkState(CGisListDB::eColumnCheckbox) == Qt::Checked); query.bindValue(":visible", visible); query.bindValue(":data", data); QUERY_EXEC(continue); } if(saveEvery) { QTimer::singleShot(saveEvery * 60000, this, SLOT(slotSaveWorkspace())); } } void CGisListWks::slotLoadWorkspace() { CGisListWksEditLock lock(true,IGisItem::mutexItems); QSqlQuery query(db); query.prepare("SELECT type, key, name, changed, visible, data FROM workspace"); QUERY_EXEC(return ); const int total = query.size(); PROGRESS_SETUP(tr("Loading workspace. Please wait."), 0, total, this); quint32 progCnt = 0; while(query.next()) { PROGRESS(progCnt++, return ); int type = query.value(0).toInt(); QString name = query.value(2).toString(); bool changed = query.value(3).toBool(); Qt::CheckState visible = query.value(4).toBool() ? Qt::Checked : Qt::Unchecked; QByteArray data = query.value(5).toByteArray(); QDataStream stream(&data, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_5_2); stream.setByteOrder(QDataStream::LittleEndian); IGisProject * project = 0; switch(type) { case IGisProject::eTypeQms: { project = new CQmsProject(name, this); project->setCheckState(CGisListDB::eColumnCheckbox, visible); // (1a) *project << stream; break; } case IGisProject::eTypeGpx: { project = new CGpxProject(name, this); project->setCheckState(CGisListDB::eColumnCheckbox, visible); // (1b) *project << stream; break; } case IGisProject::eTypeDb: { CDBProject * dbProject; project = dbProject = new CDBProject(this); project->setCheckState(CGisListDB::eColumnCheckbox, visible); // (1c) project->IGisProject::operator<<(stream); dbProject->restoreDBLink(); if(!project->isValid()) { delete project; project = 0; } else { dbProject->postStatus(); } break; } } if(project == 0) { continue; } // Hiding the individual projects from the map (1a, 1b, 1c) could be done here within a single statement, // but this results in a visible `the checkbox is being unchecked`, especially in case the project // is large and takes some time to load. // When done directly after construction there is no `blinking` of the check mark project->setToolTip(eColumnName,project->getInfo()); if(changed) { project->setChanged(); } } emit sigChanged(); } void CGisListWks::slotContextMenu(const QPoint& point) { QPoint p = mapToGlobal(point); if(selectedItems().isEmpty() && menuNone) { menuNone->exec(p); return; } // check whether all projects are checked or unchecked... bool allChecked = true; bool allUnchecked = true; foreach(QTreeWidgetItem *item, selectedItems()) { IGisProject *project = dynamic_cast(item); if(project != 0) { // as soon as we find an unchecked element, not all elements are checked (and vice versa) if(project->checkState(CGisListDB::eColumnCheckbox) == Qt::Unchecked) { allChecked = false; } else { allUnchecked = false; } } if(!allChecked && !allUnchecked) { break; } } // ...and disable entries without any effect actionShowOnMap->setEnabled(!allChecked); actionHideFrMap->setEnabled(!allUnchecked); if(selectedItems().count() > 1) { IGisProject * project = dynamic_cast(currentItem()); if(project != 0) { if(project->isOnDevice()) { menuProjectDev->exec(p); } else { actionSyncWksDev->setEnabled(IDevice::count()); menuProjectWks->exec(p); } return; } IGisItem * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { bool onlyWpts = true; bool onlyTrks = true; foreach(QTreeWidgetItem * item, selectedItems()) { if(item->type() != IGisItem::eTypeWpt) { onlyWpts = false; } if(item->type() != IGisItem::eTypeTrk) { onlyTrks = false; } if(!onlyTrks && !onlyWpts) { break; } } actionRteFromWpt->setEnabled(onlyWpts); actionCombineTrk->setEnabled(onlyTrks); menuItem->exec(p); return; } return; } if(selectedItems().count() == 1) { IGisProject * project = dynamic_cast(currentItem()); if(project != 0) { if(project->getType() == IGisProject::eTypeLostFound) { menuProjectTrash->exec(p); } else { if(project->isOnDevice()) { menuProjectDev->exec(p); } else { actionSyncWksDev->setEnabled(IDevice::count()); menuProjectWks->exec(p); } } return; } IGisItem * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { bool isOnDevice = gisItem->isOnDevice(); switch(gisItem->type()) { case IGisItem::eTypeTrk: actionCombineTrk->setEnabled(true); // might be disabled by menuItem actionRangeTrk->setDisabled(isOnDevice); actionReverseTrk->setDisabled(isOnDevice); actionEditTrk->setDisabled(isOnDevice); actionFocusTrk->setChecked(gisItem->hasUserFocus()); menuItemTrk->exec(p); break; case IGisItem::eTypeWpt: actionBubbleWpt->setChecked(dynamic_cast(gisItem)->hasBubble()); actionMoveWpt->setDisabled(isOnDevice); actionProjWpt->setDisabled(isOnDevice); menuItemWpt->exec(p); break; case IGisItem::eTypeRte: actionFocusRte->setChecked(gisItem->hasUserFocus()); menuItemRte->exec(p); break; case IGisItem::eTypeOvl: actionEditArea->setDisabled(isOnDevice); menuItemOvl->exec(p); break; } return; } } } void CGisListWks::setVisibilityOnMap(bool visible) { CGisListWksEditLock lock(true, IGisItem::mutexItems); QList items = selectedItems(); foreach(QTreeWidgetItem *item, items) { IGisProject * project = dynamic_cast(item); if(project != 0) { project->setCheckState(CGisListDB::eColumnCheckbox, visible ? Qt::Checked : Qt::Unchecked); } } emit sigChanged(); } void CGisListWks::slotShowOnMap() { setVisibilityOnMap(true); } void CGisListWks::slotHideFrMap() { setVisibilityOnMap(false); } void CGisListWks::slotCloseProject() { CGisListWksEditLock lock(true, IGisItem::mutexItems); QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { IGisProject * project = dynamic_cast(item); if(project != 0) { if(project->askBeforClose()) { break; } delete project; } } emit sigChanged(); } void CGisListWks::slotCloseAllProjects() { int res = QMessageBox::question(this, tr("Close all projects..."), tr("This will remove all projects from the workspace."), QMessageBox::Ok|QMessageBox::Abort, QMessageBox::Ok); if(res != QMessageBox::Ok) { return; } CGisListWksEditLock lock(true, IGisItem::mutexItems); QList items = findItems("*", Qt::MatchWildcard); foreach(QTreeWidgetItem * item, items) { IGisProject * project = dynamic_cast(item); if(project != 0) { if(project->askBeforClose()) { break; } delete project; } } emit sigChanged(); } void CGisListWks::slotDeleteProject() { CGisListWksEditLock lock(true, IGisItem::mutexItems); QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { IGisProject * project = dynamic_cast(item); if(project != 0) { CCanvas::setOverrideCursor(Qt::ArrowCursor, "slotDeleteProject"); int res = QMessageBox::question(CMainWindow::getBestWidgetForParent(), QObject::tr("Delete project..."), QObject::tr("Do you really want to delete %1?").arg(project->getFilename()), QMessageBox::Ok|QMessageBox::No,QMessageBox::Ok); CCanvas::restoreOverrideCursor("slotDeleteProject"); if(res != QMessageBox::Ok) { continue; } if(project->remove()) { delete project; } } } emit sigChanged(); } void CGisListWks::slotSaveProject() { CGisListWksEditLock lock(true, IGisItem::mutexItems); QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { IGisProject * project = dynamic_cast(item); if(project != 0) { project->save(); } } } void CGisListWks::slotSaveAsProject() { CGisListWksEditLock lock(false, IGisItem::mutexItems); QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { IGisProject * project = dynamic_cast(item); if(project != 0) { project->saveAs(); } } } void CGisListWks::slotEditPrj() { CGisListWksEditLock lock(false, IGisItem::mutexItems); IGisProject * project = dynamic_cast(currentItem()); if(project != 0) { project->edit(); } } void CGisListWks::slotItemDoubleClicked(QTreeWidgetItem * item, int ) { CGisListWksEditLock lock(true, IGisItem::mutexItems); IGisItem * gisItem = dynamic_cast(item); if(gisItem != 0) { CMainWindow::self().zoomCanvasTo(gisItem->getBoundingRect()); CGisWidget::self().focusTrkByKey(true, gisItem->getKey()); } } void CGisListWks::slotItemChanged(QTreeWidgetItem * item, int column) { CGisListWksEditLock lock(true, IGisItem::mutexItems); if(column == CGisListDB::eColumnCheckbox) { emit sigChanged(); } } void CGisListWks::slotEditItem() { CGisListWksEditLock lock(false, IGisItem::mutexItems); IGisItem * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().editItemByKey(gisItem->getKey()); } } void CGisListWks::slotDeleteItem() { CGisListWksEditLock lock(false, IGisItem::mutexItems); QList items = selectedItems(); QMessageBox::StandardButtons last = QMessageBox::NoButton; QSet projects; QSet projectsAll; foreach(QTreeWidgetItem * item, items) { IGisItem * gisItem = dynamic_cast(item); if(gisItem != 0) { bool yes = false; IGisProject * project = dynamic_cast(gisItem->parent()); if(project) { project->blockUpdateItems(true); yes = project->delItemByKey(gisItem->getKey(), last); /* collect database projects to update their counterpart in the database view, after all operations are done. */ if(yes && project->getType() == IGisProject::eTypeDb) { projects << dynamic_cast(project); } /* Collect all projects to unblock update later on. */ projectsAll << project; } if(last == QMessageBox::Cancel) { break; } } } // make all database projects that are changed to post their new status // this will update the database view. foreach(CDBProject * project, projects) { project->postStatus(); } // unblock update for all projects seen foreach(IGisProject * project, projectsAll) { project->blockUpdateItems(false); } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } } void CGisListWks::slotCopyItem() { CGisListWksEditLock lock(true, IGisItem::mutexItems); IGisProject * project = CGisWidget::self().selectProject(); if(project == 0) { return; } int lastResult = CSelectCopyAction::eResultNone; project->blockUpdateItems(true); QList items = selectedItems(); int cnt = 1; PROGRESS_SETUP(tr("Copy items..."), 0, items.count(), this); foreach(QTreeWidgetItem * item, items) { PROGRESS(cnt++, break); IGisItem * gisItem = dynamic_cast(item); if(gisItem == 0) { continue; } project->insertCopyOfItem(gisItem, NOIDX, lastResult); } project->blockUpdateItems(false); CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } } void CGisListWks::slotProjWpt() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemWpt * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().projWptByKey(gisItem->getKey()); } } void CGisListWks::slotBubbleWpt() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemWpt * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().toggleWptBubble(gisItem->getKey()); } } void CGisListWks::slotMoveWpt() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemWpt * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().moveWptByKey(gisItem->getKey()); } } void CGisListWks::slotFocusTrk(bool on) { CGisListWksEditLock lock(true, IGisItem::mutexItems); CGisItemTrk * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().focusTrkByKey(on, gisItem->getKey()); } } void CGisListWks::slotEditTrk() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemTrk * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().editTrkByKey(gisItem->getKey()); } } void CGisListWks::slotReverseTrk() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemTrk * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().reverseTrkByKey(gisItem->getKey()); } } void CGisListWks::slotCombineTrk() { CGisListWksEditLock lock(false, IGisItem::mutexItems); QList keys; QList items = selectedItems(); foreach(QTreeWidgetItem * item, items) { CGisItemTrk * gisItem = dynamic_cast(item); if(gisItem) { keys << gisItem->getKey(); } } if(!keys.isEmpty()) { CGisWidget::self().combineTrkByKey(keys); } } void CGisListWks::slotRangeTrk() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemTrk * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().rangeTrkByKey(gisItem->getKey()); } } void CGisListWks::slotFocusRte(bool on) { CGisListWksEditLock lock(true, IGisItem::mutexItems); CGisItemRte * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().focusRteByKey(on, gisItem->getKey()); } } void CGisListWks::slotCalcRte() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemRte * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().calcRteByKey(gisItem->getKey()); } } void CGisListWks::slotResetRte() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemRte * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().resetRteByKey(gisItem->getKey()); } } void CGisListWks::slotEditRte() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemRte * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().editRteByKey(gisItem->getKey()); } } void CGisListWks::slotEditArea() { CGisListWksEditLock lock(false, IGisItem::mutexItems); CGisItemOvlArea * gisItem = dynamic_cast(currentItem()); if(gisItem != 0) { CGisWidget::self().editAreaByKey(gisItem->getKey()); } } void CGisListWks::slotAddEmptyProject() { CGisListWksEditLock lock(false, IGisItem::mutexItems); QString key, name; CSelectProjectDialog::type_e type; CSelectProjectDialog dlg(key, name, type, 0); dlg.exec(); if(name.isEmpty() && (type != CSelectProjectDialog::eTypeDb)) { return; } if(type == CSelectProjectDialog::eTypeGpx) { new CGpxProject(name, this); } else if(type == CSelectProjectDialog::eTypeQms) { new CQmsProject(name, this); } else if(type == CSelectProjectDialog::eTypeDb) { quint64 idParent; QString db; IDBFolder::type_e type; CSelectDBFolder dlg1(idParent, db, this); if(dlg1.exec() == QDialog::Rejected) { return; } CSetupFolder dlg2(type, name, false, this); if(dlg2.exec() == QDialog::Rejected) { return; } CEvtW2DCreate * evt = new CEvtW2DCreate(name, type, idParent, db); CGisWidget::self().postEventForDb(evt); } } void CGisListWks::slotSearchGoogle(bool on) { CGisListWksEditLock lock(true, IGisItem::mutexItems); delete searchGoogle; if(on) { searchGoogle = new CSearchGoogle(this); } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->slotTriggerCompleteUpdate(CCanvas::eRedrawGis); } } void CGisListWks::slotSyncWksDev() { CGisListWksEditLock lock(true, IGisItem::mutexItems); if(IDevice::count() == 0) { return; } IGisProject * project = dynamic_cast(currentItem()); if(project == 0) { return; } const int N = topLevelItemCount(); QSet keys; if(IDevice::count() > 1) { CSelDevices dlg(project, this); if(dlg.exec() != QDialog::Accepted) { return; } dlg.getSlectedDevices(keys); } else { for(int n = 0; n < N; n++) { IDevice * device = dynamic_cast(topLevelItem(n)); if(device == 0) { continue; } keys << device->getKey(); break; } } CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); for(int n = 0; n < N; n++) { IDevice * device = dynamic_cast(topLevelItem(n)); if(device == 0 || keys.isEmpty() || !keys.contains(device->getKey())) { continue; } if(canvas) { canvas->reportStatus("device", tr("Update devices

Update %1
Please wait...

").arg(device->text(CGisListWks::eColumnName))); canvas->update(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } device->updateProject(project); } if(canvas) { canvas->reportStatus("device", ""); } emit sigChanged(); } void CGisListWks::slotSyncDevWks() { CGisListWksEditLock lock(true, IGisItem::mutexItems); IGisProject * project = dynamic_cast(currentItem()); if(project == 0) { return; } IDevice * device = dynamic_cast(project->parent()); if(device == 0) { return; } QString key = project->getKey(); project = getProjectByKey(key); if(project) { CCanvas * canvas = CMainWindow::self().getVisibleCanvas(); if(canvas) { canvas->reportStatus("device", tr("Update devices

Update %1
Please wait...

").arg(device->text(CGisListWks::eColumnName))); canvas->update(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } device->updateProject(project); if(canvas) { canvas->reportStatus("device", ""); } emit sigChanged(); } } bool CGisListWks::event(QEvent * e) { if(e->type() > QEvent::User) { CGisListWksEditLock lock(true, IGisItem::mutexItems); switch(e->type()) { case eEvtD2WReqInfo: { CEvtD2WReqInfo * evt = (CEvtD2WReqInfo*)e; CDBProject * project = getProjectById(evt->id, evt->db); if(project) { project->postStatus(); } e->accept(); emit sigChanged(); return true; } case eEvtD2WShowFolder: { CEvtD2WShowFolder * evt = (CEvtD2WShowFolder*)e; CDBProject * project = getProjectById(evt->id, evt->db); if(project == 0) { if(evt->id == 0) { project = new CLostFoundProject(evt->db, this); } else { project = new CDBProject(evt->db, evt->id, this); } if(!project->isValid()) { delete project; } } e->accept(); emit sigChanged(); return true; } case eEvtD2WHideFolder: { CEvtD2WHideFolder * evt = (CEvtD2WHideFolder*)e; CDBProject * project = getProjectById(evt->id, evt->db); if(project && project->askBeforClose()) { return false; } delete project; e->accept(); emit sigChanged(); return true; } case eEvtD2WShowItems: { CEvtD2WShowItems * evt = (CEvtD2WShowItems*)e; CDBProject * project = getProjectById(evt->id, evt->db); if(project) { project->showItems(evt); } e->accept(); emit sigChanged(); return true; } case eEvtD2WHideItems: { CEvtD2WHideItems * evt = (CEvtD2WHideItems*)e; CDBProject * project = getProjectById(evt->id, evt->db); if(project) { project->hideItems(evt); } e->accept(); emit sigChanged(); return true; } case eEvtD2WUpdateLnF: { CEvtD2WUpdateLnF * evt = (CEvtD2WUpdateLnF*)e; CLostFoundProject * project = dynamic_cast(getProjectById(evt->id, evt->db)); if(project) { project->updateFromDb(); } e->accept(); emit sigChanged(); return true; } } } return QTreeWidget::event(e); } void CGisListWks::slotRteFromWpt() { QList keys; foreach(QTreeWidgetItem * item, selectedItems()) { CGisItemWpt * wpt = dynamic_cast(item); if(wpt == 0) { continue; } keys << wpt->getKey(); } CCreateRouteFromWpt dlg(keys, this); dlg.exec(); } qmapshack-1.5.1/qmapshack.1000644 001750 000144 00000001777 12527654570 016542 0ustar00oeichlerusers000000 000000 .TH QMAPSHACK 1 "July 2014" "" "" .SH NAME QMapShack \- GPS mapping (GeoTiff and vector) and GPSr management .SH SYNOPSIS \fBqmapshack\fP [\options\fP] [ .B \-d | .B \-\-debug ] [ .B \-h | .B \-\-help ] [ .B \-n | .B \-\-no-splash ] [ .IR files ... ] .SH DESCRIPTION GPS mapping (GeoTiff and vector) and GPSr management. QMapShack provides a versatile tool for GPS maps in GeoTiff format as well as Garmin's img vector map format. You can also view and edit your GPX tracks.q QMapShack is the successor of QLandkarteGT. .TP Main features: \- Use of several workspaces. \- Use several maps on a workspace. \- Handle data project-oriented. .SH OPTIONS .TP \fB\-d\fR, \fB\-\-debug\fR Run with debuging output. .TP \fB\-h\fR, \fB\-\-help\fR Displays the help message. .TP \fB\-n\fR, \fB\-\-no-splash\fR Start without splash screen. .TP .SH SEE ALSO . .SH AUTHOR \fIQMapShack\fR was written and is currently maintained by Oliver Eichler \fB\fR. qmapshack-1.5.1/nsi/HOWTO-BUILD.txt000644 001750 000144 00000016304 12616741641 017562 0ustar00oeichlerusers000000 000000 ################################################################# # Compiling and Building QMapShack for Windows (short: QMS) # ################################################################# [Please also read the file 3rdparty.txt] QMS is build with Visual Studio 2013 as 64bit application. It can be build with Visual Studio 2013 Community-Edition, too. Required tools for building and installing ============================================ - Microsoft Visual Studio 2013 (short: VS2013) - CMake 3.0 or later, available at http://www.cmake.org/ - NSIS, available at http://nsis.sourceforge.net/Main_Page - The mingw64 toolchain (http://mingw-w64.org) is needed to compile the routino library Compile instructions - to be verified ===================================== C1.) Compile the GDAL library, http://www.gdal.org/ [Build instructions inspired by http://trac.osgeo.org/gdal/wiki/BuildingOnWindows, http://dominoc925.blogspot.de/2013/03/build-64-bit-gdal-for-windows.html,] - Download the source code of the version 1.11 (or latest) from http://trac.osgeo.org/gdal/wiki/DownloadSource and unzip - In nmake.opt, adapt the following lines, according to your build environment [my settings are given as example] MSVC_VER=1800 GDAL_HOME = "M:\lib\gdal" PYDIR = "C:\Python34" SWIG = D:\gdal111\swig.exe # NOTE: swig.exe is part of precompiled binaries for Windows supplied by http://www.gisinternals.com/ WIN64=YES - On the Windows Desktop: => select Start | All Programs | Microsoft Visual Studio 2013 | Visual Studio Tools | VS 2013 x64 Native Tools Command Prompt. - in the command prompt: => change directory to the extracted GDAL source code root folder => nmake /f makefile.vc => nmake /f makefile.vc devinstall C2.) Compile the PROJ library http://trac.osgeo.org/proj/ - Download the source code of the version 4.8 (or latest) from http://trac.osgeo.org/proj/ and unzip it - In nmake.opt, adapt the following lines, according to your build environment [my settings are given as example] INSTDIR=M:\lib\PROJ - On the Windows Desktop: => select Start | All Programs | Microsoft Visual Studio 2013 | Visual Studio Tools | VS 2013 x64 Native Tools Command Prompt. - in the command prompt: => change directory to the extracted PROJ4 source code root folder => nmake /f makefile.vc => nmake /f makefile.vc install-all C3.) Compile the routino library - Adapt and use the build_routino.bat which you can find in \nsi directory of your QMS source directory C4.) Install Qt5.4 or later - Download and run the Qt5 Windows Online Installer from http://qt-project.org/downloads - Install for VS2013, x64 Note: if you prefer offline installation you can choose the right package in OFFLINE INSTALLERS section of that page C5.) Get the QMapshack source from the repository, e.g. hg clone https://bitbucket.org/maproom/qmapshack QMapShack Note: you might have to install TortoiseHG or any other mercurial client C6.) Start the CMake GUI (you did install CMake before, didn't you) - Configure In the first run there will be errors. Now enter the directories where you have installed Qt5, GDAL, PROJ.4, routino to the respective variables. After that, Configure again. Note: in case that you only get some warnings, you anyway can try to GENERATE - Generate C7.) Open the generated QMapShack.sln with VS2013 - Change solution configuration type to "Release" - Set the qmapshack project as start project (may not be necessary) - Right-Click on the ALL_BUILD project and select build to start the compilation Creating a Windows binary installer =================================== I1.) Download the VC redistributable installer from http://www.microsoft.com/en-us/download/details.aspx?id=40784 Note: in case the restributable files are already installed in your system, this step is not necessary. I2.) Create the installer with NSIS(3.0b1) - Execute the copyfiles.bat which you can find in \nsi directory of your QMS source directory Note: In copyfiles.bat, you may have to adapt the directories - where you have installed Qt5 - where your self compiled binaries of routino, GDAL and PROJ4 are The copyfiles.bat script will create a new directory "Files" which has exactly the same content as the final installation directory created by the NSIS installer will have. Qmapshack does not have any dependencies out of its own installation directory. So instead of creating an NSIS installer you could just copy this directory to an other machine for a quick deployment - [optional] Test whether the deployment is complete: double-click on Files/qmapshack.exe ==> Qmapshack should start up and be fully operational without any restrictions - Run the QMapShack_Installer.nsi script e.g via right click on script file and choosing "Compile NSIS Script" from contextual menu. Note: there also are the options to run the script on command prompt with commandline version(makensis.exe). Or you can start windows version (makensisw.exe) to run the script. Debugging with VS2013 ===================== For bug fixing you might want to run QMapshack with the VS2013 debugger. This requires some additional configuration D1.) Set the solution configuration type to "RelWithDebInfo" Note: you would expect the solution configuration "Debug" to be used. But with "Debug" you will get a crash in pj_init_plus() shortly after start as described in http://stackoverflow.com/questions/19197791/projapi-gis-library-heap-overflow The reason for this problem is currently unknown. Any help is welcome. D2.) Right-click on the project qmapshack and open the settings dialogue - In C/C++->Optimization: deactivate optimization (/Od) - In Debugging->Environment set the path such that all required .dll's are found (see http://stackoverflow.com/questions/2119539/visual-studio-how-to-set-path-to-dll) The path depends on where you have installed/compiled Qt5, gdal, proj.4, routino In my case this is PATH=%PATH%;M:\lib\gdal\bin;M:\lib\PROJ\bin;M:\src\routino_pkg\lib;C:\Qt5\5.4\msvc2013_64\bin D3.) Compile - Right-Click on the ALL_BUILD project and select build to start the compilation D4.) Run/Debug preparations Now you can in principle run Qmapshack with the VS2013 debugger: step through, inspect variables, see the debug output But as Qmapshack expects some configuration files for gdal and routino in the directory where it's executable is placed. If those files are not there, you will get some strange error messages such as "the specified XML file is not found" at startup and the functionality for map/coordinate transformations will be limited. The easiest way to provide these files is to copy the whole content of the Files directory created by copyfiles.bat as described in step I2) _except the qmapshack.exe_ to the directory where you RelWithDebInfo executable has been created (build\bin\RelWithDebInfo). Note: If you really copy all those files, then you can skip the step to set the PATH as described in step D2). Alternatively you can set the path as described and only copy all those configuration files and resources (i.e. all files which are not .dll's + all directories) D5.) Run/Debug Congratulations: all preparations finished. Now you can _really_ start debugging!qmapshack-1.5.1/nsi/3rdparty.txt000644 001750 000144 00000003212 12616741641 017567 0ustar00oeichlerusers000000 000000 ############################################################# # QMapShack for Windows (short: QMS) # ############################################################# QMS is build with Visual Studio 2013 as 64bit application. It can be build with Visual Studio 2013 Community-Edition, too. Dependencies ============ QMS depends on the 3rd party software listed below: 1.) Microsoft Visual C++ 2013 Redistributable Package The installer vcredist_x64.exe (ca 7MB size) as downloaded from http://www.microsoft.com/en-us/download/details.aspx?id=40784 is already contained in the QMS Installer package and will be executed if selected by the user. Note: Those runtime libraries may already be contained in Windows 7 or Windows 8 installations. 2.) Qt5 runtime libraries The Qt DLL's are deployed in the QMS installation directory. They are part of Qt which you can download here: http://qt-project.org/downloads 3.) The GDAL library, http://www.gdal.org/ 4.) The PROJ library http://trac.osgeo.org/proj/ 5.) Icons for the Windows Start Menu ==> We should get rid of them and use own icons kfm_home.ico has been created from the Nuvola 1.0 icon set (http://www.icon-king.com/projects/nuvola/) gdalicon.ico has been converted from gdalicon.png from the GDAL package 6.) The routino library Source code is available http://www.routino.org/ This QMapShack installation uses a routino library which is precompiled with mingw64 to reduce porting efforts 7.) MinGW64, http://mingw-w64.org The mingw64 compiler is used to compile the routino library. This results in dependencies from mingw64 runtime libraries such as libwinpthread-1.dllqmapshack-1.5.1/nsi/QMapShack_Installer.nsi000644 001750 000144 00000024713 12573057344 021631 0ustar00oeichlerusers000000 000000 ;NSIS Installer Script for https://bitbucket.org/maproom/qmapshack/wiki/Home ;NSIS References/Documentation ;http://nsis.sourceforge.net/Docs/Modern%20UI%202/Readme.html ;http://nsis.sourceforge.net/Docs/Modern%20UI/Readme.html ;http://nsis.sourceforge.net/Docs/Chapter4.html ;http://nsis.sourceforge.net/Many_Icons_Many_shortcuts ;Deployment issues ;Deploying Qt5 for Windows: ; http://qt-project.org/doc/qt-5/windows-deployment.html ;Deploying MSVC runtime libraries ; http://msdn.microsoft.com/en-us/library/dd293574.aspx ==> Central Deployment is preferred: by using a redistributable package enables automatic updating by Microsoft. ; http://msdn.microsoft.com/en-us/library/8kche8ah.aspx ==> Distribute msvcr120.dll and msvcp120.dll ; http://www.microsoft.com/en-us/download/details.aspx?id=40784 ==> Download the vcredist_x64.exe from here !!! ; http://msdn.microsoft.com/en-us/vstudio/dn501987.aspx ==> Legal stuff ;Revision Log ; 03-Aug-2014 First version of QMapShack installer based on the existing QLandkarteGT installer ;=================== BEGIN SCRIPT ==================== ; Include for nice Setup UI, see http://nsis.sourceforge.net/Docs/Modern%20UI%202/Readme.html !include MUI2.nsh ;------------------------------------------------------------------------ ; Modern UI2 definition - ;------------------------------------------------------------------------ ; Description Name "QMapShack" ;Default installation folder InstallDir "$PROGRAMFILES64\QMapShack" ;Get installation folder from registry if available InstallDirRegKey HKCU "Software\QMapShack" "" ;Request application privileges for Windows Vista RequestExecutionLevel admin ; The file to write OutFile "QMapShack_Install.exe" ;------------------------------------------------------------------------ ; Modern UI definition - ;------------------------------------------------------------------------ ;!define MUI_COMPONENTSPAGE_SMALLDESC ;No value !define MUI_INSTFILESPAGE_COLORS "FFFFFF 000000" ;Two colors !define MUI_ICON "QMapShack.ico" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "MUI_HEADERIMAGE.bmp" !define MUI_WELCOMEFINISHPAGE_BITMAP "MUI_WELCOMEFINISHPAGE.bmp" ; Page welcome description !define MUI_WELCOMEPAGE_TITLE "QMapShack" !define MUI_WELCOMEPAGE_TITLE_3LINES !define MUI_WELCOMEPAGE_TEXT "QMapShack is a consumer grade software to work with data aquired by GPS devices. The data can be displayed on a variety of maps and stored in a database. Additionally new data can be created to plan tours." !define MUI_LICENSEPAGE_CHECKBOX ;------------------------------------------------------------------------ ; Pages definition order - ;------------------------------------------------------------------------ !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "..\LICENSE" !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY Var StartMenuFolder !insertmacro MUI_PAGE_STARTMENU "Application" $StartMenuFolder !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ ;Uninstaller - ;------------------------------------------------------------------------ !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ; Language settings !insertmacro MUI_LANGUAGE "English" !insertmacro MUI_LANGUAGE "German" ;------------------------------------------------------------------------ ; Component add - ;------------------------------------------------------------------------ ;Components description Section "MSVC++ 2013 SP1 Runtime" MSVC SetOutPath $INSTDIR File Files\vcredist_x64.exe ExecWait '"$INSTDIR\vcredist_x64.exe"' Delete "$INSTDIR\vcredist_x64.exe" SectionEnd LangString DESC_MSVC ${LANG_ENGLISH} "Microsoft Visual C++ 2013 SP1 Runtime Libraries. Typically already installed on your PC. You only need to install them if it doesn't work without ;-)." LangString DESC_MSVC ${LANG_GERMAN} "Microsoft Visual C++ 2013 SP1 Laufzeitbibliotheken. Diese sind meist bereits auf dem Rechner installiert. Versuchen Sie die Installation zunchst einmal ohne dies." Section "QMapShack" QMapShack ;Install for all users SetShellVarContext all ;BEGIN QMapShack Files SetOutPath $INSTDIR File Files\qmapshack.exe File Files\*.ico ;File Files\*.png SetOutPath "$INSTDIR\translations" File Files\translations\qmapshack_*.qm ;END QMapShack Files ;BEGIN Qt Files SetOutPath $INSTDIR File Files\Qt5Core.dll File Files\Qt5Gui.dll File Files\Qt5Multimedia.dll File Files\Qt5MultimediaWidgets.dll File Files\Qt5Network.dll File Files\Qt5OpenGL.dll File Files\Qt5Positioning.dll File Files\Qt5PrintSupport.dll File Files\Qt5Qml.dll File Files\Qt5Quick.dll File Files\Qt5Script.dll File Files\Qt5Sensors.dll File Files\Qt5Sql.dll File Files\Qt5Svg.dll File Files\Qt5WebChannel.dll File Files\Qt5WebKit.dll File Files\Qt5Widgets.dll File Files\Qt5WebKitWidgets.dll File Files\Qt5Xml.dll File Files\icudt53.dll File Files\icuin53.dll File Files\icuuc53.dll File Files\libEGL.dll File Files\libGLESv2.dll SetOutPath "$INSTDIR\imageformats\" File Files\imageformats\qgif.dll File Files\imageformats\qjpeg.dll File Files\imageformats\qmng.dll File Files\imageformats\qsvg.dll File Files\imageformats\qtiff.dll File Files\imageformats\qico.dll File Files\imageformats\qtga.dll SetOutPath "$INSTDIR\sqldrivers\" File Files\sqldrivers\qsqlite.dll SetOutPath "$INSTDIR\platforms\" File Files\platforms\qwindows.dll SetOutPath "$INSTDIR\translations" File Files\translations\qt*.qm ;END Qt Files ;BEGIN GDAL and PROJ.4 Files SetOutPath $INSTDIR File Files\gdal*.dll File Files\gdal*.exe File Files\nearblack.exe File Files\ogr*.exe File Files\testepsg.exe SetOutPath "$INSTDIR\data\" File /r Files\data\*.* ;END GDAL and PROJ.4 Files ;BEGIN PROJ.4 Files SetOutPath $INSTDIR File Files\proj*.dll File Files\proj*.exe SetOutPath "$INSTDIR\share\" File /r Files\share\*.* ;END PROJ.4 Files ;BEGIN Routino Files SetOutPath $INSTDIR File Files\routino.dll File Files\planetsplitter.exe File Files\libwinpthread-1.dll SetOutPath "$INSTDIR\routino-xml\" File /r Files\routino-xml\*.* ;END Routino Files ;BEGIN additional Files SetOutPath $INSTDIR File Files\3rdparty.txt ;File Files\libexif-12.dll ;END additional Files ;the last "SetOutPath" will be the default directory SetOutPath $INSTDIR WriteUninstaller "$INSTDIR\Uninstall.exe" SectionEnd LangString DESC_QMapShack ${LANG_ENGLISH} "View Raster, Garmin and Online Maps combined with elevation data. Work with GIS data. Synchronize your GPS device." LangString DESC_QMapShack ${LANG_GERMAN} "Raster-, Garmin- und Online Karten mit Hheninformation anzeigen. GIS Daten bearbeiten. GPS Gerte synchronisieren" Section "StartMenue" StartMenue ;create batch file for a GDAL shell fileOpen $0 "$INSTDIR\gdal_shell.bat" w fileWrite $0 "@cd /D %USERPROFILE%$\r$\n" fileWrite $0 "@SET PATH=$INSTDIR;%PATH%$\r$\n" fileWrite $0 "@SET GDAL_DATA=$INSTDIR\data$\r$\n" fileWrite $0 "@SET PROJ_LIB=$INSTDIR\share$\r$\n" fileClose $0 !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\QMapShack.lnk" "$INSTDIR\qmapshack.exe" "" "$INSTDIR\QMapShack.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\qmapshack.org.lnk" "https://bitbucket.org/maproom/qmapshack/wiki/Home" "" "$INSTDIR\kfm_home.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Help.lnk" "https://bitbucket.org/maproom/qmapshack/wiki/DocMain" "" "$INSTDIR\Help.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\gdal.org.lnk" "http://www.gdal.org/" "" "$INSTDIR\gdalicon.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GDAL shell.lnk" %COMSPEC% "/k $\"$INSTDIR\gdal_shell.bat$\"" "" "" "" "" "GDAL shell" !insertmacro MUI_STARTMENU_WRITE_END ;Create registry entries WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QMapShack" "DisplayName" "QMapShack (remove only)" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QMapShack" "UninstallString" "$INSTDIR\Uninstall.exe" SectionEnd LangString DESC_StartMenue ${LANG_ENGLISH} "Create Start Menue (deselect if you want install QMapShack as portable app)" LangString DESC_StartMenue ${LANG_GERMAN} "Erzeuge Start Men (weglassen, wenn QMapShack als portable app installiert werden soll)" !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${QMapShack} $(DESC_QMapShack) !insertmacro MUI_DESCRIPTION_TEXT ${StartMenue} $(DESC_StartMenue) !insertmacro MUI_DESCRIPTION_TEXT ${MSVC} $(DESC_MSVC) !insertmacro MUI_FUNCTION_DESCRIPTION_END ;------------------------------------------------------------------------ ;Uninstaller Sections - ;------------------------------------------------------------------------ Section "Uninstall" ;Install for all users SetShellVarContext all Delete "$INSTDIR\Uninstall.exe" SetOutPath $TEMP RMDir /r $INSTDIR !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\QMapShack.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\qmapshack.org.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Help.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\gdal.org.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\GDAL shell.lnk" RMDir "$SMPROGRAMS\$StartMenuFolder" DeleteRegKey /ifempty HKCU "Software\QMapShack" DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QMapShack" SectionEnd Function .onInit # set section 'MSVC' as unselected SectionSetFlags ${MSVC} 0 FunctionEnd qmapshack-1.5.1/nsi/MUI_WELCOMEFINISHPAGE.bmp000644 001750 000144 00000455656 12527654570 021155 0ustar00oeichlerusers000000 000000 BM[6(:''ƿ˽ʽɽȹɻ´ᰛ­ƶʶüռǨzxrۺغg^ĸ˩Юçťˮ¤wlgTOzSKvOGnI?pRK{d^{RJyOGoJ=kK>lN?˘wsphz}{w}w]Xg_Ҷe_c^ϱrnkda[ǪԶԴЫ~wpg]Vb[i]vnfup^[RN\Y̭ԺέŧkeSLwHAzLCVHyOGrJ@vL@wN@rKlK?pPEnNBoN@qQCpO@rUG|aUuVIkN@mK?mN>mP@lM?źķ೔}zxr}ŬĽîsmcZ]Tmh}|rQJvTGjFhGqL=qKnM?nIoPBmN@mO@fJkH>kGnN?mJmJ:lI:lJ9mH:nI:ȵnlzRKuMFuMExYWnjvo~\WqQJpPGmKAnJ@sSGx_SrUMlMBpQDrQErQEmL?iHuVGcYnREiK9hJ7jL>jM?jM@jK=hKlMAmLBkI@w\V|c^dYq^yZKvZMy[Lnf}qaN]H^FwN;rH9uP@|ZGuP>mJ7lL7nM:mK:oO?qSCpPAlKnF4nF4mE5mD7oJmK>nJnGpJnH:mH:lJ:kH:mG7mI8mL;mE8nG5nL8lJ;kI;lM=kK;mL8rQ;pO9rN9ܷqlsWRnQHpQImMG}dZiJkI;kJ;jK;kK:hJ;gI7hK:hNlJ=mGoSApS?mJnL<~iemK;oO?rQ@pJ8oF4oF5nJ:oI;lG9kInM@pP@mODv_]}xxkaOjScM`JbIdK`L^KYDxN=pL;nH6kC2qM:zQkH:jG9mMlG5jG3iI4iH7hE7iJ;kMlKnJjF7iE7jI9mM:mI7lH5jE3jG7iC7jG:kM;kM=mK;lI9nM?pRBnL>mL>mL=lKlLqPBpQK‘pggWiWdPcPeOcLaIaI_KsO;kI5jF4iE2hD3iE4oH3rI4lE5jD6jE3kK8mL9nG4oI2pK5oI6mK7mI9nI9pMhL>jK@lOAnN@kInJ;uT@vTClF6jH7jI7kI7kH7lI7lD5kG5jF7hH;jJ=lI;kJ=kL?jH:rXRqRGmI;mI9mK8kL;mL;xS>|T?qN=mH;hA7xrmhiI9hJ8oSDlO@jM;lL;kL;lM=jG7jD3lJ6sQ;wQ?xQmM@pTIoOAjE5rYP}fcnPCnM;kI9|c]om{c\xYPcVpdl^dNgPeNcLcM`K^JXE|UByV@mJ4jC0iE3jH6gG7hE5mJ6kH2jC0kH4mM;lI8lC3nI3nJ6kF4iG6lI:nJ9mH8lInK=vYJjG8jE5jF4kJ7jI6iG6jH5iG7hF9jI;kL;nI9kF8mO@jK=x[SmgrRImK;mL9lJ9mK;vQ?xV@nL8mH8hB5rlrlkI9kG6iB1jJ7kM:kL;mM:oP{P?zP=xShG:hG9iJ:kK:nN?nN?nK>eD9pglbdA/qP?dYuUHlJ|WBqK8kC2iE6kF4jE3iB2jC2kF3kD/mF2oO;oK8kD4pO=uQ?nK8jF5kF4pN8lK8mL>{XFpL;hE6oO?jG7kH7jE4iE5iI8iF7hE8gH9iJ:iH8kM9mM:jG6uVHvVNjazb\lH7nL9nK9pP=sS?{\JoP?vTCiC9rmeC4kG8kF4lJ6mJ8kF9nK;qQ?nMzS@yR@xP>vR>vV>wS=zQ>zP=xPmK=nM=hI=zd_sYQiB/pN=hb{]SkH9f@0oUJqggL=fH7gF7lK:iI8iH7fG;v`XiL@jI;kG:jH9fE:zskfD6lH9iF;jI=rR?lJ9kF3jE4qPArQCgA7lkqXRkG:qSL{kdLeOlTjOiOeNcMbMaL^H{XDrN>yS@{WEoJ;mF6lG6lG5mF6lD6mE2nF0lC-kE0pN;oK7lG6qO?{XDuPsN?vR@yS?zS@{SA{P?yQ>wR=uS=wT>{Q;{P;{Q;yP=yRxS?Ƶ»x|WGmK>mL?oOBmG:oJ=f_ealPCjL?jL>iH:gD4iH6hH7iH8mK=pP>qP>iF:lhyc`iB3oL>mhhahF:g@2nSExbTeF7fE7gE6kH9iH8hG7gJ@urhKBiIlH6lH5hC5|\NrRAhD;rn|{hJFrdjTgMfMfMfNhNdMcLbK^H|VAuM;oJ;vUC}aJnK:nH7pK8vPyR=vQ;wT=xR=zN:{O:yO=yQxQ>DZk`bNxQ@sQ@mI:kI>rUJmHqO>eA6yuzd\jE3kJ=zjceC6iG3fD3dB0hG6hF7jI9kJqJ:oD8wUIg[uQIielFyS=wT=xP;zN<{Q=yPpUClK9kL>|hcxd_{fbnhzaWgH:fH9fI:fH9gH7hJ6hL8cD4sZMxZHnK;gD9ws{g_fC0mO?|f\pVKgG6hD1jE2hG6hF6jI8jH:lLBpUJbC8mk~hJAiG=jI>mL?~UDYGsQAjG8lG7oM@[MrO@iH:kI:jI6sVCnK=lK=ng{zù[VxdOhPjMfMeMbLgReMbJ]I|TDmF8nE6lD9qSDx\FrP=qO:rP;uR>vP?oJ:rM:mK7jH2jF4lK8nI5nG5yTByZNrmz^VoQAkK:gG7hLnN>nI;nF:rR@oK;oF;z\RofqTOzwkI:y[KoN=vWCmM:lK8kH6jH6kI9lM;kJ9mI5lH4rP9wR:xQ6yQ7xS:zT;zT=|T={P;{O;|P:{O:zQmO;lM>xc\ysw[RkL=iKrlqkjL@jL?hI?uRC^E[D}WFmK>lI;|ZMzXHjG6kJ:jK=eI;|g\nPBlH8rQGrpi`o^eMhNhNdMbMbMgSdMaI^I~SBlE5mD3mH:sSCzZEuR=qP;pL:nLlL=mJ=nJ;pQ?mK;oK>y\Tqjjh҉wsmK?wXIkI9lM:kH7jG4iJ6jJ7kM;nN@nM:mK7pO=wSkJgI:kL:eG9{e^mK?qTGpUFpO@nM@kJ?{YD`F[CVCpK?oNC}^RjF5hG4iK:iI:jLAmgpOCmJ9oL@Ο~pc}naMeNgPeOdNdNaNeNbK_H^G}TBlG7lH5lH8rSDz\MyUBsN;qI9nH:oN:nL6qP;nM9kI7lMxT>vS>xS|bXwqhJAjI=hE:|cWsiPDeE4hG4gH9mQAnSCkL@lLAkODlTMmPEgB4cXuWLiH9rSErRCvXFqQCqQCvUCvP@[CZDoK@qPCrRCkH5jH4jK8gK:kPG|xnTIjG9za[xprbm_bNiPgOcM\GZH_K_J]E}U?XA{U@mH6lF4kH4sUEeZyVErK9qH6oH4nI5nJ5qR>oTCv[Oz_VuYLnN>iF4uRB`UuTLy\PkK:hG5hG7hE5gH6hG8gH:jK=jHrQ@xTDnJ|Q?{P?|R?|P:{Q;yQ:zS8zQ7{R:zU=yU?xS?vS:xR;vVQoMAnMAkI>iI;hF8hA5hE8jK;jF4jC2hH5iH6gF6fC6eD8dI?pXMjIjG8nM=oM=wYHpPBuTEmI;oK;]H]FsPCpODpPAjG7iE4kK:fJ?~}kUMhH@vum]o\iWeQfOfNbKzM9|Q@_K^H{T?kI4uQ;Y?nI6lB4mJ7xWI`SsL;qH6nG5mE2nF2lD3jF4kL:~bVe^|_VlF6kE3qP?}^Ry]RoQ@fF6iH8hG8hF7iH8hJ9kK;iI;kI=pN?uRCuVKlb|_TlL@h[~vqQElK;rSGyvŝeElK8kK8kK9mN:lK6jJ5mM9uP>wQ>zTA\E~U@zQzU>zS;|T9|R7}T:Y@}W@{R>{S?zT>|S>|Q;{Q;xS;yT:{Q9|P9ySjG9jF7hJ:x`ZiRJiPLuiWlYjUfPgPfNbKzO>{R@]I\FqM8kB/qL6}YAsN;kD6nJ<`R]OrL;pI5mF2lE2lD1mF3lE3jF8uYPpmd[lG6lD4mJ9oM@nJ;mJ8hI8iG8hD4hF6jL9jK8jL;mN?pM>rPAwSFxWLul{]VnJ?sl{vWIqSCg]vnujwXKmF7oK{T{Q<}R>~x}\YlPCmOBhE:gF7fG7dA6jN@hJlehF8lG5kF4hD6nL=nL9iH6iI8hE5hF6iF7hG8kN;\HYD{VD{WG{XIwkwXLoKAxmsTEpR@qQ@hG3c@/kH5oJ7mI7kK9kK9iJ7lL:uSAuQ@yU={S<{Q;{R<|T<[?W=|T?{T={V=zS<{R<{S<}V<}U=|S=~YA}Y@zTzQ>{S>{Q<|Q:{T;xTofgHeQ\GqN;jI7kI8kN@lM=oQDg[qUK~gRK~ovddSjUiUePdMbKYDpL8}YB_GZDsL5nG1pJ6ZCZDrK:{R@_M\JnL=rP:mI3hD2jB0kF3kM8s\R~~i_hF8jF5nPAw^QjK:iF3jJ8iI9hE7jG8jI8gG8sTDlYZE[E~XEbVympNB}_XsYNnN?mK9iH5kK8jJ9mL9lI7mJ8pOzS=}U<|U;|S<}Z>~[?{Q;\E^G{V>}U;}S:~T<~V=~W?dKeI~Z@zVgJzYImX[E[HrL9lE0uO<[FzO;sJ7\Jl`aPpN;iB/kI4hF2iB1iF5gMBx[Q|`OjH6jD2lK:nN|S>lWfSYBW@}S<}S=~T?\DnRgHcD_D~X@zV?}V@}S>|R=|R=|Q<|T=|V>|V>|T=lD9lIpS@gF2gG5iH7mK?xWIsKA}XJ~YG~UE]NeO]HzTDpLD}\UpQClK8gD3yov[VdaxssN>yV@wS@}\E{W?sK9~\IlRIoO?gE6jJ;bD5unhLJqrk]iViTfTgSdNcNcO\GjI6wYHbNYE^JwP=nJ4vS@sM:oE1rJ5`Nunj]rSAiA1lJ7iH7kE4iF7pWQȾ–~z~[KvXChI5jH6iD4jH5iI6hG5jI6jJ8iK;jJ:mJ9lJ;yYLzWJnD4[HwN@|qgsN>qP@hG6jI5lK8nM~T>}S<S>VAfR_I^G\F~W@~T?~R=\DiOaH`DdH]F}U@~U>~T?}T>|S<{Q<|T?}W>~V>~V@pM@mK>kHjM>hH;jG:iIjF7nI=cWpMBwmsi\L]MZJxSCuQBrOAwUHmM@jG7hF7aVnLF`YyO?sL:rM8ZBzR=oH7~_QsqhnL@fC3iD5kH9_C;mVSkXhThRmWnYeQbMaKyQ=iF5sQ?XE\HbH|VBsO>sP=mG1nF0pK7`MxpcXlL=kD4mJ8kF8nE6kG8yZQxqrQ@pQ=jF5jF7iI:jM:jH6hD4jH6nN>oRAkK:mJ9mI:zWLzUHqJ:[JqL=g^uRHhD2iD1jJ8kK9rOuP@YB~WBoebW~YG\H[DV?YAZA\B^FU@VBiScObL`IWBT>R<^FnR]DV?_G`HbQhV~U@V@T?|S>|S>^GZDWDpO@mM@lJ?kK?hJ=hJ=iJ;kK>vTJe`vVNrSH{_YbWhXj^z[NiB5oIjH8iH4kL8nP?pR@nM;oLoK6zY@yVAyT@~WB[Ei[sj^IaK^GXBWA]CaEfK_I`KyclWeNcMYEU@~Q?bKkQYC}R=[F^KshshZCW?W>V?|S=dQbP[GlL@mPDlK?jI?jNBjM@jN@lL?vUJmfzXOlJC{pcRykeWsP@yR@[FuQAuSCcSxVGrQDyUIlH:`WfHBumogjK;gE:ZNwRBoK4pL7|_N{`TeA3kH8iG6{`TsXIeC2iI6hI9xWLĺnOKcTphXiQgTq`n\gS]H~ZDvU@qN;yVFkZ\I|U?yS={W@qN9lI4mI3jB/|b[|e^uYPiI=lK;kE1mI5lK6rN=uN>lB3kG5kJ7jD4jF6lJ8lI8iI7jK8iL7oS@~bUnN>nM@|_VzZMpJ:oF3pH3lF3mJ7lI6jH5lI6uQ;{S=|S>|U@}VAxO9{W>}W?~V?~T@\EaKdOfNaK]H]GeKcI^HaKeM~kmZeLdL\FXBV@\DhPYD|Q=WC\Isem\aL\DU?XDW@lXhU]GkI>pSEnQCpRCv\OoVInQEmOEwYRxrf`nICzwl{WH]JcO}ZI}WD[FxTCsL>sM?z[PmdzZNoNBf[lF:cXibmN@fH=Ź\P{TBrO8pO8uT@sUBhI5hF5hD4pUGmP@fD4jJ8lM<}[Py[VmYq]hWgPdNjsbaN^G|XBxTBqJlS@hH6{_SysUGnJ;z^PoM?nK=lE5mF3lH3lJ7kJ7lK8rN;{T>|U?}S>~U?~W@|S?|U=|W@}U@|T@ZD\KeThSdRZJ`JbLaJ]J^IiOpePfJfK_J_J[DXAhTYD}Q=T@[BiSfPfMeMXD`JbM~km[dWlJ>uSGaR_RocqZOpSGoPFfZ~xrNEl_pewUI~^MsUFkJ<|YF^H~XFpE=~[Riawrx\SmL@qVHiE8~d_~jblM?fE?úzq~WG[HvR>pK6qQ>sWBgI3iH5kG7iH:jH9jJ9iJ:lNoM:pL:lH:sXPsqmm}fbeF}T=}T>|S?~T>T?T?~U?~V@}U@~VBXC^T\MfPfQ^LaMcMdNbK`IhOqY_FcHaH]H`J\FZHgS}WE}Q@T?X?aHbMcLfLbLiNjRw]hRh\pOB[Mk_uovVMoNApM@mK@bSo|zSEdSp`xUIyVIlK=iG7|ZIbN[IqK@i\{[PbZqQElL?qUElOE~~qPFlH?qkrj}ZIyU?qP:mH3v\LqXFhG5kJ7kI:lL?nP@jL>jM>fJ>niiWjS}q}}eQbL|T@qM:xUBvR@{UBzVAuQ@}`R{pfhD8kG8pL;\FyXCpO=pK=jC9}b^zc_z]ShJ;jJ:kF7kD6iH6iG5kF3jJ5iK7hI5iI7hE7hE6lO@oS@jM~S=~U>~U=~S<U>V@~S?S?T?}V?ZDXBYC]Pz~WFo]^J]JdPiT_I\E_GaGeIbG[BZC[EaH[GVC^GXC~R@T@V@`GdLdJdJeLfNjPkNgN`PvTG\NneuQJnJ;nJ=qK@bQnY{gcPgShY~ZPgZpO@kF:gSgQZIoI@hcmQGqPBoK=oN?nRCr]W~fapOGrkrpdrQ@kJ3jG1lD0mLjJ9iJ:kKrRHeA8mOGlI;lK:lK;jF8iF5iI5jH5iF4kL7mO?mPBiK;jF7jJ;}cYkOEeKDz_TlH7mK8kH7mK8lK7qP:zU?|U?T?~T?~T>~S>}U>U?U?S?~S?~U@~X@]GVAYE\NpdYHsc~ZEXB]J_JZCYA_E_F_G_CY@X@]EaG[DUAZCYCS@T@T@_GeIeLbKZH_JhOkRfPaOjXgTvgz\VqM@nJ>xQBjUmYubgRhQfQeVk[sO@nH>m\cOuOBhI@rPFpOCoNoJ:xUFgWnN@zvgC;mghbgD6kK;kJ9hF8hG5hG4iH6kK6kI8y[LsXJjKqnuy¹e_pNCnL=mJ~U@~W@V?T?T?S@~U@W@U?S?V@[H~n`N~YDbM~W@U?W@XAXBX@[B\B[CZBXBYBZB\C[BU@\B\CT@T>VA^GdHcIcJ]HbIgMgMhObKp^o_yj~c[zZOzXJ]NeSfU|o~UHfQfQ~XIj\uPDrK?`PxUFlLDeE>miqllL=jK>»vYRiHFzrh[x]TjJ8nP=hG6jJ8gJDus{gblJEva`ȇvrkLBpml]\Iy|dPdQuPC^Q~\OuQBpMmI8lO@jJ;hF6gI6hI5iI6jI8qREi[x[NoQ@yWDqVLо]WrLAyVGsOBqL?vT@yVAzUA|VB|VA|W@|W@|W@~V?T?~U@X@~U@T?V@~R>~S=~U@T@~T=V>XC_I\EYA~ZA|U>T=}VA~XBV@V@X@Z?Y@XAV@XBZDV@V@V?]@Y@W@X?WA`HcJfJeK^FcIfNeLcL`JkXo_vjmkz\Tm\hWYJ\IjZyLAZJ|XGvPAwh~\NxTGwnsUMlLAhSPjM?kN@ùjhvuғ{wxUG}\MvVFjI9mQ@iH8fG@vb`srlKCc]j[]JxugRhT[Oxj`aRvUDjE4aTyWLrO>eV}]OnJ<|ZL~YKmG9rP@lI;iG9kF6lJ;iK>iI:iJ9jJ8lK7lK:bRr^cPrPBjarLCoJ@}YK|XE}XD{VA}ZBZCUB~VD}WE|VA{W?}W>~X>~T@}S@W@}W@~V@XA~U>~U?~U?T>U>W?[B\FYD~W@W?~V=X>X?X>~V@TAU@X?Z@ZAUAVAXAV@U@U@U@U@X?Z?V@]EaIaIcK_EcJfM`I]C_FnZqaxjzusigW{YH]IiTxPC{WH{WErRDxl~[O{^SoSMeICbKI¿kI:mO@pRHuXL|^QrRCkJ:mN;gG9~ieƼ{WSƾrSJf_n_eSsi|qhTfPcTc[fYpPBkE7dW}x|sz[NyZPyZQvSEmJ>|\TwVJkD6mH8mJ9jF6iE6hF6jK:iI9gF5kI6iF6kM?cWykye[xQAwQ@vQC~ZI}WDWB~VA|XDWBWBXC}VC}UC{V@~Y?X@V@U@V@WAXAXA~T?~T?V?V=U>W@ZC]EYAV@V?W>W?V@U>T?VA}W@XAZ@YAVAWAUAU@V@W@X@W@ZC~[C~XA\FZEWB\D_GcJeI\DW@_Bl\ynwj~b]z\OzYH{YFvdcUuRCyVGvUGeSyVGwUHrcoK>mRMſrWTmgiI?pRIſæz]TlL?lTI}g^rQBnLXC~U@|S@~XA~YBW@W@~VBUBVCWD~VD}VC}VF}XA~XAT@X@Y@~V?~V@XAV@~T?V@U?U?V@ZE`G[CUA~V@W@~UA~U@T?S@V@W@XAYBV@U@~X@~YA~V@~UAW@YB~X@[C~]E|YA]F[DU@YCXD\EeK\FV@]Dxo{YTvUHxUFxUDi[n^kE9yr|b\h]wUFpL:{ZGlG:jfrnjfuWMoP@~\Mۙz`VnQEnPBlMArXPrULmL?fGW>W?~X@WAVBVAVBXDVCUCVCVAVBVAZA~[AW@~TA~V@~Y@XA~XAX@U?V@YDbI]EXCV@~W@~WCU@U@~TAT?V@WAYBW@V@X@~Y@}U?~W@\D[E~YC`H|\D}YA]D_FWAYBXD\EdK^HVC]Gzuc[|\N{ZL]NnezfE=y`^rp}_WnJ{X@uR?vU@|YA~W@XAT>T>W@X@XAWBYDUBTAUAUBVBXDUBUBXA~XA~XA~Z@}X@XAW@V@XA[BV@W@\DaI]EWA~WA}WAYAV@V@WAW@XAX@XAXBWA~XA~[B}V>W@]EZF[IaK}YA~Y@]A]BXAZDWC\EbI_HYE]GzYUslg^_Ph]volMD}v[XuqjD;rO@hEfW|wwZQeU]S“sUNrSBlM9kK9jI8iI:gG9hJ8pQ?vTBzYEzUDpO=mO;lN;oM~YB~WA}W@|X@{W@}ZA~YAXBWBU@U?W@XAXCXBXBW@~V@TAVAU@XBXAYB~[D~XD}YA}\A}[@~[@Y@W@YA[E~WCY@\B]B\DXA~WAYAXAVAV@WC~WAXAYCYCZCXA~ZC}ZC}X@}W>[CZFYI]HZAYA^DYBU@ZAX@\DjQ\EWC[Ez}usPI~q_Yg`x[Uy]SrkwZUiF@sSEjG={rlzrul[KsK@rOC|rqUKoQCmMAqQDx[MpZT{wqPF^Syqzl`sd]nYRwukeoF=nC9vL@yqzwjdv[VɫhJ>nN9jK9lN9iJ9jL;iK:nP=zWA}XA~XD}WBzUAsR@zW@}XB~WAWAV@~W@~W@V@}XA|WAXDXDVBVAWBXDWDWD~WA~YAX@V@~YBV@W@ZA~ZB~[D~YD}YD}YC~Y@[?Y@X@W@XDYEYCVAW@X@[BZ@~ZAXBXCW@XCXC~XCXCXBXBYA~YA}X@W@Y@[BZDVCYCYCXA]CXATBWCX@\EhQ[CVAWAoH?c\g_u~wWSvSK~{lhf_zYSvWQlOHvVHy\U|wpj{kesO?nK=z_RxjnODqQBoMAvZNs[Qs]YǿżùsVO}^Y|TLripLCoG:xPByڠyWMnN?lM:kN;kM7kL:kP@kO?vV@|ZB|YC}XBWAWA~XAX@YCWBWAXB~WA~V@~U@~XBXBWAYCWCUCUCXDXCXA~X@|[@~[?~Y>X@V@WBXBXC~YFZFYEZC}YA~Y@X@W@V@ZFZEYBVAUAV@Y@[B~[A~ZC~ZC~ZCXB~XB~WC~WCXCVBYCZEYCXAXBXCZEXDXCXBXAWAWAVAUB~U@~YC`I[BX@Z@rSIqzzqoOKvXRwqwUOmLJǿ®nMEzVK{q}~qloOAnM@zcZjdoMCpOAoLBmYR~zxǿ·{ba|ulyKAcTq`wdwsPFoH8sNvUAxWC|YB}ZD}[F}YC~X@Y@ZC~XBV@V@U@U@V@WBVAV@Y@Y@XAYDVDVCWBZD~XAY@~\@~\?~X=Y>W?V@YAYCZF[HXFWDYB~X@V?Y@YB]GZDWBWB~XBW@W@~ZC~X@~XA}ZBZA~[B~YBWCYE[EXBYC[D[DXBYAXA~XDYEWDXCYBXBWEXCWC~XC]HcNYCZB[CcU}n~uv\Y|^[ĽuodaľqPHod~x¸újKAmNBzwoRFoMCfPMĽ·qm_zOEfWo^{gvxVLnH:mG:c[Ǵv}WLsPC|YJ}YE|XCyXCzXB}[C|YA\A\BYC[E~\F[FXBYBZDYDWAWAV@XCYDZFWBW@X?~Y@~W@~ZC~YCXCXA~YAY@}YA~[BY@V>W>X>V?~ZAZBZD[GZF[GZEX@YAXAZBYDXCXBYB~YB~X@XAWAX@~XAX@YA~YA}X@}WA[D\DXBWCZCYCYB[AYB~YD~ZI`O[EYCWAWA~YC~YC~WD\I`JZBYBYByjjYwǕspŻ{[YԨogd^~˼㳨gdoMBmPEydbgIAn][ûĸù´~owhxiugmpsr~rULsTCkPCɬlb[K[J`OZIZFZE\E~\D}^C}^B\CZAZBZEZFWEYFWDYB}\G\GZA[BXBXEXFZDYBYA[?~\@|\@|ZA~ZAXAWBXAXC~YDZBZ@~X>Y@Y>X?Y@XAYDZEZD[FYEYBXBXA~YC~VCVC~XCZB}ZBX@YAXAYB[DXA~YAXCXB~YD\E\DXAYB\D^D\AY?Z@YB[H`LYDYCXCWCXBYC}WC]F\EXBW@XBsghT{lyržρffqqĽ̛~浮ywmMGlSOrfkv{n~nqn{k{lyokdiI;sUHudcS}WF[G_LbM^LYJZHZF\E\C]D\DZB[CZDYE\FYFYGZE[FZGZEZBZBYBWCWCYBYDYA\A\A}YA~XAWBZCYEZFYE~XF~ZC[B~\@Y=X?Z@ZAWAWCXDWCYFYEXBYCYBXB~XB}XB~WAXB|YD}YCYB~XBZBYB~YBWAXDVCWCYBYA[CZB\C]BYAW@XAWA[D\GVDWDWFXFXDXBXBZCZEXAW@WAogfWzrki{ppøjhuu׹z[VuVN|kkz{sxo{§`VoMAbPePcQiU_M^K`K_JaI_G[C[C\F[D[DZCZC[DZF[G[H[H[H]FZE[EZCZCZCXDXCZDZFYDXDWAWCXBYBYCXBZE[FYDYD}[E}]D[@YBYBXBYCWAXBVBYCXEYEZFZEWDXA~XA~YC~YCYB~ZBYCZDZBZD}ZD~[CXBUAVAXAZ@[@ZA[AZAW@XAYAW@YBZEWCVEWEXDZDYBXAX@YBW@WAWAxk`Uedqmþ䘆|c_spĿ{ikkWYǵױéxheVcUxj`RaNkWcSfTseaR^K`McMdJ`H\E]F\F[F[D\DYB\E]G\HZF\H]H\E\DYBZD[D[EZEYC[DZDYGYEXBXBXEXDXBYDYCYEYE[D\G~]D}\D[FXDXCYCYCXAXBZCYBZDZD[DYCZB}[B|[C~YEZD}YC~ZBZBZB~ZD~ZC~[D~ZCYDYBXAYAY@[BYBXAZCZCYCYBYCXCWAYDYEYEYEYDYCYAXAXCYCWCyvQJf`ji·ޑqVLv\W⺟i_wUMiKFlMD{VKh\{rѽҸq_UvRDcSp\s^oWp[bPeRhWcQhVl\^N_NePhUeP_L]G_G]F]G[D]E]F[E]G[IXH\I\GZF[DZCYD\E\D[DZBYB[DYCYCYDYCYDXEWEYEZC[EYDZBZD[D[CYEXGXFZFYDXAWAZCXBWDXCZBZB~[B}[B|YC~ZF[E[C[DZDZBYBZD~ZDYDYDZDXAXCWAXBZBZC[F[EYDYDXB[DZCZC[BXB~XC[EZCZBXB~YDZAWBf\uMBwSMuu斅dLHe`qMBzYJaQvVMzXN_M`M`L]I\K_SdYpdvssfk]dTXH^IgSlVoXlWlUgPhPrZgRcOkWodqecS[L_MePhTfQ_I\H_I^I[G]G]F]G[H\J\H\G\HZFZFZD\E[E]G[E\D[CZA[AYB[AYB[D[BZEYEYF[HZEZCYCXAYBZDXEXFXEXEYCZCYCYCYDYDXBZCZA~ZB\C~ZD[F[DZDZFZDZDZE[EZEYEZD[BZBYAV@WBZC\CYCXC[EYEXB[E[D[D[C[AYCZEYCZCZBZBYBXExYTsI@{_\úŻجÓplrNDuQFxTG`MbK]MZL]K^K`JbJbJbMaO]J|ZD|XB|XD{XC{XA|ZC_I`L^K]J_KdOiRkUfQhPkTfNoWnWkTjWeY^LaMfRlZfScN^JaL_J^H]G[E]G]I^G]H[G[D[E\E[D\D[D\G^GZCZC[C[BZAZAXBWBYCZDXEXHZJ[E]E\G[EZC\DZEZGYF[EZDXCYDZB\D\CYAZBZAYB[E[FZD[C\CZEZC\EZEYFYFZGXDZCZDYCYBYCYDZEXAZBYDYEZGYFZC[CZDZBZCYBZB\D[DZDYBWCxscFCüɾǹƷż~\UvOA|XIvSFzWGaI_H^I\J[I[H]F_F]F^J]I^H_I_J`I`I_H]G^J^KYFYEYF]F^GbL`JhPlR`IfQnVnSlSll_cNdNjVo^yll^]KgQ`JbK]I]I]GcMaJ[F[E\D[D\F\F[D\D\F\DZB[EZB[B\C[DXCWDXDYCXGXGZG[F\CYBZCZB[EYF[F[E[EZDYCXCZC\D\DXAXAZBYB[EZDZD\E[D[C\C]D\E[EYFYGZFZF[G]F]D[C]F^HY@[CYF[D_JZFZDZC[E[EXAXBZCYD[FZEYDYD½ȿƹij´¾c[uRC{WE\K|XH\G^F\F\F\H[HYE[F[E\D_G^H]I^I^H]F^G^H[H[FXDYE[EZE\F]HbJaJhPhQ`JgQnVlTmUmVgY{pfWgSgUk]wpd\HjRfMeM_H^F]FcLcJ]G\G\E[E]H[E[D[D\D[DZD\F\F^E\EZEZFXEYDYCYDYEZEYG[DZBZ@^C\E[E[D[DZE[H[F\D[B[C[DYCXB\C\C[CYDZFZH[F[D[DZC\D[DYEZD[C[E[F[D[D[E^G_G~ZA[D\E]FbJ^EXBZAYCYCYCXBYDZFZF[CZCZCĽú¸ȹj]~]K{ZI{YF`K]H]E_G]F[D[G]I\G[E\G]D]E]F^F\E^H_I[F]H]H[EYC\D^E^E]F]G^G]GgNmR`LgPoSoVnVoXkXzovffVn_xll]_LnWgNeM^J^H_GbJaI]H\G[D\F\H]F^F\G\F\C[D\G[G]F_D_E\FZEZEZD\D[CZCZE[DZCZB[A]C\D[C[EZG[G]G\DZD[DYBXCWC\E\C[CYEZGYFZF~[E[EYCZDZEZC[C[CYBZCZC\EZD[D]F~\E\E\F\E^G\DYAYAYCZC[CZCYDYCYC\E[CYCûظfWaJcL^K^I_H]H]F\E\EZC\F^G]H[E]E]E]F]E^F`H`I_I\G]F]F\F\E^D_E_F\E\D]F^HdJmPeNfOeMhQmVnXmZlZsap^dPq^lZcNcLhQ_JaK^I\E_FcKaJ[I\G\E^E\G\H]H[E\D^E\C\D^F]E\C]E[D[E\E[C\C]D\D\EYDYCYAY@[A\C\D\EZFZE\G[E[F[F[DWCXC[E[C]F\E[EZEZD[D[EXEWFZF[D\F^EZCXBZC\E[E[E[EXC[E\FYD[E\DZ@X@XB[C]D[E[CZB[C]FYBWBɺ}`MbJdL`L_K_H_H^G^G]E]F[F\E[C\F\F]F[E]D^E_HaIaJaK^G]F\D]E^F]E\F^G]J[F\E^FbGiLfNfP`KaKkTnXnXnVgRaQaOl]{lgUfPfQ\JaNaL\FbHfJcL]J]H_G^H]G]H[H\D_F^E\D\D]D\DYCYB\E]G\G[G\E\F]H\DZDZC[BZBZA]D\E\D[FZF[G[G\F[D^GZEZDYDXE]E\G[EYC[C[D\E[FZEZHXF\H]IZD[CZA[C[D[C[CYB[DZC[EZD\EZCXBYB[DZDYC[C[A[C\E[GZFŮǵҺī{ocdP_HeLaJ_I^J`I^I^G_F_F_D^F]G[EZC[D^F\F[E\C\C\F]GaL`J^H^H]F_G]G]F_F\G]I]F[D\C_EcKbLgPeNaLhQmUoUnVgRbRdPlZ}rcm_o]_IbO`M^HeJkLhM]H_G^I^K]F_H]FaJeM^E\EZDZDZE[E\E]F\HYG[H\GZFZI[GZDZCZB[DYB\E]HZE[E[EZG[G[EZC_E\D[C[CZC\D[C]D\E[C\D]F[DYEZHXEYD[H[E\D\D\D[DZDXCYB[B[BZBZCYBYAXB[DZDXCXDXCZB\B]EZFZE}l^aPbNdNgUhXjXj[i\k^gXdTaP]K^L]K_JdMbKaI`H^G^G^J^I^G]F]F\E]E]F\F]E\D\E]F]E[G[FZBZC[F]J\H\H]I]G^G^F^F`H]G^G`F`F]E]F^H^J_HfOdMbLkTpVnXeSbPgQgRwe}nuiwgcObP`N_JfIjMfN`H_G_G_I]H^F`FaI`F^D\FZD]G[E[E[E[FZGZG]I[G[HYG[F[E[E]E\DYBZC[D[D]EZDYDZEZD\DaF[D[D]E\D]C\D]F\FZD[C^E]B\C[FYFYFZG\E]E\E[DZF\HYD[C\C\AZ@YBXAXAYBYC[DZDYC~[E}[CZC[FZGZFbSbOgPgTdQgOhMfNfNfOcObObObPeOdO`N`NcPbM_J^H_I_I^I^H]F]E^G[E]F]E\E_H]F\D\F]E]E\H[G[F[E\E]I\K\I\G]G\E^G^G`J^IaI_G]F\E\E\D[EYF_K`J_JgPoUqVjTiTkTfPq]qo\iWbO`MdO_KdKcKbJaIaIbJdLaK_I`GaH^F\F\G\H^H\G\F[D[F\G\G\I\I\JZG[F[E\E]F]E]E[B]D^E\E[FYG\GZD[C^D\D^C`E^E\D\FZEZD[D\D^E\C^D\EZEZDZD\D]EYB\F\G]I[HZE\F]C]B[BZCZBXBXBZCYDXE[E[D\F\FZD[EfTeSeQcPbMcKcLcLcNcObOaM`M`MaMaL]J\I_K`K]I]H]G_I^H]G^F]F^F]E]E]F[D_F^F\D\D\D[DZG\F^F\E^F^H\H^H`H_G\E\E^G_H_H\H[F]F^G^F\E[DYE\H^I[G_IjSlSnVoWmVfQnW{elXgQaKaLgO`LbNbNdMaIaIbJgL`J`IcJaI_H^G_H]I`J^H]G\D\E^G]GZH\I[F[D\E[D[F\F\E]G\C]B^D]D[F[F\E^E]C]E`EgEbE_F[DZD\C[BZDZD\E]D\E[FZDZBZCZBZC[E]H]I]H[G[D]E]D]D\A\B[D[D[DYBZE[FYDZE\G^HZF\FcPdPdNcM`KbJbLaLaK`L`KaJ_J_K_HaIaJ^H_IaJ^H]G^G^I^I^I_H]G]F^F\E]E\F_F]E\E\E\EZF\H\F]G\F]FaF]E_GcH]D]D\H\G^G_G]I^I_F]F\G_G[DZE]H]G\G]EiQmUoWoWoVfSmXmWiScMaJ_J`JdLaLcMiPeMbIbHdI`JbLdJaJ`H`I_JcNaL]F]F\E\F\DZCYF\G[D[D\D[C^F^F]E]G[F[C~]D[C[D[E\D]E^D]D`FcH]F^G[GZF[DYCYCZCZE]E\EZEZC\B\C\B]D]F\H[H\H\G[E\D\D\EZD[C[B\E\EZDYCYC[E\E[D]F\FZDfQcPcNbMbMdNdObNbL_I`IaI`HaKaIaI`IaJbIaI^G_HaH^J^I`J^G^F]G^G\E\D]E_F\E\D\E\E\F\I\G\E[E]DcG]E^F`H]E[E[H]G`H_H_J^I\G]F_G_G^G^H_H[H]G`GkOlQkTpXpWiRlWlXkWcOaJ_H^HdLcOaMkRgOcIdIbI_JaJbLbL_J^J`LdQ_J^I`J^F]F]D[C\E_G^F\E\C\C^F_F^G[F[F[F^G\DYD\E\D]D\C\D_F^GZH[I[HYF\E]C[CZD[F[GZEZF[E\D\D]C\C[E[I[IYEZE[G[G[D[E[D\C]D]EZC\D\D[DZD]F]E]FZDZEhScNbNcNdPdMdMbLbKcLcLbJcJaKaLbK`H`I`HbI`I^GaI`I`H_I^G_I^H\E\E\D]E_F]F\F]F^H`H\F\E]G]G[E`G_G_F_G]F[HZH\H^G_H^H\F^I_H_F]H^I^F^G]H]E_HeNlQmQmUoVmTnToWmWgQcL`IePq^fPdLmTeNgOlQcK^H^Im[n\aNbOeReQdLaL`K]H[F\G]G]G]G\E\C^C`D^F\G]I[G[G^G]D\CZC\D^E]D]D\E\G_I\I\H]G]F\D_C]D[E[FZE[EZF]G_G\F[DYB[EZE[GZGZE^G]G[F\E\F]E^E]E^F^F]E^F\E[D\D[D[D[FhTdOcOcMbNbNcMbKbKbKdLcKbI`J_LaL`K^J_I_H`J^I]I]G^G^G_G^G]E]E_E]E]D_F^F\F`H_H]J\F[E\F]F\G\F^GbJbIaI`L\J\I]H^H]G\D^G^G^G[G\F_H^H]G[G]GaGjNjMlQnTnRpTnSmUlThO`GhOs^cNdNjUeSmWqWdM`I_JhVxegUeS{ik\hWeRbL^J^K]J]H^G]F]D]C_E^F\F]G\IZGYF_H^F\D[C\E_E]D]D\DZE_L\I]H_H^F]D^DZD[E[EZEYEZG[G\FZDZDZD[DZE\G\G[F]I^F\H[G]F]F]F_G^I[F[G]H\H[D[D\D[C]DlVfReRdPbOcObMcLbJbLbLbKbKaL_K`K`LbLaI_H_I`J`I]G]F_G_G^D]C_D_E_D^D^E^F\F^H^J]H]G_G^E^E^F]EaIhNeLbIbH[I\G]G]G\F_H`G[E]E\D`I^H_I]G\G]H`GeHfKiMnQoRnPmQnUlRiObMeLjNfPcNiUjXmXkTeObIaJgT|mn``Pl\i[n`eSeO`L`L^J^J]H]F]F^F]F]F]G\F[F[H\F^E_G^F\D^G`E_D[D[C\E_H]H]I_I^F[EZD[DZC[I^J[FZGZG\H\G[EZDYCZC[E]GZG\D^EZEZC\C]D^E]F[FZFXF[IZG[E[D]C]D\D{elYgSfSdPeQePdNcLcM`N_NaNbN`MaL_K`KbJcJ_HaJaJ^H]G^F^E^E^D^E_FaF]G\G]F^J^I^J^J_H_G^E^E_G`JbJgNaI`HdL_I^H]E\E\E`H]F]E]D\E]F^G^G]F^G\F^GbHgKkPmRjPhNiPjRjOcK_KbMfLcJbKfPfSmZnXdNdMeQhUpxhdPhVm\yhiYeR_N]K_K]L]H]H[F]F^F\F]H\H\G^G[D[E]G]F\G]G]F^F[C[D]EaIaJ[I[G]F\FZE\D]C\FbJ]FZF[G\H]I\H[HZD\DYC[EZE\B\C[CYC\F[D\D[F[F\FZFYEZF\F]E]F^F]Gy|klYiUcPeQfSjSfOcP_P`ObPaLaMbN_L_LaJaJ`IaIaJ_G`HaFbG`F_G^D^CaC_F]H\F\I]I_K]J`HaG^D^F`JgNgM`H\EaHeMaG\F\E\D^E^F\C^E]D]D\D^H`I\E\D]F^H_HeLiOkQfPdMeMfNhMbJ]F_I`H_G^GaIdNfPiSdNdOo\kWkXfVv_r`revesgTaK_K_K_K^I]H]G^F]H\E]I]I]G^G\E^F^H_K^J[F]F\E]C]B^EbI`I\E\E[DZFZE\E\E^F^F[E[G\F[E\G^H\IYF\DZB[D]F]D[B]D[D\G]H\E]E]EZDYD]E]G^F`H^G\EZFywgr]mXeRgUpYlTcPbQbPcPbMcLbMaN`M^H_G`G`GaH`G`F`H^G^F^F^C^B]B_B]B[D[H\H\G]H`HbI_F\E`JkPhN`I]GcJdJ_H_H`H]G^H]H\D^F^C_C\E^G`I]F[E\F[F\FcLgNgMdLeMcMfMeLaL[F\FbJbI`H`IaIfNhPbK_MkYjYgVdUq^vk]vhgUcLeLcK`K^K\J^J]F]G`G_H^G]G^G_E_F_G^J^H\F]F]F\E\F^FaH^F\EZE[E\F]G[E\F_F[DZEZG[F[D\E_I_HZEZC\D\E]E]C[D[C[C\D]E\D^F]D\E\F_F^G[F[F]G\E\ExuugveqbfTjVhTfSeRaObNcMcLcM`M`L`I`G_E_F`HaH`D`F^H^F_F_E`E`D_E]E^F^G^G]E^F^F_G_I]G`JeNcM`J^HcJeHaJ]H]G\H_I^H\E]E^D`E_I^F`G]F\F]G]F]DdIfLcK`I_I`KcO`J`H]E]EaI`J_J^G[HcMeMdMcOgReRkZl]eVq^r_h[m\dNaKbLaK`M_L_K^I]F`H`H`G^G\E\E]F]G^F[F[G\F_G_G\E]F_G^G\E]E[E\E^E]F\F_G]E[DZG]I\H\G^E^G_G^F[D]D^F[D\D^E[C\D]E[C[D\E[F]E[D[E\E\FZF\F\E^F|}szmohUfReQgRgOgPcObLcNbN_JaI`H_H^H^H`G`F_F`GaG^F^F`EaF`F^F_F`G`G_G^F^G^E_H]H_J^J^J^H^FcGbI]H]G^G_I\F]G^F_D]EaG^G\F`I^G\G\G^H`H`JaIbI`F^E_F_J]H_G_D_EaH`J_K]G\HaLcKeMgPmUgQhXk[m\o]jWiYbPaMbKaJ`IdNaMaL_J_I_G`G`G`F\E]F]E]E^G]H[I\H^G^G^F_F_G_I]F_F\F\F\F\F^F^F[DZDZF\F\G]H^G_G`F_G]G^F^H\I\H]H^G^G`J^F\F[E]H^J[F[D]E]F\F]F]E\Dx|wiVbMgSiViUgRdOdPbOaL`GbI_H`IaK_H`H_G`G_G[E_E_E^F]G^G_F^D^E]G\H]H^J]I^G_I_H^I^G`EbF]GZH^JbK_I]G]G^I^E_E_E_F]F^G]F]I]H]G_I^I^I`H^E`F`G`G_H^F_FaHbIbK`I_G]F_HcKfOcOlVpXnZgTqco[fS`ObLbJ`JbMaLaM`L`J_GaIdK_G^G]F\E^F]F\H[I]I\G]H^I^G_G`I]G_F^H\G[E]E^E]D[CYD^G[EZE\G]G_HbF`FaH`I\I[H\HZF]F^G^G\G]H]G[F\G]E\E_I`H^E_G]I\F»yool\dSdShXsudeRcOcPbLaIbJaJbLaL`J_I_H`H`I]H^I_H^I^J_J^H]I[I\I^H]J^L\H`H_G^F^H^J_H_E]F[H\KdOaJ_H\F\E_F_F^E^F]F]F`H^J]I\H\H^G^G_H`G_G_H`H_G\E^GbIbJ`H_I`I_H`IcLcN_Nwc{excmYjSnZjSaNbM_IaKcMaL]J`K`J_GaIbJaJ`J^G^H^F]F]G[H\H\G\G]H]G_H]F[G_H_G^F\D[D]EaH\FZE\G[FZE\G]H`I`F`FeIbK\H\G_E\DZD^G\F\I]H]G]G\H^G_H_H_H^E_H]G[Gž½sci[j]eV}j}jp\jVeQcLcLcLbKaMaN`K_I_H_H_F_H`IaK^J]JaK_L^J\I^I`J_L\H\F^F`F_G^H]F\G^F^G\G\HeKcK_I`H^E`F`F`G]F^F^FaI_H_H^G_GaFbG^H_I^H]G_H_H]F]FaJ^H`G_I]G^G^I`J`J_Lq_q]mWnYjS|kkXuabNdOaM^IcLaL_KbJ`I`I`J`I`KaM_I`I_H_G^I\J\H]G]G\E\E^G_F]F_F^E^E_E\F_EdH]G\F\H^H[G[F\F^H_G_GcIeJ]I\F_D^E]D\D[D[E\F[E\E\G]H^H]G[E^F_F\EZEýüvjvmp]ybyb}ep[dNeNdNcLaLeP`K`J`J_H_I`HaI`J^H`J`K_K^J]K]K_IaH_G_F`G^G_H]I]F]F^F^G]G]GcKfNbL`I`I`H`H`G`F^E^GaIaHaHaGaG\F`F`H_I]H]I^H^I^IaJcL]I`J]I[H^H^H^JbM`Mscp]jUmYs^}k{meUn\cRfRaN_KdMaK`L`JaJ`I^H_GaH`I`J_I_JaL^I^J`I]F]E^F]F]F_F`G_H]E^F`F^F^G`I`I^H_H`I\G\F[G\G]F\FaIfJ`H]E\B`C]D\C^H\G\G\G\F]F^G\E\E\E^E]E]F^G¸pa}g{ew`s^gSbLdMdMbKdLbKaJ`I`JaN`H`IaJ^H_J`K_K`K^I^H]GaHaH_GaJ\H`J^H^I]G\E]G]F_HcKhOcL^I]H_H_HaI_H\F\F`GbG^H^G`F]G^H_J^K]H_L_L_L^J`KcL`KbM^J^I^H`I_JaMaMyhxgwgmXr]uas`fUiVeSjScMbKdLaM`M_I_IaI^G^H`J_J`KaMaK`M_IaI`G^G]E]D^F_G_F`F_H_F^G_I_H]E]F^H^I`H`I]G]H[F\E^F\FaHfJ`E]D]C]C]C^D]F]F\F\G[F\F]F\FYD[F_G[D]E`FŽxy|mwchSeNeMbLcLaM_K`LaKaLaJ`H`H^H]I_H`HbJ`HbG`G`HbIbHbH^H_H_F`I_H_GaJ^H^GbJfMeLbL_J_I_I_I_I]G[F^H^G]H^GcIaI]G\I]I^H`LaO_L\J^LaMaMcOaJ^F^G_J^LaMbNqazk{kfQr\p[yejXeTgTkTdKdLeMbKaK`L_J`G`G]J^K_J^KbNaNaK`L`JaIbK^H^E`G`G_F`G_F`I`I_J^F]E\F]G_IaI`H^F]F\D^B`E`EcIdH_E\C_E]E\D]E`D_E]D[E\F]E]D_G]F\E`G]G\E_FĺǺwjvhuchPdMdMdPaNbMaNbN`LaK`I^G]F^IaJaIaGaGbH^G_HaH_I_K^H_F_H]EcFcF_E\G`LbKcKfMaJ`I_H_H_I_I]H\I^J\G]F^H]H\G\G]G\G^JbO_N_K_K`MbMfMbJ_G^H^I_IaJaMq`l\rbgUlWnW|gkXcRgTjScJfOeOcMdNaK`JaJaG_I_K^J_IbK_JaL`K_I`IaI`H^F_G_G]G]G]E_H^I^H_F]F^G]G\I_HbHbH_F_D^B_CaGbGaF\E]C_D]D[E]F_F^D^F\F^G_G\E\D^D^F_G_F_F_HſȿŻʱ~ǿyopfOlUiSdPcPcNcOdMaKaKbL_J^J_IbIbH_FaI`I^G]G_G`H^H^G]G^H]F`EbF^E]G^H\FbIfLbKaJ_IaI`I^H^I]H^K]F\E\G[F^G\F^G]F^HaL_M_KaLbNbLeJaI^G^I`K_JaKfQ{gkZxm]lYnWoXkTgSfSiRbMhQgScOcNfN`L`JbJbJdKaIbKaK^H`JaLbKbKaJaJ_H_H^G^I\I]F]E\F\F^G^H]H]H^H_G]F`G^E]E_E`EbKbI]F^G]D\C_D^F\E\E_H`H^HZH\F]F\C`B_FaI`H]F]FǿijspºŽĻylvffQo\iVdNbNdOeNcNcNaLaJ_J_L`JbIbI_G\E]G^G]H^H`H]F]I`I`I^H_H_E`G^G`H_HcKgM`J`I^G`HaI_H_H`I]I\E\E^F_D^F^G^G^G_J`J\H^IbKbLbMcLaJ_G_G`H`JaJcLzehX}ip^r_o[lVlVgShTiScOjVl[fReOiTfQ`JdKhMeKbJeMdM`KbMbOdOcMaKaKaK`J`I^H\J_J_I]E]F^I^H^I^G_G^GZE^G_G]E^GaHcIbJ\F\F\E\C_B_E[E\FcKcL]H[K[H\D^C`C^EaH_H^I_JyñǕ~ɻxkvfWeRiTiUfRcOdPcNbLdMbM`L`M`M`MaM`KaK_J^I_H^G^G^H_I_J`HbJ^F]F^E`GaHbK_IaJdMbJ_G^D^E^FaGaGaJ^J^F^D^E_F`H_H_G^F_H^H^GaHcJaJcMeM_HaJaH`GbI`GaIdMePgScQygm^q]lXgUfReQaOl\{mdSeSiUhS_LgMiNeMcLjWgVaNn\jXcOhQaMiVhT_LaK_J_J_I^H_G]F]F^F_E`H^H]G]F]G\E[D\FaJ`H^G\F\E]F^E^B\C[D]FaJeM\HYIZGZC_D_E_EdKaM]L]MfTv÷ԯȿ¹ºskqim]q^hRfRfOeNcMaJcJdMcNaNaOaN`M`LaKbKaI_H_G_H]H_K`L_H`I^H]F`G`G_I`I_I_KcLcK`I_I]H^FbGbH_G^F_F`F_F_H`G^G]G^H^G^F`FbHaHaIbJeM`IaH`G^GaGaGaHcMdNgQdRo\iWp[gWeTcRdQaNm`|cUhXo_gS`NcMiOgObKo\|odSqczlfSs_cSkYjXbN`L_J_K`I_H^F]F]E^D`D_F^G]F^F]F\F\G`JcK^G^F\E]F\D_E^E]F]G]G`HcJ`H[I[F[D\E]D]EdKeO^LZLfQlWqYw`u¼ȹ}zvyp_eReOcKbI`HcJdJcKbLaNaMbLaKbKbKaKaIaH]H^I^I_LbJ`H]F_G^F`E`H^H_I_KbKaIbJaK_IaIaGaH`J_G^G^F]G^H^H]G]G`J_I`HbH`G_EaGbIcKcKaIbJ]FbGcJ`JcLcMdQdRbRgUxeh\fWgUfSbPk^dXy}siaQePhPfObLkUxcgVvgxifTiWbQ`P`ReQcN`NaNcM`J_I_I^F_FaG^F]F]E]F]F\GZI^IbI_H_H^F^E^F\F[D^G^H^GbJeLbK_I]G]F]F]F]IaJcL^K]JgSePgWhViRzc½°ѴphUfOdKbI_GaIbJcKcKaL`KcKcLbJcLbLaKaK^H_I`K`JbI`H_F^G^F`E_H^G^G`I_K]I]H_H^EaG`I^G_G_G`G^F\F]GaI_H^H`H_I^H`G^G^FaHaH`HbKbMbM\H_IaJbKcKcLdPhWo`iXp]p^kZgWk[eWg]ricRfRlVgPeOkWiVfVkXiVeRdR`Mykypl[`NaPbMbK`K`G`H^G]E]F]G^H^H_G]F[G]H`I^G^F]F_F_H\F[E\F\FbJhMeL`I_H_H_HaJ_H`KaK`J_J^IeSePfQePgSjTeR|ws¸ոɺnbeNfNeNcKcKdLcKcKcLbKdLcKbJcLcKaLaMaJ_J_I`HaH`I`G`HaG`I^G]H_I`K^J_I^H^H^GaI^H]F`H_G_H_I]H\F`G`J`J_I^I_I^H_I_HaJaI^H`JbLbM_J_HaIbJdJcJdOgUyih[shvfjZm\zkk^n`|w}s|iYfTjXjUgS{nl]eVo\kUcNcN`KwfqbaLcO`LdLaJaLaK^I^H_H^H]H^G_G_I]I]H]G_H^G]G_G`H`H^HZD[CbIfMdLcJ`I_IcLcL_JdMbK]GaHaGiQgQgPdQePlShUvi{ofַʿɩcOgPcObNaMbLcKbKcLcLeMdMbJbKbKbMbLbLaJaJaI_H^G`IaGaG`H_H_H_I_H_G_G^G^H`J`J^G^F_G_G_I^I^H_H`I^H]G_H`H_I`IcL`K_I_I]G^IcMfPaM_K`JcIeHgMfOeNwdukvkuuisfo`n`ysdscrgzsejYsem[hUxuhjZp\eQeMeMaKk[vn^cPdPeNcLcMaMaM_K_L_K_I^I_I_I]E`I]H_I^G]E_G^F^F_G_G^GZC\CaEdJdLdK_HbLgPbLdLiLbJ]FbGfKnXgSfPePeQiRgTcTl\p{kubm̹jZgPdOcObMbLcKdKcLaJeLbM_JbMdOkUcNaL`K`I`IaJ^G`HaHaGcHaI_H`J_I`H_I_HbI`I_I^H_G_G_I`J_G^H]G_G^G_G_GaIaJaKaK`K_L^G_H^HdLjQeN`K^IaIcIcLcLcMj{mxr{xykk^|px{mdxobhUzjvim^eRdRfOeMdKv]qZeNfNfObKbLeN`L`K_J`K_J^I_I`H^F]E_H_H_H^F]D^E_E`F`G^F\E\D]E`E`FdKeM^KePiOhOeMeL`I`IbHfKrahTdNeOgRhSfSgTgTfRlXyboZm\ƺnbs^eRgPbM`KcLdMcLcLeNbN^LbMdOlWbK`K`KaJbJdKcJ`HcKcIcKaI^HaJbJaI^FaH`I_H_J`J^G^F`I`J]F`H`I^G`HbJaH`I`K_K`I`J_I_H_I^HbKgOeOaKaIbKaJcKbJdMkSfRxqksirɽ|xsikZsem_wehTeQdObKeLmNlMgNfOcNaJbMePbO_MaLaLaM_J`I`H^G]E^G\G^E_F_E_E`G_G^F^G^F]E^F_F^EaHbI^KcObLeNfOaO^KaHaHfK|imZePePgSiTgSeShRfRgSiSp[s`suù̻wop]hUgRcNbKcKdMbLbLeMeMcLbLfPgPcLaLaKcLdJdJdJbJbJaH`HaH`H`JaJbK`H`G`H`JaL_H`I`I_I_I`IaJ`I`IbI`H_F_G`I`JaJ`H^G`G_G`HcJbKbKbKbLaLaJdMdKeMgPePrayw~j[ſœvopdpadSiUhTfRfOdMeMlMmMhMfNbMaMaNdPcNaM`MaK_J`IbIbHaH`H`I^G]F^G]E_E]E^G^G^H]G]E^F]G\DaIcK^MiUcLbMgOcLcOeNaJjQn]kWeQgShTjViTfSgSiThSgQmUq[nYs`m]xmɹſȹujiSkScObMcKcLbLdNgNfMdLcMhSgPcNcLaIbHaGaHcJcKaJaI`JaK`JbL`LbMbL`I_J`J_I^H_H_I^J`J_IaLaJaK`I_HaJbKaK`I`G`E`G_H^H`GbIaI^J`K_K^KaKaLhOgNgPdNhWhYwĹŹ|vkvdfTfUhUhSgNcJhMkMgMfOcLbKaMdMcNbMcP`LaKaIaI`HaIbI_J_I`I]G^G_G^E^G_H^F^F_F_G`J^FaJcMaNmXdMbKfMgPgQgOeQpYhVhUeRhShThUhUeReRhQgQgPgPmVlVoZo]jXo^ƾÿÛhUgRcPcMaKeLhQeNhOfLcJcMhPfOdNhOcIaFbGbJdMeMaJaKbLbKaJcJbJaJcKaIbKaL`K_L`KaK_JcKaJaJaIaI`I_GbIcK_IaJ_F_E`EcH`H^GaG`G_IbM`K`KaKaJhNeMfNfOjXxygq`ruf[~syoujjZp`gQaHbLfNcMfOdM`KbMbLcNdOdPbOcNcL`JbKbIaHbK`K`IaI`I`I`H_H^G`H`H`H_H^I_IbLbMeRlYfObIbKeOdNbMdQlTgTfSdRhSjTjThTeRhRgOgPiShRgRgQpZmZhShTkXyȰj]eTeSdPaLaLcNcNfOfLcKdOfOfQbNmSjOcJcLbKfNfNaIcJcHbIcLcKcJcLbMaL`J`LaMbKaKbLbJbIaIaHaHaIbJ`IbKbI^G`I`H^G_H`FaF`FaH^I`LcN`O`N^K_JgPcKdLcMdOvdn]iTmZrbi]Ǽ{u{}iUgQeP`NfPdLbMgOePfRcOdPdObLdNaKcJbI`JaKaJ_IaJ`I_I`KaK`G^F_HaH_H]J_IcMdPhYrffPaJaLcOdO`M_KhNhSnViSgSkShQfPfPgQfQiQhQgQgRgPhRePeQgQhVl[l^Ҹ{pk]cTbPaMbNaNfSiReNbKeNhOdP`NjPlOeJcLbNdNeMbJbIaFbGbIcKeJfKcLaLaLaMaKbMaMbKaJbJbH_G_HaHaJbMdMbJ^H^HaJ`K`J_I_G`G`J^KaN`M`OaN`M`KiPfMaKbLdMuf|omYfPmYn_oaùºبĻǼذwphTfPcOiQiOfNhRjUjVfOfPfPcMaMfPeM`I_JaL`K^H`H`J_IaKcM`J^H]G`I_H`J`LcQj^zq~iXeNcLcNeM`K^KiQjWu\jSiTmTjQfOdOiRhRgQhPfQhSgPgOfNeMfOfSm[wck\szlm_la`LdQdPnVkQeOcMjQoUhQbNfMjOdKbJaLbMdMaKbJbHaHdKdKeKfLbJbLbLbLdMaLbLdKcLcJbHaHaIbIaIaKdKcJ`KaJaL`NaKaK`J`J`LbLbM_M`Km[gUaNgNeM`KdLdMpcymqb`Nzeqn^{Źרĵ}xǶud|qo^iTpZgPlPiOdMiTlZiUgPgPcNdPdPdQcPaKdMbO_N_J]G^H`I`IbM`M^L]JaL_I`J`NsgxqbbP`LbLdN_KaLcPlXpZhSlVlViQgReRhRhRjUjShQfOgOhPfOfNfNhRlXjXmXlUn[sbr`m[xn`NePfShQeOePcKkRx^iRaMdMhOcKbKcKbLeNbKbIcLbJcLbMaKbK`KcMcMdMdMbMdMdMeMdKbIaJbLaMaLaJbJcLbLaLaK`KcKcKbLaN_McNePbOcPhUfPaKuel^_IcMcLiVp`iXiUvaor`m^ǵxozhq_q`s_r]lZ}lnZhRhUjUhPiPeMbLgSmYgRpXpYfPfQlZeRbOePgSeSaOcNaJ`IcK_I`N_M_MbOaL_KaLcLgSzoem_aPaLhQbMcRylp`ufSkVjTjUiTiRgQhSqm[gPfPhPhPeRgQgOiQlUlViSiUiWhThSfQq`m^cNeOkTfRbMeNbLjRs[fPbJcKeLaJaLbMbNfMdJeLdLcKbLcMaJ`H_IcLfLfMdNkWePfOiMfLeKdLcN`LaLbKbLdMaK`LaNcLdLeLbKaMbMbNeOiUmZ`MbN`Jm\k\^KdOfMnXubhVq]}isdsbq`p_zjzqao]wfwdzaoYkUmXhTeSeQePfOdMcMdMeNmVlVxas\pWhQn[fUeU|kn\eQcQaNcLdLaJaJbN`NbObObNbOgShQaNzlsk}ndiWeRbLfNeNdT{sdhWgTfSp_lVhPiRlWq^lWhReQfOgPeQfRgQhRiRjRgSdSdTfUeRdQePePcNdOfPkWq\eNfLiPnThPcKbKdKeMcKaJaLgLdJeJdKbKcMdLbK`I`JcMeMdNdNnYfQfNiNhNcNdNePcNbLdMdNbLbNaNaNbKcJdLdMcNdMdOaKmYk]eVeR`KcMcObPdQfPq_xdr_cOwdviylsejY|jyqrazgl|gwapWv[q\gVhWfTlTgPcLeMeKfNnUo[xin\lUrdxm^hWxpcdPcPbOcLfMcJaJcNcNdOaMcMjQkVfQdRvdresibSdRaNbLcMcKfSxxdfXgThUuao[lVlVjUoZn]fSgQgQfOgQgSfSiUiRfPfRgSdScRdRdSeRcPbOcNeNnY|fdNdMiOlPgMcKcJcKgLdJaKdLhLcIcIbI`K`LdLeLbIbIaKbLdMdNcNdNfNgMeM`LbOcNdOdNcNfNdMbOaO_NaLaKbLdNcMbNdOm_vpm^kZaOyehTeRdQcOn[rbvbePl[ymm_v{k{oyk}ro[va|udjViXlYfQfOeMfLfNoVrb{ocNymbl]wmbNdOcPcMgNfNbLcOdNcObNcLhP|kiYcQj[z{cVhWbObMdM`IjWsjXzkk`iVjUr_ud~mnZfSqdj]iShQhPhPkThSjTgReOdQeQdPdSeRhThSdPcNdMdNiSpYbNeOiOhKeLbKcJdJdKcKcKbNeKdJdKcJ`J_JeLiLdJcJeLbLbLfNfNbKeKeKgNcNdOeMcMbLbOdPeNbNbObOcNdNbLdNcMgOaN֕{o`gUiUn[aPcMeSwmwp|leRhX{~vg{uq]maykzudrdyieQiRiPhMgMjTyjŸ{pbNzng]repffSgSgQdMiQkTfQcOaMbLbM`MgOuam\iWnbxkqeiScK^IhSverbzgVkVoYvbkXn_~ug_ɼ{vfUkSkRgPjRjTiW}klXeQdPePfRfRiRkTiSeOdNeNfNeNdMdMfMeKdKbMbKaHbJcLcKaKdLeLeLeLaJaIfMdMcKdKaMbNbNcMeMdLaLaLhRhQfOfP`KaKdNcNdMcOcPcPcOeNeMeNcNfSaOȟ{hWq^fSdVse\~}nxhgUi[}oqn`}qyxzin^wsscxykzl|ozjdQjVjQiOhPdNuexfhVylmdvksijWp]kTeOdPjUjScNaN`NbLaNePfOcPhXk]k^|n~u|vl[aLaNdS~u}kekWlVn[fTwoeZǻľkajUiRhQiRiTjYrragUfQePfPfRhTiRjRhOeNeLfNeOcNdNfLeLcLcNcKbJbKbLcMaKaKbLbLaI`JbKdLbMcLaK`KdMdMdLbMbNaLaKbKdLfNdNbLaLcNdNbLbMbNbPcNdMeNeNdMdPcPiX{i[}liUjZlfôzmsek\k^ub}ip_r_n|lzgdYrkǻr~g[xfxhuisvffShXiTfRfQdNkZ}jn\shy{hTkVhRfQfTkZhSbNaQ`PaO`NbPdPbOdSl\iZm^pc~iWgUk\īvo~sxjvngZΕ~ykYkTjSiSiTjWr_sbhWgRfPePeReSgQhQhPfMeLfNeMeNeOeLdLdLdMcKbKcLePbMaLcOdNbLbJaJcLeMdMfOcLbMcOePfNdKdLcMaKaIaIaJbMdLdMeMdMbKcMcNdNdMdMeMfMeMeMcNiUvdvgymgVq^gReS}}}rk_j^zwp_}ikWu_mra|pgsi{o}wji`zwln_cTvhrfdSfSgZylsdsmȽ­ſ{siWnXp]n]gUfScRdRcN`ObPbNcOeRl[pacSk\wugjXlZiUp]j\תگwrûk^mVjSiUiViUjXl[iUhQhPgPgSgTgPgOfNhOiOfPhPfPdPfNeMcKcKdLbNbNePcNaMbPfQcMbKbKeMdMcMeNcNbMaKfMeLcLfMdLbMbKaJaK`LbKcKbKbNbNbMdMcLbMdMeMcLcMeNeNeQdPgTgUeRjTgPeS{r{vwok\kYjWr\~l{l_k[yjzg}kyzobxihXwirecVfWvqvkrd}wɷۮhXlZ}jsbm\p^iTeRjZuajXcSbQdSp`o`eVfWxekWfPgRcPfScT½ŹĽýĸƭmdrdkTmWlVjUhUiUhShPhQgRhRhPfPeOgPmTiRkUhRfReOfNfLeKdLbMcObMbNbMdNiSbLbKcKeLdMbL`KbLaLbKgLdIfMiQfOcPbLbKaKbKcJaJaKdNcMbLcMaKaMdNeMdMfNfMcOdQdQfRePeQlYq_jZ~sx|umk^o^mYvboypbl_vcr`m[{j¶ĺpe|nqek]obuhcZvyqʖ{yqdsfzsqewm[eRwh}pbwa_NymgVhVeUmZjWdQdQdPeTdRα{ufiUlXkUkViUhPhPhOgPjQgReOfPmUmVmWiTgSfRfOeMeMcLbMcLdMdPcPdPfOeLbLbLiOhMdJcKdLcLdMgKfKgNiQhQcNbLcMcMdLdLbLbKeLeMdMcNcK`LbNbLeMdLcLbOcOcPdQdQeQiUp^p\r^}}xsgn`yjm[l]papfshp`o^jXziwflWr_z|~ujl^ue|mqyjZuewk{yyivim~pwvvnjXiTmZ}sweYhThVfVuegWbPeQdPeSfTǾǿʶupm`lXkUkViVhSiPhQiRgQdPdRfTkVlTnYmYgQfQfOdNcMdLdLeNdNdNdOdNeMeLcKjPiMgKdLcKdMeMfKgNdMgPjUcMdNeLdLfLgKbKdLdLcMeOdNeNcM`KaKcKaJbMdLbMdOdPdPfQhPePhRfRrvqezopfngmck\m\gVjXgUjWs_ps|uvqbkZn]mauf{lykzuk^kZhVnXlUiVlZxfrm_sdl]}km]mgfTkXeXuj\aPfRfTlZiUriyҬŸ׽uqmal[kZgUgUiTiRiShSfSfUjSjRrZr[gRfQfPeQePeMgMfNbMdNfQcObKeKgKeNeMgMcL`IeLeLfKgLeMfQiTbPiRgOcKfLeMbKeMfNbLbMdMeKdKdKcJbLcLgMdLdMdMbNdOePdQeQePfQiWiXx~v|Ƨy~yuøyuzniXiXgVdTiVub|v®ίwlo]ub|kv~qξjYjTlVgTfPfQcOfPeOkXwdmbʿwowqŻdRiX}mqpafUiWgV}uj~sud¸Ь׾|~mgj\jZhXjWgRhRiShTgTiShTiUmWjTiQgQfQiQhQiOfJeKdMfNeMaLeMdNcNcNfNcLaIgMcLdKeKaKfOfOcNgNgNcKdLeOcLgOhOdLcJeLdLbIcIcJdKgNfKcKcMcNcMdLdNcPcOdOeOfScSmYykvyn{}ž|xsggS}mschRkWj\}~vsa~gsji¹ylZmXlVhQgQfRdOdOdOdNiXsipfqhiWsanrgpbkYznhl^{jvҝpnlak[k[jWhThThVhUgQgQiUnYfRhQgReRhShPgMfKeNdMeNfOcMdOcPcNcOcNbLeMmTdMcJeKdKfKeMcLdMcMbMdMdMdLgNhPgNfOeMcKbJdJcIdJeJbKbKbOdPeLcJcMdNcObNfNePdPeSgUhYkZpc{lzl}sui}nbwij[ja{ww|sym~v|qxdj}gyc{dza}cj}hrs`p[pZjUgRjReNdNeOeMeQq_q|jiZvjzj|iwdiXhXf[ùeYyfzjߵÿǾǦd[g\k\kXjUjTjUhRhQhSgRfPhRfRdRkVkShPgOfPeNfPfPeMeOcOdQdRcOcMeNiPeNcLeMfMeKhLdMcObOePhQbLcLeLhPfNeLdMbKaJdIcHcJeKdM`OdNhNdKcKdJcLdPfOeMeNdNeRdRdUhWjUgPp]|jr_{l{uf]ؿǹukmybn\p_zf|fzdze}f|fyf}ewuav^ybq\xbo[bPdPfPfNfMcKcNhSeQovfygxd|gp^jZn`xyyhjVziucӳļϽǜi_kXkUkUjSiShShTgQgSeRjRkRhOhOhPgPePgPdMdNePdPcQfSePbNdQePeOdMeLeKeLeLbMbMcNcPdObMcMcLfNfNeLdLbLbMdMdKcKeMfOeOfNhMcKdLfNcMdMfMdMsbfUcOeRcRpak\cOkViWiWm\o^}{xxɸrybq[u^u_gXm\ze{gwaq^krqv^y`x`{bgp\bReQiQgOfMfNdNcMdPaLeOiSiQmWp[gVp_vgvm^iWnVmUqygueʍyxự|xn`kVjSlXiTgRhShTgQmV{hr`fPmUmVhQiPeOdPfQeNdPdNdNeQdQcQdQdOgNiLfJfLdObObOdRcQbMcLbKfNgNcMaLcKeMdLdKdLgNdNdPeMhNdMdOePcMeOeNdMlZfSdNcQbQm]gWcPfRgTdRfUm\l\{kzvkĿûvp`s^q]m]ub{gunu^wz||hj}ew`s\w]y^{_v[mTeSgSfPeNfOdNeOdQdQeOdNePfRePgSo\q`}nrl\pUlSwrfż½ۛû»ƿÙqjk_iWkXjUlWhShQgQsbxekVv`rZkQhNfPePhSiPeOdNeNfReRgRcQbOePeOfMiMeNcObOdPeRdOdM`KeNhObL_MbKeLfLgMdMcNdMbLdPjUcMcNdObMbNdQdNcLdNcMcObOxgk\aOcQfTfSgUmZhSfQkXm\k[|n|pʽvzg}nxg}gqn}e~fr`r_q^v_v\s[kTqYwZw[u\pYgRnXnXiUkWeQdQfTdQeQdRbQcPdRgRgQhSm\~noenVkT~k^¸ԿզüĹέnckXlWlWiRiQeOhUnXu]w_mTjSiQgQjShPfMfLgNgPfRgRcQdPePaPeMeLbLcLdLaMcOdMdOaLfPiTdNaPaMdMdNeNdMbOcNaLeSq_cOcMdMcMdOdOePfPcObMdNbN~uugbOeSfRcNfOjVfQhTjVfViYm[udym~Ÿĸ~nvdn\ycx`|b}b{azc|j}mo~hrWgQiRnTrXrWnUfPoXr\kWkWkWjTiTeNhPhTbPbQcOdOdOePcNygugpYo[{hyeq`zü޻خӴǽ½ķΙxrulgWkUiTiTgUgVo\{cmUlViSiRkSjRgNiPgNfOgQfQeQeRdSbQeNdLdLfNeMcNcNdNdPcMeOiTfOdOcLdNcNdNcNcNaMaMbMeObNcNeOcOeOdMdOdNcMbNdNcNp`lZbNfPgQdNgOhRfPfQfTwo`kWkWjWlXo]u~vj{|ygn\uaxc}g~d}chrzizi~goXhQlRhPqXuXmRgPoVp[gSiSnVmVhReNlToZhTgTcPfNfOfPcQhUjUr]mYq_p]vj~nnöʺݸع⼶ſ˽objViUlWiXhWjWkVlUiRjQpViQfPlUjSgQfQfRfSeRdQbOdPeMdKcNdNcMfPeQgQcObNgPfPeNhPfPcPiShRfPdObMbLbLcLbMeNdMcMcMbNcNdPdOgPeNgNfNgRjUhQfOfMiRiTeMeQwhkVkWhSgSlXn]|jwhu}nr{jsam\ygk{gzlir_p]v`fWiRmThQqXwYrUhQnUqZfShTmVkSgOfNlShSs`pZgPdPfPiQdPcPdPpZlXwinb|xfྐྵھɿƻüۛ{vl\nXkXhWhUiSiRkRoUmThRnVlTgQhRhQhRhQdPdReSdMdMbMcNfPdOcMfNfPhUlWeRfQlTkUbRqm^vcfScLdMdNdOcOcMfOcNcNcPcQeQcPbOeNlRhPp]mYdMhQeMlVyhcNgQq`kXiUjThUeRgThVhWhYq`vezncvcxfo]{kyiuvcq^r^q_fVhShPoTrVoUsXmUjTmVjVhRlSiQhPgNiQgSkWs_zemWgQkUeQcPcOnYl[slɋldֵÿܽύupqaoZkWkVjUiViTkTjSoXs\hSdOkQhOfPgPfQeSfReOeNcMcMeOdNdMdLfNgPjUePePiRnWdUqewffVcQcNbLcOaMcLeOeOdMbNaOcOdPeQfNhNfLhOhPgNhOjRfPnYgRcPp`seTfTjVhSgVgUgTgViV|fh|qcx}qr}u{n~ygr`vcpbkWmXlWvWsWpYrZhSfRlWs^mYmTjRkTfQgQjUePwepkUjSgTdOcPcPq\l]Љha̸ɾḱsjjYk^gXiYjWhRfQfRoXqYhQfQmTgPgQfOgPhPiPkQfNcMdNdNdOcMeMfNdMcMdNdPfRgSiYujxjiZdOePeQbP_McOcOcOeOeOdOaNdPoWfLeJfJfKfMdLlXwdfPjSkT_NwgysaPzfk\cTeTeSfRoXmXrn}zqamXw[w[s[nXeQjWlYre~sjUmYsafQhSdSmdmam\hWhUhVfSoXn^}}ja߽ƾüƿ䩟½}hWlYhSgQgQiQiRkTu]kSiRfQgPiOlPnQiQgRgQgPdNeMgNfNeMeNcOfQiUcMfSsdkZ~liXePePfScOcPeObPaRcOcPdPcOfPkQfLcJeLhNfNbKo\xdlWgReRbNs]{hxj_PrkZbPeSfTeTfQgRnj]wżŶxl}iz|vu_u_rZu`j_cUl[wmzmylkdw}i\obl]jWkW{jn`h_ſƿº˾軴ǻk^n]mZjThQgPfSgSs_lWgQfOeNgPjQjQfOiQgPhOfNiQiOfNfOfPfPiUo[dNfOgMhOfQfPeQbPgQcOjRiQeRdNaObObMdNgOjPgNdMbNhPhOePfSfQhRdQbPcNgOgQgSmWdTsckWcPdQfRfRfQePrbzaQ}oȸz~nvjvv|j|gq^p`zlfh[˼̕xowfvwv~|r|jaxjlWoY{f}jiXf]¼毞̌kcp\p]jWiUiUfRgSiRhSgSeOeMgNkOnTjRjRfOgOgSpYmSeNfReOfOjSoYgQfOjRfNfOgOfOcOcNeNgPcOeQdNeNgPbNbNiPlSeNdNdPeOdOeRbPdQhTdQcOcLcLeOiShUdSkVgRcQdOcPeSdQeRgTfRfTiVp\o`wisezȿÜzox{ylߓvql]rm_n{vgp\wug{mqgykulufqZq[lWmVwdxfk^߸ǿޥk]o]jXjXiVrem_hSiTgOgPeNdNgPmVnWlTiPgOgRlUmUgQfQdQhRgRrYlShSiThOhPjPhPdOcMgQfQeOeMcKfMiOfOdMjSjTdMdOfPdMaNbNaOeUkZfTdQdNeMePgTdQcOkWfQdPePdPeRfRgSeQhTfSgTgSgSlTnXp^zukʿǾyhs~ul]hYwil[rbq~o~oq_sdi[uk{|pvh|nrqvd}jvckUnUnVpYj\¶뺭k`m\jZgWgVxi[n\jXgReNfOeQiTjVlTkSjTjWhUmUkSjRePeOeOgQgQgSgSgRhPhNfOeOgNfPgPhPeNeMfLeNfObLv`o\cOePdMbLcMcOlYlXuaeQdNcNdNeRgTragWp_gWdQfQgRfRhRjSgQgRfSfTfUgShQgQgPgShWiYiYnancg^piphjXhSiUmZi[|ohgYrd{{rwo~mg}r|q{n|npanyglTnUnVpZmbҫuen_l[iYn[rdpfucpa{ksaeMfPfPgSfTgSnWnZhRxau]mTfOgPfOdMcOhSlViUgPjQjReNgPiSiRgNfOgPfOeNfPaNvrd`NdPeMcLdMcPkXjUnZePeOcOdPeRiSpZgTfTeSdPdQgQgOhRfRcOfQeOfQgSgSfRgReNp[{jkZq]iWfVnbvouijXlXp\}k~oưžuz{ry}oymmVnWnUnToWoYui˽Է̟qbn[kYlZp\ocriubp[zfwdhPjSq^hYdSfTgShSgPnXoXjQfNhRgQeOePiRmWiSePlUmUgQjTqYqYgNgMhPeNdNePcP}ml\bPcPePdNbLcPdPhRfSePePbNdPeQeSeRgQbMbOcNcNfPeOeQfRdOdOeNiQfPfPgPiQfMjQlVjSlVlXfU|n~omWhTiUl}is˰zryo}rqc{noVnUmUoVqZu^xjĶĺrgq\oYlZnZrfƹ|kZr]oXqYnWlk_iVnZkViWgRePfQgQhRhShRfPiRmWnXiTkVjUiUmUuYqYiRjRjRiRgSfQfRiSeReRbObMfNgPeQdPiUiTbMdMcLeMeMcMdOgOdMcNcNdNgPcNcOgOfPcOdMgPfQfPgOhOeKgLgOgNhNgPgQw`lu^nVfRkTv[r]l~dnZmZs~q~}|qcqe~rviyjl\nWmVlUnXu`zf|nƿϖyqr]r\r]mZxjטzubr\pYnYrZ|xm}pnziYfOdNeOeQjV{jiVlUmYpZpYiVjVnVqXrXmTjTnWoXiSeOiQoUgQePdNbMdMilXbOiVn\dOcMdPdPcLcLfMhNeNfOeMgPhQeNeNiOfNbMcOdNhQiShPkSeNfNlRhOeMeNgRnVsYuXrXiTnVpWmTsZ|aw_mWp[q^o_kYiVm]rn[kXwcygl]l\ram[nVmUnWlVonp`ƽȘ{rsexcwcq^kzһj[pZmWs^r\vf|l}gl]hRgQfSeRfU}mjYhSs^pq\gQjTkUoZp[nVjRlTlSjRfOiQoVgQfPgPdLeNs]jWeQgUp`fSePdOdQdNeLdMhNgOePgOiPgOdNeNiPgOeNdNeMlUoWnToUgOiPmUlTiThVkWlWw^z^oVmWlUgOjRrZ{ar[mXxau`s`pZkTfSkWiTeRp\r^hUhTkXnZpYpXnWnV{bybs^Ļհynrar[z`uɿǻn_mVmXq]q^ncjXvau`q[vbyhiTgRfVeUdQiRhRr[ljVfOiQiT{nZiR{fmWgPgQoZpYkThRr\eOdNlWhSgRePiUmYePePiQgPeMdMdMdOcNfNiOiPeNeMhOkQkPeNgOmWnWrVuWjQfPhRp[r^p[sZpXw_v\iUpYlUbMiQpYs\s\r[zbyayapZlVkTkTgPfPmXu_fRfQgTeSqZpZnYoWnVw^s^趪~pmWw`pǿm]kTmXo\p`}gWq^p^s_wf|}vk[kWhUhSgRfQjTjSiPgPfOfPl{ggRdP}r^bLkXranTiVhXdNo[r^hTeOiWylkWgOkRfPcMfNeOdQcNgOiQiPfOgOfNhOiOeLeNlWoYnVoVhRiSiUkUs[u[x^u]w_mWlTrYjSdNgQoWnWu^u_{c|d{dpYnVoWnUfOfOfPoWfQdQdRfRs\q[x^rYo\k|jĶžĿúúݵxnVn|folXmTlXlVmZ}nsfveymucp_k_ļrmqdnYlUgTgQhSkTjQhQfPeOeMgOeNePgQePdPgTzhjUfMhQ}khSfOlUxdhVdOqfiVmRpWeMeMmYn\eQcNlZq]iNfPhOfNfNhMfMeQlXlWiWlWnZu^p]dOjSpXya|d{dq[gOmTmVjRjToYkTqZ{c}gjzgmWmUrZmUhPfNfNgOgPePdOdOv^sZy]pYp\|ixŸùŹƿŮ~usqZvr]qYu[v]nRnRnVlTkRr\kjTşpbj`ݪpbmWjWhSiRkSkShReOdNePgPfOdPePdOdReQeMePdNeKdJgNiQgQePdRcNn^sePgOkRdMdN|l~ro^eSk]sfhQhPkQjRdLfNo\jWmYqYmXlYs]|e}ieQjSrZ|dmszdiQlVw^rZmUkUjShTr]}gi{fkVlUu\oXoXgPjRgOgMgNdNcNoYpZq\o[oZvõǾǼΰzmo^o^nWnWnTlQlRmUkTkRlTpYfQszi]ޞhZnXlTkTkSkSkTq^kWhSeQgQfPnZo\ePiTkTgOfNeMgOhPgQgRiTfTdPeOdJeMbLdLeNdMmYrap]cRwkfQgPhQjSfOdNs`zgjWoWrZp\mZgUnVjQro`p\xbv^lVs^lWiSmWyc}g~fnYgTkUrZv_iUlUkTgOhOdMdNq\r\o\oYp^Ļúлû}sl}qiRmUlTkSmSoTlRkQkSkViXomk\mӜiYmWlTiSiQyfrur_ePjVnXzcwbiTlTjSgOiPgOhOlTlVeQzeu`eNeQfQfPdNbMdOgReOdMdOeQgSlYhScMras_dPcQxer_eOgQoZscaRr]lWeRzkol\m[r_valXyevxoergvb}eqZjUu]v^u_nYs]nXfPeNdNbMw_q]m[o\sežƻĸʼɿɾȻɿӪƙ|rkTpVmUkSnVv]nVlSkTlWlYq{fpj^q_̍qemWkVkThQ~m}lq`p`hSkVw^|cwaoXpWjSjQoXpYkSmVr[jVyvhUqbk\fSeOgSgRgQgPgRgShRePePfOePgQhQfReSgReNeNgNiXvqo]nZdS{xjjYgViVjVkUhTpcϙyyfsm]v`yaq[lXo\kXgQfQfPeN}r`p\qcƾȾ·ǿѱzmSoVnUnVu\u`kTkSmVnXn[xbr^qfȽ{wi[ޚkVnWmVkVlWjVkWlYjUiUoXkqvcrXnTnWv^x`oXmV{bp\vwvc~mmnZoXx]v^jSdO{jwfhSkTiSgQfRgRjRjSfRfQePfPhOiWȷj\{kaVuljVr_ncqgo^fUmeȼþj]~mwgwelWoYwaqmZeShSjSiRqfpa{ƿǾļƫn]oVmUmWq_ygs^gQjToXq[~jq]jW}uj[}mĸ֯hUoYlXkXlXiVkWlUmVkVjRyd||o]p[zazaqZnX~fp[r~k{hkm|cw_v]oWjSeSxvkVlUiSjSgTfSkToUiRfRdQfQjSjRn[m[xjrgzi|f\ŵӝulaɛiY}h[gVqczieRiW|in`eUhUgSfR{yɿɿǻǾŽԵxlmWnWo\|lmVjUnUnWso]jXlsak\s_¯pcs`k[hWjWiViUiTlUlTjRoXr\oramX{bx^lUo[qudq}~oybi|by]pZiTjUjV{kwo[nWiUpXq[kVoWu_o[o\jXp]pZiQnVqZgTfTfUeTjZyjȴogfZĆofdTk[udgUm\wp`iWjYjYiUeQȯĶʿÿǿɿǾ˜upk]mZrbrwbnVmVoWs^u`mXz`s]n\o^ĸÜ{~mmZiVkVkUkUiSoVpViQlQnTnWkV|flWoZpZiSvb}oakr{h{eg}cz`lUiRjTkVu`wcnZnYn[o\r\nXxdyeqaxi{i|gx`oXv\~a}dn`k`mahXhVrjg\fTlZl^rb}j\ðſsewx|piVlWkXgSɿǼʾʿɿʽĻϳh[rc~omVkWmVq\wdww`w\z^x^oYjYü~w{jq_kWlVmTmViUpWpVkPjQkRjRp]kmZjWkWiTuaygjzdxblVjSjTlWw_x_oXpYr\xdxepa{luƹx}q{kq_s]w^nolĽl]r\p^jZl\hWr]qXjY÷˼xrǽگlgeYkahYʿɿǽǼɿȽȿɾʞ~{m^|kZiXoZgs`v_x_w^y\w]lUiTpý¹}oq\oZkUlTmUmUlTlUmUrYlSkSlSqa~n_lXiWjVn]uzfvaxapZlTiTnWu_s`p]v_~ly|svwoyqŹpfo[r_ľ̓wnw`{em]o`zuxbqfҹ}xóϪ̚ƽ¾½ȿɿɾȼʽ˿ŷ´ĸաqdzop\jWv_~es^r\w^oYpYrZmUkTr_Ž{or]lTnWnZjTmTlTmUlTr^lWqZs^nbjYjWkV{nzrxdv]ybp\kVkUoZwusj{w~s|koauxgȹ׿П̛k[|ukɪumޫun|uūǾȿȿɽƾƻȿȿǾ˾ɺʷƳȻȾɺɼǼŹȾƽǽǹö÷ǽҥwnyinZkXq\v_s\u[v]nYpZq[nVjSsaĻumr^lWq[s]lUqXkUjTkTkSkVs_q^pdnbuefW|ѹwj}l~hrZs]uq`p[n^{躷|jWu`pave{Ś|ѱh\{wժᤙbRiZg[l`zoɿȿȽĸĸǼɾƼƽʽʿɷƴðïƷ˾ʼɹĴ÷ĸŹȾƺ¬ĮŵƼɿίxsjYmZkXkVmUnVmXmXu]v^s\lWs`ymp[oWrZ{fr[mViRjTr`o_o\wfxlĵ¶lb{Н~kXm[oZoZx|jn\odʺmfæk^wakVmZoZ~ip_}pw{ʰrj{~wװp_ul\lWp\ɿƼƻôĶƿ¿ɾɾǽɾɼɻɽ˾ƶñƱĮŵij¬sĿǻʿƵwpoqmjóȾİøndmZnZkViUiSjSjTwau^nXp\zdqŹq\lSq[zfpVmUlSm]}fXl^sڻ޶i[p_n[nVmXkWn[k[qhijqlðžǽsgk]m\nYlTlVlVmWiYwsb|kyoǼɽyqxg~kƺ۴}op`wgs]o\ŹĶĸƽʿɾɾɾȼɻ˽ʽɾǽɿĶ~{vmmɼŷȼǷplligghhjil|zИ|wkYp\kVlUkTjTjTlWlVjQmUs^{fǿźlXjRpYxamTmUkU}qПfW|iucvdѨh_qbn\lXkXmYm[kZvnȝۛxnyh~osbmZrbnYlVmXk\xhq^lp_ʾvreqd·¶Ե೥yjnZrv`oZöɽɾɿɾɼʾǼƺƸǶulkklmlkjjmnkklkhhhigfijijlovyvv̹ȻskpbsflXlUlUkUkTlSlSlWnWrZy÷ƺhUiSkUgu_iWkWqc˯fTgTykn\ܚngukqen[o\p^rh୤uhdVk]r_na{luko_mVmWo]wixdolYl\³i]ǽŪkWw~mpZp[ǻʿȿƼƽǼǽƿƿɿǻƹƵƳǰikkllkjijjjryslllkijiffghgeehjjijiijmki¨{p`r\lWmVmVkUiUlVq[wekVjUlWzcvap\lYlY~qjWeSǹ~siY||uݭiXp]{hzg~k_qfxjygrf{l_pclZxjoak\lYiY|woZxar_k]ůg]кǷ°ızm]yuclVmXȿĸúþĺƽǾǽƽƼƼźŶųĮĩ{ljijjkkiihgghijihjihhhgfhhefgghhjijiiijj|n´vgzcjWq\p\kSjToVy\z~jw`lWlUmWr\r]s\n[nYu\sYmYmWw`xe~m]wm࿻䰣|jnYoXzc~hwcue}n}wcmaygn^lXoXrYmXkZkXkXyg|kkaϱkbɹĸºο~v{k{s`mVkWɽȼĹ÷ȾǼȾȿɾƻŽŽǼƽŻƺĶųînjhilijjhggfghggffiheffgggfffghhhiiiggfhi~jobu{n~jqXnVnVwZm}mjoYjVhUnXoXr[pYkSrYrZkUkUqYlW|sjypĻþr[oWmWr\xdwczf~z~lmcv|lo\rZw\oYmZjW}l]zgxisið{ucƿ}ñv~porq~lxe~ku]s^l[ŷŷµŻŹŹǼƸóƻŹƸƷŶijñij|nljhhjghihhhghjiffgggeeghigfeggghhhfffdfhggiv}scyhĴ{r]pWnUv\u}kjp\lViWjWlVu]pWjSnWuZnYnZpZjTwhĿϾϿ~ioVnWpZqZu`yd~gvpmmxkvgqzhpY|au^r^nZy}osvsi¿ùuqrbn]i{dwdvdzgku]{hqaŸĵŹŻöŸ±ƺźŵŸŶ±nikigffhijijiijjihfhhhgeehghffedehggddddegfgjijvdp_~i|joYmVxavz~ilqp]kVjUmWv]pWlSkTqXu[w]rZkVyeʮ}hrm|cpYmVjmn\gf|grnmmnsmwgzlxgoZyakqyg}xžпĹyop[q]o{f{gxcn}kq\}e{e´µŶijĵ´±ƻǻ°ljjighhhhjjjiiiijjjjfefdfgfigeeeghgg~fddgefgijgj{gvdhf}q_o\q\ijjghxiUlVq[qZqXlTlUkUnWu\nWoY{fȺx`fg}eqYmWxbxdubfyaxcq}gh{filnpuxfo\pa|uwl{ú׹Ĵug{foXybvs`waq_r~o_p]waï°Ŷƺ¬rkkjigggeefihfggghiijfefdegihhfedffeeffhgfghhiihk{gik~hp^~l\ljhgfh[u]u^pYqZmSkSq[p]nZ|lrdl\zƼz`e~drZqZmXu`~e|c}ev\s\h{dxar\|dkprg}v¿{v®ϭzrgxexcv^{fs]wbn\}k|ramXp\ĶŹŸ}wjkjiigfggfhhhfeehhhhfdffdehjiihffghfeeeggffghjjih~g{dhiq]npdzolfd~p}i{dxaw]lQlSnWnYm[}ݵffs^n[o\jYzwx`nw^mVkgs_p^xϐuoŶ򵪫۹zzػö}|uhvf~ju^ydwa{fr`~lx}mp[nWŹƺõõnklkjighhhghhgghiihghhfefdghghhhffeffefffhfeefhih~ggz`|bgzbvbwg|qrmhewzhyaix^mTpVuZmYq`|ƽvav]oZs_yap{yfwwcusùҟ⽷ƹº»z­{wfpzgm|gxb|d{f|iqubr^źķ±Ȼŷillihggjihhigfghghfgghggedhfehhggffghffhhhgggghee~ddeezbvas_m\}uȹhfzr_u]~f|cs[rZw`zdvayƼĺ|l]q^q\|gp¨{~pŸҾĹѼżۭ»ܺ}~qj}gh~f}ivsbq¸ú·óƸ°qhikighhhhiigfgghhgeffgggffeddhhghgffhhhhhffhhffededd}fu`r]mWlYueƶ~ggusr^zcxa|d|cycxcwqhȾzsbr`}nw{gug̤ĽŽ¿Ǿïózkpqmmo÷µö³´°lkkjjkjiihhhhfdeghigfgffgffggffgegigffghjihffhhedcdeef}ezb{bpZr\ycq_{o»loqlzbsan[r[r\s_}cwd¸ƼǺŷ}pa}vf|ˮ⿷ȺϺʿǾϼźnjrqun}õsiiijkkjihiiigecdgghhgfedfhgfggggeefgggghhhgghggffcegfhhggxaybg}gzf~ohh~e{dlWoYmYn[xeĻͱy~xl껵ɨyuݺᾷżŰŹɯĻ}kq`xfzg}xw°Żùpjjjjjkjhiijjhgeghggffeeghfefgedfeeefggghhggggggdfdcfhiihf}f}egeeyzemWndop_o[o]n[zlƶľ}~uqezxгƽųżö篠~jvgsd~k|{~pñ±okjiijihhiijkihgiihheedehfedeeddegffeghgggfffffecggffhhijhfff{_~ahr_oZr~exjmYp]u`qƹ~෰ŽóŶǻİŻzl۰ryui{kƴmmkihghihghijhgiijhgeddefeefggeddeddfgeefffhgeedejjhhffhjhf}ed}a~a|^pYmVvr|e}¾iXo]n\zg|zu{Ķɞξ´~­δvhט{w}wzªxmkjghhhigfiiihihghgfebddeedgigfhedddggghfeghhecfhikghhgghg}e}efhgeqZjTjxonY|iufwk\wxȽĽ̿ʺռ±}ĮµљzyĸŸvrkkjggghhgefggfgeeghedddcdhgghggheedcfhggefgggedfkjigghihhhg~ffgh{coVnXjVmYu^o[p_|zz}ĵÿɿ˿˾ʽʽıvy¨ʻʽ¿ӽǾҿzliihihihgfffffgffggecegedfggegggfeedefhgffeeybffhhhjkgggfiihg~h~ggix`sZxawas^o[werźӭ~ǿȽȾɿɿɼʼʽ̾{xxxv˻ȿŻ־¹νөskĴ{ihhhjjihfeeghihggigefffdecdgeegddeeceeefeedcfgfhiikjfffgihfge~fgg|d|d}efxas`ygŷĺ־Ư¿ȽȽȾǽȼǻȻȻȼ­{xxxyzxǺ~ĽȾü̾ќ~ʺhhghiljgeefijjigghhfdeefecddggde~gfdddfffgefdd~aefhjjiifcegfffedeff~e|d|d}gwevanȷ̽ǻķ̾ɿʿʽŷǸɼȺȺƹǹȷƶƸź¯}zxxxxy{ȹ~ļԽǮjiijkkiggeegiiiiffigfeeeecbdggfhfffdfeefgededz]~aghjiiigddeeffggghg}c{b|e}fuaoXnY~|ճ}ij´ȽȼǺȻɻɺȺȺȸƷǶǵǷŹƼ|}zyywv{|}ŷ|üö°íwjil~ioqyjkjjihhhhefhghhggghgfecedddceefdcegdffgedeegfediggjihgedhfffeeeefw`ya~fe{dqYmVr\|jѽľμκϽĴȻɼȻǺƷȹȺȹǹƸŶƶǶǸƺȿ||{yy|{|z³{ǽznklmsvnnrjkkjihgffgfgghfefhhgfecdegeeddeeccdefhfgedegffggghhjheehhhffhfeed~e}fff~gw_s\{cilopqɴ˾˿ʽɼƹŷŷƹȻȻƸŶŷƷǹǼǽô{}|yyz{z·źrw}nkkmnxilnkjkiiigeghjhghfedegghgeeeefgeefdcdeh~highgefedfhffgijheffehgeeededfjggi}e~ejhjnoomr˿ʿɽɾʽƻƹǷƶŶŶƹȺǸŵųƵŷŸȻ{~zz}~zx{|xȸĽzrqqxuikmpq{pfhz|ihgijgggghiigghhfgeghgeefeeeffffccdfgiihihhgeefg|eqYyahigfddghgfffddddeeegghghikmmnm˷ʺʾȼǼȻǹĵƶƶŴŵǸǺŷĴųƵŶƷƺ±}zyz~{x{||zĿ|opvvvo}iknppzzj~gj}qohhffihhhfghhhhghgigiggfgeddefddfffffghihgghfggfg}emVr[eeefedghigggdfdddddfhffhhjlmmmsw̾ʾɽƺǺǸƵƳƱƳijĴƸƸƸõôĵöĶȽµ{xzz~}|}|~y¯xosvwuuwnmquwysqihuxlkihgghfeefgfgihhhhjijhgggfeedefeeefefhgffgfgfffehhs_xceffgeegh~hhhgffefgffgihhhgjklopqqq̹ɼǺŸƷƵƳdzưİıijŷƸĶöööŷȽʾŸȾµ||||~}|ĭêx}ipvwwwuvuorsuxuhhjqplliijhggfeegghhhfgghiiihgghgfefhgegfdefffggggffgehhh~gfggigeffgggggefgffhhihiiikklnnoppʻɼǶŷŵųųűŰïïİųĶĵŴó³õĵƺȾɿƻµķǻ{{{}}}lirvvxywwvpnqvvme~ehnzzolkjijkghihggiihhggfgiihifgggfgffeeffeeddegeefegggggihgiiiffgggfhhgfffggijhijiijkllmmonsŶŴƶij±ðïĮðİįİij°ŰİóöŶŻƻ÷ɿ{}|{Ź}jirvvvvvuuvqwvwm~f~e~ervj~hpljjkhghhhghggghihhgfffgfeedggeeegfffffcbbfeefefff~fff~ggghihhhhihiihghhiiijiiikllkmnnnnmmðijñİð®­îįïðįĮůijöĶǸŸ´Ƿokpvssuvsoooq{~og~fhj}kismiiiihhgffffgffikiihhecfedddgfgffheddeefgeefgedehg~f}e~dghhgfgihfhjjjhhhiijhhjjijkkkmmonnmlİï®îïîïóõijĴñüĺɿîvporsropromoor{qfghkh~fkxyqmjghfghgghffeffhgghhhgddegedeeeeeddeccegeeeddecdfgg~f~feggggghgggjkkihighiihikjkjhjjmnnnnmĬ¬¬¬¯­ððİݶ̿пyspruqppnqlijjlqn}hijoignsywmmliighheeffddffgfehighcbfgeddeddfbdceeggedefffeddeghhgggfhgeeffjjihhhghhhhkkkkkjjkmomnpm|®­Ʒxuvppurmpspnkimnkhs{mnujlvwvvjljjhgggfdeecfggggfeeffddggfggggffcdedegggfffggcccdggf~ggffhghhfgijiigggigijjkmkklkllnlmpmw³ǿȾ~zwsvrsolnpnkimsvqhvkjlvwwkw¯mkiifedfehiggggfgfefhgdeggihh~hgghhggedefgggffgddcceh~g}ghgghhjjiijiiihghiijkkjkjkllkllmnposǼzswuwxppppqonkjmrwr}gz|hjp}}ko|kjihhhedefhgigggfgihgggfghgffhhiihfefdggghgfffdccdfhgfgeegijjiiggiijiiiijjkjkkklmmmnmnnorĸʸukossvvspoopporrqqqnqowrvywyxzƮijz©ojjihiheeefhgffggfhggihhggiifeghhghfeeeeefgfefecb~deffeggdeghiihihhifhhijklljjkkkllllonmmpoijxmrswxyyyyxvzzuwrusxwzwvyz~ȴhjml}igijjihggggigfddfghfhiiifhhgefffffeggfdeeefffgeed}d~eff~ccgeefffhhhgfgeghghjkkjjlllmmlkkllnpp~{vwxsvxsw~|y}wuvvz{xx{|{{xghhnhjlhffhkihhffdghgedfghiiiggihggfefffedfdddghggf~eddcegg}e}ffdddegefhhghfhhfiiikjjlljkklmllllmnpqxij{z~yuqjmxrqopzyxvxxwvxx||z{yyyyhffjmijjggfefihghfehhhegghhhiihfhhhhffhfeeeefeegghhfgeddcc~d~f}e}d~eeeddeeehhggggihgikkjjkjkjjmmllllooppszs|z~uhh~gj~i}hlsuxyywux|zz{z}{xwxz{gffhihhhhgffegggeeeefgfhhhiijighihhiffhhheceeffgghgf~eedbbc~d~e~e~b~ddd~deddefffehffhjjkijlkklllklmlmnmnnmsǹ~rswyvs~xwɹx|rf~f~fkpsyxxzx|{zyyzxxy||~gghhihhihgffggggffffeghihijjihhhiiijhhhhfeceefgffgf~e~edccbdf~e}dd}c}c}d~bcbeffgfefffhjjjjjijkkmkkkmmnpnmnno³ǻyvupu~vx}}zxpppqwqnqy{|{{®|zxyyxyxz||fefhihgjighefggffeffeffffhiihgghhihiiggffdddefgfgffddc~ecbcd~ed~f~db}`~`{addddfddeffhkjjlklmllkjkloonnonnnmijxz{lmxzu|{zzxvxyz{zyz}|~~ɽ{y{|z{edeihhghjiffffgggefefggefjihhghigighhhigecccfihefhfdd~dfffdeg~e}dbbb}`~_ccceedefefhikmlmppnkiijjlnllmonlm{~~~|sv~m{|}~xxy{zxxyyy{||}®ȼ˾|}|ǻתggggjiiiiihhhgeefihfhhhhfhhgggghhihggghgebacehfeddeeghf~ghefhfcbceba~cdcdegeefiiimnlmonmkggiklmmmmnollv´~|~ws}r~~|{ywyywxxz{z}{y{ȹĽ໪}}óѫi}g~i~kjjjhhhhghgggggijilihhghhggghihhiiihgedcedffdddgfgg~gfee~fgeddcecbcdccdffhiijkkkmmmllliilllmmmmnooor´Ÿzxzy|p~q|xxxywvwyzzy{{ĺm~lmnljhhhihhghihffgillkihhghggihhhhhiihhgdeefgecddfgffff~fggfddcccdcde~eeefghhhijklmllllkikkmmlkmmproppȻƹ}~|yvwxyyyyxuw~̾lopnmkghjjhiiiijhhiiiigggghigfffjigggggggeeeggeeegiffffghhhfegfefggffhgghfihgiijklkmmkillklmmmnnnpoprȽx|yxyvvwwwy|Ͽ­ľǿ欄lnomnighjihiikjghiijiihhjiijifefiigeeeeeffghhjjjhjgfddgjihhgefgggfggghghihggfgfhlklmlllmmllmmnonlnprsĵwuz|wz{|zzzwxzϹÿ̾ijȺ̾Ⱥ|exdkkzdg~dkkkggjjihhijihiihkihjkggghgfffcdeeghiijkljiigddgijjhhfee~ffggfhhghijihfefgjklmmllmlkmmmnnnnoprrµxvqpvx}|}|}}yzzy}{~ɺô˼Ʒʿɾxav`zcjhs]u^kkjiijiiijjjijkhiiihkjjkhihfffdfgfggiijjkjlhffeghhhhhhff~f}fggfhghijjigefeilkkmmmlljkklnooopqqsr{}|xuv~~px|}{|~|Į¿ǟxaw`s]}dg{biklljhhgikkjjjjkhhghehkkjhiigegfhiggfhg~gze|ekljjhhgghhhggghhhigigfgikjihfggilllmlmkkklmonnnoopssqrsvu}~ouq}nu{~|||dz±Ǹw^s\v_|cu^hkjjiiiihiklllkjkkhhi}fya~fkjhkkhghihigfehffglkkkkjijhhhhhfhhgikiiihijiihjjiijkjllllklllmnmmnqqprqpsrqõ~pyx}~|}´ìȾǺ˾¿ܠx`v_}cu^u`|g|ejjhhhhhillkkolkjjjghhjlmmliijiiiifdeedffjnmklkihhhighhhiiijjiijjijhikkljkjkmllllklljnmnqopqqpqu}g{g|s~Ÿ~ɶɾʿþƱưī}fi{aw^~b{a|eihgggihiijkjkjjjjhhilmmkkjhijihiigedcb~c~e~f}gze|fljjijihgfgiihhhikikkljijijkkjkkklmmmlmlnonpussrrvuvzcyaxŸŽx~xz~ɼʼʽ®Ⱦ̽ʸ˸̽ʻɻĴƷ­ƫ}czbs_{_}a}ehhhffdfhgfzeiiiki{fvcub{djknnokiihkjjhgfeddbbeizevcxd}iklk|f{eihhghiihjjljjkjikihikjjiiklnnmljllmopsrqmsrqzy~nñ̹ǰ̻ëx}}||ɸλĦƫưȴêűűybxay`}`fhiiifecehg}eu`fehh{cs_waqauaydxb{dwa~elljhijhgfeeeedbcfkjknkkk~f~gggihg}hnjklnlilli}ghjiijjjkmnmonmkkmnmoqrqmrqswx}zxwy~||{̾ǸνɽɺȶǷƸƺuaq_r_{az`|cjjhddd~egii~e|d{gwdn\o]s_wbsaxbxau]s[s\x_~fjkjjjhggiihhdv]x`dgloqmmliiikiihjlmllnmllkwdyelljkkgmmhhnqpolnpnnqqsr~jmo~j|hziyi}m~|zŶ˻ɵĬ̷}~ñۜr\s^p]u_xdihgefed}e|fhjhxcj}hwdv`v_p^n^r_var]q^u^v]w_ikllkikii~f~df|czbybzdikmlloommijiiikkmmlnnlji|e~holjlijmmm~inppnmnonoponn|fycydq`{jnzïzϾƮŮħŦũƪ£ũ~ħɵƶƵíî˼֦b}ahkqlfede~d~d}d}d}eih}e|dgi}g}eydsaq_ubwas_waybzdklmlllkkfu\ya~eef~f|digzf|g|f~gjjjiihjiik~gmnnnmkiijknmkklnopqpoonqnpooonmkigln{mصýþƿű­ƭåɱŪ¤ĨàĤŦĤŧť~|ɷɻêþ֣|ew_imggedef~e~d|c|cgi}ff{excgghig}ehjhlkhkonommmmj~d|dihgffhg|g|hxbubuayczd}ehiigi}f{dlqpoomkmnonpolnpmounpsprpoponlkjjinrqzlĽľʹƱǯįȸéĥʵŨʸë~ŤƥŦ¡éʽǻĵ̾þĦɷþРu`u`|fnigfeff|e~e}d|egg}fg{c|c~dg{e|f~f}ef~fj~hiikljkh~g|fydzb{`}djjieedf}fweyd{d|e{e{fzcgi|d{czcxb|djrrppnjkoooqnmnjyeudwf|i|jluvpmlnnljigg~hn~lyjư¦ æȵ£¥áŪ˻}žǥäʶŵŵŴ˽ìο˿»½ƣv`gpheee}e~ee}e|f|e|dfhjhwcxb|c~ezeudsau^v^y`{e}fhxdsdwbwav`{c|dv^pZr\q\u^w_zaf}d~efgjmkikmplkigfffgoohln~gxa{agklpze}izds_s^r[v`zgweudllnmmomkkj}ehlzk}n|mŸġŸÞɮĥġĤ ǯƳȵ~ȴʼ̻˼̽ë˵ȳưϾǬʳοİŭĩλ~eeev^y_|`r]{fhg~e|h{h{b{cfijm{eyafegze}effzflxdo_n]p]q^o]u^y`w_u]r]p]q^q[nXu]ybv_yafjppllpnmkihghhhlnnlkg|gycw`vas_s^r_u_r]q\pZqZs`vcxe|i~lpsppnnoljhijziqzàŪŤáŬèƭ  ̽Ͽ««ǸȽɻ®æĨϿпǮũϸȳ˼¼˿žɺħǩ˶a}`w_v\{]z^oZwf~igb}e|e{a|afhmjygxc|f~f~e}deg{f~ikwds`w_wbv`xczex`y`ybwav`s]sYr[rZq[u^zbjkmnmnqnkjihghgfhnsnjjji}i|iyfzfxdzfzfzcu]q[r_zdmrrqsqrqpnlkkihjwhzǮžƬ䝡ã¨à̽μ̽кʶ˺ĭģã̸ȳã¦Ǭƪʵ¹ɺ˽dzƨƤƥȫz]x]rXuYz\w\q]{fxdyb}b|b|`}`}a~bgjkj|gydvaycza|dgh}ip^p]n\r]xb}hl{gwa{digieza~e~fybzd|ekkknmlnmmjgjihggilqoljklrvurprlmkzdxckqrnkyfkpsqmkkljhkwjʰæßç£äʹпǰª¤¡Ʈ˴ħĦä£̶¦ġĤƬƨǦȯɷǴdzĨĥƦƥȪx^oWmVuXvYqYs]|fzfs^s]~c{_|_|^z]}_ejkkj|g~e~fzfwck}fxdwbq^xbzdw`vaqasdvdgghe|dzdiki}hgljihmnl}i}h~hiighf~dgm~jhilnnpnhj~gzcfgzcyd|h}g{eyhsdxe{gweormk}i~i|g|g~iviŭDZɴĬ̿ȬǧŸßäȭťĥ¡ģġɭǩɪǫ̺ǮƭƦťà ɯs]oZnXvYsYjVo[s]p[nZqZ}az`}^}\{^|a}fhj|h~hiiihjk|d{c{g~hlniikolikif{`|a{cwc}j|h}f}fkiyb}gnn{h}ghhhgge}cdybze|gziojzc{c}eyaji{cx`u_s`wesdqcuauaucygxeygmoljj~h}i}j~kxiŰ©«˼îŴŸºêĠƣǥĥĥããžãŸǥȧƤɯʴŤǨĥĥιrZp[p[sZmViUyby`s\s\r[s[z_~_z`zazazdgigilhf~efzbs]u]v_}hkhhkmklkkiiegnonihilnlmpomjiihge}c|dzc|fze}h}jnj~ghmnlffv^oZr]sbuc|j|kudwchzfvg~kpplj~i~h~jwg~Ʈʶ¬©ƥšȨŪǫŸŸàţȩȫȮƧȧƤƣÞŤºrZpXpZjUkTqYy_w^zau]s\u[w^y_y`zazazc~ehhgzd~ggwbs`s]p[xaxar\r]s^zekwep`vck~gggekkliihghhimqlkljhhigyb{e{gkvbucnjhlmpokkjf{bzbs`wd}kwfscrawczf{jopol|g{f}g}kxhϼȹɸijDZªʷŪūȫŸǩğĨǰæážÚßťåǫ˶¤ţšƤçǭĨ˳oYrZs\kUkTqZs\o[q[q[s[u[q[u]{_|`{cybwb|fh|dzdeixdu`w_y^|au^s^}d}ejirdpe{k{gu`ubr`wa~gvauau^|c~f{f}g{eyazbye}hjlihhhg~f|f|gj{fubrbvc}ekijlyg{fi~e}dv_s_sczhzixg|inusqurkk}j{i|lzkƫºðʶϾ̺˸̻ÿȽǨŧŦ˰ɮ˱äĤǦŤǮºġÝĠğğàŦèϸ£áåğÝŦȯàååpYs[mXiSlTu[r[q]r\s\w]rZr\z^{^|a|ezcwa{eyexbf}dfycu`wa{by`u]v]d~fze~gnoxfr^r`qauazdyaxcvav_u_ybkli|g~gilg}eycv`{e~dd|d}d~ejrqmmopnoppnnlh~g|h~msvorwoyg{hzf}ikj}i|k}lzlťĬνůɺϽ̺̺˷˸οȾƱƩƧŠģȬȯѽŧǧãĠȤǡȩɭƤĞğŠšƧī˸ʰġţäáàŧȪžģťäqZpZmWiUoWqWs[v_v`w`zcw^x^{_{_~b}czc|c}b}egzbil|gydhgv_s^r\p\u_u_vbkorar_u_r`r`var^s`u`w_x`ya|djijlliw_w^r_o^s_v_r^xav`xbxb{fuvnk}impmkmnqnmnvvozg~jnxixfwdscs`xd{fu`zexhymĦçŨů˶λϻɴʳýɾȵƨǪƩåƪƬʴɰàŞǠƣǣɡǡơşěƞŠţġƪʮŠơťäƩǩšĞĤġţŤmWnXnVpXpWqWu]s^zf{ejjy^|a}d~ce~c~de}dgiomkji|g}g~fv]q[q]p\xdnjs_v_x`x`xazbr]q^r^q^ubuczgiihjkybs\v^v^u_r`wbx`|c|dxbq]p\{ixgq^s^r\x`|f{du_v_xa~immkyhvfucwayd{fyfyfvdvdvbw`u_u^x_xeqŦãǰνȫ˴ǯɷȸǰǧǦƦĤĬʽǸǰƪœĝơšƠǠǡƣġŞššƤßšţƣġŤĥǬãĞšţģġu\qZqYrZs\v^w^v^zc|fi|eoZx`e|d{dc~d}e}c|dh~hxcliwau_hl{dzeikxdyfyb|d|cw`w`|a}bx`v`waydjikjjkmk}gxbv_xavbr`vayadd}dyezewas_v`u^y`v]w^{czcu]q\q^ubsaqbuescs`xaxbucyhvgsfveygzfv`v_v`vdy¤ȶʳǦɯȰǪȩȧʭ˰ʴ̽ɽȴƪģĤĥġĞơߞĠĠģĤŤÞĞġġž¡ʭĤÞÞĞğťĠr\pXs^w`v_sZoXpZs]w`z`s\y`w^v]}e{d}g}ew\y^}cfxbxd|hxcs\qYy_v]q\s`wa|fq_n[o[w]y`w^~ee~cs_waxc{elnpnnonlj{g~h~g}fzc|c}d~eff}h|iyeucvbq_w`w]v]x`x`u]s`uaubxdkvdwcvavbxcxfwgudwhyivdscs`ye}kvfDZĩ̼ʵǩŦ¡ãťȨʬʬȫǬƦƥƥţƥģŸÝÝŝĝŞğޞ¤ ßĤáŸĥãŸàġĠߊƟnXpZu^waxb{g|insizbxajwbo[mYx`g}g|e{b~dfxbyf|hu_v]rYz_sZmXn\q\w^q^p\nYoZrZrZyaya}def|dycze}hlpssonqmilmkje~ddd~f~g}gzexfudwdvbs]u^r]s_q^uasdwc|f|hucu_wbye|hzivfzizkyhudrbzjqvj˽ȶɷνȴ˷̽ȳǯǰʱȯƩǧǨǦȧ žĠĝĚĞááãŞšáƧäãģĤĦäßĝÞu_xbw`wd}lxi~dgkmlix_u_{d}g}f}gf~f{fxgzfydr\u[uZpYr[r^o]u^r\o[mWnWoYoYp[p]u^}a}dgj}ilomo{iuax`}fjjllkji{e|d}d|f~fffhlp}iw`u]s^u_ydwfudxds^zcmyexexeqdsgyjzjl{h|hnwh~owmȶ̿þʽɼ˾¼ɸŪǦǧŤŸáŠĝğĠġĠŤģȭéģĤģŤ ŸÝŝŞv`r]r]|iqyxyg~n{gxb}ghya}cgu^s\w`}e~ezd|f}g}ghwcu_q[r[pYqZq]q^q_qau`q[lUnWpYq]r\s\s\q\q\wa}h~l{iuauaq^q^s_u_u^vazci}fvavap_o]o]q^zd|f|d}fmnj{cw_w`r\}g~lzgydzcvbrmwdrcufug~nom|fzf}jwh}m|qŴνοîºоϿƬǩŤģšŠơǡƠşƠĝŸáģģťáƫƬŤŤŤŤžœġğĜv_pZzfrrrs`o[qZs[p[o[r[oWnXpZv_v^zcxd|h}g~gi}hu_q[pXoYo[wa{cv_{d}hr\pXs]q[p\q]u^s\o[n[vcygucwbubq^r`r`v^v^s\s[r^r`r`q\o[mYq\yd|f}fwdwd|g}e~fx_y_|c{dorrnsuvksbp`raudygucsbwcygxhufwhypο¾¾νϽûƿоȱȮǫƭƯɴɴǯǮȭɮɮ˰ϳɮťŨɱùξǯǩǩǨȥţğşŞŞƧȪĤĥȫħƭŨƤţƣšĤàĠß¡~iq`kur}}hr[q[p[q[q\rYuZoXkXlVmUmToTpXs\rZsZ|cjhzdycr^q_s_u`wav`v_{av^r[u\u_u_q^q]o\q_ubsbse|gwcoar`u_v^w^s\s]q_sauas`s]q\w_|c}egvbp^u^u]s\q[s\q]vaqzpzg}ixx|is`r`qbsdqar`s_uawewgvgrdxʽ̽̽¾˵˷ʷ̶ʴʳǮɬȬȩȩǫɭȫǪʬʬɫɬʫɩɨȨɩȪģģħĪȯ̷ɰǬȰʳǯDZȱƫáģƦģŤɫ̷ƫǪƦǤƣơŠŦťáä¥xmo~|f{_xap[q[r\u]q\sZv\v]lXmWlVmUlTmVkTmToSv]zcyb{f{gygk{dr_xdq^q\sZs\v]r\u]u_u`zer`sbybydwfkjxcwbxahjhixcvbzezdw`s]u\sZsZx^s]q]q\s^u]u]s]q]r^muydr_wbk}mzev_uaububraucs`q_vcxgxhqe¾λϽüɷǮʭɭʭ˳ʶɵʶʹȷȱǭūȫɭɬɫʫ˪˪ʩȩɩȩǪħƥǥģßġƦǨɩǬȯʷϼʶ˵ǯƬƬϼȯƤȤȥǧšƠǨŤà£ƫufyivb{gp~fq_wbzcr_p]p^v_x_r[u\x`m[w_nWnUmUlUkUlUmSqXy`q[va~k~kkj}fxbo]r^}fwbr\v^u]w`{fxdwd|g~h|e{f~jyf{ezbxb}fjjk{dzeyeuaq_p[oYpYpWnVoYu^v`s`q\s[r[u^w_k|gq^s`vaxcsar^v_v`r`ucyg|l}lscxgyg{jvg˹λ˹ʴɳɱʱɳɸɽƾ˻˱ɮɬʪʪ˫ʪʪʪɩǨũũƩƣġũɪʫȬƩŨŨȮ˱Ȭȳ˵ʷɱǧƥǦƟǡƧţßĥǬvfqcsbwczcybu^r_zbq^n`qbsbxbv`yar\p]q[nYnWmUlWlVnVu\w^r[oYo[wakk{dzcvaq^p^zfkzeycyczb|er_s`}f~k|fuauczeydvaw`v`ycyfsbzfzf}f~g{hq_nZqZpXoXpYpXs[w^x^s[r]wdmis`s^v_s`vbsbr_v`vavc|k|kyj}kwevfxg|ioc½½нϺ¿üʾʹʹɺ̺˱ɬʪʫ˫˫˫ȨȨƧŧƪũƥ¤˴ȰǬƫƫƪȩƪǫǩĤãŪθпʸīĨĪŦšƣũǭ~pyj{g|gnjx^qZy^{c{g|j{izfhhq^uavan[q\v`wauaua}ewalZnZu_v]{b~fycxbvao^o^~hmpo{is_v_w`r_uaxczfwdrbudrava|dvas`yfjhrb{ekk|gs]u\sYrYpXpXqZs\qYs\ls|gwar^s]u^s_s`q`uas`r`vbyiyhxfvfsdzhzire¾˽̾¿̾ιɱ˵̷̹ʱʭ˫˫ʫʫǧǧǨŦŦŧƩǩǩƪǪǪƪƪƫǬǫƧ  ơğĠũ˵пǨǫƨŤrrwnxnxbs[|cj|f~iyf~hkgs`~gydvbhk{g~j|gs`q`q]q]u]ejhh|gwcyflwcr_ubxdsbq^vavbyb~fgkygrascsbvcyfu`zdl~g|fxeqazf}hjg}dv]rYoWpXr\xcs]yd|g~jubq_q_v`{cy`war`s`uaq_ucxiwgwfxh|ks|luiξ¿¾пλ̺κϽϿϼ̸˵ʮʩʪʬȫǩʭǭǬȬǪȪɫǫƫƩĥƩǩáãťƤšťǩĥ ©˷ûεǬŧäģ|vrponnik~hvcvbxfzfzbu_uaual}knjzdu`s`raq^u_s\qZybjmqyizgku`n\m]n\q^wczd~hkhj}h{dzdudxdhedfyar`wdvdvd}i}f{bx`{bgy`pXpWuZs\r^u_v`yesaq]q]s`wa}d}cs]u^s_ua|hwewf{k|lqp{lvjý¾ϾοξнλʷǯȯDZȱ̴μɹɵɳɱɮɬƩƩĤţŦƦţƥƧŦŤŦƥġŨũŧħƪŧĤŦĤvoxv~jxcxcyfwbvcsau^s\v^s_ucrbyf{gs`v`u`r_s_vbu`q[nZs^zfqr}j~iw`u_u_}gihkkikjw`qZs^xfijhc{]{azezhop}hq\oZqZqYv]v]s[p[s^u_r\pZu^yds`p\q\q\r]u^|d|ds]r]q^wbwg}lnzrxmqi|v̿ξϻικ̼˻˻̻̽ýÿμ˺˹̶ȯȰDZŪƤƦƦƧŨǧǦƥţŦħĦĥƧŨĤŤƥĦw~jnopxfq^p]r`ubs`q^nZoYq^q^o\r_q`q_s_q^r^r^s_s^p\o\o\q_ycloi}gikkjjijjjl~hp[w`{glkybx_u[w_}ipklwclWo[s[rYnVoWoWu]s\q[pYpZu^q^q_r_r^q]q[r]wa|gwcu`r]rbyh|jzky}pzrϿƶ˶˵ʱɱ˶̸ȮǩȫʴƽŪȤǤƥƧƨƦǥǦǥƦæåäǨƧţġũɴzk~jlsrqrq{es_u_ybw`o[o[r^r]oYnYs^r]q]q\pYpZpZs_s_o\n]q`u`r\wamkrqikkkljjkkmhhlmxdu]s\s\s]zcoom~fqZoYpYoWmUpWoXpXu\v^s[y_|cr_o]s_s_r^r]s_u_xbvawcuarasdvfwipzn¸˻ɶʱɯȭȭˬ̬ˬɪƩǫ϶úѼǮǨɧǥŦĦäŦƧǧǩƨƨĦĤåèƮ˹ڰwsqsqurppmkhij{bp[nZs`u^r]s_u^s^p[qZqZpZqZs`s`q]p]xcxbr]q\x`ya~gk}hkihh{cw`ydzeybu`{c~f|fr_q[nYq\}el|gj}dr]oYoYu[x^pXoWpXnWqZy_~dix`q]q^s^xbxcr_r_s`ycwauauas`rayhzi~rξιʱɮʭ˫̫˫˫ˬʪʪɪɱüϹ̶˴мо̺˴ɪʫɫɩȩŧĤáǪƪŨǬǫħǯ˸nprvrpooomjjjkhzcs_s`ubvau^s^pZpYpZs]u\r[s`u_r[p\s_vaq_q^u_q]s]v]u\s]wazfyb|cycr^r_q]pZp\r`u`u_oYr[xbzbycxcs_s_o]nYpZu\v]pXpVrYu]q[mXw_j|f~i~hhiwbr]r_ucubs`s`r_uavdxhwn¾¼»̾κ˶ȯɬʬʬʳʰ˭̯̱˱ɳɱɴʵϷϼλʳǪɩǧƧȧȪɬ˪ʪɪǨƧäģģƫǫĦèëȵſɵũĦoprssoqppmj}ehijli~i|ivemYnVqXpZpZpZr\u^v_s\q[oYo[q_r^q^s_r^q\rZoYoZp]q_s`yaik{er^s]q\s^s]r\v]w_zc{dh|fxdwbp]oZoYoXqYnUoUs[v_u^r\s^}hkm|jl~i~hx`r^rbqarbubu`vbvesgǺý¼нмϻ̸λξ̼̹λ̹˴˱ʯ̴̱̳϶Ϻѽп̹˴ϵϴϴ̱ɭ˭ʫɫʫʩʪʬɪɩǦťţàĤũŤ¡ƮûɴũǧɪŨopq~plpqqkxbv^x_x^y`fhhkvgp_mYoXpYr[s\r\s[rZr[q[r^p\o]r^w`u^q]q]p\q\o]q^v_r]|bhzdybs_r^sajycr^wbs`vb{d}g{fu`zeoZoXoXlVpZv^xazbv_w_~gjwdscrcqcze}ezbucueudwdxcwc}j½üľ¾оϽμ˷˸˹˷θκϻιηδ̰ʭɬʮʰɱȫȩɪˬˬ˭˰̰˭ˮɫɨɧɣȥŧĦģĥĦȱƳëǰλʴɮǪšƣǧƦǧʼ|qqkw`v_w^v^v]v]y`ijum\lWqZr\q^q\s[s[p\q]r^q^o]vair^o\o]o[q^r]s]r[r\w_~dhfwa~ipo^q_r`n]q^ydydw`wbvdv`w_v\w^xa|c|cwauaubvb}in{i}kzh{ezcloxgqcvfvep©ɴƾľϿϿþо̼̺̹̺κ̷ʱʭʪɪɪɫ̬ʬʫʪʬʮϳα̭˪ɨɨȦȨƦŨƧŧȱ̼˽ҿɮƨǩȩɫɫȥŠƣƤƦȵŬDZøĸ~nih{cu^u]w^v]waycxby`dsvhmWmYsZv[v]r]r`|g}guawbxcq_n]r\uZsYsZrYpXr[u]x`gh~f|evan\r`u`r^u_v`|d{d{ehhfy_|ewbr\r_q_r_sb}hsp~hjjyc|gpzirbscwh{kɬǪǫƪǭο̼̽˹˷ɱɭʬ˫ʫɭʬɬɫʫʬ˯αή˫ȫʮɬǧȩȫȮѾ˭ȩǫǫǫȫɪɨƥġƥƦƦƧȦǧħ¦Ʈҿ˹y{i{gj}fyc{fxcwbucvdvdye}g}zwdwaubyh|kze}hzfxdvar^pZv[uYuXuZs[oYrZu_r_v_}d~dc}eydycwawav^s^v`x`v`hhg}ds]q[o\p_q^r`saxe|dx`u`vaubwd{g}iziverbzjoɪȫȮʮɭɱüοξ˼˶ɰɯʰɮɬɫɭɫ˫˫˫ήϴϷзɫɪʰʰ̶ÿʴȫʨ˫ɭȫȪƪǧɨȧȧǩʯɫƧȦȦǩƧƫηռԻԽӾȶvrppoqnmk~ls{{pbl[q^zc~fx_u[v[sZs[s\p[qZq[q\q[rZrZz]ef}e~eu^p[o\s]v]s]~eze|dzas]q[r]q]p_q`ucubs_vavbwavbzg|h{gyexeraxeläˮɭɮɭɮʭϽýϼη˵̶ζδʱɯǯʮ˭̯ϱεϹ˰ʧ̪˰ιľȮǧʩ˩˫ɬǬɪƨǨɫȫɬε˳ƩɯƧƦǨƩɰӽӼӼӻӹսõÿ̽ɷȷ~oquwurpszȹĵ~nh~eyav]u]r]r_q\pXpYrZsZpXqYrZ{a}dxaw_|cgv_v_w`u\u^u]s]p[u\u\r\s_p_q_s`sasauau_w_u`xdzfyfvcvaycilƩʮʮȭɬɭί˶Ŀ¾Ͻомлι˷̶δεδ˰ʭʪʪɪɮûưǩʪɧɧʨɪȪǩŪƭɶμ¾̼пƪǫĦǪһӽҽӼԻԼһӼѼѼԾƶʾs~krvvunlqϽxkhg}e{eydzeybv^r\s\u]r\qXqYv`{de~cw`xb{d{d{c}c|ee~du_q[pYpYr^r_r`u_vas`r^u_s^u`xdzfwfxf{g|gl̴˵Ϻ̶ɭɬ̰α˴Ͼÿÿ¾поϿμ̹εˮʭȩȩǨηϽƫƧȩƩȩʪɫȫǪȰ̹ÿǭȮŪŪ˴ӻӽӼԻպԼѾҾԾӾҼӻҹз̶лнǷǶyvwzzwpmzbzfuqlijighjlkjkjjzdz`{cgih~d{bzbhnolkjikzjm`n^n[p^u`u`zbw_s^s_ydzf{ixgygyhykѼѻѽϿ˵ʭʮʬ̫˫ʫɮĹľ̸̻˵̴ηϸκľ˶ƦġƥȪɬ˰ιιϺ̸ǪҽϼżӺԼԼһӺԼԽҾӾӽҼҼӼԾԿҺкм˼ιzw|zzyql~ejrnjjijiijjjmmlkkkjiikkigmonmkijsŭŽyorcvcpknh{cwbygxgvg~oѽлѻϸ̵ɴ̹̺ʭˬ̮̮ʮɮϹſϿ̹˴̯ʱž̳ƧŦĤƤɪɭζϻϺѽϿ»½˴ŨĴɾ»ԽռԼӼӼԽҿӽԼԻӻԽӿ÷ϼýӻrqsqng~fosojjlklkjkknplikjgdilljiijklmnmmvʴľynxdzdzewdrqxűмθ̷ϸϸθ̻λ˹ȰˬˮɮɭʮȮѼϿ¸ÿýϽξοϼɳȱѽ»ʮƧƦƦŨȫǨɰ̷̷ϵΰϵʶƫǩ÷ź¶žƿӿԾӽҽӽҾѼԼռӻԼԾķѿýŷujlsvro~imoll|jih~g|g{fyeggignokjijihlkmnoomųɽʾȾx{qѾѾϾϿοϽλ̸ʶʳʴ˶˴ʴ̷λεɩȫǪʰɶžϿžǭǪȪȪȫǪɮ̸пʷη϶̰ʱ̻ǩǩźԿӾҿѽҽϽԾպּּҼƵſŰ{qpnn}j{ixg{jzi{h{fwa{excyb}chfopmkmljijkkikosor|Ǻȱķ¿ϿϾ̽̾¿þ½˶ŨħŦšƣǦзƼʴĥǨȪǮϸʯǧɬϿι̻Ʊ̵ζʰɪǨƿƿźֺԿĽɹʽij|xqpmj~h}h~gegyd}ee{azbprpopljhfhi|exelk}jv{xwɰáŤơǡǦʪʫ˭лʻ­ƫɮǧƧϻϾγǪ˵ľϾ̸ξûʰ˭ʪɫȫǨžѻѱȹɻ˾ɺĪ{{r}iprnnni}jmv}Ʒ³ļĬţäţƣǥƤȩ˫ɩƪ̻¼ȶǭ˸˸ϸϺмθ˵¼çǪũǩǩɧȱ£ξĻɽůǬɯ̴зԸѻѿ̵˺dzŵƵŶı£ĦĤġťťƦʪɬʬɨʰ̼Ͻ̺λüѿɱƬǭȮƱϾιħƧŦƨǪȨɴſǯƣƦȧǧȥȦȦɭйȩɧȧȨˮȮȮɭƭμʻǷɹ̹Ͽʹĥ¤ġãţţƥɩˬ˪ʨȨȪ̶½ſ˷ǬȫɭȫɭȭƪǩȩŨŨƩȧƥ̻ãȦȩɪɩȨȧȦɧʩȨŤƟşơššġũ˹ſϽϾ¼ѽĦå¡ãšǥƥƥɩɫʪ˫˩ɧɧϱĺнɱǫȪɫɭȭǬȭǭȭȭƧ̳ϻƮááȻĦǥǧȩǧƤơơɧʩɨǦǣǣǤǦƦȥŤŦʳ»нѽ˷åŦ¤¤äţǦȧǣǣȥǨɪʩ˪˫ɨɥȧʫ̰˸ĿƾʮɪˬȬɫɫȬȫǬǪȫȫǫŧƩƮĩ¤äŪťǥƥǥťŤƣɧȧǦȦƤƦɨȨǨƥƥǤĥǰþѽнκȬˬɬθ˾¤£äĥƣǥɩȨǦǦǤȦɨɫˬ˪˪˪˪ʩʩȨȫλ̺ɬʩʫʫɫȩɩɪǪǬȫǩƧǦƧũũ¤åĦȵãǦơƠƣƣȥɨǧƥƥǥɫɬƦȨŧŦŤťĥƪö¼¹˶δʫ˭ʬϾå¥ŤŦťƥɧʩʫʩɧǦȦɥʨʫ˫̫˪̫ˬ̬Ю̭ϹϾǰŨǪȫɩʪɫɬɫɫɩɪɪɫɪɫȪƨƩŪȬƪƪååũƥƣǡƤǣǤȦǧȦǤŤɫθ˶ŨƩŦƥƤƥƦǦȬþþλɮ̮ʮϺŽDZʮǫǪȬ˱ħĤģŦƦƥɧʩʩʩȨɨɨɨȪ˫ʫ˭̫̪ά˫˫ʬʬȪȪɫʫʪʨʩɫȪȫʪʩɪɫȬȫɭʮǬưȱ˷ο˻ĩĥɶġȤȤţǤȠȡǤǣơŤţŦɰĩƨŤŦƥǦȨɨɨȪǫ̶Ͼ̻μþ̺Ȱȭɩ˨ʨʩƦʳūũȯĪƪȬŧãĤťƧǤȥɧȧɩɩȩȪʫɪɬɬʫ˪̪̫˫ʫɫɪȩȦǥɧȦɧȧȪȩɩʩʪɫȪǩʪ˭˴ȴʷϿýʹȭƧħǤǥţƥȦǣǡơšƣƤġàƦƧĥŧƦǧȩɩʨɫȫǦǧʱҾþ˻ɰȭˬ˭ʮˬ˪˪ȨŦŦħãäĦĥħģġĤǥɦʧɨǥȦɨȨƦɪɩ˪ɫʫʫɪʪ˫˫ɫɪȬ˪ȦƥƥƧǨǥȧȪʩʩʪʪʮɰɫʬɶϼξξλ˵ʮʮŨŭǣǤǡȡȤǡƠƠĠƣƤģŤŤƧƩŨĥƨȪɪȧȧƥȩɫɮǪŨȰιǴƬɬʬ˱̳ɪʪʫʪɪȩŦǩǪǪŧåŧŨĤţţƥȦʩ˨ǣƣǦȩǨȨȩɪȪȪʫɧɩˬȫȫɬȩɦǥǦƧƨǨǥǦȨɩʨʩ˭̷ϿϿýпϺ̶˰ʯȦưŤȦƤǣȩʰġţšŤŤġšţƠǥʳ˼ȱĪǬȫǪƨũǫȭȭȮȪǩǨǦʫѽ̸ʵɮʳ˸Ļ̻ȩʨɧʩɩȩȪɫǨǩɰŪçƦƣŧŧǧ˭ȧɧȧƤƤŦǪȥȨǨȧǧȧɪ˫ʫɬ˴ɯɩɧȧǨƪƩǩȩȩɨɧʧʧϸϻ˳ƦɶãƥǦäʷĥţƥƥšţţŠơȪ˵Ӿиȭɫɬɫƨǫɫʭʭʬʬ˭̮̬ʪǫɮɱ̸оº̹ʳȭƣƤǨçȰ˹νϾDZƦȧȥɧɨɧɨȬѺϽ̼ŬãŤŤƦʰƩʱʳʭȨƥĤƦɩǧǨƧȥƣƥȩ˫˪ʮɳɬʨʩɩǫȬǪǪɩɪɩɨɨɧʭθàѿĤƣƦ¤κìãŤŤƥţţƣƣŤǦɧ˫еѼʳȭɫǪǮɱɭʫ˫˫˫̭˭̭Ϯ̭άˬȬǫǰ̶϶ʫǦȦŤĥáæåġĤƥơǡȦɪʨɩǩ̬ѶվƩŤĦĦǫκʵ˶ʵŨƪĨħɬȪǥǤȥȨȩɩʨ˫ȮɰɬȪɩɧǧȩɨɧʨȩȪȩɪȨ˰ƿѺϷ˶˶оιƯ¡äƥť¤˺¥ţţţƤŤƧťƤǦʪή̱ʰмϾ̺ɱɴϾɳɪ̫̪̪̬̭δϵϱ̰˰˳ɱμкǪǨǦǦƥţŤĤťťťƣǡǥǩɩɩȧɨƦƥȪϷʰȮɮɫŦ˱ϽĩƭéȬδǧơȤȦʬʰʬʨʱ˻˷ǬɫʨɧɧǧǦɦʩɪȨǦȦɨǬɭȩɪʪȫǩȪεê äϾīģƤƤȩȨɫǨƤȨ̯˯ϴ̱ɯп̾Ͻ̼ȳɰʯɭ˫ɪ̰϶ϵ̰ʮ̳̺кȬɦǥǥǧĦåƧǨǦǥƥƥƥĥŨǪǧƤŤťǥƦƩȮѼѼʭɪ˱ʷ̷ǰη˵ƧƧȧɨ˳̸Ȭ˱ξɱ˵ʭʫ˰ϵ˭ȤʨɪɩǥƤơƣƦȪȫʬά˭̮̮̬ʫ˯ηδŤ̼¸ſкáǦǦɪʩʪȩȧȧɪʮѺϿпɰм˼ü¼̻θɮˬ̭ˬ̭˩Ϸ˺˵˫ȥȦǤŤťţǧǩȨǧŧĦåŤƦŦŦƥĦŦƤȨǩƥũп˷ɱɭɰϻĽѼīĦǨɨɨϸɱʵ¸µȳι̸ȮɯϸʯȩɪɪǦŤťƣšťǩȬʬάʪɩ˪ˬϯϭ˭ɬŤťǤȧʪ̬ʪɩɧɦɨʫˮϸƾɴ˺̿ý̻нη̰ȪȨʫҿüɵʮ̩ȥǥƣŤƣƠƥũƧťĥĦ¤ĤţġĥĦĦƦƤƦƨŦŤĥϹκʮʬɯѿϼ̷˳ȫʬʭɯûλǭɫ̰˴˳ɬɩʫȪʮ̱ȧȦǧƧƧƦƦǥǧȨ˨ɦȥǤɦɧɩȫǪŤʵɫƧ̮̬ʫ˭ʨ˩˫ʪʪе̶оõȶʻϿι˳˱ζѼкϼŽʱɩ˫˩ɦȥƤťģġƤƧŦŦĦĥåĦšţŤĦƧƨƦŦĨŧťǤǥʰѻζȮɯȫȳпзˮαǫлϻǫȭʬɫɪʫ̫˩ɨʨʫʮ˱ɫȨȪǪƩǪǧǦȧɥǦǥǣƣŤǥŤŧŦʱбΰ̮˱̵ɫɪ˯ʮʭз˴̺ºμʺ̶Ȭʯ̶ϸʮȪʬɬʪȤǤƥƤšģãƨŨħĦƦĥťƤƦŦŦƧƧǨǨƦƧǧǧǥŦɵ˹ɱ̮Ȫκʵ϶ʯηϻƫȬɭɭ˭Ʃ϶̮̪ɫʪɨťɫɫȦɧɫɭʮɫɬɫȧɩɪȨƥǧǥƥƧť̸пк˷и˯˰϶̹̱αºýƩʬɪǦɥʧʩʫɧȣǤťĥĤťħũŦŦŦƥƦƥƥǤƥǦǧǦȩɩȧǧɨĥʵλѿɰ˵нмϽпкɯ˭ʬɫʫɬѽӼάέ̬̭ȧǥƥɨɩʨʭʫʬɬʬɬ˫ˬʮʬȩȨȩƧƥģѾѿϻк¸ηѾ̰̱ɳȬʫʩǩɨȧȩǦǦƧťƤƦƦƥƧƦǥƧǧȧǦǦǦǦȨȧǧȧʧȧɨǪɱ¾ϼʰʰйзˮˬ˫ˬ̪ʫθ̰ʪˬʭɫǦǥǦȨɫɫɬȫɬˮ̭ˬ˭˫ˬʬʬɬɭȫǨģü̻ѹʫ̵˺ŧǥɧȨɮɫǦǦǥǤƣƤťƥƦƦƧǪǪƧȨȩɩʩʩȨɪʫʪɨɯýʹ̶йĹйζ˯̭̫ʫ˯̳˱˭ɬƨǪƩƥƦŦȧʫȮɫʭˬ̭̪Ϋ̮ˬΫˬ˭ˬȫɬɬƤ¹нϼʵĥǦʫɫƦƦȧƦƥƥȧǧǦȥƥŪƫƩǩǩȫɪʪʭʮʭʮл̺ɫ̭ѶҺкϷγ̮̳α˭ϹѾ̸īƩȩǦƧŨƧȨɫʪ˫̬̬ΫΪέ̮̬ˬ̮̭ʫɪʫǥϽʳƨɩȨǧȧǧɨȧʩʪȨȧȥǧƩŨǪȪʬʭɬʭ̴Ϻſļȫ˪ɬɫɮɭ˱ʮ˭ȩ̰ȿ̹åƥǨȨǩɪ˫˫̫˫̬έϫΫ̮˭̬ˮ̬̩ʪʪǥҽǪȩʩȦȦɧʧɬȭɩɪʬɬƦǨǨʨ˫ɬȱѿĹĹûļпϽʷθɩťƭϼ̸ƦƤʫ̭˭̫̫˫̬έά̭˭̭̬ʭ̬Ϊʪ˭ȧüоǫ˪̪ʩ˪ʨȬȭʭ˫ˮҼ̴Ȫʩ˩ɪɴǼûʹĬưпǭɮ̳αʭɨ̫ή̯̬̭̬˫̬ˮ̬ˬˮǫ¹ɯȪʩȩǫȱɴǬʪʩɫ˯̰̭ʭѿʻο»пνȶǭ˪˩̬̬Ϋ˫ˬ̪ʫɬŪ̸Ȱʷ»Ҿ̶ҹҺηҾùĽ̻ʰ˱˰ʯ˰ζ̶˴̷кν˸̿˿ξ̿̿̿˿˿ɿɿο˾˿̿̿˾̿̿ʿʿʾʾɿʾʾ˿ɿʿʿɾɿɿɿɿʿ̿̾ο̾˿˿ʾ˿̿˿˿ʿ˿˿ʾʽʿʿʿɿɿʿʿɾɿɿʾʾξο̿˿̿˿̾̿˿̿̿˾˾̿˿˿̿˿ʿʾʿɿʿʿʾʿʿʾʿʾʾʿɿʿʿʿ̿̾̾̾˾̿˿˿˾˿˾˿ʿ˿˿˿̿ʿ̿˿ʿ˿˾˿̿˿ʿɿɿ˿ʿ˿ʿʾʿʿɿʽɿɾʽʽʾʾʿʿʾʽʾ̾̿˿̾̿˿̾˿̿ο˿̿˿˿˾˽˾ʿ˾˿˾˿ɿʿ˿̿˿̿˿ʿ˾˾ɿʿ˿ɿʿ˿ɿʽʿʿʾʾɿ˿˾˾ʿ˿˿ʿɿʿʿɿʿɿɽɾʾɿʼʾʾʾɾʿʽʽʽʿȿ˾˾̾̿̿˿˾˾˿̽ʾ˾ʿʿ˿ʿ˾˾ʿɾ˾˿ʾ˿̿ʽʽ˿ʾ˿ʿ˿̿˿˿˾˾ɿʾʽ˾ʿʿʾ˿˿ɿʿʿʿʿʿʿʾʿɾɾʽɿʿʽʾɿʽ˾ʾɾɾ˼ʾɿʿɽɾɾɽ˾˾˾˿̿˿˿˿˾˽˾˿˾̾˿̾˿ʿ˾˾ʿ˿˿˿˾ʿɿʽʾɿ̿˿ʿʿ˿ʾʿʿ˾ʾʿʿʿʾʿʾʿ˿˾ʾʿʿɿȿʿ˿ʽʽʾ˿˽ʿʿʿɽɾʿɾʿȿɾʿɿʾʿʿɿʾʽɼɽɾ˾ʿɾɾʻ˽ʼʽ˿˿˿ʿʿ˿ʿ̿̽˿˿˿ʿ˿˾ɿʾ˽̿˿̿˽̾ʿʾ˿˾˿˿˿˾˽˽˽ʿʿ˿ʿʽ˾˿˾˽˽ʾʾ˾ʿʿ˿ɿɿʾʾ˿̿̿ʿʿ˾ʾ˿˿ʿʿʽɾʾʾʿʿʾʿʿʿɿɾʾʿɿʿɿʿʾʿɿʿʽʾ˿ʽʾ˾ʾɿʾʾʾʾʿɿȾȾȿɿɽʿʿɽʼɼɼɼ̾˾ʾɾʽʽʼɽ˾˿ʾ˿ʾʾʿ˾˾ʾ˽˾ʽ˼˾˿ʿʿ˿˽̾˿˾˿ʿ˾˾ʽʽ˽ʽʾ˽˽˽˿ʽʽʾɿɿʽ˾˿̿ʾɿʿʿʾ˾˾˽˾ʿʿɿʿʾʽʾ˿ʿ˾ʽʽ˽ʽʿʽʾʽ˼ʽʿʿʿʿʾʽʾʾɽʾʾɽʼʿʾɾɿɾɽʿ˾˾ɾɿɾʾʾʽɽʽʿȿɾʾʿʾʽɾʾʽɽɾȿȿɿɿɿɿɾȿɿɿʾʼʽɾɽʽɿɾʽɼ˼˾˿˽˽˾ʽ˾˾˼˽˾˾ʽʾ˿˿˿˿ʾʿʿ˾ʿ˾ʽʾ˿ʾʿʿʾ˽ʽʽʽ˿˽ʿʿʽʽʽʽɾɿʽʽʽ˿˾ʾʽ˽ʾɿʿʽʿʿʿʽ˾̿˾ʿ˾˾˼˼˽˾ʾ˽ʽ˾˾ʽɼʽʾɾɾ˾˿̽˾ʿ˾ʿ˽ʽʾʾʿɾʾʾʾʿɾɽ˽ɿɾɽɼʾɾɾɾɾɽʽɼʻʻ˾ɿɾɿȿ˿ɾȾɾɾɼʼʽʽʽɿɾʿʾɿɿȿʼɼɿɽɼʽɾȾȾ˾˿ʿ˿ʼʾʽʽʾʾ˽ʾɽʽ˿ʿʾʾʽ˽˾ʿɿɿʿʿʿʿʿʽʾʽʽʾʽʿʿʽ˾ʽʿɿɾɾʿʽɿɾʾʽʽ˼˽ʽʼʽʾʾɿʼʽʿʿʾ˽˽ɿȿɿɿʾʼ˼˽ʿʿɽʽʽʽʿɾʻʽɽʽʾ˿ʽ˼ʾʽʽɾɾɿɿɽʽʽɽʼɼɽɾɾʿʾʼɽɾɼʽʾɿȾɽ˻˻ɽɾɿɾɾɽʽʾʽʼɽɽɽʼ˼ɼɾȾȾȿȿɾɽɽȽɼɽʼʼɼɽɾȾ˿ʿʾʿʽ˾ʾʽʽ˿ɾ˾ɿʽʽʽʽɾɿʿʽʽɾʾʿɿ˽ʽɽ˾ʽ˽ʼʽʾʾʼ˾ʽʼɽȿɽ˾ʾʽʽʽʾʼʾʾʼ˼˽ɿʾɾʼɾʾʾʽʽʿɿʿ˿ɾʼʽɿʽʽ˼˼ɾɿɿɾʽʽʽʽɾʿʾʿʽʽʾ˿˽˼˼ʾʽʾ˼ʽɿɿɿʾʾʾȼʻʼʽ˽ʼʽʾʽɽɾɼʼɽɽɽɽɾʽʽȾɾɼʼʽʽ˻ʼʽɼɽɽʼʼɽɽʿȾȼʼʽɼɼɼɼʻɼɽʽɼɼqmapshack-1.5.1/nsi/QMapShack_Installer32.nsi000644 001750 000144 00000024433 12527654570 022000 0ustar00oeichlerusers000000 000000 ;NSIS Installer Script for https://bitbucket.org/maproom/qmapshack/wiki/Home ;NSIS References/Documentation ;http://nsis.sourceforge.net/Docs/Modern%20UI%202/Readme.html ;http://nsis.sourceforge.net/Docs/Modern%20UI/Readme.html ;http://nsis.sourceforge.net/Docs/Chapter4.html ;http://nsis.sourceforge.net/Many_Icons_Many_shortcuts ;Deployment issues ;Deploying Qt5 for Windows: ; http://qt-project.org/doc/qt-5/windows-deployment.html ;Deploying MSVC runtime libraries ; http://msdn.microsoft.com/en-us/library/dd293574.aspx ==> Central Deployment is preferred: by using a redistributable package enables automatic updating by Microsoft. ; http://msdn.microsoft.com/en-us/library/8kche8ah.aspx ==> Distribute msvcr120.dll and msvcp120.dll ; http://www.microsoft.com/en-us/download/details.aspx?id=40784 ==> Download the vcredist_x64.exe from here !!! ; http://msdn.microsoft.com/en-us/vstudio/dn501987.aspx ==> Legal stuff ;Revision Log ; 14-Nov-2014 32-bit variant Installer forked from 64-bit original ; 03-Aug-2014 First version of QMapShack installer based on the existing QLandkarteGT installer ;=================== BEGIN SCRIPT ==================== ; Include for nice Setup UI, see http://nsis.sourceforge.net/Docs/Modern%20UI%202/Readme.html !include MUI2.nsh ;------------------------------------------------------------------------ ; Modern UI2 definition - ;------------------------------------------------------------------------ ; Description Name "QMapShack32" ;Default installation folder InstallDir "$PROGRAMFiles32\QMapShack32" ;Get installation folder from registry if available InstallDirRegKey HKCU "Software\QMapShack32" "" ;Request application privileges for Windows Vista RequestExecutionLevel admin ; The file to write OutFile "QMapShack_Install32.exe" ;------------------------------------------------------------------------ ; Modern UI definition - ;------------------------------------------------------------------------ ;!define MUI_COMPONENTSPAGE_SMALLDESC ;No value !define MUI_INSTFILESPAGE_COLORS "FFFFFF 000000" ;Two colors !define MUI_ICON "QMapShack.ico" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "MUI_HEADERIMAGE.bmp" !define MUI_WELCOMEFINISHPAGE_BITMAP "MUI_WELCOMEFINISHPAGE.bmp" ; Page welcome description !define MUI_WELCOMEPAGE_TITLE "QMapShack" !define MUI_WELCOMEPAGE_TITLE_3LINES !define MUI_WELCOMEPAGE_TEXT "QMapShack is a consumer grade software to work with data aquired by GPS devices. The data can be displayed on a variety of maps and stored in a database. Additionally new data can be created to plan tours." !define MUI_LICENSEPAGE_CHECKBOX ;------------------------------------------------------------------------ ; Pages definition order - ;------------------------------------------------------------------------ !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "..\LICENSE" !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY Var StartMenuFolder !insertmacro MUI_PAGE_STARTMENU "Application" $StartMenuFolder !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ ;Uninstaller - ;------------------------------------------------------------------------ !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ; Language settings !insertmacro MUI_LANGUAGE "English" !insertmacro MUI_LANGUAGE "German" ;------------------------------------------------------------------------ ; Component add - ;------------------------------------------------------------------------ ;Components description Section "MSVC++ 2013 SP1 Runtime" MSVC SetOutPath $INSTDIR File Files32\vcredist_x86.exe ExecWait '"$INSTDIR\vcredist_x86.exe"' Delete "$INSTDIR\vcredist_x86.exe" SectionEnd LangString DESC_MSVC ${LANG_ENGLISH} "Microsoft Visual C++ 2013 SP1 Runtime Libraries. Typically already installed on your PC. You only need to install them if it doesn't work without ;-)." LangString DESC_MSVC ${LANG_GERMAN} "Microsoft Visual C++ 2013 SP1 Laufzeitbibliotheken. Diese sind meist bereits auf dem Rechner installiert. Versuchen Sie die Installation zunchst einmal ohne dies." Section "QMapShack" QMapShack ;Install for all users SetShellVarContext all ;BEGIN QMapShack Files SetOutPath $INSTDIR File Files32\qmapshack.exe File Files32\qmapshack_*.qm File Files32\*.ico ;File Files32\*.png File Files32\qt_??.qm ;END QMapShack Files ;BEGIN Qt Files SetOutPath $INSTDIR File Files32\Qt5Core.dll File Files32\Qt5Gui.dll File Files32\Qt5Multimedia.dll File Files32\Qt5MultimediaWidgets.dll File Files32\Qt5Network.dll File Files32\Qt5OpenGL.dll File Files32\Qt5Positioning.dll File Files32\Qt5PrintSupport.dll File Files32\Qt5Qml.dll File Files32\Qt5Quick.dll File Files32\Qt5Script.dll File Files32\Qt5Sensors.dll File Files32\Qt5Sql.dll File Files32\Qt5Svg.dll File Files32\Qt5WebChannel.dll File Files32\Qt5WebKit.dll File Files32\Qt5Widgets.dll File Files32\Qt5WebKitWidgets.dll File Files32\Qt5Xml.dll File Files32\icudt53.dll File Files32\icuin53.dll File Files32\icuuc53.dll File Files32\libEGL.dll File Files32\libGLESv2.dll SetOutPath "$INSTDIR\imageformats\" File Files32\imageformats\qgif.dll File Files32\imageformats\qjpeg.dll File Files32\imageformats\qmng.dll File Files32\imageformats\qsvg.dll File Files32\imageformats\qtiff.dll File Files32\imageformats\qico.dll File Files32\imageformats\qtga.dll SetOutPath "$INSTDIR\sqldrivers\" File Files32\sqldrivers\qsqlite.dll SetOutPath "$INSTDIR\platforms\" File Files32\platforms\qwindows.dll ;END Qt Files ;BEGIN GDAL and PROJ.4 Files SetOutPath $INSTDIR File Files32\gdal*.dll File Files32\gdal*.exe File Files32\nearblack.exe File Files32\ogr*.exe File Files32\testepsg.exe SetOutPath "$INSTDIR\data\" File /r Files32\data\*.* ;END GDAL and PROJ.4 Files ;BEGIN PROJ.4 Files SetOutPath $INSTDIR File Files32\proj*.dll File Files32\proj*.exe SetOutPath "$INSTDIR\share\" File /r Files32\share\*.* ;END PROJ.4 Files ;BEGIN additional Files SetOutPath $INSTDIR File Files32\3rdparty.txt ;File Files32\libexif-12.dll ;END additional Files ;the last "SetOutPath" will be the default directory SetOutPath $INSTDIR WriteUninstaller "$INSTDIR\Uninstall.exe" SectionEnd LangString DESC_QMapShack ${LANG_ENGLISH} "View Raster, Garmin and Online Maps combined with elevation data. Work with GIS data. Synchronize your GPS device." LangString DESC_QMapShack ${LANG_GERMAN} "Raster-, Garmin- und Online Karten mit Hheninformation anzeigen. GIS Daten bearbeiten. GPS Gerte synchronisieren" Section "StartMenue" StartMenue ;create batch file for a GDAL shell fileOpen $0 "$INSTDIR\gdal_shell.bat" w fileWrite $0 "@cd /D %USERPROFILE%$\r$\n" fileWrite $0 "@SET PATH=$INSTDIR;%PATH%$\r$\n" fileWrite $0 "@SET GDAL_DATA=$INSTDIR\data$\r$\n" fileWrite $0 "@SET PROJ_LIB=$INSTDIR\share$\r$\n" fileClose $0 !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\QMapShack.lnk" "$INSTDIR\qmapshack.exe" "" "$INSTDIR\QMapShack.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\qmapshack.org.lnk" "https://bitbucket.org/maproom/qmapshack/wiki/Home" "" "$INSTDIR\kfm_home.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Help.lnk" "https://bitbucket.org/maproom/qmapshack/wiki/DocMain" "" "$INSTDIR\Help.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\gdal.org.lnk" "http://www.gdal.org/" "" "$INSTDIR\gdalicon.ico" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GDAL shell.lnk" %COMSPEC% "/k $\"$INSTDIR\gdal_shell.bat$\"" "" "" "" "" "GDAL shell" !insertmacro MUI_STARTMENU_WRITE_END ;Create registry entries WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QMapShack" "DisplayName" "QMapShack (remove only)" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QMapShack" "UninstallString" "$INSTDIR\Uninstall.exe" SectionEnd LangString DESC_StartMenue ${LANG_ENGLISH} "Create Start Menue (deselect if you want install QMapShack as portable app)" LangString DESC_StartMenue ${LANG_GERMAN} "Erzeuge Start Men (weglassen, wenn QMapShack als portable app installiert werden soll)" !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${QMapShack} $(DESC_QMapShack) !insertmacro MUI_DESCRIPTION_TEXT ${StartMenue} $(DESC_StartMenue) !insertmacro MUI_DESCRIPTION_TEXT ${MSVC} $(DESC_MSVC) !insertmacro MUI_FUNCTION_DESCRIPTION_END ;------------------------------------------------------------------------ ;Uninstaller Sections - ;------------------------------------------------------------------------ Section "Uninstall" ;Install for all users SetShellVarContext all Delete "$INSTDIR\Uninstall.exe" SetOutPath $TEMP RMDir /r $INSTDIR !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\QMapShack.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\qmapshack.org.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Help.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\gdal.org.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\GDAL shell.lnk" RMDir "$SMPROGRAMS\$StartMenuFolder" DeleteRegKey /ifempty HKCU "Software\QMapShack" DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QMapShack" SectionEnd Function .onInit # set section 'MSVC' as unselected SectionSetFlags ${MSVC} 0 FunctionEnd qmapshack-1.5.1/nsi/build_routino.bat000644 001750 000144 00000003476 12616741641 020640 0ustar00oeichlerusers000000 000000 rem Batch file to compile routino for Windows using mingw64 rem Please adapt environment variables in section 1) to your system rem Routino source download: see http://www.routino.org/software/ rem Prerequisite: mingw64 toolchain installation rem http://mingw-w64.org/doku.php ==> http://mingw-w64.org/doku.php/download/win-builds rem Download msys as described in http://sourceforge.net/p/mingw-w64/wiki2/MSYS/ rem from http://sourceforge.net/projects/mingw-w64/files/External%20binary%20packages%20%28Win64%20hosted%29/MSYS%20%2832-bit%29/ rem Unzip to C:\msys rem Download from http://win-builds.org/doku.php/download_and_installation_from_windows rem http://win-builds.org/1.5.0/win-builds-1.5.0.exe rem Section 1.) Environment variables rem ROUT_SRC_PATH: location of the routino sources (svn or extracted from archive) set ROUT_SRC_PATH="M:\src\routino" rem ROUT_PKG_PATH: directory where to store deplyment artifacts rem such as compiled binaries, header files, xml configuration files set ROUT_PKG_PATH="M:\src\routino_pkg" rem add mingw64 toolchain to PATH set PATH=C:\msys\bin;%PATH% set PATH=C:\msys\opt\windows_64\bin;%PATH% rem Section 2.) Compile routino cd /d %ROUT_SRC_PATH% make clean make rem Section 2.) Deploy routino del /s/q %ROUT_PKG_PATH% mkdir %ROUT_PKG_PATH% cd /d %ROUT_PKG_PATH% mkdir bin copy %ROUT_SRC_PATH%\src\*.exe bin mkdir doc xcopy %ROUT_SRC_PATH%\doc doc /s /i mkdir lib copy %ROUT_SRC_PATH%\src\routino.dll lib copy %ROUT_SRC_PATH%\src\routino.lib lib mkdir include copy %ROUT_SRC_PATH%\src\routino.h include mkdir xml copy %ROUT_SRC_PATH%\xml\tagging*.xml xml copy %ROUT_SRC_PATH%\xml\routino-profiles.xml xml\profiles.xml copy %ROUT_SRC_PATH%\xml\routino-tagging.xml xml\tagging.xml copy %ROUT_SRC_PATH%\xml\routino-translations.xml xml\translations.xml pause qmapshack-1.5.1/nsi/gdalicon.ico000644 001750 000144 00000010276 12527654570 017547 0ustar00oeichlerusers000000 000000   ( @ qqbqqqqqqqqVq AW :GHq!qɈʊʊq}ʉʉ_wʉʉʕ˻Q\ggg999###FFFYUUU###666VVVɼt000ppp{Ĥ000[[[ooo000000000eeeʜp7``` 111Z+++QQQɽȠ ---ˢ___ɇRRR888IIIuuuʄqovvvZ+++¦utp[[[ʕ777&&&ʁRRRZKɿqtqqdVVVOOOZ+++ 222ʑhhhZZZ,,,{RRRF5L7[qqq+{{{ccca+++ ơʘ888ʙuWRRRF5F5K7iqqqJJJʪććo˧+++ ***ʎġfffCCCu]ORRRF5F5F5wFqqq8>HHRR֭Ν##HHooooذ00߾ǏrrbbSS,,>>88$$͛oooooooooooooooouuoooo!!ooooooooCCСGGoooo׮˖ Ҥ^^==~~``oooo͚@@͚ss oooomm…]]Рܸ//oooo&&==<<DDȐooooǏ22ѣoooo ŋ ܹooooڴXX ܹΜooooС޼66))WWoooo##ƍĈӧ44ĉooooӦyyݻoooossĉÇر}}^^IIooooĉXXժ޼11mmVVLLŋԨoooo ҥrr „uu]]oooo͛GGܹŊLL>>oooozzFFii99ooooҤܹϟŋΝoooo~~KK߾jjpp  <# Q;hIfa7? 5(:1@10@(9.3A2!3/@-*:(:($':jbg''EJDHM2`.-/;"9%0+:1*3%9/)3 6&1*92&/#2'[,hd]x%$+gBC4./$01D 1*4-B!.0=%9)0@!2%.(=**5#1 yNeef]@44-B-5'29A!0,50;,0=&3#.2:!/(2.:*19 fffa_u'' %(XNN+:!,6B#0*44?02?(5(5@.".-:!.9```_[i((:>3E)7$50?#1(8)<3(8%2#3)8-$5.<'X*\\\\]W vKJL[.=.=.A,:-=.?)<-?+:%3'9'4%5#5%0 w R`````_^P~~yzzzy u u t u to6d(  34  .h!34 :%&?kP[%Iz1.- @Ma}4e{Wc#,\%J s pm|OՉXiFlyvqOІ)OpJ} qp{Fˊ@S̲˩*l ;}ppI [PʥVf ;g`vw\UVXdc P)Vpnzd&D,[!Y^inN =uE_bg:S!$)#'#')/-6-:05 G s .eSVF&/&9%7%8#6#4"3$2CW }#=D.A'4'7&6%4%4$4&6&!Z]F E3j,1*6*6(6(3'3'4'1 U WTb7D/?,;,>)<)8'8):EWVU)-&$""-(0` % %! :Wq|saN:' +IAgcG/x_I4"  7[4].VwW{$@cB*qZE1 (FwCs{+SKd+wU<& lU@, 6U/W=~uT btAj=MlN6gP<+ .r:h&a^- Yi>\j-4 }hG1X T8c'ZdDM9+ Xi>Ys$0yeP 8g'L *QB/ Sc6}Pz{@eJr!-RE2M^-w }A eEl ,UG3r`S1La(clj ~"C eDk )VJ7to5I^$A{|ʼn+ais` ~"C eCk%-WO;#CVJy*X؈2theVVgV ~"C dBk%-YR>- BVOsGoֹ޹HRM ~"C cAj$+\UA0 AWPteFilWDC }"E c?j'-_\G3;QRqwE}GcB~S:9%F b?i'.O7 c;NSl!)~vAd'$Kv01%F a>h|~0)p DvB_uNa7[m?aP3 Bo+1%F a=hvz^MbvnjfvyotxrXwvd>\cR%@k*1%F aB>DFL?F=I4K5<4544./dbg { '<`XZ\]`bcefmY"DF)4@ ?I'2= 7B$0; 5?#0:0:&2:$AG%2=E&0SV%%P3d9q99 TSUWX[\^`cd'@954AJS$2>5@HP(-:7BJQ(*74?FM%$36ADK'.1<@H$)00YQce.. SOPRSVWY[b4f+('7-!3+"5&$5'&8)#4%%6&'7&/=&(7$(8 ,6 79i'vi_((!!BRKMNQRTYF $>E)4C'=K'5D &;I).= 9G '-= !;H),< !7E+2> 5<?'bh66 'MIGILMPP1D@AJKU %1?AIJR '*;BIGP)+AF3"$ 3@'.<2<$*1k*}e_00 4?<=:MI AJMWLW9BISKU 5?GRHR4=GPHR#-7AKDO#&1DK:A }B(a0h22 )9;U>;*9(6#-)1)6!0%/#+#0,%4 ."0*(1$*6(*3()5$.6 ) K6 ]d55I*+1RW(3>;F&4>AJ'3A$?I$-8 ?G&,8 ;D#,7"%YV\%%ybdFRT[>HBMQX5?AJKU 2@=GKR 3=)aA-bA0xS;bOzkiZqgrɾxͿ˵azU|W{XuTsPtRvPuOvT}]yXwPuPc@kDrNvRnImIf@yan|dzfmUkLyV^~ɻheDqQmLpRway_¾;Ŀı̿hMa>w]eEfH{hvjN}gr\{sźxtcHiRkUjUV7]?_B^Ab@kWmE(_>]>V9{S7oG-Z>[=Y8V4wK-vO3rP6_B.\@/_D4^@1]>+gH4cD4fK;_>(gJ9}`Trbq^~{jxƺóȾ}XxOvR~\ySxSzVyUySxTwRxRxSwRwQpKvQvQxVjEoHvRvRtOrNkFhkLrM~\~j}ZsNeɹzV{VxU[{nQiHoMiJ~gɶpbqr{W~ZjurkQjKeHoT{g{s[yfeIkRrϾjPpYn[_I^EiSU-vQ5wP8\<^?\<_@\:Z:]=U6}T9pK1\=+X=([@-]B3cD3y^QrkbPvf|lvdȾxtM{T{V{VzU{U{TyUyTxTyRySyRyTySvRtPxQuQsQtPwRySvPxQtQvRrOnGmIuQnHȻƸuyR~WzU}Y{U|W|W{V|WvShJqPmKiFxYlȹijǹȹƶƷwzR}X{VzT}W{WxSvQ~^gw\}`pVc@cDź˻y\lLpQnlxboRpToXbKQ+jG-aC_=[ZiG7z}q}i~fl}Y|UyTxQ|X{XzW{X¿ǺȻ®ò͸vN|W{VzVzUzTyTyTzQxRyQwQwQxRyQySxTySzRzRvQpLvQyTyTzRyTySxUxUyTyT{U{V{U{X{T`c{T{V|V|V{V{X|V|TzW{X{VzUyY{X{XxV|W|X|Y~Y}X^|X{Y|X{W|V{UzW{XzVyV|WzV{WvTfƥȷɹ˻ÿftzd}^nƯ}xmfgmvo\oF(tL1{V;[<T5cHcFW>oL4d@&uO4gLgK}V:_8$ʷxc{`zVyUxQzQzSzUzSyUzU{XzVsfý}V{S|U{VzW{TyTyTyRxOwPwOwQxRxRyQxTxRxQxSwSuOjDsNxRwRxRySySxSxSxRyTyTzT{S{T|U{V|W}X|W|U{U|U|V{V{UzUzVyTzTzVzTyVzW{W{X{V|V{WztzV}X|Y|Y|W|V{VzV{WyWxV{X|X}X|Z|YwTʹȲɶɺŴycc~``gfrjhlooàf|Q2|V;iKZ<{R5_BaF\?oL4sP8^B[C`HeLwer~YvR|XzRzUyUzVySyRxRySzU{SzUzV{UxPôȽ~Z~XdehxRsJzS{UzU{W{WyUyUyRwQvQuQvPwPxRxSxRwQwSxSxRwSwRvRwQvRvQwRxRwQyRwUwTxRyUzVzTzS{U{VzT{U{V|V|T{VzUzXzVyTyVyUzT|W{W{V{T{W{W{X{XzWkvyT|X|X|W|V{W{VzVzVzUyX{X}Z}Z}[^~^tįȺrecees|prymnmjkihvN0~Y>aFtO5zS:~Y=`BcFYA[@lG-sQ8\BuT`uOyQ{TzU{UyTyTzRxRwRwTyUzU{VzWzT{W{ViȾö{TzSxQvRvOvPyRzTzUzV{TzTyTzTxSuRtPvQwQxSxSwRwSuSxSxUySyRwSwTwSxSxUyRyTySySyUyTwRxRzTzUzUyUyVzUzVzX{W{V{UyUyVzUySzU{WzXyWyVzV{V|V|X{XyXyX\t`{W|W{X|Y{Y{W{WzWyUxUxU}W}X}Y~\^~^jvedokhn{y›mlkjklg{U6`BhPcH]@iL^AdGW9tO5rO7eIvQxRvQzW{U{TzU{VzRxSxQwRxRxTzSzSzWzV{U{X|ZzW¸vN{SrOpPwSuQuNxPzVzVyTxSyRyPuOhChEkGnJrMsNvRyWxUwPuQsOmHrMwUtQlHnLrMtQvQzVzWyVxUwRzSzTxTzSzSzV{T{W{W{V{XyWzV{VyUzW|X{WzWwTyTvUuW{Z|YzWzW{WzW|W{W|W|Y{Y{Z|W{UzVyUyUxRzS|V}[_]_cıu~kptmŽɶɱƿƭojmlhefˍ^@vcwlTfQaCeEcCgQfLrV|WxRyV{UzUzUzTzSyTySwQwRxRyQyUzVzVzV{UzVzWzVzTcctOjHgCgCeBjJtPwQ{TzSzRySxPqKeCb@eCa?`=c>dCgFoKrMkJkIdBdAjFqQjGdBc@c?dCb@hDmLqOyVwSzU{VzV{V|T{UyVzVzWzVxWzX{X|YzVzW|YyVqQtTnJiJlMwW|[yWzWy[}\|Z|Z}[|Z|Y|X|W|X}XzWzXzUzW|X}Z_^`axrppqygj}ȯjTtI-~|rXo\vdiKhEa[wRwPxSyV{W{UvTwSzQyRwQwRxRxQxRyTyVzV{T{TzUzW{T{VzQuPyRmGcAdBeBfBfCiGwSzUzRzQsMfCc@cBlJgDdCeCiHpNlJlKjJiGvRsPwSyTqMiEhEe@bAb@b>c?c?eBiHsOsRlGjJpPwTzWyUyVyVxUwUzW{XzXxTxTzWrRcGhHeDdHeHkLpRuRuSoPvU}[|[}]{Y|\{Z{WzW|\|[|Y|Z}[|[}Z~^^afkp}ȱĭɸ»ƿҿμģǪkT_<"^JhNcDsKySvNvOxRyUxUzUzUzTySwRvNwPwQxSxUvSxQyTyTyTzUzTyUzU{SzSpLuRySqLqPpNjIiHfDoMzRySyUqLgD`?`>lIkIjIhHhFjGnLnLmLpNwSxUzVxUwSmIdCc@b?b@b>dAgFfEkIgDeCdC^<]hKuQvRsMsOvSxTyTyWzWzVzVyUySxVuPtMyTzV{XxUwTsQwQwVxSyRxUyUyUyTwPzTxUzWtSrPtRoMmKuRvOuQ{VyU{UyUvT}Y|XwSwU}YzVwUzX|UzVyW|XzVvTwStOqMtPtQqOoMrPtRsRtSvUuVuSpNjI_=aAa?bCpTpPtXmLeFbBdEeHdBb>cAgFdBhGiKfHaAgGfHbDcCeEdDeEnMsPvVvWpPsVqUoQuV|\sTsStXvYyYtYt[sZqU¥кɨȣʡɡʦɤɤɥͫʤбӼӻӻ˞{dwVwUyUvQvRzW{X{X{XzWzVvRrNnKpNeCcBkJqQsRqPmLhFgHvVuSvQxTzTyUlKhGjHgGfDeFeFkIuSoLjHeCeDsRrPqLsOpMtRqNgHjIpLoMkIhFpNkKiHiIiKlMkJhIeFfEgFlKpNsSyWuSyXtStSpNoKyY}cjIc=lO|uQrOgDeDlIfDdCfDfEfGaBcDdFgIcDbAdAhGiJ{`wYoOsSsVlLiLjInNuYvYtUrRsUrSnPtYpVmUw`ĿùŻ÷¶̬ɡȠɤȣȟǟɤʤж˧ήӷջ˿˯gwQxWwRzV}\rQtRsQnNyVwUkJfEdCcCdEeFeDfEkJxXuRkIjHkMjHkKyTxQjHdFhHfCcCdDfHhJpNlJb@dEdBeBhIkIiFeBdBbAa@dCdCeCeDaAa@hFeBc>c>cEhMmOlPhJkMiMfHfFqOyWyV|Z{YwTqPsSxV~c~z_rvSrOj~dgEoMvTtQmMjJjJkIgJmLrQw[}fu]}anQkLfGjIqRoRlMkNoTmRrTmQkKlMsUtXnTjQn¹ҽ¶ҹѮά˫̫̬ϲέɥɥɥͫΰʨ̬˨ˣʢɠtˠoJzUsOmLoOqSfGjHtTkKlHmJhGjIfFeDgEhGhEc@cClKiEiIiHjKlOnLvNrOiInJlIkHkIqNpOoKnMe@c@gJeFdDdCpNlJrOjIgHfGeCfCeEgEc@dCgEdBc>gEiKiNoRmTkOjOnQlMkNmSlMnLmJmLuUxUrOxY~`|_ay[{`zZ|X^xYkHqUnOkImNzZvVwVyXwSxPqOmMnPw\hm~buXoRlPkOlNvYuXyXuWrTtVsViGiJlOjLmQlTùöѸ˨ʥ˭ΰͭʥ̥̥ɤŞȣΨʤɥxˢtQrPaAaBeFlKiHcDpQxYjFeBhGfDeDdBd?gEeDdAcBfDoNoNkLhKqRlIqMtRvSuRvSvUvTlMlMpMlFc?fEnPhIdCeGpNsMpNhFgHgHdCfBfDhEdBdBeBdCeBiKgIiMmSmRkMmNmPlOlOmTkLeCdBdBeDiJiHiKkPkQwZ}`rvloT|bdvYz^{^zXrOqPuUlLxY{\yWuUpQfIotmlvVkKnRjLfHgIoPsTsTnPqSlOgFpPsWpThMżѷӳ־Ǽ׾ҳ˨γӻѷ̬̥̥ɣĝʣͦʢʢˤq˛mJiIcBdCgDlHhGeFeDuSgC_e?hEiLiIgDjIkHxQqOiEfEeDeAdCdDeCdAcAfDfDeEgGeFjOnQpVmQnPnRlRlQiNgFeAeAdCdAbCb@gHmTlkhipÝ|txYeh~`~[xpqQiq~awYvXnOlֽ̮ţͲqr~f|akpSiHmMqRmPrSmNgKqTjO~fּǿҶϯѯЯѸҵϱϱϬΦˤǞʣΧʢͧѨУhː`jJuUpkhehgktÙsqooeš}ͱӳڿؽŭ;DzƮì۾Ʊ˵īru˷ӽʤIJտǽϯʣȠşơǡʥΧͣˣˡkˎ_6hFjIdBgEgFuUyXhIeEpMlIfEvVvTiIeDgFeDgFkJhJgFgGlKoPjGiGfChHjLhIjHiGhGnLfFdCfAgAkGjHiFkHlJhBgCtPxUtPqNmKpOmJpN}[{YuSjIgGhIhGiKgJkOoUoSpTmPeCfEnLxXx[vXf_hnlhfekikoosj{δͽȳϪѬϬͬиȢͥѭټɽӷͪ˨ʪȢɡƠǠɢΧˤ̣ˤ̡nˏ]7eDlMiGmLdCqP~^sSfBqKuPrRyXuRvTkKfFfEjHjKhGhFjIxUwVpHlHcCfDhHlKqOyYtRiHfEcAhDrLpLkGhGoMlIkGkJnKxTrNkHuP|X~ZuUrTjOiKeCeDfFgDhGgFhGmRnToTlOiHhIyZjijmqnmkkeillmnthƦίʠʥʨͪӶЬͭʨͩΨͩϩΪ̦ͧոӽʣʢȡɣɢ̢ˤΩ̧ΣlˌZ5`AmPjKjJkKjHoQxWeBfBmKwSzV|Z~\{XiIfDiGgGgIgHjGnJpOuPuMjInMpMmMnMkJhHeBgDeCiHlJmIkHiGpMmKmInMwVmKiGjG}XyWnRmTmQjNpTfdtTnOiIfEc@hIjRgKiKgGcDvTfejjjmnpkgikmnpnĤ³Ծ˦̥ӶѺѵӲ׾Ҷɣʦ˦ϯίҷеΫѵѴЭͨѲֻɢʣȡȡɡ̣Ψά̢̣gˆV/`@jLiGhJhLbCcCqOhDa?cCuS|Z|[~]{XnNiJhFhGiJhHhAd>eDiHsOuPxTsRqPgDcAeDfGfDgDoPyXwVoLnJiGfFjKajd{ZsQnNgHgKiMjNjNgơşyrrj|\kLjNsQwU][`jkhjoommgglooml̰ķ׽ջտбϪˤʧʦʤͩźջѲӳϪͩΫΨ̧ʣ˦Ǣɣʤ̢Φɠ˟ˤfˇV3bBiJnNdFeEeKfGeBcAgEiHmLmOuVxYqPpOlKhHjJkJiIiFgCkGoLqNoMsOyWvRfBiGrQnLkIkIvXz]uTsQiJfGmNeljhie~`~`~]{_it{wssu|~tovoideijikorpggln˜spmͲǼЮͧͪΫջŷ׾վӹбҰӮЫɢ̦ɥ̧̤̩̬͢˥ΨeˈV4dClLiKeFdDgLeHgFcAeEnMiFjJlMmMvUwTiFhGiIiHjHiGfErOrNlIjGnJpLpLjGkIrQsQyVnLnQkMhHnLkJqQdnmnhgjotuz{zuuuqszux{ÚytnfbgjjkkkppgjonnuҺȺʻ̽ŶƷǸ°ϬέΩΨͨöյ~cˈV4cAfGdEdCdBfIfKeFbAdBfAjGjJkLjIoNsRnNgEiHoNkJdDeCiEfDhFhFnMsRpNjJpLrNpPsRpO{]uUqRoMtSkkkmojjjvzruÙyuvtšztrvwtwvsneegkjjikmjflktгʺ;Ŵijŵֻұؼ׾fˋ\;hGeJfJgHhFiLiNgJdDjJqMqMsQsUqQmMqPuVkKlItQhGfFkImLgDiFwT{VrPqPpOrOtQ{Z\esljbfpnopqqonvuxÝ~ywysstØtuytvttnhijmpmlmpnonzð̼ʹêڱ{~}~z}¢~vx~}}ɬαίϯѯбΰͱβϰβαвϰѳҶѶϳҶչӵҵѵҴѴҵҴҵггвѱѰϱͱͱϰбѲѳҲб̷ּݿ¥¦¥¤åεҷӶӵӵԷպӹԹպӸҸԸӸԸվӺԻպպּ׼ӸӹԸԸռԺջԷԶշԷӷԶշոԹԸԸɸǰܺťƩβѴѵҴѵҶչҶҷռԹҷԷֺʹƳӷ׽ԺպֹպҷӸչӹպչֺӷԷԷӶԸԸԷӷҵؾƮݵĦħŧɫϳѳгѳдѴҴҶҷҷӷӷҵʺƳҶӸԺӸҵҶҷԹԶԸԸԷԵӵӷԸҶӶŲŬݴ¥¦Ȯ˰гѴѳҳвгҵѵӷԷԹҷҶ­ԷͼξųƴȷؾӸԷ׼®ֻıһݳìӸԸѵϴгϴвбѳѵҵгƳƵҴԺ˱ɸ§ɯոӷегϳϲϰвѴҴвԼ˰ĬæγѳдҶдгѴβϰѲҴҵαȷջĪʯ¥̰ħдӶҳҵѱҳѵѴбЯвѴѲѵˤɡAyzHɰп±ͳոвʭвѴѳҴҵӵԷոշбааϲвʸ(rtR8st@ȸԿǶεȭƬμѴӷϲѱҴѴֹ׺ػնϱбҵˤƙ8tvMЯΩ׽ϫΪ‘ŘͨѰ9suFȝֻˤӐʡԸͨѴ׿ͪϮˤϭаּ̨Ȟřպа̨ջջΪʣսǵʳϻκӷϱбϰӶշӵвո̺Ż||rtwy~~Bּxym̧{|ɾ˥xyqt!Iz{7uv79psxy Ò^xzsutv|}ˣNuw0¸zyz ҵZ{|uwwxLvwhʾyzsuqt vx]ͫyz+ֽ˹Ų®ʸѾؽٿDzʵ̹xxyyzU>vx$ջvxkɠԷ˥z{ɽCuvƼ_~~ؿ4{||}1lxy׼*|}7SD vx&Ktv.·yxy ּЮ}~{|H'~~yzdř}}xz#qg6սsvhtw&ռֺ{{%v~~wyfԷvxhnsu5ˤz{ɾ5vx4Vvw׾6wx~~z{ɽȼЮqyzͨJtv-·xxyֽȡ}}}} wxcJwyҴvxM2rt"պʽ||z{·wxz(չvxk|}}}̦ϭz{ɽuuwxy9{|׽7sv=ұ xymչƚ2~~̨Jtv.vxy Ҵ$ru .4 wxc2uw3}~{|#Ժkyz}~Ĕ9xyؿֺuwuřxyxzTЯz{ʽĔK++||ּ6uw.ͧyz¶x˾6||yzuw3Jvxtwx ѱּk9'(xyd:wx |} z{"ҵky{}}Ò8xyպuwrHuwɼ yzʽҴǚCuw,6}}vչF||~ɠsxywywxSԹIz{{|UϬ4z{ջǛЯƙwxƹ~rvx\Ӵάķyջuw]Tpsά׿˾||z{oؿyz'Ե {|B^uwExzɽ˦uw}~ uwz{Ȼ3oq wyvxuwT)yz"ǹ}ʡ׾ٿIvxsuxzsvPTnr yztv&ֽ@wytv}~yz|} Ըtv_Bpt ֽո{|ұk~xybҲ ~ xzp̥z{ʼֺpI69RŖĸyO˽̦H